From 2e56935997aeb7e0b2b11368dc03ba943472f22c Mon Sep 17 00:00:00 2001 From: Rodrigo Freire <109775603+rodrigoFfreire@users.noreply.github.com> Date: Wed, 9 Apr 2025 17:22:14 +0100 Subject: [PATCH] Fix invalid number of space characters inserted for tab (#27336) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #25941 Release Notes: - Corrected SoftTab indentation handling for lines with mixed spaces and tabs across .go files and other file types. - Renamed the editor test `test_tab_with_mixed_whitespace` to `test_tab_with_mixed_whitespace_rust` as it only tested this behavior for Rust buffers, which have auto-indentation support. This change clarifies that the test does not cover default files without language-specific features. - Added a new editor test `test_tab_with_mixed_whitespace_txt` to ensure proper coverage for files with no associated language. While investigating the issue — initially thought to be Go-related — I discovered that the underlying problem was how soft tabs were calculated in `Editor::tab`, given that the problem could also be observed on `.txt` files The correct soft tab indentation is now determined by treating all `\t` characters before the cursor (on the same row) as new indentation levels, resetting the remainder counter accordingly. https://github.com/user-attachments/assets/78192e98-2b81-43cb-ae6f-7c48cd17d168 --- crates/editor/src/editor.rs | 14 ++++++++++---- crates/editor/src/editor_tests.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6290a0606a27dd7e9e78fd877c7c4fbbfab738b7..9fe0e81ab0a4d4b5b71267a867dda4ff38150577 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8209,12 +8209,18 @@ impl Editor { IndentSize::tab() } else { let tab_size = settings.tab_size.get(); - let char_column = snapshot + let indent_remainder = snapshot .text_for_range(Point::new(cursor.row, 0)..cursor) .flat_map(str::chars) - .count() - + row_delta as usize; - let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size); + .fold(row_delta % tab_size, |counter: u32, c| { + if c == '\t' { + 0 + } else { + (counter + 1) % tab_size + } + }); + + let chars_to_next_tab_stop = tab_size - indent_remainder; IndentSize::spaces(chars_to_next_tab_stop) }; selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 761add2715d62a7ca747df0cbbef700783c420bb..68eff95163b8b7a5be9717f619ac3d44f5462113 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2918,7 +2918,32 @@ async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppConte } #[gpui::test] -async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) { +async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) { + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(3) + }); + + let mut cx = EditorTestContext::new(cx).await; + cx.set_state(indoc! {" + ˇ + \t ˇ + \t ˇ + \t ˇ + \t \t\t \t \t\t \t\t \t \t ˇ + "}); + + cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx)); + cx.assert_editor_state(indoc! {" + ˇ + \t ˇ + \t ˇ + \t ˇ + \t \t\t \t \t\t \t\t \t \t ˇ + "}); +} + +#[gpui::test] +async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) { init_test(cx, |settings| { settings.defaults.tab_size = NonZeroU32::new(4) });