vim: Add basic 's' support

Conrad Irwin created

Change summary

assets/keymaps/vim.json  |  3 ++-
crates/vim/src/normal.rs | 26 ++++++++++++++++++++++++++
crates/vim/src/test.rs   | 10 ++++++++++
3 files changed, 38 insertions(+), 1 deletion(-)

Detailed changes

crates/vim/src/normal.rs 🔗

@@ -45,6 +45,7 @@ actions!(
         DeleteToEndOfLine,
         Paste,
         Yank,
+        Substitute,
     ]
 );
 
@@ -56,6 +57,12 @@ pub fn init(cx: &mut AppContext) {
     cx.add_action(insert_end_of_line);
     cx.add_action(insert_line_above);
     cx.add_action(insert_line_below);
+    cx.add_action(|_: &mut Workspace, _: &Substitute, cx| {
+        Vim::update(cx, |vim, cx| {
+            let times = vim.pop_number_operator(cx);
+            substitute(vim, times, cx);
+        })
+    });
     cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
         Vim::update(cx, |vim, cx| {
             let times = vim.pop_number_operator(cx);
@@ -471,6 +478,25 @@ pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
     });
 }
 
+pub fn substitute(vim: &mut Vim, count: usize, cx: &mut WindowContext) {
+    vim.update_active_editor(cx, |editor, cx| {
+        editor.transact(cx, |editor, cx| {
+            let selection = editor.selections.newest::<Point>(cx);
+
+            let end = if selection.start == selection.end {
+                selection.start + Point::new(0, 1)
+            } else {
+                selection.end
+            };
+
+            editor.buffer().update(cx, |buffer, cx| {
+                buffer.edit([(selection.start..end, "")], None, cx)
+            })
+        })
+    });
+    vim.switch_mode(Mode::Insert, true, cx)
+}
+
 #[cfg(test)]
 mod test {
     use gpui::TestAppContext;

crates/vim/src/test.rs 🔗

@@ -98,3 +98,13 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
         assert_eq!(bar.query_editor.read(cx).text(cx), "jumps");
     })
 }
+
+
+#[gpui::test]
+async fn test_substitute(cx: &mut gpui::TestAppContext) {
+    let mut cx = VimTestContext::new(cx, true).await;
+
+    cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
+    cx.simulate_keystrokes(["s", "x"]);
+    cx.assert_editor_state("xˇbc\n");
+}