vim: Add ability to spawn multicursors at beginning/end of line (#18183)

Closes #17842

Release Notes:

- Added the ability to spawn multiple cursors through the g-A and g-I
motions while in visual select mode.
This commit is contained in:
Sergio C. 2024-09-24 13:21:57 -03:00 committed by GitHub
parent b69c6ee7df
commit 4a4d8c1cab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 1 deletions

View file

@ -15,7 +15,7 @@ use util::ResultExt;
use workspace::searchable::Direction;
use crate::{
motion::{start_of_line, Motion},
motion::{first_non_whitespace, next_line_end, start_of_line, Motion},
object::Object,
state::{Mode, Operator},
Vim,
@ -37,6 +37,8 @@ actions!(
SelectNextMatch,
SelectPreviousMatch,
RestoreVisualSelection,
VisualInsertEndOfLine,
VisualInsertFirstNonWhiteSpace,
]
);
@ -51,6 +53,8 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
vim.toggle_mode(Mode::VisualBlock, cx)
});
Vim::action(editor, cx, Vim::other_end);
Vim::action(editor, cx, Vim::visual_insert_end_of_line);
Vim::action(editor, cx, Vim::visual_insert_first_non_white_space);
Vim::action(editor, cx, |vim, _: &VisualDelete, cx| {
vim.record_current_action(cx);
vim.visual_delete(false, cx);
@ -374,6 +378,39 @@ impl Vim {
}
}
fn visual_insert_end_of_line(&mut self, _: &VisualInsertEndOfLine, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |_, editor, cx| {
editor.split_selection_into_lines(&Default::default(), cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(next_line_end(map, cursor, 1), SelectionGoal::None)
});
});
});
self.switch_mode(Mode::Insert, false, cx);
}
fn visual_insert_first_non_white_space(
&mut self,
_: &VisualInsertFirstNonWhiteSpace,
cx: &mut ViewContext<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
editor.split_selection_into_lines(&Default::default(), cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(
first_non_whitespace(map, false, cursor),
SelectionGoal::None,
)
});
});
});
self.switch_mode(Mode::Insert, false, cx);
}
fn toggle_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
if self.mode == mode {
self.switch_mode(Mode::Normal, false, cx);
@ -714,6 +751,52 @@ mod test {
ˇ"});
}
#[gpui::test]
async fn test_visual_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {
"«The quick brown
fox jumps over
the lazy dogˇ»"
},
Mode::Visual,
);
cx.simulate_keystrokes("g I");
cx.assert_state(
indoc! {
"ˇThe quick brown
ˇfox jumps over
ˇthe lazy dog"
},
Mode::Insert,
);
}
#[gpui::test]
async fn test_visual_insert_end_of_line(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {
"«The quick brown
fox jumps over
the lazy dogˇ»"
},
Mode::Visual,
);
cx.simulate_keystrokes("g A");
cx.assert_state(
indoc! {
"The quick brownˇ
fox jumps overˇ
the lazy dogˇ"
},
Mode::Insert,
);
}
#[gpui::test]
async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;