Finished implementing vscode, emacs, and mac style pageup/down. Added keybindings ctrl-v, alt-v for emacs up/down and shift-pageup, shift-pagedown for vscode style. Also improved incorporated pageup/down into context menus

Mikayla Maki created

Change summary

assets/keymaps/default.json          |  31 +++
crates/editor/src/editor.rs          | 272 ++++++++++++++++++++++-------
crates/editor/src/editor_tests.rs    |   2 
crates/terminal/src/terminal_view.rs |   4 
4 files changed, 235 insertions(+), 74 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -3,8 +3,12 @@
     {
         "bindings": {
             "up": "menu::SelectPrev",
+            "pageup": "menu::SelectFirst",
+            "shift-pageup": "menu::SelectFirst",
             "ctrl-p": "menu::SelectPrev",
             "down": "menu::SelectNext",
+            "pagedown": "menu::SelectLast",
+            "shift-pagedown": "menu::SelectFirst",
             "ctrl-n": "menu::SelectNext",
             "cmd-up": "menu::SelectFirst",
             "cmd-down": "menu::SelectLast",
@@ -60,13 +64,18 @@
             "cmd-z": "editor::Undo",
             "cmd-shift-z": "editor::Redo",
             "up": "editor::MoveUp",
+            "pageup": "editor::PageUp",
+            "shift-pageup": "editor::MovePageUp",
             "down": "editor::MoveDown",
+            "pagedown": "editor::PageDown",
+            "shift-pagedown": "editor::MovePageDown",
             "left": "editor::MoveLeft",
             "right": "editor::MoveRight",
             "ctrl-p": "editor::MoveUp",
             "ctrl-n": "editor::MoveDown",
             "ctrl-b": "editor::MoveLeft",
             "ctrl-f": "editor::MoveRight",
+            "ctrl-l": "editor::CenterScreen",
             "alt-left": "editor::MoveToPreviousWordStart",
             "alt-b": "editor::MoveToPreviousWordStart",
             "alt-right": "editor::MoveToNextWordEnd",
@@ -118,8 +127,18 @@
                     "stop_at_soft_wraps": true
                 }
             ],
-            "pageup": "editor::PageUp",
-            "pagedown": "editor::PageDown",
+            "ctrl-v": [
+                "editor::MovePageDown",
+                {
+                    "center_cursor": true
+                }
+            ],
+            "alt-v": [
+                "editor::MovePageUp",
+                {
+                    "center_cursor": true
+                }
+            ],
             "ctrl-cmd-space": "editor::ShowCharacterPalette"
         }
     },
@@ -451,10 +470,18 @@
                 "terminal::SendKeystroke",
                 "up"
             ],
+            "pageup": [
+                "terminal::SendKeystroke",
+                "pageup"
+            ],
             "down": [
                 "terminal::SendKeystroke",
                 "down"
             ],
+            "pagedown": [
+                "terminal::SendKeystroke",
+                "pagedown"
+            ],
             "escape": [
                 "terminal::SendKeystroke",
                 "escape"

crates/editor/src/editor.rs 🔗

@@ -173,8 +173,11 @@ actions!(
         Paste,
         Undo,
         Redo,
+        CenterScreen,
         MoveUp,
+        PageUp,
         MoveDown,
+        PageDown,
         MoveLeft,
         MoveRight,
         MoveToPreviousWordStart,
@@ -214,8 +217,6 @@ actions!(
         FindAllReferences,
         Rename,
         ConfirmRename,
-        PageUp,
-        PageDown,
         Fold,
         UnfoldLines,
         FoldSelectedRanges,
@@ -287,7 +288,12 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(Editor::undo);
     cx.add_action(Editor::redo);
     cx.add_action(Editor::move_up);
+    cx.add_action(Editor::move_page_up);
+    cx.add_action(Editor::page_up);
     cx.add_action(Editor::move_down);
+    cx.add_action(Editor::move_page_down);
+    cx.add_action(Editor::page_down);
+    cx.add_action(Editor::center_screen);
     cx.add_action(Editor::move_left);
     cx.add_action(Editor::move_right);
     cx.add_action(Editor::move_to_previous_word_start);
@@ -326,8 +332,6 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action(Editor::go_to_prev_diagnostic);
     cx.add_action(Editor::go_to_definition);
     cx.add_action(Editor::go_to_type_definition);
-    cx.add_action(Editor::page_up);
-    cx.add_action(Editor::page_down);
     cx.add_action(Editor::fold);
     cx.add_action(Editor::unfold_lines);
     cx.add_action(Editor::fold_selected_ranges);
@@ -620,6 +624,18 @@ enum ContextMenu {
 }
 
 impl ContextMenu {
+    fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
+        if self.visible() {
+            match self {
+                ContextMenu::Completions(menu) => menu.select_first(cx),
+                ContextMenu::CodeActions(menu) => menu.select_first(cx),
+            }
+            true
+        } else {
+            false
+        }
+    }
+
     fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
         if self.visible() {
             match self {
@@ -644,6 +660,18 @@ impl ContextMenu {
         }
     }
 
+    fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
+        if self.visible() {
+            match self {
+                ContextMenu::Completions(menu) => menu.select_last(cx),
+                ContextMenu::CodeActions(menu) => menu.select_last(cx),
+            }
+            true
+        } else {
+            false
+        }
+    }
+
     fn visible(&self) -> bool {
         match self {
             ContextMenu::Completions(menu) => menu.visible(),
@@ -676,6 +704,12 @@ struct CompletionsMenu {
 }
 
 impl CompletionsMenu {
+    fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
+        self.selected_item = 0;
+        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        cx.notify();
+    }
+
     fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
         if self.selected_item > 0 {
             self.selected_item -= 1;
@@ -692,6 +726,12 @@ impl CompletionsMenu {
         cx.notify();
     }
 
+    fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
+        self.selected_item = self.matches.len() - 1;
+        self.list.scroll_to(ScrollTarget::Show(self.selected_item));
+        cx.notify();
+    }
+
     fn visible(&self) -> bool {
         !self.matches.is_empty()
     }
@@ -823,6 +863,11 @@ struct CodeActionsMenu {
 }
 
 impl CodeActionsMenu {
+    fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
+        self.selected_item = 0;
+        cx.notify()
+    }
+
     fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
         if self.selected_item > 0 {
             self.selected_item -= 1;
@@ -837,6 +882,11 @@ impl CodeActionsMenu {
         }
     }
 
+    fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
+        self.selected_item = self.actions.len() - 1;
+        cx.notify()
+    }
+
     fn visible(&self) -> bool {
         !self.actions.is_empty()
     }
@@ -3863,6 +3913,23 @@ impl Editor {
         })
     }
 
+    pub fn center_screen(&mut self, _: &CenterScreen, cx: &mut ViewContext<Self>) {
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        if let Some(_) = self.context_menu.as_mut() {
+            return;
+        }
+
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate_action();
+            return;
+        }
+
+        self.request_autoscroll(Autoscroll::Center, cx);
+    }
+
     pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
         if self.take_rename(true, cx).is_some() {
             return;
@@ -3891,6 +3958,72 @@ impl Editor {
         })
     }
 
+    pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            if context_menu.select_first(cx) {
+                return;
+            }
+        }
+
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate_action();
+            return;
+        }
+
+        let row_count = match self.visible_line_count {
+            Some(row_count) => row_count as u32 - 1,
+            None => return,
+        };
+
+        let autoscroll = if action.center_cursor {
+            Autoscroll::Center
+        } else {
+            Autoscroll::Fit
+        };
+
+        self.change_selections(Some(autoscroll), cx, |s| {
+            let line_mode = s.line_mode;
+            s.move_with(|map, selection| {
+                if !selection.is_empty() && !line_mode {
+                    selection.goal = SelectionGoal::None;
+                }
+                let (cursor, goal) =
+                    movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
+                selection.collapse_to(cursor, goal);
+            });
+        });
+    }
+
+    pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext<Self>) {
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            if context_menu.select_first(cx) {
+                return;
+            }
+        }
+
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate_action();
+            return;
+        }
+
+        let lines = match self.visible_line_count {
+            Some(lines) => lines,
+            None => return,
+        };
+
+        let cur_position = self.scroll_position(cx);
+        let new_pos = cur_position - vec2f(0., lines + 1.);
+        self.set_scroll_position(new_pos, cx);
+    }
+
     pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
         self.change_selections(Some(Autoscroll::Fit), cx, |s| {
             s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
@@ -3923,6 +4056,72 @@ impl Editor {
         });
     }
 
+    pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            if context_menu.select_last(cx) {
+                return;
+            }
+        }
+
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate_action();
+            return;
+        }
+
+        let row_count = match self.visible_line_count {
+            Some(row_count) => row_count as u32 - 1,
+            None => return,
+        };
+
+        let autoscroll = if action.center_cursor {
+            Autoscroll::Center
+        } else {
+            Autoscroll::Fit
+        };
+
+        self.change_selections(Some(autoscroll), cx, |s| {
+            let line_mode = s.line_mode;
+            s.move_with(|map, selection| {
+                if !selection.is_empty() && !line_mode {
+                    selection.goal = SelectionGoal::None;
+                }
+                let (cursor, goal) =
+                    movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
+                selection.collapse_to(cursor, goal);
+            });
+        });
+    }
+
+    pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext<Self>) {
+        if self.take_rename(true, cx).is_some() {
+            return;
+        }
+
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            if context_menu.select_last(cx) {
+                return;
+            }
+        }
+
+        if matches!(self.mode, EditorMode::SingleLine) {
+            cx.propagate_action();
+            return;
+        }
+
+        let lines = match self.visible_line_count {
+            Some(lines) => lines,
+            None => return,
+        };
+
+        let cur_position = self.scroll_position(cx);
+        let new_pos = cur_position + vec2f(0., lines - 1.);
+        self.set_scroll_position(new_pos, cx);
+    }
+
     pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
         self.change_selections(Some(Autoscroll::Fit), cx, |s| {
             s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
@@ -5550,71 +5749,6 @@ impl Editor {
         }
     }
 
-    // Vscode style + emacs style
-    pub fn move_page_down(&mut self, _: &MovePageDown, cx: &mut ViewContext<Self>) {
-        let row_count = match self.visible_line_count {
-            Some(row_count) => row_count as u32 - 1,
-            None => return,
-        };
-
-        self.change_selections(Some(Autoscroll::Fit), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) =
-                    movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
-                eprintln!(
-                    "{:?} down by rows {} = {:?}",
-                    selection.end, row_count, cursor
-                );
-                selection.collapse_to(cursor, goal);
-            });
-        });
-    }
-
-    pub fn move_page_up(&mut self, _: &MovePageUp, cx: &mut ViewContext<Self>) {
-        let row_count = match self.visible_line_count {
-            Some(row_count) => row_count as u32 - 1,
-            None => return,
-        };
-
-        self.change_selections(Some(Autoscroll::Fit), cx, |s| {
-            let line_mode = s.line_mode;
-            s.move_with(|map, selection| {
-                if !selection.is_empty() && !line_mode {
-                    selection.goal = SelectionGoal::None;
-                }
-                let (cursor, goal) =
-                    movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
-                selection.collapse_to(cursor, goal);
-            });
-        });
-    }
-
-    pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext<Self>) {
-        let lines = match self.visible_line_count {
-            Some(lines) => lines,
-            None => return,
-        };
-
-        let cur_position = self.scroll_position(cx);
-        let new_pos = cur_position - vec2f(0., lines + 1.);
-        self.set_scroll_position(new_pos, cx);
-    }
-
-    pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext<Self>) {
-        let lines = match self.visible_line_count {
-            Some(lines) => lines,
-            None => return,
-        };
-
-        let cur_position = self.scroll_position(cx);
-        let new_pos = cur_position + vec2f(0., lines - 1.);
-        self.set_scroll_position(new_pos, cx);
-    }
-
     pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
         let mut fold_ranges = Vec::new();
 

crates/terminal/src/terminal_view.rs 🔗

@@ -142,8 +142,8 @@ impl TerminalView {
 
     pub fn deploy_context_menu(&mut self, action: &DeployContextMenu, cx: &mut ViewContext<Self>) {
         let menu_entries = vec![
-            ContextMenuItem::item("Clear Buffer", Clear),
-            ContextMenuItem::item("Close Terminal", pane::CloseActiveItem),
+            ContextMenuItem::item("Clear", Clear),
+            ContextMenuItem::item("Close", pane::CloseActiveItem),
         ];
 
         self.context_menu.update(cx, |menu, cx| {