Merge pull request #1838 from zed-industries/tab-panic

Max Brunsfeld created

Fix panic when hitting tab at the beginning of a line with mixed tab/…

Change summary

crates/editor/src/editor_tests.rs | 39 +++++++++++++++++++++++++++
crates/language/src/buffer.rs     | 46 +++++++++++++++++++-------------
script/bump-zed-minor-versions    |  3 ++
script/lib/bump-version.sh        |  7 ++--
4 files changed, 72 insertions(+), 23 deletions(-)

Detailed changes

crates/editor/src/editor_tests.rs 🔗

@@ -1566,7 +1566,7 @@ async fn test_tab(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
+async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
     let mut cx = EditorTestContext::new(cx);
     let language = Arc::new(
         Language::new(
@@ -1623,6 +1623,43 @@ async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
     "});
 }
 
+#[gpui::test]
+async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
+    let mut cx = EditorTestContext::new(cx);
+    let language = Arc::new(
+        Language::new(
+            LanguageConfig::default(),
+            Some(tree_sitter_rust::language()),
+        )
+        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
+        .unwrap(),
+    );
+    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+    cx.update(|cx| {
+        cx.update_global::<Settings, _, _>(|settings, _| {
+            settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
+        });
+    });
+
+    cx.set_state(indoc! {"
+        fn a() {
+            if b {
+        \t ˇc
+            }
+        }
+    "});
+
+    cx.update_editor(|e, cx| e.tab(&Tab, cx));
+    cx.assert_editor_state(indoc! {"
+        fn a() {
+            if b {
+                ˇc
+            }
+        }
+    "});
+}
+
 #[gpui::test]
 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
     let mut cx = EditorTestContext::new(cx);

crates/language/src/buffer.rs 🔗

@@ -1068,32 +1068,40 @@ impl Buffer {
         self.edit(edits, None, cx);
     }
 
+    // Create a minimal edit that will cause the the given row to be indented
+    // with the given size. After applying this edit, the length of the line
+    // will always be at least `new_size.len`.
     pub fn edit_for_indent_size_adjustment(
         row: u32,
         current_size: IndentSize,
         new_size: IndentSize,
     ) -> Option<(Range<Point>, String)> {
-        if new_size.kind != current_size.kind && current_size.len > 0 {
-            return None;
-        }
-
-        match new_size.len.cmp(&current_size.len) {
-            Ordering::Greater => {
-                let point = Point::new(row, 0);
-                Some((
-                    point..point,
-                    iter::repeat(new_size.char())
-                        .take((new_size.len - current_size.len) as usize)
-                        .collect::<String>(),
-                ))
-            }
+        if new_size.kind != current_size.kind {
+            Some((
+                Point::new(row, 0)..Point::new(row, current_size.len),
+                iter::repeat(new_size.char())
+                    .take(new_size.len as usize)
+                    .collect::<String>(),
+            ))
+        } else {
+            match new_size.len.cmp(&current_size.len) {
+                Ordering::Greater => {
+                    let point = Point::new(row, 0);
+                    Some((
+                        point..point,
+                        iter::repeat(new_size.char())
+                            .take((new_size.len - current_size.len) as usize)
+                            .collect::<String>(),
+                    ))
+                }
 
-            Ordering::Less => Some((
-                Point::new(row, 0)..Point::new(row, current_size.len - new_size.len),
-                String::new(),
-            )),
+                Ordering::Less => Some((
+                    Point::new(row, 0)..Point::new(row, current_size.len - new_size.len),
+                    String::new(),
+                )),
 
-            Ordering::Equal => None,
+                Ordering::Equal => None,
+            }
         }
     }
 

script/bump-zed-minor-versions 🔗

@@ -92,6 +92,7 @@ cat <<MESSAGE
 Prepared new Zed versions locally.
 
 To push this:
+
     git push origin \\
       ${preview_tag_name} \\
       ${stable_tag_name} \\
@@ -100,9 +101,11 @@ To push this:
       main
 
 To undo this:
+
     git reset --hard ${old_main_sha} && git push -f . \\
       :${preview_tag_name} \\
       :${stable_tag_name} \\
       :${minor_branch_name} \\
       ${old_prev_minor_sha}:${prev_minor_branch_name}
+
 MESSAGE

script/lib/bump-version.sh 🔗

@@ -28,10 +28,11 @@ cat <<MESSAGE
 Locally committed and tagged ${package} version ${new_version}
 
 To push this:
-    git push origin \\
-      ${tag_name} \\
-      ${branch_name}
+
+    git push origin ${tag_name} ${branch_name}
 
 To undo this:
+
     git reset --hard ${old_sha} && git tag -d ${tag_name}
+
 MESSAGE