vim: keymap tweaks (#2674)

Conrad Irwin created

A few small tweaks to fix some of the community issues

Release Notes:

- vim: Fix `escape` in command palette
([#1347](https://github.com/zed-industries/community/issues/1347)).
- vim: Allow `^` as a motion in actions
([#856](https://github.com/zed-industries/community/issues/856)).
- vim: Allow `ctrl-c` to exit visual mode
([#1447](https://github.com/zed-industries/community/issues/1447)).

Change summary

assets/keymaps/vim.json                 | 16 +++++++++++++---
crates/vim/src/test.rs                  | 14 ++++++++++++++
crates/vim/src/test/vim_test_context.rs |  2 ++
crates/vim/src/vim.rs                   | 23 ++---------------------
4 files changed, 31 insertions(+), 24 deletions(-)

Detailed changes

assets/keymaps/vim.json 🔗

@@ -35,6 +35,7 @@
       "l": "vim::Right",
       "right": "vim::Right",
       "$": "vim::EndOfLine",
+      "^": "vim::FirstNonWhitespace",
       "shift-g": "vim::EndOfDocument",
       "w": "vim::NextWordStart",
       "shift-w": [
@@ -92,7 +93,10 @@
       ],
       "ctrl-o": "pane::GoBack",
       "ctrl-]": "editor::GoToDefinition",
-      "escape": "editor::Cancel",
+      "escape": [
+        "vim::SwitchMode",
+        "Normal"
+      ],
       "0": "vim::StartOfLine", // When no number operator present, use start of line motion
       "1": [
         "vim::Number",
@@ -165,7 +169,6 @@
       "shift-a": "vim::InsertEndOfLine",
       "x": "vim::DeleteRight",
       "shift-x": "vim::DeleteLeft",
-      "^": "vim::FirstNonWhitespace",
       "o": "vim::InsertLineBelow",
       "shift-o": "vim::InsertLineAbove",
       "~": "vim::ChangeCase",
@@ -305,6 +308,10 @@
         "vim::PushOperator",
         "Replace"
       ],
+      "ctrl-c": [
+        "vim::SwitchMode",
+        "Normal"
+      ],
       "> >": "editor::Indent",
       "< <": "editor::Outdent"
     }
@@ -321,7 +328,10 @@
     "bindings": {
       "tab": "vim::Tab",
       "enter": "vim::Enter",
-      "escape": "editor::Cancel"
+      "escape": [
+        "vim::SwitchMode",
+        "Normal"
+      ]
     }
   }
 ]

crates/vim/src/test.rs 🔗

@@ -4,6 +4,7 @@ mod neovim_connection;
 mod vim_binding_test_context;
 mod vim_test_context;
 
+use command_palette::CommandPalette;
 pub use neovim_backed_binding_test_context::*;
 pub use neovim_backed_test_context::*;
 pub use vim_binding_test_context::*;
@@ -139,3 +140,16 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
     cx.simulate_keystrokes(["shift-v", "down", ">", ">"]);
     cx.assert_editor_state("aa\n    b«b\n    cˇ»c");
 }
+
+#[gpui::test]
+async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
+    let mut cx = VimTestContext::new(cx, true).await;
+
+    cx.set_state("aˇbc\n", Mode::Normal);
+    cx.simulate_keystrokes(["i", "cmd-shift-p"]);
+
+    assert!(cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
+    cx.simulate_keystroke("escape");
+    assert!(!cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
+    cx.assert_state("aˇbc\n", Mode::Insert);
+}

crates/vim/src/test/vim_test_context.rs 🔗

@@ -21,12 +21,14 @@ impl<'a> VimTestContext<'a> {
         cx.update(|cx| {
             search::init(cx);
             crate::init(cx);
+            command_palette::init(cx);
         });
 
         cx.update(|cx| {
             cx.update_global(|store: &mut SettingsStore, cx| {
                 store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
             });
+            settings::KeymapFile::load_asset("keymaps/default.json", cx).unwrap();
             settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
         });
 

crates/vim/src/vim.rs 🔗

@@ -12,7 +12,7 @@ mod visual;
 
 use anyhow::Result;
 use collections::CommandPaletteFilter;
-use editor::{Bias, Cancel, Editor, EditorMode, Event};
+use editor::{Bias, Editor, EditorMode, Event};
 use gpui::{
     actions, impl_actions, AppContext, Subscription, ViewContext, ViewHandle, WeakViewHandle,
     WindowContext,
@@ -64,22 +64,6 @@ pub fn init(cx: &mut AppContext) {
         Vim::update(cx, |vim, cx| vim.push_number(n, cx));
     });
 
-    // Editor Actions
-    cx.add_action(|_: &mut Editor, _: &Cancel, cx| {
-        // If we are in aren't in normal mode or have an active operator, swap to normal mode
-        // Otherwise forward cancel on to the editor
-        let vim = Vim::read(cx);
-        if vim.state.mode != Mode::Normal || vim.active_operator().is_some() {
-            WindowContext::defer(cx, |cx| {
-                Vim::update(cx, |state, cx| {
-                    state.switch_mode(Mode::Normal, false, cx);
-                });
-            });
-        } else {
-            cx.propagate_action();
-        }
-    });
-
     cx.add_action(|_: &mut Workspace, _: &Tab, cx| {
         Vim::active_editor_input_ignored(" ".into(), cx)
     });
@@ -109,10 +93,7 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
     cx.observe_keystrokes(|_keystroke, _result, handled_by, cx| {
         if let Some(handled_by) = handled_by {
             // Keystroke is handled by the vim system, so continue forward
-            // Also short circuit if it is the special cancel action
-            if handled_by.namespace() == "vim"
-                || (handled_by.namespace() == "editor" && handled_by.name() == "Cancel")
-            {
+            if handled_by.namespace() == "vim" {
                 return true;
             }
         }