Skip to content

fix(#142): dispWidth counts display columns, not codepoints (CJK/emoji borders) - #155

Merged
justrach merged 1 commit into
mainfrom
fix/dispwidth-cjk-emoji-142
Jul 11, 2026
Merged

fix(#142): dispWidth counts display columns, not codepoints (CJK/emoji borders)#155
justrach merged 1 commit into
mainfrom
fix/dispwidth-cjk-emoji-142

Conversation

@justrach

Copy link
Copy Markdown
Owner

Fixes #142.

Problem

The zigzag REPL table renderer's dispWidth (src/repl.zig) counted codepoints, each as width 1. East-Asian wide characters (CJK, kana, Hangul, fullwidth forms) and emoji are 2 columns; combining marks are 0. So column-width computation and cell padding in renderTable/renderRecords/wrapCell were wrong and the box-drawing borders (│ ┌ ┼ …) misaligned on any non-ASCII content.

Fix

  • Add codepointWidth(cp) — wcwidth-style: 0 for combining/zero-width (combining marks, ZW*, variation selectors, BOM), 2 for the East-Asian wide + common emoji ranges, 1 otherwise. ASCII/Latin-1 short-circuit before any table lookup, so the hot path is untouched.
  • dispWidth now decodes each codepoint and sums real widths (with graceful fallback to 1 on decode errors).
  • wrapCell's hard-split for over-long words is width-aware too, so a wide word can't overflow its column.

Scope note: emoji-presentation via a trailing VS16 (U+FE0F) on a text-default base is left at width 1 (documented in the code) — proper VS16 promotion needs lookahead; the CJK/kana/Hangul/fullwidth + emoji-plane cases that actually misalign tables are covered.

Test

New unit test in repl.zig (run by zig build repl-test17/17 pass) covers wide CJK, hiragana, fullwidth, precomposed-vs-combining, emoji, and ZWSP. Main zig build test also green.

🤖 Generated with Claude Code

…i borders)

The zigzag REPL table renderer measured cell width by counting codepoints, so
East-Asian wide characters (CJK, kana, Hangul, fullwidth forms — 2 columns) and
emoji (2 columns) were undercounted and combining marks overcounted. Column
widths and cell padding in renderTable/renderRecords/wrapCell came out wrong and
the box-drawing borders (│ ┌ ┼ …) misaligned on any non-ASCII table content.

Add a wcwidth-style codepointWidth() (0 for combining/zero-width, 2 for the
East-Asian wide + common emoji ranges, 1 otherwise; ASCII/Latin-1 skip the
table) and make dispWidth decode each codepoint and sum real widths. wrapCell's
hard-split for over-long words is now width-aware too, so a wide word can't
overflow its column. Emoji-presentation via a trailing VS16 on a text-default
base is left at width 1 (documented) — the CJK/emoji-plane cases that actually
misalign tables are covered.

New unit test in repl.zig (run by `zig build repl-test`) covers wide CJK, kana,
fullwidth, precomposed vs combining, emoji, and ZWSP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@justrach
justrach merged commit f0862d6 into main Jul 11, 2026
6 checks passed
@justrach
justrach deleted the fix/dispwidth-cjk-emoji-142 branch July 15, 2026 10:23
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.

REPL table renderer: dispWidth counts codepoints, not display width (CJK/emoji misalign borders)

1 participant