feat(terminal): cli and bindable action to clear all buffers for a specific pane (#2239)
* fix typo * Add clear screen action * add proper test * added bindable action; remove pointless default impl * add default binding * use imsnif's variant * remove commented example config * remove log line --------- Co-authored-by: Aram Drevekenin <aram@poor.dev>
This commit is contained in:
parent
638f2ad4ff
commit
bdb39b19a6
14 changed files with 83 additions and 2 deletions
|
|
@ -153,6 +153,7 @@ ACTIONS
|
||||||
next ID.
|
next ID.
|
||||||
* __MoveFocus: <Direction\>__ - moves focus in the specified direction (Left,
|
* __MoveFocus: <Direction\>__ - moves focus in the specified direction (Left,
|
||||||
Right, Up, Down).
|
Right, Up, Down).
|
||||||
|
* __Clear__ - clears current screen.
|
||||||
* __DumpScreen: <File\>__ - dumps the screen in the specified file.
|
* __DumpScreen: <File\>__ - dumps the screen in the specified file.
|
||||||
* __EditScrollback__ - replaces the current pane with the scrollback buffer.
|
* __EditScrollback__ - replaces the current pane with the scrollback buffer.
|
||||||
* __ScrollUp__ - scrolls up 1 line in the focused pane.
|
* __ScrollUp__ - scrolls up 1 line in the focused pane.
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ pub(crate) fn assert_session_ne(name: &str) {
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
if name.contains('/') {
|
if name.contains('/') {
|
||||||
eprintln!("Session name cannot contains '/'.");
|
eprintln!("Session name cannot contain '/'.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ impl InputHandler {
|
||||||
.send_to_server(ClientToServerMsg::Action(action, None));
|
.send_to_server(ClientToServerMsg::Action(action, None));
|
||||||
},
|
},
|
||||||
Action::CloseFocus
|
Action::CloseFocus
|
||||||
|
| Action::ClearScreen
|
||||||
| Action::NewPane(..)
|
| Action::NewPane(..)
|
||||||
| Action::Run(_)
|
| Action::Run(_)
|
||||||
| Action::ToggleFloatingPanes
|
| Action::ToggleFloatingPanes
|
||||||
|
|
|
||||||
|
|
@ -1109,7 +1109,16 @@ impl Grid {
|
||||||
Some((self.cursor.x, self.cursor.y))
|
Some((self.cursor.x, self.cursor.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Clears all buffers with text for a current screen
|
||||||
|
pub fn clear_screen(&mut self) {
|
||||||
|
if self.alternate_screen_state.is_some() {
|
||||||
|
log::warn!("Tried to clear pane with alternate_screen_state");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.reset_terminal_state();
|
||||||
|
self.mark_for_rerender();
|
||||||
|
}
|
||||||
|
/// Dumps all lines above terminal vieport and the viewport itself to a string
|
||||||
pub fn dump_screen(&mut self, full: bool) -> String {
|
pub fn dump_screen(&mut self, full: bool) -> String {
|
||||||
let viewport: String = dump_screen!(self.viewport);
|
let viewport: String = dump_screen!(self.viewport);
|
||||||
if !full {
|
if !full {
|
||||||
|
|
|
||||||
|
|
@ -433,6 +433,9 @@ impl Pane for PluginPane {
|
||||||
)]))
|
)]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
fn clear_screen(&mut self) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
fn clear_scroll(&mut self) {
|
fn clear_scroll(&mut self) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,9 @@ impl Pane for TerminalPane {
|
||||||
fn dump_screen(&mut self, _client_id: ClientId, full: bool) -> String {
|
fn dump_screen(&mut self, _client_id: ClientId, full: bool) -> String {
|
||||||
self.grid.dump_screen(full)
|
self.grid.dump_screen(full)
|
||||||
}
|
}
|
||||||
|
fn clear_screen(&mut self) {
|
||||||
|
self.grid.clear_screen()
|
||||||
|
}
|
||||||
fn scroll_up(&mut self, count: usize, _client_id: ClientId) {
|
fn scroll_up(&mut self, count: usize, _client_id: ClientId) {
|
||||||
self.grid.move_viewport_up(count);
|
self.grid.move_viewport_up(count);
|
||||||
self.set_should_render(true);
|
self.set_should_render(true);
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,12 @@ pub(crate) fn route_action(
|
||||||
.send_to_screen(ScreenInstruction::MovePaneBackwards(client_id))
|
.send_to_screen(ScreenInstruction::MovePaneBackwards(client_id))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
|
Action::ClearScreen => {
|
||||||
|
session
|
||||||
|
.senders
|
||||||
|
.send_to_screen(ScreenInstruction::ClearScreen(client_id))
|
||||||
|
.with_context(err_context)?;
|
||||||
|
},
|
||||||
Action::DumpScreen(val, full) => {
|
Action::DumpScreen(val, full) => {
|
||||||
session
|
session
|
||||||
.senders
|
.senders
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ pub enum ScreenInstruction {
|
||||||
MovePaneRight(ClientId),
|
MovePaneRight(ClientId),
|
||||||
MovePaneLeft(ClientId),
|
MovePaneLeft(ClientId),
|
||||||
Exit,
|
Exit,
|
||||||
|
ClearScreen(ClientId),
|
||||||
DumpScreen(String, ClientId, bool),
|
DumpScreen(String, ClientId, bool),
|
||||||
EditScrollback(ClientId),
|
EditScrollback(ClientId),
|
||||||
ScrollUp(ClientId),
|
ScrollUp(ClientId),
|
||||||
|
|
@ -327,6 +328,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::MovePaneRight(..) => ScreenContext::MovePaneRight,
|
ScreenInstruction::MovePaneRight(..) => ScreenContext::MovePaneRight,
|
||||||
ScreenInstruction::MovePaneLeft(..) => ScreenContext::MovePaneLeft,
|
ScreenInstruction::MovePaneLeft(..) => ScreenContext::MovePaneLeft,
|
||||||
ScreenInstruction::Exit => ScreenContext::Exit,
|
ScreenInstruction::Exit => ScreenContext::Exit,
|
||||||
|
ScreenInstruction::ClearScreen(..) => ScreenContext::ClearScreen,
|
||||||
ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen,
|
ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen,
|
||||||
ScreenInstruction::EditScrollback(..) => ScreenContext::EditScrollback,
|
ScreenInstruction::EditScrollback(..) => ScreenContext::EditScrollback,
|
||||||
ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp,
|
ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp,
|
||||||
|
|
@ -1764,6 +1766,18 @@ pub(crate) fn screen_thread_main(
|
||||||
screen.render()?;
|
screen.render()?;
|
||||||
screen.unblock_input()?;
|
screen.unblock_input()?;
|
||||||
},
|
},
|
||||||
|
ScreenInstruction::ClearScreen(client_id) => {
|
||||||
|
active_tab_and_connected_client_id!(
|
||||||
|
screen,
|
||||||
|
client_id,
|
||||||
|
|tab: &mut Tab, client_id: ClientId| tab.clear_active_terminal_screen(
|
||||||
|
client_id,
|
||||||
|
),
|
||||||
|
?
|
||||||
|
);
|
||||||
|
screen.render()?;
|
||||||
|
screen.unblock_input()?;
|
||||||
|
},
|
||||||
ScreenInstruction::DumpScreen(file, client_id, full) => {
|
ScreenInstruction::DumpScreen(file, client_id, full) => {
|
||||||
active_tab_and_connected_client_id!(
|
active_tab_and_connected_client_id!(
|
||||||
screen,
|
screen,
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ pub trait Pane {
|
||||||
fn push_right(&mut self, count: usize);
|
fn push_right(&mut self, count: usize);
|
||||||
fn pull_left(&mut self, count: usize);
|
fn pull_left(&mut self, count: usize);
|
||||||
fn pull_up(&mut self, count: usize);
|
fn pull_up(&mut self, count: usize);
|
||||||
|
fn clear_screen(&mut self);
|
||||||
fn dump_screen(&mut self, _client_id: ClientId, _full: bool) -> String {
|
fn dump_screen(&mut self, _client_id: ClientId, _full: bool) -> String {
|
||||||
"".to_owned()
|
"".to_owned()
|
||||||
}
|
}
|
||||||
|
|
@ -2419,6 +2420,12 @@ impl Tab {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
pub fn clear_active_terminal_screen(&mut self, client_id: ClientId) -> Result<()> {
|
||||||
|
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
|
||||||
|
active_pane.clear_screen();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn dump_active_terminal_screen(
|
pub fn dump_active_terminal_screen(
|
||||||
&mut self,
|
&mut self,
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -708,6 +708,35 @@ fn dump_screen() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clear_screen() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let mut tab = create_new_tab(size, ModeInfo::default());
|
||||||
|
let map = Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
tab.os_api = Box::new(FakeInputOutput {
|
||||||
|
file_dumps: map.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
|
tab.new_pane(new_pane_id, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(2, Vec::from("scratch".as_bytes()))
|
||||||
|
.unwrap();
|
||||||
|
let file = "/tmp/log-clear-screen.sh";
|
||||||
|
tab.clear_active_terminal_screen(client_id).unwrap();
|
||||||
|
tab.dump_active_terminal_screen(Some(file.to_string()), client_id, false)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
map.lock().unwrap().get(file).unwrap(),
|
||||||
|
"",
|
||||||
|
"screen was cleared properly"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_floating_pane() {
|
fn new_floating_pane() {
|
||||||
let size = Size {
|
let size = Size {
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,8 @@ pub enum CliAction {
|
||||||
},
|
},
|
||||||
/// Rotate the location of the previous pane backwards
|
/// Rotate the location of the previous pane backwards
|
||||||
MovePaneBackwards,
|
MovePaneBackwards,
|
||||||
|
/// Clear all buffers for a focused pane
|
||||||
|
Clear,
|
||||||
/// Dump the focused pane to a file
|
/// Dump the focused pane to a file
|
||||||
DumpScreen {
|
DumpScreen {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ pub enum ScreenContext {
|
||||||
MovePaneRight,
|
MovePaneRight,
|
||||||
MovePaneLeft,
|
MovePaneLeft,
|
||||||
Exit,
|
Exit,
|
||||||
|
ClearScreen,
|
||||||
DumpScreen,
|
DumpScreen,
|
||||||
EditScrollback,
|
EditScrollback,
|
||||||
ScrollUp,
|
ScrollUp,
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,8 @@ pub enum Action {
|
||||||
MoveFocusOrTab(Direction),
|
MoveFocusOrTab(Direction),
|
||||||
MovePane(Option<Direction>),
|
MovePane(Option<Direction>),
|
||||||
MovePaneBackwards,
|
MovePaneBackwards,
|
||||||
|
/// Clear all buffers of a current screen
|
||||||
|
ClearScreen,
|
||||||
/// Dumps the screen to a file
|
/// Dumps the screen to a file
|
||||||
DumpScreen(String, bool),
|
DumpScreen(String, bool),
|
||||||
/// Scroll up in focus pane.
|
/// Scroll up in focus pane.
|
||||||
|
|
@ -255,6 +257,7 @@ impl Action {
|
||||||
CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]),
|
CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]),
|
||||||
CliAction::MovePane { direction } => Ok(vec![Action::MovePane(direction)]),
|
CliAction::MovePane { direction } => Ok(vec![Action::MovePane(direction)]),
|
||||||
CliAction::MovePaneBackwards => Ok(vec![Action::MovePaneBackwards]),
|
CliAction::MovePaneBackwards => Ok(vec![Action::MovePaneBackwards]),
|
||||||
|
CliAction::Clear => Ok(vec![Action::ClearScreen]),
|
||||||
CliAction::DumpScreen { path, full } => Ok(vec![Action::DumpScreen(
|
CliAction::DumpScreen { path, full } => Ok(vec![Action::DumpScreen(
|
||||||
path.as_os_str().to_string_lossy().into(),
|
path.as_os_str().to_string_lossy().into(),
|
||||||
full,
|
full,
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ macro_rules! parse_kdl_action_arguments {
|
||||||
"ToggleMouseMode" => Ok(Action::ToggleMouseMode),
|
"ToggleMouseMode" => Ok(Action::ToggleMouseMode),
|
||||||
"PreviousSwapLayout" => Ok(Action::PreviousSwapLayout),
|
"PreviousSwapLayout" => Ok(Action::PreviousSwapLayout),
|
||||||
"NextSwapLayout" => Ok(Action::NextSwapLayout),
|
"NextSwapLayout" => Ok(Action::NextSwapLayout),
|
||||||
|
"Clear" => Ok(Action::ClearScreen),
|
||||||
_ => Err(ConfigError::new_kdl_error(
|
_ => Err(ConfigError::new_kdl_error(
|
||||||
format!("Unsupported action: {:?}", $action_name),
|
format!("Unsupported action: {:?}", $action_name),
|
||||||
$action_node.span().offset(),
|
$action_node.span().offset(),
|
||||||
|
|
@ -689,6 +690,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
|
||||||
},
|
},
|
||||||
"Detach" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
"Detach" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
||||||
"Copy" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
"Copy" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
||||||
|
"Clear" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
||||||
"Confirm" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
"Confirm" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
||||||
"Deny" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
"Deny" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
|
||||||
"Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),
|
"Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue