From 0fb19c0b5246eea9072dfd2265d64a206226edf1 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 16:21:58 +0800 Subject: [PATCH] fix: expand tabs to 8-column stops in diff and file views Replace the hard-coded two-space tab rendering with standard 8-column tab stops so hard tabs align like most editors and terminals. Fixes #2803 Co-authored-by: Cursor --- src/string_utils.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/string_utils.rs b/src/string_utils.rs index c2cb6808a4..4110ec20fa 100644 --- a/src/string_utils.rs +++ b/src/string_utils.rs @@ -1,5 +1,5 @@ use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; /// pub fn trim_length_left(s: &str, width: usize) -> &str { @@ -15,13 +15,36 @@ pub fn trim_length_left(s: &str, width: usize) -> &str { s } -//TODO: allow customize tabsize +const TAB_WIDTH: usize = 8; + +// TODO: allow customize tabsize (e.g. via .editorconfig) pub fn tabs_to_spaces(input: String) -> String { - if input.contains('\t') { - input.replace('\t', " ") - } else { - input + if !input.contains('\t') { + return input; } + + let mut out = String::with_capacity(input.len()); + let mut column = 0usize; + + for ch in input.chars() { + match ch { + '\t' => { + let spaces = TAB_WIDTH - (column % TAB_WIDTH); + out.extend(std::iter::repeat_n(' ', spaces)); + column += spaces; + } + '\n' => { + out.push('\n'); + column = 0; + } + ch => { + out.push(ch); + column += ch.width().unwrap_or(0); + } + } + } + + out } /// This function will return a str slice which start at specified offset. @@ -51,4 +74,13 @@ mod test { assert_eq!(trim_length_left("👍foo", 3), "foo"); assert_eq!(trim_length_left("👍foo", 4), "foo"); } + + #[test] + fn test_tabs_to_spaces() { + use super::tabs_to_spaces; + + assert_eq!(tabs_to_spaces("no-tabs".into()), "no-tabs"); + assert_eq!(tabs_to_spaces("\tfoo".into()), " foo"); + assert_eq!(tabs_to_spaces("a\tb".into()), "a b"); + } }