Skip to content

Fix text-presentation checkmark width for Docker progress output#5

Merged
tomlm merged 4 commits into
tomlm:mainfrom
jchristn:fix/docker-progress-padding
Jun 22, 2026
Merged

Fix text-presentation checkmark width for Docker progress output#5
tomlm merged 4 commits into
tomlm:mainfrom
jchristn:fix/docker-progress-padding

Conversation

@jchristn

Copy link
Copy Markdown
Contributor

Summary

This PR fixes Docker Compose progress-row alignment by correcting how XTerm.NET calculates the cell width of text-presentation emoji-capable characters.

The issue was identified while integrating XTerm.NET through Termrig's Avalonia terminal control:

Root Cause

InputHandler.GetStringCellWidth previously treated every code point classified by NeoSmart.Unicode.Emoji.IsEmoji(...) as width 2:

int runeWidth = Emoji.IsEmoji(rune.ToString()) ? 2 : UnicodeCalculator.GetWidth(rune);

That is too broad for characters that can be rendered in either text or emoji presentation. Docker Compose emits U+2714 HEAVY CHECK MARK (\u2714) as a plain text-presentation character, without U+FE0F. Windows cmd.exe renders that glyph as a single terminal cell. XTerm.NET counted it as two cells, which shifted the rest of the row by one column.

The visible symptom in Termrig was a missing alignment space before Docker's status text:

Expected/cmd.exe:
 ? Network docker_default              Created

Before this fix in XTerm.NET-backed rendering:
 ? Network docker_default             Created

What Changed

This PR changes the base width calculation to use Wcwidth.UnicodeCalculator.GetWidth(rune) and preserves the existing variation-selector adjustments:

  • \u2714 is width 1.
  • \u2714\uFE0F remains width 2 because U+FE0F explicitly requests emoji presentation.
  • Existing CJK/wide-character behavior continues to come from UnicodeCalculator.
  • Existing variation selector, ZWJ, skin tone, keycap, and regional indicator logic remains in place.

Regression Coverage

The PR adds tests for:

  • Plain checkmark text presentation: \u2714X advances two cells total and stores the checkmark as width 1.
  • Explicit emoji presentation: \u2714\uFE0FX advances three cells total and stores the checkmark as a width-2 glyph with a spacer.
  • Docker-style progress alignment using a checkmark prefix, cursor-forward movement, and Created status text.
  • CSI Ps X erase-character preserving cursor position.
  • CSI Ps C cursor-forward clamping at the right margin without wrapping.

The CSI Ps X and CSI Ps C behavior already appears correct in the current code; those tests document the VT semantics that mattered during the Docker progress investigation and protect against future regressions.

Reproduction

Run Docker Compose in a terminal embedding XTerm.NET:

cd C:\Code\Pneuma\docker
docker compose up -d
docker compose down

Docker emits progress rows containing text-presentation checkmarks and column-sensitive status output. When \u2714 is counted as two cells, the status column is shifted.

A minimal library-level reproduction is included in the tests:

var terminal = new Terminal(new TerminalOptions { Cols = 20, Rows = 3 });
terminal.Write("\u2714X");

Assert.Equal(2, terminal.Buffer.X);
Assert.Equal(1, terminal.Buffer.Lines[0]?[0].Width);
Assert.Equal(1, terminal.Buffer.Lines[0]?[1].Width);

Report

I also included FIXES.md in this branch with the investigation notes, reproduction examples, Termrig links, and validation details so the reasoning is preserved in the repository while reviewing this PR.

Validation

Run from the repository root:

dotnet test src/XTerm.NET.slnx

Result locally:

Passed: 589
Failed: 0
Skipped: 0

@jchristn

Copy link
Copy Markdown
Contributor Author

Note: I merged fix/terminal in Termrig, so that branch is no longer present (everything is in main on Termrig).

@jchristn

Copy link
Copy Markdown
Contributor Author

Also, thanks for producing this fantastic library!

Copilot AI left a comment

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.

Pull request overview

This PR fixes a terminal cell-width miscalculation for text-presentation emoji-capable characters (notably U+2714 HEAVY CHECK MARK) to prevent Docker Compose progress-row misalignment when rendering through XTerm.NET.

Changes:

  • Update InputHandler.GetStringCellWidth to rely on Wcwidth.UnicodeCalculator.GetWidth instead of broadly forcing NeoSmart.Unicode.Emoji.IsEmoji(...) to width 2.
  • Add regression tests covering checkmark presentation width, Docker-style status column alignment, CSI Ps X (ECH) cursor preservation, and CSI Ps C (CUF) right-margin clamping.
  • Add/update investigation and reproduction notes in FIXES.md.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/XTerm.NET/InputHandler.cs Removes broad emoji-based width override so text-presentation symbols like U+2714 compute correct cell widths.
src/XTerm.NET.Tests/InputHandlerTests.cs Adds regression coverage for checkmark width handling and VT cursor/erase semantics relevant to Docker progress output.
FIXES.md Documents the investigation, reproduction steps, and validation notes for the Docker progress alignment issue.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread FIXES.md
Comment thread FIXES.md Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@tomlm tomlm merged commit ff3cc2b into tomlm:main Jun 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants