mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Add a helix-specific substitute method (#38735)
`vim::Substitute` is a little different from the helix behavior, so this PR adds helix versions. The most important difference (for my usage, at least) is that if you're selecting whole lines then helix drops the `\n` from the selection (much like vim's lines mode, except that helix bases this behavior on the selection instead of having a different mode). Release Notes: - N/A
This commit is contained in:
parent
c5a67d85ab
commit
58ff46962d
2 changed files with 120 additions and 2 deletions
|
|
@ -497,7 +497,8 @@
|
|||
"shift-u": "editor::Redo",
|
||||
"ctrl-c": "editor::ToggleComments",
|
||||
"d": "vim::HelixDelete",
|
||||
"c": "vim::Substitute",
|
||||
"c": "vim::HelixSubstitute",
|
||||
"alt-c": "vim::HelixSubstituteNoYank",
|
||||
"shift-c": "vim::HelixDuplicateBelow",
|
||||
"alt-shift-c": "vim::HelixDuplicateAbove",
|
||||
",": "vim::HelixKeepNewestSelection"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use text::{Bias, SelectionGoal};
|
|||
use workspace::searchable;
|
||||
use workspace::searchable::FilteredSearchRange;
|
||||
|
||||
use crate::motion;
|
||||
use crate::motion::{self, MotionKind};
|
||||
use crate::state::SearchState;
|
||||
use crate::{
|
||||
Vim,
|
||||
|
|
@ -48,6 +48,10 @@ actions!(
|
|||
HelixDuplicateBelow,
|
||||
/// Copies all selections above.
|
||||
HelixDuplicateAbove,
|
||||
/// Delete the selection and enter edit mode.
|
||||
HelixSubstitute,
|
||||
/// Delete the selection and enter edit mode, without yanking the selection.
|
||||
HelixSubstituteNoYank,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -68,6 +72,8 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
let times = Vim::take_count(cx);
|
||||
vim.helix_duplicate_selections_above(times, window, cx);
|
||||
});
|
||||
Vim::action(editor, cx, Vim::helix_substitute);
|
||||
Vim::action(editor, cx, Vim::helix_substitute_no_yank);
|
||||
}
|
||||
|
||||
impl Vim {
|
||||
|
|
@ -604,6 +610,54 @@ impl Vim {
|
|||
editor.change_selections(Default::default(), window, cx, |s| s.select(vec![newest]));
|
||||
});
|
||||
}
|
||||
|
||||
fn do_helix_substitute(&mut self, yank: bool, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.update_editor(cx, |vim, editor, cx| {
|
||||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if selection.start == selection.end {
|
||||
selection.end = movement::right(map, selection.end);
|
||||
}
|
||||
|
||||
// If the selection starts and ends on a newline, we exclude the last one.
|
||||
if !selection.is_empty()
|
||||
&& selection.start.column() == 0
|
||||
&& selection.end.column() == 0
|
||||
{
|
||||
selection.end = movement::left(map, selection.end);
|
||||
}
|
||||
})
|
||||
});
|
||||
if yank {
|
||||
vim.copy_selections_content(editor, MotionKind::Exclusive, window, cx);
|
||||
}
|
||||
let selections = editor.selections.all::<Point>(cx).into_iter();
|
||||
let edits = selections.map(|selection| (selection.start..selection.end, ""));
|
||||
editor.edit(edits, cx);
|
||||
});
|
||||
});
|
||||
self.switch_mode(Mode::Insert, true, window, cx);
|
||||
}
|
||||
|
||||
fn helix_substitute(
|
||||
&mut self,
|
||||
_: &HelixSubstitute,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.do_helix_substitute(true, window, cx);
|
||||
}
|
||||
|
||||
fn helix_substitute_no_yank(
|
||||
&mut self,
|
||||
_: &HelixSubstituteNoYank,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.do_helix_substitute(false, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -1241,4 +1295,67 @@ mod test {
|
|||
cx.simulate_keystrokes("s o n e enter");
|
||||
cx.assert_state("ˇone two one", Mode::HelixNormal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_helix_substitute(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
cx.set_state("ˇone two", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("c");
|
||||
cx.assert_state("ˇne two", Mode::Insert);
|
||||
|
||||
cx.set_state("«oneˇ» two", Mode::HelixNormal);
|
||||
cx.simulate_keystrokes("c");
|
||||
cx.assert_state("ˇ two", Mode::Insert);
|
||||
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
oneˇ two
|
||||
three
|
||||
"},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
cx.simulate_keystrokes("x c");
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
ˇ
|
||||
three
|
||||
"},
|
||||
Mode::Insert,
|
||||
);
|
||||
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
one twoˇ
|
||||
three
|
||||
"},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
cx.simulate_keystrokes("c");
|
||||
cx.assert_state(
|
||||
indoc! {"
|
||||
one twoˇthree
|
||||
"},
|
||||
Mode::Insert,
|
||||
);
|
||||
|
||||
// Helix doesn't set the cursor to the first non-blank one when
|
||||
// replacing lines: it uses language-dependent indent queries instead.
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
one two
|
||||
« indented
|
||||
three not indentedˇ»
|
||||
"},
|
||||
Mode::HelixNormal,
|
||||
);
|
||||
cx.simulate_keystrokes("c");
|
||||
cx.set_state(
|
||||
indoc! {"
|
||||
one two
|
||||
ˇ
|
||||
"},
|
||||
Mode::Insert,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue