From 9cbb698b9693a79b0983ce79552a52d0b6b988b2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 27 Oct 2022 15:33:49 -0700 Subject: [PATCH] Fix panic when hitting tab at the beginning of a line with mixed tab/space indent --- 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(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index ccae9db92b..5f26ddb3b8 100644 --- a/crates/editor/src/editor_tests.rs +++ b/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.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); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5a75f1a56f..e99f643e62 100644 --- a/crates/language/src/buffer.rs +++ b/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, String)> { - if new_size.kind != current_size.kind && current_size.len > 0 { - return None; - } + 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::(), + )) + } else { + match new_size.len.cmp(¤t_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::(), + )) + } - match new_size.len.cmp(¤t_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::(), - )) + Ordering::Less => Some(( + Point::new(row, 0)..Point::new(row, current_size.len - new_size.len), + String::new(), + )), + + Ordering::Equal => None, } - - Ordering::Less => Some(( - Point::new(row, 0)..Point::new(row, current_size.len - new_size.len), - String::new(), - )), - - Ordering::Equal => None, } } diff --git a/script/bump-zed-minor-versions b/script/bump-zed-minor-versions index 40009d382a..d63210d4f3 100755 --- a/script/bump-zed-minor-versions +++ b/script/bump-zed-minor-versions @@ -92,6 +92,7 @@ cat <