diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 9328b0325f..597388368d 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -326,6 +326,7 @@ "bindings": { "i": "vim::InsertBefore", "a": "vim::InsertAfter", + "d": "vim::HelixDelete", "w": "vim::NextWordStart", "e": "vim::NextWordEnd", "b": "vim::PreviousWordStart", diff --git a/crates/vim/src/helix.rs b/crates/vim/src/helix.rs index 21abb5cbaa..3358538991 100644 --- a/crates/vim/src/helix.rs +++ b/crates/vim/src/helix.rs @@ -5,10 +5,11 @@ use ui::ViewContext; use crate::{motion::Motion, state::Mode, Vim}; -actions!(vim, [HelixNormalAfter]); +actions!(vim, [HelixNormalAfter, HelixDelete]); pub fn register(editor: &mut Editor, cx: &mut ViewContext) { Vim::action(editor, cx, Vim::helix_normal_after); + Vim::action(editor, cx, Vim::helix_delete); } impl Vim { @@ -226,6 +227,27 @@ impl Vim { _ => self.helix_move_and_collapse(motion, times, cx), } } + + pub fn helix_delete(&mut self, _: &HelixDelete, cx: &mut ViewContext) { + self.store_visual_marks(cx); + self.update_editor(cx, |vim, editor, cx| { + // Fixup selections so they have helix's semantics. + // Specifically: + // - Make sure that each cursor acts as a 1 character wide selection + editor.transact(cx, |editor, cx| { + editor.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.move_with(|map, selection| { + if selection.is_empty() && !selection.reversed { + selection.end = movement::right(map, selection.end); + } + }); + }); + }); + + vim.copy_selections_content(editor, false, cx); + editor.insert("", cx); + }); + } } #[cfg(test)] @@ -268,4 +290,84 @@ mod test { Mode::HelixNormal, ); } + + #[gpui::test] + async fn test_delete(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + // test delete a selection + cx.set_state( + indoc! {" + The qu«ick ˇ»brown + fox jumps over + the lazy dog."}, + Mode::HelixNormal, + ); + + cx.simulate_keystrokes("d"); + + cx.assert_state( + indoc! {" + The quˇbrown + fox jumps over + the lazy dog."}, + Mode::HelixNormal, + ); + + // test deleting a single character + cx.simulate_keystrokes("d"); + + cx.assert_state( + indoc! {" + The quˇrown + fox jumps over + the lazy dog."}, + Mode::HelixNormal, + ); + } + + #[gpui::test] + async fn test_delete_character_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::HelixNormal, + ); + + cx.simulate_keystrokes("d"); + + cx.assert_state( + indoc! {" + The quick brownˇfox jumps over + the lazy dog."}, + Mode::HelixNormal, + ); + } + + #[gpui::test] + async fn test_delete_character_end_of_buffer(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("d"); + + cx.assert_state( + indoc! {" + The quick brown + fox jumps over + the lazy dog.ˇ"}, + Mode::HelixNormal, + ); + } }