Skip to content

fix(preview): expand tabs based on current line position#1371

Open
Br1an67 wants to merge 1 commit into
yorukot:mainfrom
Br1an67:fix/issue-1290-tab-expansion
Open

fix(preview): expand tabs based on current line position#1371
Br1an67 wants to merge 1 commit into
yorukot:mainfrom
Br1an67:fix/issue-1290-tab-expansion

Conversation

@Br1an67
Copy link
Copy Markdown

@Br1an67 Br1an67 commented Mar 9, 2026

Fixes #1290

Description

This PR fixes the tab expansion logic in the file preview functionality. Previously, tab characters were always replaced with exactly 4 spaces, regardless of the current position in the line. This caused misaligned columns when viewing files containing tabs.

The fix tracks the current display width while processing each line and expands tabs to reach the next tab stop position. This matches standard terminal behavior where tabs align to fixed column boundaries.

For example, with TabWidth=4:

  • At position 1: tab expands to 3 spaces (reaches column 4)
  • At position 3: tab expands to 1 space (reaches column 4)
  • At position 4: tab expands to 4 spaces (reaches column 8)

Related Issues

Fixes #1290

Screenshots (Optional)

Before: Tabs always expanded to 4 spaces, causing column misalignment
After: Tabs expand to reach the next tab stop, properly aligning columns


Pre-Submission Checklist

  • I have run go fmt ./... to format the code
  • I have run golangci-lint run and fixed any reported issues
  • I have tested my changes and verified they work as expected
  • I have reviewed the diff to make sure I'm not committing any debug logs or TODOs
  • I have checked that the PR title follows the Conventional Commits format

Diff stats:

 src/internal/common/string_function.go      | 15 ++++++++++++++-
 src/internal/common/string_function_test.go |  8 +++++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

Summary by CodeRabbit

  • Bug Fixes
    • More accurate tab expansion using dynamic tab stops for correct text alignment.
    • Improved character display width calculation, including non-breaking spaces and newline handling.
  • Tests
    • Updated and added test cases validating tab expansion, spacing, and control-character output.

Tab characters are now expanded to reach the next tab stop based on
the current display position in the line, rather than always expanding
to exactly 4 spaces. This matches standard terminal behavior where
tabs align to fixed column positions.

For example, with TabWidth=4:
- Position 1: tab expands to 3 spaces (reaches column 4)
- Position 3: tab expands to 1 space (reaches column 4)
- Position 4: tab expands to 4 spaces (reaches column 8)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

The change adds display-width tracking (curWidth) to MakePrintableWithEscCheck to compute tab expansion dynamically to the next tab stop (4 columns). NBSP increments width; non-control graphic characters update width by their display width; newlines reset curWidth.

Changes

Cohort / File(s) Summary
String formatting logic
src/internal/common/string_function.go
Adds curWidth tracking and replaces fixed 4-space tab expansion with dynamic expansion to the next tab stop (4) based on current line width; NBSP and graphic character widths update curWidth; newline resets curWidth.
Tests for tab behavior
src/internal/common/string_function_test.go
Updates existing tab/newline expectation and adds multiple tab-expansion test cases covering different pre-tab widths and chained tabs; preserves other control/ESC and NBSP tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibbled spaces, counted each stride,

Tabs now hop to the next tab-side.
NBSPs add to the parade,
Newlines clear the width we've made,
Hooray — alignment takes a proper ride! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing tab expansion to be position-aware rather than fixed-width.
Linked Issues check ✅ Passed The PR directly addresses issue #1290 by implementing dynamic tab expansion based on current line position, matching standard terminal behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing tab expansion logic in string processing and corresponding test updates; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/internal/common/string_function.go (1)

254-278: ⚠️ Potential issue | 🟠 Major

Don’t let ANSI escape payloads advance curWidth.

With allowEsc=true, Line 256 preserves \x1b, but Lines 271-278 still count the rest of the escape sequence ([1m, [38;...m, etc.) as visible width. That makes Line 249 expand a later tab from the wrong column. For example, MakePrintableWithEscCheck("\x1b[1mA\tB", true) will treat the tab as if it starts at column 4 instead of column 1, so the preview still misaligns for colored content. Please keep ANSI sequences in the output without incrementing curWidth, and add a regression case for a tab after an ANSI sequence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/internal/common/string_function.go` around lines 254 - 278, The code
currently writes ANSI escape sequences when allowEsc is true but still
increments curWidth for the bytes that follow the EscapeChar; update
MakePrintableWithEscCheck (and the loop using allowEsc/curWidth/EscapeChar) to,
upon seeing an EscapeChar and allowEsc==true, write the ESC and then
consume/write the rest of the CSI/escape sequence without modifying curWidth —
i.e., after writing ESC inspect subsequent runes and keep writing them until you
reach the ANSI sequence terminator (ASCII range '@'..'~', e.g. 'm' for SGR),
skipping any curWidth increments for those runes; also add a regression test
asserting MakePrintableWithEscCheck("\x1b[1mA\tB", true) treats the tab as
starting at column 1 (so the preview aligns correctly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/internal/common/string_function.go`:
- Around line 254-278: The code currently writes ANSI escape sequences when
allowEsc is true but still increments curWidth for the bytes that follow the
EscapeChar; update MakePrintableWithEscCheck (and the loop using
allowEsc/curWidth/EscapeChar) to, upon seeing an EscapeChar and allowEsc==true,
write the ESC and then consume/write the rest of the CSI/escape sequence without
modifying curWidth — i.e., after writing ESC inspect subsequent runes and keep
writing them until you reach the ANSI sequence terminator (ASCII range '@'..'~',
e.g. 'm' for SGR), skipping any curWidth increments for those runes; also add a
regression test asserting MakePrintableWithEscCheck("\x1b[1mA\tB", true) treats
the tab as starting at column 1 (so the preview aligns correctly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87e325cf-1355-4a9e-b8c4-12b71da3a5f2

📥 Commits

Reviewing files that changed from the base of the PR and between c48efc3 and 915caec.

📒 Files selected for processing (2)
  • src/internal/common/string_function.go
  • src/internal/common/string_function_test.go

@Br1an67 Br1an67 force-pushed the fix/issue-1290-tab-expansion branch from 915caec to 83684b0 Compare March 18, 2026 00:43
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/internal/common/string_function_test.go`:
- Around line 180-185: The inline comment for the test case {"a\tb\tc", "a   b  
c"} is incorrect about the second tab's start position; update the comment next
to that test entry (the line with the literal "a\tb\tc") to reflect that after
expanding the first tab and the 'b' the cursor is at position 5, so the second
tab needs 3 spaces to reach position 8 (e.g. change the comment to something
like "// Position 1: 3 spaces, then position 5: 3 spaces to reach 8").

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45218cbe-a644-453e-80f2-bd821d0ba853

📥 Commits

Reviewing files that changed from the base of the PR and between 915caec and 83684b0.

📒 Files selected for processing (2)
  • src/internal/common/string_function.go
  • src/internal/common/string_function_test.go

Comment on lines +180 to +185
// Tab expansion tests - tabs should expand to reach next tab stop (TabWidth=4)
{"a\tb", "a b"}, // Position 1: need 3 spaces to reach position 4
{"ab\tc", "ab c"}, // Position 2: need 2 spaces to reach position 4
{"abc\td", "abc d"}, // Position 3: need 1 space to reach position 4
{"abcd\te", "abcd e"}, // Position 4: need 4 spaces to reach position 8
{"a\tb\tc", "a b c"}, // Position 1: 3 spaces, then position 4: 4 spaces to reach 8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test cases are correct, but comment on line 185 is inaccurate.

The test logic and expected outputs are correct, but the comment on line 185 is misleading:

// Position 1: 3 spaces, then position 4: 4 spaces to reach 8

After the first tab expansion and 'b', the position is 5 (not 4). At position 5, 4 - (5 % 4) = 3 spaces are needed to reach position 8, not 4 spaces.

📝 Suggested comment fix
-		{"a\tb\tc", "a   b   c"}, // Position 1: 3 spaces, then position 4: 4 spaces to reach 8
+		{"a\tb\tc", "a   b   c"}, // Position 1: 3 spaces to reach 4, then position 5: 3 spaces to reach 8
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/internal/common/string_function_test.go` around lines 180 - 185, The
inline comment for the test case {"a\tb\tc", "a   b   c"} is incorrect about the
second tab's start position; update the comment next to that test entry (the
line with the literal "a\tb\tc") to reflect that after expanding the first tab
and the 'b' the cursor is at position 5, so the second tab needs 3 spaces to
reach position 8 (e.g. change the comment to something like "// Position 1: 3
spaces, then position 5: 3 spaces to reach 8").

Copy link
Copy Markdown
Owner

@yorukot yorukot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Br1an67 Thanks. I think this should be handled in ReadFileContent(...), not in MakePrintableWithEscCheck(...).

MakePrintableWithEscCheck(...) is a shared sanitization helper, but this problem is in the preview path. Preview content gets read and truncated before rendering, so expanding tabs there feels more correct and avoids changing global behavior.

@yorukot yorukot added pr needs work PR needs work before it can be further reviewed/merged and removed pr needs work PR needs work before it can be further reviewed/merged awaiting pr review labels Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr needs work PR needs work before it can be further reviewed/merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

superfile doesn't expands tabs correctly

2 participants