- Package: clayterm
- Version: v0.6.0 (reproduced on
main @ d75fb4b)
- Runtime: Deno 2.x
- OS: platform-independent (WASM); reproduced on macOS
Describe the bug
We measure text width by summing per-codepoint widths with no grapheme-cluster awareness, so a base character followed by U+FE0F (variation selector-16) is mis-measured—the base keeps its text-presentation width and FE0F adds 0, instead of the pair clustering into one width-2 emoji glyph. 🌡️⚠️✅ measures 1+0+1+0+2 = 4 columns instead of 6, and a lone ⚠️ measures 1 instead of 2. Plain CJK (你好你 => 6) and already-emoji-presentation bases (🌊🌊 => 4) are correct, so the gap is specifically base + FE0F.
To Reproduce
A base codepoint immediately followed by U+FE0F should be a single width-2 cluster. The lone ⚠️ box reports bounds.width 1 (want 2), the 🌡️⚠️✅ fit box reports 4 (want 6), and a fit-bordered box prints ┌──┐ instead of ┌──────┐.
Expected behavior
FE0F promotes its base to width-2 emoji presentation instead of collapsing to 0. 🌡️⚠️✅ is 2+2+2 = 6, and a fit-bordered box around it is 8 cells wide (6 content + 2 border) with a top border of ┌──────┐.
Additional Information
Failing test case on nm/repro/vs16-emoji-width (test · diff).
The width table is fine; the gap is in the measurement loops. measure() (src/clayterm.c:677) does w += wcwidth(cp) with no lookahead (src/clayterm.c:693-695), and wcwidth() (src/wcwidth.c:1095) maps U+FE0F to 0 (src/wcwidth.c:547) by design; render_text() (src/clayterm.c:269) has the same per-codepoint loop (src/clayterm.c:288). The fix likely lives in those two loops as a one-codepoint lookahead—when the next codepoint is U+FE0F, treat base + FE0F as a single width-2 cluster and consume both, leaving the table and wcwidth() untouched.
main@ d75fb4b)Describe the bug
We measure text width by summing per-codepoint widths with no grapheme-cluster awareness, so a base character followed by U+FE0F (variation selector-16) is mis-measured—the base keeps its text-presentation width and FE0F adds 0, instead of the pair clustering into one width-2 emoji glyph.
🌡️⚠️✅measures 1+0+1+0+2 = 4 columns instead of 6, and a lone⚠️measures 1 instead of 2. Plain CJK (你好你=> 6) and already-emoji-presentation bases (🌊🌊=> 4) are correct, so the gap is specifically base + FE0F.To Reproduce
A base codepoint immediately followed by U+FE0F should be a single width-2 cluster. The lone
⚠️box reportsbounds.width1 (want 2), the🌡️⚠️✅fit box reports 4 (want 6), and a fit-bordered box prints┌──┐instead of┌──────┐.Expected behavior
FE0F promotes its base to width-2 emoji presentation instead of collapsing to 0.
🌡️⚠️✅is 2+2+2 = 6, and a fit-bordered box around it is 8 cells wide (6 content + 2 border) with a top border of┌──────┐.Additional Information
Failing test case on
nm/repro/vs16-emoji-width(test · diff).The width table is fine; the gap is in the measurement loops.
measure()(src/clayterm.c:677) doesw += wcwidth(cp)with no lookahead (src/clayterm.c:693-695), andwcwidth()(src/wcwidth.c:1095) maps U+FE0F to 0 (src/wcwidth.c:547) by design;render_text()(src/clayterm.c:269) has the same per-codepoint loop (src/clayterm.c:288). The fix likely lives in those two loops as a one-codepoint lookahead—when the next codepoint is U+FE0F, treat base + FE0F as a single width-2 cluster and consume both, leaving the table andwcwidth()untouched.