Split ContextMenu actions

Conrad Irwin created

This should have no user-visible impact.

For vim `.` to repeat it's important that actions are replayable.
Currently editor::MoveDown *sometimes* moves the cursor down, and
*sometimes* selects the next completion.

For replay we need to be able to separate the two.

Change summary

assets/keymaps/default.json               | 11 +++
crates/editor/src/editor.rs               | 74 ++++++++++++++----------
crates/editor/src/editor_tests.rs         |  2 
crates/editor/src/scroll.rs               |  4 -
crates/editor/src/scroll/scroll_amount.rs | 24 -------
5 files changed, 57 insertions(+), 58 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -515,6 +515,17 @@
       "enter": "editor::ConfirmCodeAction"
     }
   },
+  {
+    "context": "Editor && (showing_code_actions || showing_completions)",
+    "bindings": {
+      "up": "editor::ContextMenuPrev",
+      "ctrl-p": "editor::ContextMenuPrev",
+      "down": "editor::ContextMenuNext",
+      "ctrl-n": "editor::ContextMenuNext",
+      "pageup": "editor::ContextMenuFirst",
+      "pagedown": "editor::ContextMenuLast"
+    }
+  },
   // Custom bindings
   {
     "bindings": {

crates/editor/src/editor.rs 🔗

@@ -312,6 +312,10 @@ actions!(
         CopyPath,
         CopyRelativePath,
         CopyHighlightJson,
+        ContextMenuFirst,
+        ContextMenuPrev,
+        ContextMenuNext,
+        ContextMenuLast,
     ]
 );
 
@@ -468,6 +472,10 @@ pub fn init(cx: &mut AppContext) {
     cx.add_action(Editor::next_copilot_suggestion);
     cx.add_action(Editor::previous_copilot_suggestion);
     cx.add_action(Editor::copilot_suggest);
+    cx.add_action(Editor::context_menu_first);
+    cx.add_action(Editor::context_menu_prev);
+    cx.add_action(Editor::context_menu_next);
+    cx.add_action(Editor::context_menu_last);
 
     hover_popover::init(cx);
     scroll::actions::init(cx);
@@ -5166,12 +5174,6 @@ impl Editor {
             return;
         }
 
-        if let Some(context_menu) = self.context_menu.as_mut() {
-            if context_menu.select_prev(cx) {
-                return;
-            }
-        }
-
         if matches!(self.mode, EditorMode::SingleLine) {
             cx.propagate_action();
             return;
@@ -5194,15 +5196,6 @@ impl Editor {
             return;
         }
 
-        if self
-            .context_menu
-            .as_mut()
-            .map(|menu| menu.select_first(cx))
-            .unwrap_or(false)
-        {
-            return;
-        }
-
         if matches!(self.mode, EditorMode::SingleLine) {
             cx.propagate_action();
             return;
@@ -5242,12 +5235,6 @@ impl Editor {
     pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
         self.take_rename(true, cx);
 
-        if let Some(context_menu) = self.context_menu.as_mut() {
-            if context_menu.select_next(cx) {
-                return;
-            }
-        }
-
         if self.mode == EditorMode::SingleLine {
             cx.propagate_action();
             return;
@@ -5315,6 +5302,30 @@ impl Editor {
         });
     }
 
+    pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            context_menu.select_first(cx);
+        }
+    }
+
+    pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            context_menu.select_prev(cx);
+        }
+    }
+
+    pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            context_menu.select_next(cx);
+        }
+    }
+
+    pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
+        if let Some(context_menu) = self.context_menu.as_mut() {
+            context_menu.select_last(cx);
+        }
+    }
+
     pub fn move_to_previous_word_start(
         &mut self,
         _: &MoveToPreviousWordStart,
@@ -8666,17 +8677,20 @@ impl View for Editor {
         if self.pending_rename.is_some() {
             keymap.add_identifier("renaming");
         }
-        match self.context_menu.as_ref() {
-            Some(ContextMenu::Completions(_)) => {
-                keymap.add_identifier("menu");
-                keymap.add_identifier("showing_completions")
-            }
-            Some(ContextMenu::CodeActions(_)) => {
-                keymap.add_identifier("menu");
-                keymap.add_identifier("showing_code_actions")
+        if self.context_menu_visible() {
+            match self.context_menu.as_ref() {
+                Some(ContextMenu::Completions(_)) => {
+                    keymap.add_identifier("menu");
+                    keymap.add_identifier("showing_completions")
+                }
+                Some(ContextMenu::CodeActions(_)) => {
+                    keymap.add_identifier("menu");
+                    keymap.add_identifier("showing_code_actions")
+                }
+                None => {}
             }
-            None => {}
         }
+
         for layer in self.keymap_context_layers.values() {
             keymap.extend(layer);
         }

crates/editor/src/editor_tests.rs 🔗

@@ -5340,7 +5340,7 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
     cx.condition(|editor, _| editor.context_menu_visible())
         .await;
     let apply_additional_edits = cx.update_editor(|editor, cx| {
-        editor.move_down(&MoveDown, cx);
+        editor.context_menu_next(&Default::default(), cx);
         editor
             .confirm_completion(&ConfirmCompletion::default(), cx)
             .unwrap()

crates/editor/src/scroll.rs 🔗

@@ -378,10 +378,6 @@ impl Editor {
             return;
         }
 
-        if amount.move_context_menu_selection(self, cx) {
-            return;
-        }
-
         let cur_position = self.scroll_position(cx);
         let new_pos = cur_position + vec2f(0., amount.lines(self));
         self.set_scroll_position(new_pos, cx);

crates/editor/src/scroll/scroll_amount.rs 🔗

@@ -1,8 +1,5 @@
-use gpui::ViewContext;
-use serde::Deserialize;
-use util::iife;
-
 use crate::Editor;
+use serde::Deserialize;
 
 #[derive(Clone, PartialEq, Deserialize)]
 pub enum ScrollAmount {
@@ -13,25 +10,6 @@ pub enum ScrollAmount {
 }
 
 impl ScrollAmount {
-    pub fn move_context_menu_selection(
-        &self,
-        editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
-    ) -> bool {
-        iife!({
-            let context_menu = editor.context_menu.as_mut()?;
-
-            match self {
-                Self::Line(c) if *c > 0. => context_menu.select_next(cx),
-                Self::Line(_) => context_menu.select_prev(cx),
-                Self::Page(c) if *c > 0. => context_menu.select_last(cx),
-                Self::Page(_) => context_menu.select_first(cx),
-            }
-            .then_some(())
-        })
-        .is_some()
-    }
-
     pub fn lines(&self, editor: &mut Editor) -> f32 {
         match self {
             Self::Line(count) => *count,