Add keyboard shortcuts for scrolling in terminal (#13508)

Aleksei Gusev created

Fixes #4917, #12231

Release Notes:
-  Added keyboard shortcuts for scrolling in terminal ([4917](https://github.com/zed-industries/zed/issues/4917), [12231](https://github.com/zed-industries/zed/issues/12231))

Change summary

assets/keymaps/default-linux.json         |  8 +++
assets/keymaps/default-macos.json         |  8 +++
crates/terminal/src/terminal.rs           | 44 ++++++++++++++++++++++++
crates/terminal_view/src/terminal_view.rs | 39 +++++++++++++++++++++
4 files changed, 95 insertions(+), 4 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -659,7 +659,13 @@
       "pagedown": ["terminal::SendKeystroke", "pagedown"],
       "escape": ["terminal::SendKeystroke", "escape"],
       "enter": ["terminal::SendKeystroke", "enter"],
-      "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
+      "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
+      "shift-pageup": "terminal::ScrollPageUp",
+      "shift-pagedown": "terminal::ScrollPageDown",
+      "shift-up": "terminal::ScrollLineUp",
+      "shift-down": "terminal::ScrollLineDown",
+      "shift-home": "terminal::ScrollToTop",
+      "shift-end": "terminal::ScrollToBottom"
     }
   }
 ]

assets/keymaps/default-macos.json 🔗

@@ -703,7 +703,13 @@
       "pagedown": ["terminal::SendKeystroke", "pagedown"],
       "escape": ["terminal::SendKeystroke", "escape"],
       "enter": ["terminal::SendKeystroke", "enter"],
-      "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
+      "ctrl-c": ["terminal::SendKeystroke", "ctrl-c"],
+      "shift-pageup": "terminal::ScrollPageUp",
+      "shift-pagedown": "terminal::ScrollPageDown",
+      "shift-up": "terminal::ScrollLineUp",
+      "shift-down": "terminal::ScrollLineDown",
+      "shift-home": "terminal::ScrollToTop",
+      "shift-end": "terminal::ScrollToBottom"
     }
   }
 ]

crates/terminal/src/terminal.rs 🔗

@@ -64,7 +64,19 @@ use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
 
 actions!(
     terminal,
-    [Clear, Copy, Paste, ShowCharacterPalette, SearchTest,]
+    [
+        Clear,
+        Copy,
+        Paste,
+        ShowCharacterPalette,
+        SearchTest,
+        ScrollLineUp,
+        ScrollLineDown,
+        ScrollPageUp,
+        ScrollPageDown,
+        ScrollToTop,
+        ScrollToBottom,
+    ]
 );
 
 ///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
@@ -982,6 +994,36 @@ impl Terminal {
         self.events.push_back(InternalEvent::Clear)
     }
 
+    pub fn scroll_line_up(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::Delta(1)));
+    }
+
+    pub fn scroll_line_down(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::Delta(-1)));
+    }
+
+    pub fn scroll_page_up(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::PageUp));
+    }
+
+    pub fn scroll_page_down(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::PageDown));
+    }
+
+    pub fn scroll_to_top(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::Top));
+    }
+
+    pub fn scroll_to_bottom(&mut self) {
+        self.events
+            .push_back(InternalEvent::Scroll(AlacScroll::Bottom));
+    }
+
     ///Resize the terminal and the PTY.
     pub fn set_size(&mut self, new_size: TerminalSize) {
         if self.last_content.size != new_size {

crates/terminal_view/src/terminal_view.rs 🔗

@@ -21,7 +21,8 @@ use terminal::{
         term::{search::RegexSearch, TermMode},
     },
     terminal_settings::{TerminalBlink, TerminalSettings, WorkingDirectory},
-    Clear, Copy, Event, MaybeNavigationTarget, Paste, ShowCharacterPalette, TaskStatus, Terminal,
+    Clear, Copy, Event, MaybeNavigationTarget, Paste, ScrollLineDown, ScrollLineUp, ScrollPageDown,
+    ScrollPageUp, ScrollToBottom, ScrollToTop, ShowCharacterPalette, TaskStatus, Terminal,
 };
 use terminal_element::TerminalElement;
 use ui::{h_flex, prelude::*, ContextMenu, Icon, IconName, Label, Tooltip};
@@ -251,6 +252,36 @@ impl TerminalView {
         cx.notify();
     }
 
+    fn scroll_line_up(&mut self, _: &ScrollLineUp, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_line_up());
+        cx.notify();
+    }
+
+    fn scroll_line_down(&mut self, _: &ScrollLineDown, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_line_down());
+        cx.notify();
+    }
+
+    fn scroll_page_up(&mut self, _: &ScrollPageUp, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_page_up());
+        cx.notify();
+    }
+
+    fn scroll_page_down(&mut self, _: &ScrollPageDown, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_page_down());
+        cx.notify();
+    }
+
+    fn scroll_to_top(&mut self, _: &ScrollToTop, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_to_top());
+        cx.notify();
+    }
+
+    fn scroll_to_bottom(&mut self, _: &ScrollToBottom, cx: &mut ViewContext<Self>) {
+        self.terminal.update(cx, |term, _| term.scroll_to_bottom());
+        cx.notify();
+    }
+
     pub fn should_show_cursor(&self, focused: bool, cx: &mut gpui::ViewContext<Self>) -> bool {
         //Don't blink the cursor when not focused, blinking is disabled, or paused
         if !focused
@@ -743,6 +774,12 @@ impl Render for TerminalView {
             .on_action(cx.listener(TerminalView::copy))
             .on_action(cx.listener(TerminalView::paste))
             .on_action(cx.listener(TerminalView::clear))
+            .on_action(cx.listener(TerminalView::scroll_line_up))
+            .on_action(cx.listener(TerminalView::scroll_line_down))
+            .on_action(cx.listener(TerminalView::scroll_page_up))
+            .on_action(cx.listener(TerminalView::scroll_page_down))
+            .on_action(cx.listener(TerminalView::scroll_to_top))
+            .on_action(cx.listener(TerminalView::scroll_to_bottom))
             .on_action(cx.listener(TerminalView::show_character_palette))
             .on_action(cx.listener(TerminalView::select_all))
             .on_key_down(cx.listener(Self::key_down))