Repository: zellij-org/zellij (language: rust)

Issue:
Feature req: clear scroll buffer action
One of the terminal features I use often that I feel would make sense in zellij is clearing a pane's scrollback buffer.  I do a lot of multi system management and clear my buffer every time I switch to a new task.  This is less work than restarting the terminal (or in zellij replacing the current pane).  


Discussion / hints:
For me, the `reset` command does the trick.
Interesting.  While it technically solves my request it takes a full 1s to return whereas a purpose built method should be near instant and would not frustrate the typical impatient user.
Fair. reset does a lot of things and deliberate calls `nanosleep` afterwards. Probably terminals were a lot slower when the command was written.
Try `clear` or `tput reset` for a faster version.
Nice, I am putting this in my bashrc right now:
`bind -x $'"\C-l":clear;'`

This does the requested operation instantly on `Ctrl-L`.

https://stackoverflow.com/a/29844745

For me `clear` does not clear the scrollback buffer on macOS (arm64) for whatever reason but `reset` does the trick. I imagine a built-in function is probably still the best solution in the long-term.
Interesting. On my machine, `tput` and `clear` come from the ncurses package.

In fact, the function you want is already is already integrated in zellij and can be triggered by writing the correct escape sequences to the terminal. For example, like `clear` is doing that with
`^[[H^[[2J^[[3J`

I attached the sequence below for convenience, you can `cat` it in your terminal.
[clear.txt](https://github.com/zellij-org/zellij/files/10475436/clear.txt)


Fix patch:
diff --git a/docs/MANPAGE.md b/docs/MANPAGE.md
index cd662ef4cf..ad06d7ba7a 100644
--- a/docs/MANPAGE.md
+++ b/docs/MANPAGE.md
@@ -153,6 +153,7 @@ ACTIONS
   next ID.
 * __MoveFocus: <Direction\>__ -  moves focus in the specified direction (Left,
   Right, Up, Down).
+* __Clear__ - clears current screen.
 * __DumpScreen: <File\>__ - dumps the screen in the specified file.
 * __EditScrollback__ - replaces the current pane with the scrollback buffer.
 * __ScrollUp__ - scrolls up 1 line in the focused pane.
diff --git a/src/sessions.rs b/src/sessions.rs
index 0ddcfcb223..5f3354de9c 100644
--- a/src/sessions.rs
+++ b/src/sessions.rs
@@ -207,7 +207,7 @@ pub(crate) fn assert_session_ne(name: &str) {
         process::exit(1);
     }
     if name.contains('/') {
-        eprintln!("Session name cannot contains '/'.");
+        eprintln!("Session name cannot contain '/'.");
         process::exit(1);
     }
 
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index e764786c44..3efe6a7225 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -298,6 +298,7 @@ impl InputHandler {
                     .send_to_server(ClientToServerMsg::Action(action, None));
             },
             Action::CloseFocus
+            | Action::ClearScreen
             | Action::NewPane(..)
             | Action::Run(_)
             | Action::ToggleFloatingPanes
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index 8046c53d89..140df36003 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -1109,7 +1109,16 @@ impl Grid {
             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 {
         let viewport: String = dump_screen!(self.viewport);
         if !full {
diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs
index c6896f90dc..95f42e48c0 100644
--- a/zellij-server/src/panes/plugin_pane.rs
+++ b/zellij-server/src/panes/plugin_pane.rs
@@ -421,6 +421,9 @@ impl Pane for PluginPane {
             )]))
             .unwrap();
     }
+    fn clear_screen(&mut self) {
+        // do nothing
+    }
     fn clear_scroll(&mut self) {
         // noop
     }
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index d48df67a75..469cfa0938 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -499,6 +499,9 @@ impl Pane for TerminalPane {
     fn dump_screen(&mut self, _client_id: ClientId, full: bool) -> String {
         self.grid.dump_screen(full)
     }
+    fn clear_screen(&mut self) {
+        self.grid.clear_screen()
+    }
     fn scroll_up(&mut self, count: usize, _client_id: ClientId) {
         self.grid.move_viewport_up(count);
         self.set_should_render(true);
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 53b43ecdc0..ffe9227105 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -172,6 +172,12 @@ pub(crate) fn route_action(
                 .send_to_screen(ScreenInstruction::MovePaneBackwards(client_id))
                 .with_context(err_context)?;
         },
+        Action::ClearScreen => {
+            session
+                .senders
+                .send_to_screen(ScreenInstruction::ClearScreen(client_id))
+                .with_context(err_context)?;
+        },
         Action::DumpScreen(val, full) => {
             session
                 .senders
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index a2fa3e5066..f5c5ef18a6 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -157,6 +157,7 @@ pub enum ScreenInstruction {
     MovePaneRight(ClientId),
     MovePaneLeft(ClientId),
     Exit,
+    ClearScreen(ClientId),
     DumpScreen(String, ClientId, bool),
     EditScrollback(ClientId),
     ScrollUp(ClientId),
@@ -311,6 +312,7 @@ impl From<&ScreenInstruction> for ScreenContext {
             ScreenInstruction::MovePaneRight(..) => ScreenContext::MovePaneRight,
             ScreenInstruction::MovePaneLeft(..) => ScreenContext::MovePaneLeft,
             ScreenInstruction::Exit => ScreenContext::Exit,
+            ScreenInstruction::ClearScreen(..) => ScreenContext::ClearScreen,
             ScreenInstruction::DumpScreen(..) => ScreenContext::DumpScreen,
             ScreenInstruction::EditScrollback(..) => ScreenContext::EditScrollback,
             ScreenInstruction::ScrollUp(..) => ScreenContext::ScrollUp,
@@ -1700,6 +1702,18 @@ pub(crate) fn screen_thread_main(
                 screen.render()?;
                 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) => {
                 active_tab_and_connected_client_id!(
                     screen,
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 4134594b61..7dfb4ebc0e 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -235,6 +235,7 @@ pub trait Pane {
     fn push_right(&mut self, count: usize);
     fn pull_left(&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 {
         "".to_owned()
     }
@@ -2304,6 +2305,12 @@ impl Tab {
         }
         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(
         &mut self,
         file: Option<String>,
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index 3947dfad39..7ae7e6597f 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -212,6 +212,8 @@ pub enum CliAction {
     },
     /// Rotate the location of the previous pane backwards
     MovePaneBackwards,
+    /// Clear all buffers for a focused pane
+    Clear,
     /// Dump the focused pane to a file
     DumpScreen {
         path: PathBuf,
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index f1522c1de9..bb61adf251 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -255,6 +255,7 @@ pub enum ScreenContext {
     MovePaneRight,
     MovePaneLeft,
     Exit,
+    ClearScreen,
     DumpScreen,
     EditScrollback,
     ScrollUp,
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index b2ec8d8287..a83c32a1f5 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -119,6 +119,8 @@ pub enum Action {
     MoveFocusOrTab(Direction),
     MovePane(Option<Direction>),
     MovePaneBackwards,
+    /// Clear all buffers of a current screen
+    ClearScreen,
     /// Dumps the screen to a file
     DumpScreen(String, bool),
     /// Scroll up in focus pane.
@@ -251,6 +253,7 @@ impl Action {
             CliAction::MoveFocusOrTab { direction } => Ok(vec![Action::MoveFocusOrTab(direction)]),
             CliAction::MovePane { direction } => Ok(vec![Action::MovePane(direction)]),
             CliAction::MovePaneBackwards => Ok(vec![Action::MovePaneBackwards]),
+            CliAction::Clear => Ok(vec![Action::ClearScreen]),
             CliAction::DumpScreen { path, full } => Ok(vec![Action::DumpScreen(
                 path.as_os_str().to_string_lossy().into(),
                 full,
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 64fe0bac47..1bd1356c13 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -68,6 +68,7 @@ macro_rules! parse_kdl_action_arguments {
                 "ToggleMouseMode" => Ok(Action::ToggleMouseMode),
                 "PreviousSwapLayout" => Ok(Action::PreviousSwapLayout),
                 "NextSwapLayout" => Ok(Action::NextSwapLayout),
+                "Clear" => Ok(Action::ClearScreen),
                 _ => Err(ConfigError::new_kdl_error(
                     format!("Unsupported action: {:?}", $action_name),
                     $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),
             "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),
             "Deny" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
             "Write" => parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action),


Test patch:
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs
index 1652eb5ae3..5c41040e99 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -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]
 fn new_floating_pane() {
     let size = Size {