vim: Set current line as default sed command scope (#17234)

Horam Zarri created

Closes #16977

Release Notes:
- added current line as default sed range to match vim's behavior
- changed tests accordingly

This also simplifies `ReplaceCommand` implementation by changing
`Option<CommandRange>` to `CommandRange` .

Change summary

crates/vim/src/command.rs                      | 21 ++++++++++-----
crates/vim/src/normal/search.rs                | 28 +++++++++----------
crates/vim/test_data/test_command_replace.json | 15 ++++++++--
3 files changed, 39 insertions(+), 25 deletions(-)

Detailed changes

crates/vim/src/command.rs 🔗

@@ -656,13 +656,11 @@ pub fn command_interceptor(mut input: &str, cx: &AppContext) -> Option<CommandIn
             query.next();
         }
         if let Some(replacement) = Replacement::parse(query) {
-            Some(
-                ReplaceCommand {
-                    replacement,
-                    range: range.clone(),
-                }
-                .boxed_clone(),
-            )
+            let range = range.clone().unwrap_or(CommandRange {
+                start: Position::CurrentLine { offset: 0 },
+                end: None,
+            });
+            Some(ReplaceCommand { replacement, range }.boxed_clone())
         } else {
             None
         }
@@ -789,11 +787,13 @@ mod test {
         cx.set_shared_state(indoc! {"
             ˇa
             b
+            b
             c"})
             .await;
         cx.simulate_shared_keystrokes(": % s / b / d enter").await;
         cx.shared_state().await.assert_eq(indoc! {"
             a
+            d
             ˇd
             c"});
         cx.simulate_shared_keystrokes(": % s : . : \\ 0 \\ 0 enter")
@@ -801,7 +801,14 @@ mod test {
         cx.shared_state().await.assert_eq(indoc! {"
             aa
             dd
+            dd
             ˇcc"});
+        cx.simulate_shared_keystrokes("k : s / dd / ee enter").await;
+        cx.shared_state().await.assert_eq(indoc! {"
+            aa
+            dd
+            ˇee
+            cc"});
     }
 
     #[gpui::test]

crates/vim/src/normal/search.rs 🔗

@@ -42,7 +42,7 @@ pub struct FindCommand {
 
 #[derive(Debug, Clone, PartialEq, Deserialize)]
 pub struct ReplaceCommand {
-    pub(crate) range: Option<CommandRange>,
+    pub(crate) range: CommandRange,
     pub(crate) replacement: Replacement,
 }
 
@@ -338,20 +338,18 @@ impl Vim {
         else {
             return;
         };
-        if let Some(range) = &action.range {
-            if let Some(result) = self.update_editor(cx, |vim, editor, cx| {
-                let range = range.buffer_range(vim, editor, cx)?;
-                let snapshot = &editor.snapshot(cx).buffer_snapshot;
-                let end_point = Point::new(range.end.0, snapshot.line_len(range.end));
-                let range = snapshot.anchor_before(Point::new(range.start.0, 0))
-                    ..snapshot.anchor_after(end_point);
-                editor.set_search_within_ranges(&[range], cx);
-                anyhow::Ok(())
-            }) {
-                workspace.update(cx, |workspace, cx| {
-                    result.notify_err(workspace, cx);
-                })
-            }
+        if let Some(result) = self.update_editor(cx, |vim, editor, cx| {
+            let range = action.range.buffer_range(vim, editor, cx)?;
+            let snapshot = &editor.snapshot(cx).buffer_snapshot;
+            let end_point = Point::new(range.end.0, snapshot.line_len(range.end));
+            let range = snapshot.anchor_before(Point::new(range.start.0, 0))
+                ..snapshot.anchor_after(end_point);
+            editor.set_search_within_ranges(&[range], cx);
+            anyhow::Ok(())
+        }) {
+            workspace.update(cx, |workspace, cx| {
+                result.notify_err(workspace, cx);
+            })
         }
         let vim = cx.view().clone();
         pane.update(cx, |pane, cx| {

crates/vim/test_data/test_command_replace.json 🔗

@@ -1,4 +1,4 @@
-{"Put":{"state":"ˇa\nb\nc"}}
+{"Put":{"state":"ˇa\nb\nb\nc"}}
 {"Key":":"}
 {"Key":"%"}
 {"Key":"s"}
@@ -7,7 +7,7 @@
 {"Key":"/"}
 {"Key":"d"}
 {"Key":"enter"}
-{"Get":{"state":"a\nˇd\nc","mode":"Normal"}}
+{"Get":{"state":"a\nd\nˇd\nc","mode":"Normal"}}
 {"Key":":"}
 {"Key":"%"}
 {"Key":"s"}
@@ -19,4 +19,13 @@
 {"Key":"\\"}
 {"Key":"0"}
 {"Key":"enter"}
-{"Get":{"state":"aa\ndd\nˇcc","mode":"Normal"}}
+{"Get":{"state":"aa\ndd\ndd\nˇcc","mode":"Normal"}}
+{"Key":"k"}
+{"Key":":"}
+{"Key":"s"}
+{"Key":"/"}
+{"Key":"dd"}
+{"Key":"/"}
+{"Key":"ee"}
+{"Key":"enter"}
+{"Get":{"state":"aa\ndd\nˇee\ncc", "mode":"Normal"}}