From 1fae99a7c4ae3be73ad63095767033944c7ff961 Mon Sep 17 00:00:00 2001 From: Benjamin Westphal Date: Tue, 23 Jul 2024 05:22:10 +0200 Subject: [PATCH] vim: Add motion support for toggle comments (#14919) ### Summary This PR adds support for count and object motions to the toggle comments action in Vim mode. The relevant issue is [#14337](https://github.com/zed-industries/zed/issues/14337). For example, `2 g c j` will toggle comments three lines downward. `g c g g` will toggle comments from the current cursor position up to the start of the file. Notably missing from this PR are `g c b` (toggle comments for the current block) as well as `g c p` (toggle comments for the current paragraph). These seem to be non-standard. The new module `normal/toggle_comments.rs` has been copied almost verbatim from `normal/indent.rs`. Maybe that ought to be abstracted over but I feel I lack the overview. Release Notes: - vim: Added support for count and object motion to the toggle comments action ([#14337](https://github.com/zed-industries/zed/issues/14337)). --- assets/keymaps/vim.json | 8 +++- crates/vim/src/normal.rs | 4 ++ crates/vim/src/normal/toggle_comments.rs | 57 ++++++++++++++++++++++++ crates/vim/src/state.rs | 5 ++- crates/vim/src/test.rs | 23 ++++++++++ crates/vim/src/vim.rs | 1 + 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 crates/vim/src/normal/toggle_comments.rs diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index d6e61893b7..df9b41b23a 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -253,7 +253,7 @@ "[ d": "editor::GoToPrevDiagnostic", "] c": "editor::GoToHunk", "[ c": "editor::GoToPrevHunk", - "g c c": "vim::ToggleComments" + "g c": ["vim::PushOperator", "ToggleComments"] } }, { @@ -434,6 +434,12 @@ "<": "vim::CurrentLine" } }, + { + "context": "vim_operator == gc", + "bindings": { + "c": "vim::CurrentLine" + } + }, { "context": "BufferSearchBar && !in_replace", "bindings": { diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 749428159e..aceacefc19 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -9,6 +9,7 @@ pub(crate) mod repeat; mod scroll; pub(crate) mod search; pub mod substitute; +mod toggle_comments; pub(crate) mod yank; use std::collections::HashMap; @@ -39,6 +40,7 @@ use self::{ change::{change_motion, change_object}, delete::{delete_motion, delete_object}, indent::{indent_motion, indent_object, IndentDirection}, + toggle_comments::{toggle_comments_motion, toggle_comments_object}, yank::{yank_motion, yank_object}, }; @@ -237,6 +239,7 @@ pub fn normal_motion( Some(Operator::OppositeCase) => { change_case_motion(vim, motion, times, CaseTarget::OppositeCase, cx) } + Some(Operator::ToggleComments) => toggle_comments_motion(vim, motion, times, cx), Some(operator) => { // Can't do anything for text objects, Ignoring error!("Unexpected normal mode motion operator: {:?}", operator) @@ -273,6 +276,7 @@ pub fn normal_object(object: Object, cx: &mut WindowContext) { target: Some(SurroundsType::Object(object)), }); } + Some(Operator::ToggleComments) => toggle_comments_object(vim, object, around, cx), _ => { // Can't do anything for namespace operators. Ignoring } diff --git a/crates/vim/src/normal/toggle_comments.rs b/crates/vim/src/normal/toggle_comments.rs new file mode 100644 index 0000000000..cbd00e757e --- /dev/null +++ b/crates/vim/src/normal/toggle_comments.rs @@ -0,0 +1,57 @@ +use crate::{motion::Motion, object::Object, Vim}; +use collections::HashMap; +use editor::{display_map::ToDisplayPoint, Bias}; +use gpui::WindowContext; +use language::SelectionGoal; + +pub fn toggle_comments_motion( + vim: &mut Vim, + motion: Motion, + times: Option, + cx: &mut WindowContext, +) { + vim.stop_recording(); + vim.update_active_editor(cx, |_, editor, cx| { + let text_layout_details = editor.text_layout_details(cx); + editor.transact(cx, |editor, cx| { + let mut selection_starts: HashMap<_, _> = Default::default(); + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let anchor = map.display_point_to_anchor(selection.head(), Bias::Right); + selection_starts.insert(selection.id, anchor); + motion.expand_selection(map, selection, times, false, &text_layout_details); + }); + }); + editor.toggle_comments(&Default::default(), cx); + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let anchor = selection_starts.remove(&selection.id).unwrap(); + selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None); + }); + }); + }); + }); +} + +pub fn toggle_comments_object(vim: &mut Vim, object: Object, around: bool, cx: &mut WindowContext) { + vim.stop_recording(); + vim.update_active_editor(cx, |_, editor, cx| { + editor.transact(cx, |editor, cx| { + let mut original_positions: HashMap<_, _> = Default::default(); + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let anchor = map.display_point_to_anchor(selection.head(), Bias::Right); + original_positions.insert(selection.id, anchor); + object.expand_selection(map, selection, around); + }); + }); + editor.toggle_comments(&Default::default(), cx); + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let anchor = original_positions.remove(&selection.id).unwrap(); + selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None); + }); + }); + }); + }); +} diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 8adaa7271d..1e1d9c47c4 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -71,6 +71,7 @@ pub enum Operator { Register, RecordRegister, ReplayRegister, + ToggleComments, } #[derive(Default, Clone)] @@ -326,6 +327,7 @@ impl Operator { Operator::Register => "\"", Operator::RecordRegister => "q", Operator::ReplayRegister => "@", + Operator::ToggleComments => "gc", } } @@ -351,7 +353,8 @@ impl Operator { | Operator::Uppercase | Operator::Object { .. } | Operator::ChangeSurrounds { target: None } - | Operator::OppositeCase => false, + | Operator::OppositeCase + | Operator::ToggleComments => false, } } } diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 5d027aec73..90704df901 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -1268,6 +1268,29 @@ async fn test_toggle_comments(cx: &mut gpui::TestAppContext) { "}, Mode::Normal, ); + + // works with count + cx.simulate_keystrokes("g c 2 j"); + cx.assert_state( + indoc! {" + // // ˇone + // two + // three + "}, + Mode::Normal, + ); + + // works with motion object + cx.simulate_keystrokes("shift-g"); + cx.simulate_keystrokes("g c g g"); + cx.assert_state( + indoc! {" + // one + two + three + ˇ"}, + Mode::Normal, + ); } #[gpui::test] diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 3595106625..336fd4ffc4 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -677,6 +677,7 @@ impl Vim { | Operator::Lowercase | Operator::Uppercase | Operator::OppositeCase + | Operator::ToggleComments ) { self.start_recording(cx) };