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)).
This commit is contained in:
Benjamin Westphal 2024-07-23 05:22:10 +02:00 committed by GitHub
parent eb210ca248
commit 1fae99a7c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 96 additions and 2 deletions

View File

@ -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": {

View File

@ -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
}

View File

@ -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<usize>,
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);
});
});
});
});
}

View File

@ -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,
}
}
}

View File

@ -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]

View File

@ -677,6 +677,7 @@ impl Vim {
| Operator::Lowercase
| Operator::Uppercase
| Operator::OppositeCase
| Operator::ToggleComments
) {
self.start_recording(cx)
};