vim: Switch to normal mode after toggling comments (#13412)

crwen created

Release Notes:

- vim: Fixed switching to normal mode after `g c`(vim::ToggleComments)
in visual mode
([#4439](https://github.com/zed-industries/zed/issues/4439))

Change summary

assets/keymaps/vim.json  |  4 +-
crates/vim/src/normal.rs | 18 +++++++++++++
crates/vim/src/test.rs   | 55 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 75 insertions(+), 2 deletions(-)

Detailed changes

assets/keymaps/vim.json 🔗

@@ -612,13 +612,13 @@
   {
     "context": "Editor && vim_mode == normal && !VimWaiting",
     "bindings": {
-      "g c c": "editor::ToggleComments"
+      "g c c": "vim::ToggleComments"
     }
   },
   {
     "context": "Editor && vim_mode == visual",
     "bindings": {
-      "g c": "editor::ToggleComments"
+      "g c": "vim::ToggleComments"
     }
   },
   {

crates/vim/src/normal.rs 🔗

@@ -64,6 +64,7 @@ actions!(
         JoinLines,
         Indent,
         Outdent,
+        ToggleComments,
     ]
 );
 
@@ -79,6 +80,7 @@ pub(crate) fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace
     workspace.register_action(convert_to_upper_case);
     workspace.register_action(convert_to_lower_case);
     workspace.register_action(yank_line);
+    workspace.register_action(toggle_comments);
 
     workspace.register_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
         Vim::update(cx, |vim, cx| {
@@ -437,6 +439,22 @@ fn yank_line(_: &mut Workspace, _: &YankLine, cx: &mut ViewContext<Workspace>) {
     })
 }
 
+fn toggle_comments(_: &mut Workspace, _: &ToggleComments, cx: &mut ViewContext<Workspace>) {
+    Vim::update(cx, |vim, cx| {
+        vim.record_current_action(cx);
+        vim.update_active_editor(cx, |_, editor, cx| {
+            editor.transact(cx, |editor, cx| {
+                let mut original_positions = save_selection_starts(editor, cx);
+                editor.toggle_comments(&Default::default(), cx);
+                restore_selection_cursors(editor, cx, &mut original_positions);
+            });
+        });
+        if vim.state().mode.is_visual() {
+            vim.switch_mode(Mode::Normal, false, cx)
+        }
+    });
+}
+
 fn save_selection_starts(editor: &Editor, cx: &mut ViewContext<Editor>) -> HashMap<usize, Anchor> {
     let (map, selections) = editor.selections.all_display(cx);
     selections

crates/vim/src/test.rs 🔗

@@ -1211,3 +1211,58 @@ async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
         .await
         .assert_eq("twelve ˇtwelve char\ntwelve char");
 }
+
+#[gpui::test]
+async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
+    let mut cx = VimTestContext::new(cx, true).await;
+
+    let language = std::sync::Arc::new(language::Language::new(
+        language::LanguageConfig {
+            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
+            ..Default::default()
+        },
+        Some(language::tree_sitter_rust::language()),
+    ));
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+    // works in normal model
+    cx.set_state(
+        indoc! {"
+      ˇone
+      two
+      three
+      "},
+        Mode::Normal,
+    );
+    cx.simulate_keystrokes("g c c");
+    cx.assert_state(
+        indoc! {"
+          // ˇone
+          two
+          three
+          "},
+        Mode::Normal,
+    );
+
+    // works in visual mode
+    cx.simulate_keystrokes("v j g c");
+    cx.assert_state(
+        indoc! {"
+          // // ˇone
+          // two
+          three
+          "},
+        Mode::Normal,
+    );
+
+    // works in visual line mode
+    cx.simulate_keystrokes("shift-v j g c");
+    cx.assert_state(
+        indoc! {"
+          // ˇone
+          two
+          three
+          "},
+        Mode::Normal,
+    );
+}