Skip to content

Expand tabs and replace control characters before measuring and drawing text [patch] - #202

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/159-control-characters
Oct 9, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/159-control-characters

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #159

What was wrong

Measuring, clipping and drawing all treated a tab as a one-cell character, but the terminal moves the cursor to the next tab stop. So "Name\tValue" in a border was measured narrower than it drew, and it overwrote the right border. Escape and other C0/C1 characters also went straight to the terminal, so any displayed text could clear the screen or move the cursor mid-frame.

Change

  • TextElement.NormalizeControlCharacters runs on every line in SplitIntoLines, so measuring and drawing see the same text:
    • a tab becomes spaces up to the next TabWidth (8) stop, counted from the start of its line
    • any other control character (C0, DEL, C1) becomes ControlCharacterPlaceholder (U+FFFD), which takes one cell
  • As defence in depth, SpectreConsoleProvider.WriteAt replaces any control character that still reaches it, such as text written to the provider directly.

Tests

  • TabInABorderMeasuresToTheWidthItDraws: "Name\tValue" measures 15×3 in a border, draws as Name Value, and stops before the border.
  • TabsExpandToTheNextTabStopOnEachLine: tab stops are counted per line.
  • ControlCharactersAreDrawnAsAPlaceholder: ESC, BEL, CSI (U+009B) and DEL are drawn as U+FFFD and measured to match.
  • TextElementWritesNoControlCharactersToTheTerminal and WriteAtReplacesControlCharacters: the captured ANSI output contains no ESC except the provider's own cursor moves.

With the fix reverted, all 8 new cases fail. With it applied, the full suite passes (261/261).

🤖 Generated with Claude Code

https://claude.ai/code/session_01GJ23oLAT8FnLaMo3MfBviR


Generated by Claude Code

…ing and drawing [patch]

A tab was measured as one cell but jumped the terminal cursor to the next tab
stop, so "Name\tValue" overwrote its border. An escape or other C0/C1
character reached the terminal raw, so text could clear the screen or move the
cursor mid-frame.

TextElement now expands tabs to the next 8-cell tab stop of each line and draws
every other control character as U+FFFD, in SplitIntoLines so measuring and
drawing agree. SpectreConsoleProvider.WriteAt replaces any control character
that still reaches it, as defence in depth.

Fixes #159

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GJ23oLAT8FnLaMo3MfBviR
…f their files

Keeps this change clear of the lines #204 edits, so the two merge
in either order without a conflict. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GJ23oLAT8FnLaMo3MfBviR
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 4351199 into main Oct 9, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/159-control-characters branch October 9, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants