Repository navigation
fix(terminal): render country flag emoji as one glyph - #10732
Closed
amirerfantim wants to merge 2 commits into
Closed
amirerfantim wants to merge 2 commits into
amirerfantim wants to merge 2 commits into
Conversation
Country flags are two adjacent regional indicators with no ZWJ, so the ZWJ-only join rule in OrcaUnicodeProvider left each indicator in its own cell. Chromium then shaped them separately and painted two lettered tiles instead of the flag. Pair regional indicators into a single two-cell cluster, resetting charKind on the pair so a third indicator opens the next flag rather than chaining. Two cells matches the pair's total wcwidth (each indicator is East_Asian_Width Neutral), so cursor budgeting stays in step with CLIs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
📝 WalkthroughWalkthroughAdds regional-indicator detection and pairing to terminal Unicode width handling. New tests cover flag clustering, consecutive flag pairs, unpaired indicators, cell budgeting, cursor positioning, ZWJ emoji sequences, and other wide glyphs. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #10731.
User-visible change
Country flag emoji (🇺🇸 🇩🇪 🇯🇵) rendered as two lettered tiles in the terminal instead of the flag. They now render as a single flag glyph.
Visual change: yes, terminal glyph rendering only — no UI/layout/component change. Before:
printf '🇺🇸 🇩🇪 🇯🇵'paints boxed letter pairs (🇺 🇸). After: it paints the flags. No screenshot attached because the difference is font shaping in the terminal grid; the cell-level assertions interminal-unicode-provider.test.tscapture it precisely (the row string is identical either way — only the cell clustering differs, which is exactly why this went unnoticed).Root cause
A country flag is two adjacent Regional Indicator Symbols (U+1F1E6–U+1F1FF) with no ZWJ — e.g. 🇺🇸 = U+1F1FA U+1F1F8.
OrcaUnicodeProvider.charPropertiesonly joins across ZWJ, so each indicator stayed in its own cell. Chromium then shaped each one in isolation and drew a lone letter tile rather than shaping the pair into a flag.Note the indicators are not in xterm's Unicode 11 wide table (
wcwidth= 1 each), so total column budgeting was already 2 — the bug is purely missing cluster joining, not width.Fix
Pair regional indicators into one two-cell cluster, mirroring the existing ZWJ branch:
charKindresets to0on the completed pair, so a third indicator opens a new flag instead of chaining into a three-glyph cluster (RI RI RI→ flag + lone indicator, per UAX Break up large files into well-organized modules #29).wcwidth(each indicator isEast_Asian_Width=Neutral→ 1 + 1). This keeps cursor budgeting in step with what CLIs compute, so positioned writes don't drift.Considered and rejected: swapping to
@xterm/addon-unicode-graphemes(full UAX #29 segmentation). It would compute widths by spec rather than by the CLI convention this provider deliberately encodes for ZWJ sequences, regressing that tuned behavior and the terminal rendering goldens. The targeted change fits the existing architecture.Tests
src/shared/terminal-unicode-provider.test.ts(new) — asserts cell contents + widths viacreateRendererParityTerminal: pair folding, consecutive flags, trailing unpaired indicator, column budgeting, and a ZWJ/CJK/plain-emoji regression guard.src/main/daemon/headless-emulator-unicode-width.test.ts— flag cases in the existing style, covering the daemon mirror used by mobile/remote.Verified the three cluster-level tests fail without the provider change and pass with it. The width-budget assertions pass either way by design — they lock the two-cell contract against a future "fix" that returns
precedingWidth.pnpm lint(oxlint on changed files) andpnpm typecheck(tsconfig.node.json) are clean. Ran thesrc/main/daemon,src/renderer/src/lib/pane-manager, andsrc/renderer/src/components/terminal-panesuites: 4123 passed. The only failures arenode-pty-fd-leak.test.ts/shell-ready.test.tswithposix_spawnp failed— reproduced identically on a clean tree with these changes stashed (unbuilt nativenode-ptyin my sandbox), unrelated to this change.Code review summary
src/shared, no platform branches, no paths, no keybindings. Identical on macOS/Linux/Windows.Notes for reviewers
Skin-tone modifiers, variation selectors and keycap sequences are deliberately out of scope here — they're separate cluster classes and would each need their own width decision. Happy to follow up if you'd like them covered.
Maintainer validation (2026-07-26)
pnpm typecheckpassed. Targeted oxlint passed. The provider, daemon width, renderer/daemon parity, and hidden/reveal fuzz suites passed: 17 tests passed and 2 were skipped by their existing gates.pnpm lintreached pre-existing localization-coverage findings in untouched settings-search files; all changed files passed oxlint, formatting hooks, and typecheck.