Improve Helix insert (#34765)
Closes #34763 Release Notes: - Improved insert in `helix_mode` when a selection exists to better match helix's behavior: collapse selection to avoid replacing it - Improved append (`insert_after`) to better match helix's behavior: move cursor to end of selection if it exists
This commit is contained in:
parent
8b0ec287a5
commit
a6956eebcb
2 changed files with 112 additions and 4 deletions
|
@ -220,6 +220,8 @@
|
||||||
{
|
{
|
||||||
"context": "vim_mode == normal",
|
"context": "vim_mode == normal",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"i": "vim::InsertBefore",
|
||||||
|
"a": "vim::InsertAfter",
|
||||||
"ctrl-[": "editor::Cancel",
|
"ctrl-[": "editor::Cancel",
|
||||||
":": "command_palette::Toggle",
|
":": "command_palette::Toggle",
|
||||||
"c": "vim::PushChange",
|
"c": "vim::PushChange",
|
||||||
|
@ -353,9 +355,7 @@
|
||||||
"shift-d": "vim::DeleteToEndOfLine",
|
"shift-d": "vim::DeleteToEndOfLine",
|
||||||
"shift-j": "vim::JoinLines",
|
"shift-j": "vim::JoinLines",
|
||||||
"shift-y": "vim::YankLine",
|
"shift-y": "vim::YankLine",
|
||||||
"i": "vim::InsertBefore",
|
|
||||||
"shift-i": "vim::InsertFirstNonWhitespace",
|
"shift-i": "vim::InsertFirstNonWhitespace",
|
||||||
"a": "vim::InsertAfter",
|
|
||||||
"shift-a": "vim::InsertEndOfLine",
|
"shift-a": "vim::InsertEndOfLine",
|
||||||
"o": "vim::InsertLineBelow",
|
"o": "vim::InsertLineBelow",
|
||||||
"shift-o": "vim::InsertLineAbove",
|
"shift-o": "vim::InsertLineAbove",
|
||||||
|
@ -377,6 +377,8 @@
|
||||||
{
|
{
|
||||||
"context": "vim_mode == helix_normal && !menu",
|
"context": "vim_mode == helix_normal && !menu",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"i": "vim::HelixInsert",
|
||||||
|
"a": "vim::HelixAppend",
|
||||||
"ctrl-[": "editor::Cancel",
|
"ctrl-[": "editor::Cancel",
|
||||||
";": "vim::HelixCollapseSelection",
|
";": "vim::HelixCollapseSelection",
|
||||||
":": "command_palette::Toggle",
|
":": "command_palette::Toggle",
|
||||||
|
|
|
@ -4,18 +4,28 @@ use gpui::{Context, Window};
|
||||||
use language::{CharClassifier, CharKind};
|
use language::{CharClassifier, CharKind};
|
||||||
use text::SelectionGoal;
|
use text::SelectionGoal;
|
||||||
|
|
||||||
use crate::{Vim, motion::Motion, state::Mode};
|
use crate::{
|
||||||
|
Vim,
|
||||||
|
motion::{Motion, right},
|
||||||
|
state::Mode,
|
||||||
|
};
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
vim,
|
vim,
|
||||||
[
|
[
|
||||||
/// Switches to normal mode after the cursor (Helix-style).
|
/// Switches to normal mode after the cursor (Helix-style).
|
||||||
HelixNormalAfter
|
HelixNormalAfter,
|
||||||
|
/// Inserts at the beginning of the selection.
|
||||||
|
HelixInsert,
|
||||||
|
/// Appends at the end of the selection.
|
||||||
|
HelixAppend,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
||||||
Vim::action(editor, cx, Vim::helix_normal_after);
|
Vim::action(editor, cx, Vim::helix_normal_after);
|
||||||
|
Vim::action(editor, cx, Vim::helix_insert);
|
||||||
|
Vim::action(editor, cx, Vim::helix_append);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vim {
|
impl Vim {
|
||||||
|
@ -299,6 +309,38 @@ impl Vim {
|
||||||
_ => self.helix_move_and_collapse(motion, times, window, cx),
|
_ => self.helix_move_and_collapse(motion, times, window, cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn helix_insert(&mut self, _: &HelixInsert, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.start_recording(cx);
|
||||||
|
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||||
|
editor.change_selections(Default::default(), window, cx, |s| {
|
||||||
|
s.move_with(|_map, selection| {
|
||||||
|
// In helix normal mode, move cursor to start of selection and collapse
|
||||||
|
if !selection.is_empty() {
|
||||||
|
selection.collapse_to(selection.start, SelectionGoal::None);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
self.switch_mode(Mode::Insert, false, window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helix_append(&mut self, _: &HelixAppend, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.start_recording(cx);
|
||||||
|
self.switch_mode(Mode::Insert, false, window, cx);
|
||||||
|
self.update_editor(window, cx, |_, editor, window, cx| {
|
||||||
|
editor.change_selections(Default::default(), window, cx, |s| {
|
||||||
|
s.move_with(|map, selection| {
|
||||||
|
let point = if selection.is_empty() {
|
||||||
|
right(map, selection.head(), 1)
|
||||||
|
} else {
|
||||||
|
selection.end
|
||||||
|
};
|
||||||
|
selection.collapse_to(point, SelectionGoal::None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -497,4 +539,68 @@ mod test {
|
||||||
|
|
||||||
cx.assert_state("«ˇaa»\n", Mode::HelixNormal);
|
cx.assert_state("«ˇaa»\n", Mode::HelixNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_insert_selected(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::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.simulate_keystrokes("i");
|
||||||
|
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
ˇThe quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog."},
|
||||||
|
Mode::Insert,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_append(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
// test from the end of the selection
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
«Theˇ» quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog."},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.simulate_keystrokes("a");
|
||||||
|
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
Theˇ quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog."},
|
||||||
|
Mode::Insert,
|
||||||
|
);
|
||||||
|
|
||||||
|
// test from the beginning of the selection
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
«ˇThe» quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog."},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.simulate_keystrokes("a");
|
||||||
|
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
Theˇ quick brown
|
||||||
|
fox jumps over
|
||||||
|
the lazy dog."},
|
||||||
|
Mode::Insert,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue