vim: Fix change surround adding unwanted spaces with quotes (#42431)

Hans and dino created

Update `Vim.change_surround` in order to ensure that there's no
overlapping edits by keeping track of where the open string range ends
and ensuring that the closing string range start does not go lower than
the open string range end.

Closes #42316 

Release Notes:

- Fix vim's change surrounds `cs` inserting spaces with quotes by
preventing overlapping edits

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>

Change summary

crates/vim/src/surrounds.rs | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)

Detailed changes

crates/vim/src/surrounds.rs 🔗

@@ -282,6 +282,7 @@ impl Vim {
                             // that the end replacement string does not exceed
                             // this value. Helpful when dealing with newlines.
                             let mut edit_len = 0;
+                            let mut open_range_end = 0;
                             let mut chars_and_offset = display_map
                                 .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left))
                                 .peekable();
@@ -290,11 +291,11 @@ impl Vim {
                                 if ch.to_string() == will_replace_pair.start {
                                     let mut open_str = pair.start.clone();
                                     let start = offset;
-                                    let mut end = start + 1;
+                                    open_range_end = start + 1;
                                     while let Some((next_ch, _)) = chars_and_offset.next()
-                                        && next_ch.to_string() == " "
+                                        && next_ch == ' '
                                     {
-                                        end += 1;
+                                        open_range_end += 1;
 
                                         if preserve_space {
                                             open_str.push(next_ch);
@@ -305,8 +306,8 @@ impl Vim {
                                         open_str.push(' ');
                                     };
 
-                                    edit_len = end - start;
-                                    edits.push((start..end, open_str));
+                                    edit_len = open_range_end - start;
+                                    edits.push((start..open_range_end, open_str));
                                     anchors.push(start..start);
                                     break;
                                 }
@@ -323,8 +324,9 @@ impl Vim {
                                     let mut start = offset;
                                     let end = start + 1;
                                     while let Some((next_ch, _)) = reverse_chars_and_offsets.next()
-                                        && next_ch.to_string() == " "
+                                        && next_ch == ' '
                                         && close_str.len() < edit_len - 1
+                                        && start > open_range_end
                                     {
                                         start -= 1;
 
@@ -1236,6 +1238,23 @@ mod test {
             Mode::Normal,
         );
 
+        // test spaces with quote change surrounds
+        cx.set_state(
+            indoc! {"
+            fn test_surround() {
+                \"ˇ \"
+            };"},
+            Mode::Normal,
+        );
+        cx.simulate_keystrokes("c s \" '");
+        cx.assert_state(
+            indoc! {"
+            fn test_surround() {
+                ˇ' '
+            };"},
+            Mode::Normal,
+        );
+
         // Currently, the same test case but using the closing bracket `]`
         // actually removes a whitespace before the closing bracket, something
         // that might need to be fixed?