fix(output): clear stale continuation cell when wide grapheme shifts to new column (13047)#5
Open
beorn wants to merge 1 commit into
Open
fix(output): clear stale continuation cell when wide grapheme shifts to new column (13047)#5beorn wants to merge 1 commit into
beorn wants to merge 1 commit into
Conversation
…to new column (13047)
When a wide emoji (📋, 📄) shifts to a new column across renders, the
continuation cell at the new column-plus-one lands at a position that held
narrow content in the prior frame. diffBuffers correctly marks both cells
as dirty, but changesToAnsi's "skip continuation when main was just emitted"
optimization dropped the continuation entry — leaving the terminal's stale
narrow glyph visible next to the new wide grapheme ("📋t 1", "📄i 4").
The skip is correct only when prev[x-1] was also wide — then the terminal
auto-advances past the wide grapheme into the continuation column. When
prev[x-1] was narrow (the wide grapheme is NEW at x-1), terminals with
wcwidth disagreement do not auto-claim x, so the stale narrow glyph stays.
Fix: narrow the skip. When lastEmittedX === x - 1 (continuation matches a
just-emitted main), check prevBuffer.isCellWide(x-1, y) — if prev[x-1] was
narrow, emit CUP + space at x to overwrite the stale glyph before skipping.
Threads prev through changesToAnsi as an optional parameter (incremental
paths pass it; bare callers continue to opt out).
Repro: 3 fixtures in tests/features/wide-emoji-continuation.test.tsx —
80-col flex-end group shift (dense path), single-column shift (scatter
path), and direct narrow→wide-at-same-column transition. All red before
fix, green after.
Bead: @km/silvery/13047-wide-emoji-continuation-cell-stale
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.
Summary
📋t 1,📄i 4symptoms).changesToAnsito only skip whenprev[x-1]was already wide. Whenprev[x-1]was narrow (wide grapheme is NEW at x-1), emit an explicit CUP+space at x to overwrite the stale glyph — terminals with wcwidth disagreement do not auto-claim x.prevthroughchangesToAnsias an optional parameter; incremental paths pass it, bare callers opt out.Root cause
diffBuffersalready marks the new continuation cell as dirty (packed metadata differs from prev —continuationflag mismatch). The bug was inchangesToAnsi, which skipped the continuation entry under the assumption that the terminal auto-claimedxafter the wide-char main atx-1. That holds whenprev[x-1]was already wide (cursor advances 2 columns past a wide grapheme), but not whenprev[x-1]was narrow — under wcwidth disagreement on emoji like 📋/📄, the terminal advances only 1 column and the stale glyph atxremains visible.Test plan
bun vitest run --project vendor vendor/silvery/tests/features/wide-emoji-continuation.test.tsx— 3/3 green (was 2/3 red — Fixtures B + C failed without the fix)bun vitest run --project vendor vendor/silvery/tests/features/— 228+ pass (pre-existing flake inlayout-churn-leaks-pixelsexists onmainand is independent of this fix)SILVERY_STRICT=2 bun vitest run --project vendor vendor/silvery/tests/features/wide-emoji-continuation.test.tsx— green under canary + incremental invariantsBead:
@km/silvery/13047-wide-emoji-continuation-cell-stale