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:
parent
b69c6ee7df
commit
4a4d8c1cab
2 changed files with 86 additions and 1 deletions
|
@ -292,6 +292,8 @@
|
||||||
"g ctrl-x": ["vim::Decrement", { "step": true }],
|
"g ctrl-x": ["vim::Decrement", { "step": true }],
|
||||||
"shift-i": "vim::InsertBefore",
|
"shift-i": "vim::InsertBefore",
|
||||||
"shift-a": "vim::InsertAfter",
|
"shift-a": "vim::InsertAfter",
|
||||||
|
"g I": "vim::VisualInsertFirstNonWhiteSpace",
|
||||||
|
"g A": "vim::VisualInsertEndOfLine",
|
||||||
"shift-j": "vim::JoinLines",
|
"shift-j": "vim::JoinLines",
|
||||||
"r": ["vim::PushOperator", "Replace"],
|
"r": ["vim::PushOperator", "Replace"],
|
||||||
"ctrl-c": ["vim::SwitchMode", "Normal"],
|
"ctrl-c": ["vim::SwitchMode", "Normal"],
|
||||||
|
|
|
@ -15,7 +15,7 @@ use util::ResultExt;
|
||||||
use workspace::searchable::Direction;
|
use workspace::searchable::Direction;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
motion::{start_of_line, Motion},
|
motion::{first_non_whitespace, next_line_end, start_of_line, Motion},
|
||||||
object::Object,
|
object::Object,
|
||||||
state::{Mode, Operator},
|
state::{Mode, Operator},
|
||||||
Vim,
|
Vim,
|
||||||
|
@ -37,6 +37,8 @@ actions!(
|
||||||
SelectNextMatch,
|
SelectNextMatch,
|
||||||
SelectPreviousMatch,
|
SelectPreviousMatch,
|
||||||
RestoreVisualSelection,
|
RestoreVisualSelection,
|
||||||
|
VisualInsertEndOfLine,
|
||||||
|
VisualInsertFirstNonWhiteSpace,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,6 +53,8 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
||||||
vim.toggle_mode(Mode::VisualBlock, cx)
|
vim.toggle_mode(Mode::VisualBlock, cx)
|
||||||
});
|
});
|
||||||
Vim::action(editor, cx, Vim::other_end);
|
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::action(editor, cx, |vim, _: &VisualDelete, cx| {
|
||||||
vim.record_current_action(cx);
|
vim.record_current_action(cx);
|
||||||
vim.visual_delete(false, 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>) {
|
fn toggle_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
|
||||||
if self.mode == mode {
|
if self.mode == mode {
|
||||||
self.switch_mode(Mode::Normal, false, cx);
|
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]
|
#[gpui::test]
|
||||||
async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) {
|
async fn test_enter_visual_line_mode(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue