vim: Make `cc` and `S` auto-indent (#9731)

Hans created

Fix #9612 

Release notes:

* Changed `cc` and `S` in Vim mode to only change the current line after
its indentation. #9612

Change summary

crates/vim/src/normal/change.rs          | 54 +++++++++++++++++++++++++
crates/vim/test_data/test_change_cc.json | 12 +++++
2 files changed, 65 insertions(+), 1 deletion(-)

Detailed changes

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

@@ -48,7 +48,25 @@ pub fn change_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &m
                             true,
                         )
                     } else {
-                        motion.expand_selection(map, selection, times, false, &text_layout_details)
+                        let result = motion.expand_selection(
+                            map,
+                            selection,
+                            times,
+                            false,
+                            &text_layout_details,
+                        );
+                        if let Motion::CurrentLine = motion {
+                            let scope = map
+                                .buffer_snapshot
+                                .language_scope_at(selection.start.to_point(&map));
+                            for (ch, _) in map.chars_at(selection.start) {
+                                if ch == '\n' || char_kind(&scope, ch) != CharKind::Whitespace {
+                                    break;
+                                }
+                                *selection.start.column_mut() += 1;
+                            }
+                        }
+                        result
                     };
                 });
             });
@@ -398,6 +416,40 @@ mod test {
         .await;
     }
 
+    #[gpui::test]
+    async fn test_change_cc(cx: &mut gpui::TestAppContext) {
+        let mut cx = NeovimBackedTestContext::new(cx).await;
+        cx.assert_neovim_compatible(
+            indoc! {"
+           The quick
+             brownˇ fox
+           jumps over
+           the lazy"},
+            ["c", "c"],
+        )
+        .await;
+
+        cx.assert_neovim_compatible(
+            indoc! {"
+           ˇThe quick
+           brown fox
+           jumps over
+           the lazy"},
+            ["c", "c"],
+        )
+        .await;
+
+        cx.assert_neovim_compatible(
+            indoc! {"
+           The quick
+             broˇwn fox
+           jumˇps over
+           the lazy"},
+            ["c", "c"],
+        )
+        .await;
+    }
+
     #[gpui::test]
     async fn test_change_gg(cx: &mut gpui::TestAppContext) {
         let mut cx = NeovimBackedTestContext::new(cx).await;

crates/vim/test_data/test_change_cc.json 🔗

@@ -0,0 +1,12 @@
+{"Put":{"state":"The quick\n  brownˇ fox\njumps over\nthe lazy"}}
+{"Key":"c"}
+{"Key":"c"}
+{"Get":{"state":"The quick\n  ˇ\njumps over\nthe lazy","mode":"Insert"}}
+{"Put":{"state":"ˇThe quick\nbrown fox\njumps over\nthe lazy"}}
+{"Key":"c"}
+{"Key":"c"}
+{"Get":{"state":"ˇ\nbrown fox\njumps over\nthe lazy","mode":"Insert"}}
+{"Put":{"state":"The quick\n  broˇwn fox\njumˇps over\nthe lazy"}}
+{"Key":"c"}
+{"Key":"c"}
+{"Get":{"state":"The quick\n  ˇ\nˇ\nthe lazy","mode":"Insert"}}