Skip to content

Unicode 18.0.0 support, releases today - #238

Open
jquast wants to merge 5 commits into
jq/libwcwidth-rcfrom
jq/unicode-18
Open

jquast wants to merge 5 commits into
jq/libwcwidth-rcfrom
jq/unicode-18

Conversation

@jquast

@jquast jquast commented Aug 31, 2026

Copy link
Copy Markdown
Owner

We expect Unicode 18.0 to release today.

  • Match new 18.0.0 UAX #20 rules on grapheme clustering InCB/Indic_Conjunct_Break
  • parse names from unicode.org data instead of current python
  • wcstwidth() correction table updated with latest ucs-detect results (contour and rio are better-conforming)

@codspeed

codspeed Bot commented Aug 31, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 32.31%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 21 regressed benchmarks
✅ 87 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_iter_graphemes_combining 76.4 µs 285.9 µs -73.29%
test_iter_graphemes_emoji 42.5 µs 112.3 µs -62.16%
test_iter_graphemes_ascii 46.2 µs 107.4 µs -56.96%
test_clip_cursor_cr_control_codes_ignore 341.5 µs 604.2 µs -43.48%
test_clip_cursor_cr_overwrite 1.8 ms 2.8 ms -37.35%
test_clip_long_cjk_overtype 7.5 ms 11.6 ms -35.43%
test_clip_long_cjk_past_window 326.7 µs 477.6 µs -31.6%
test_clip_cursor_csi_backward 571.7 µs 833.8 µs -31.43%
test_clip_long_cjk_control_codes_ignore 329.8 µs 479.9 µs -31.29%
test_clip_complex_sgr_no_overtype 180.4 µs 244.1 µs -26.09%
test_clip_complex_sgr 186.8 µs 251.8 µs -25.81%
test_clip_dense_ansi_no_overtype 419.3 µs 536.9 µs -21.9%
test_clip_dense_ansi_control_codes_ignore 419 µs 536.4 µs -21.88%
test_clip_japanese 97.6 µs 124.3 µs -21.49%
test_clip_dense_ansi_overtype 547.3 µs 657.6 µs -16.77%
test_clip_with_ansi_no_propagate 101.1 µs 121.3 µs -16.72%
test_wcstwidth_term_program[ghostty] 862.2 µs 1,032.9 µs -16.53%
test_width_term_program[ghostty] 915.8 µs 1,086.8 µs -15.73%
test_clip_dense_ansi_past_window 656 µs 774 µs -15.25%
test_wrap_japanese 5.8 ms 6.6 ms -11.18%
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing jq/unicode-18 (36f165b) with jq/libwcwidth-rc (1042e5f)

Open in CodSpeed

@jquast

jquast commented Sep 15, 2026

Copy link
Copy Markdown
Owner Author

The remaining test failure is because the 3.14 step also runs "tox -e fetch" but without the new --draft argument that was used, it can be ignored.

Unicode 18 releases today, so this could be merged and released as soon as end of day.

@jquast
jquast marked this pull request as ready for review September 15, 2026 07:01
@jquast jquast changed the title Unicode 18 preview (draft) Unicode 18.0.0 support, releases today Sep 15, 2026
@jquast

jquast commented Sep 15, 2026

Copy link
Copy Markdown
Owner Author

I have reverted to the python-only version of iter_graphemes(), as I can't say for sure if or when standard python's unicodedata.iter_graphemes will be fixed for the InCB/Indic_Conjunct_Break rules changed in unicode 18, so this is expected to degrade performance for any grapheme-related functions.

@jquast
jquast changed the base branch from master to jq/libwcwidth-rc September 15, 2026 11:27
jquast added a commit that referenced this pull request Sep 15, 2026
Implements C Python wrapper of our C11 iter_graphemes(), previous to
#238 we used std python's
iter_graphemes, but, as i wrote:

> I can't say for sure if or when standard python's
> unicodedata.iter_graphemes will be fixed for the
> InCB/Indic_Conjunct_Break rules changed in unicode 18, so this is
> expected to degrade performance for any grapheme-related functions.

And so it seems we should implement a C Python shim to our own
iter_graphemes(), so that we support Unicode 18 on all python versions
equally by our own up-to-date impl.
@jquast
jquast added this pull request to stack #249 September 15, 2026 13:18
gcb_of() classified every codepoint with a chain of up to ten sequential binary
searches, and GCB_OTHER -- the common ASCII and Latin case -- paid all ten.  It
is the hottest loop in the library, driven per codepoint by grapheme iteration,
clip() and wrap().

The classes are disjoint under that precedence order, so they pack into a single
nibble-per-codepoint table and one lookup replaces the chain.  update-tables.py
picks the page size by measuring, so the table is re-costed on each regeneration.

Grapheme iteration is 2-2.4x faster, for 15 KB of rodata.
tests/test_gcb_class.c checks the table against the chain it replaces for every
codepoint, and against the Extend table, which terminal_override.c relied on
directly.

Also fixes the Makefile 'test' recipe: $(foreach) joins its expansions with a
space, so each test binary was invoked with the following '@echo' as its
arguments and only the first header was ever printed.
The version is read from the generated wcwidth_config.h, but the regex required
MAJOR.MINOR.PATCH followed immediately by the closing quote.  A PEP 440
pre-release such as '0.9.0rc1' did not match, the replace returned the whole
#define line unchanged, and project() rejected it -- breaking the cmake build
and the ctest CI step.
Add clang_format and clang_format_check tox envs, wired into 'format' and
'lint', and add the check to the CI lint matrix.  The hand-written C was never
fully formatted, so this reformats it in the same commit.

Generated tables under src/tables are excluded from the format targets; their
layout comes from the templates, and formatting them would fight every
regeneration.
Regenerate all tables for Unicode 18.0.0, and refresh the ucs-detect terminal
override tables from the latest results.  UAX #29 revision 48 relaxes GB9c: the
leading \p{InCB=Consonant} requirement was dropped, so a linker no longer needs
a consonant before it to join the following consonant.  Revert to our own
iter_graphemes() rather than python3.15's stdlib, which differs in subtle
negative ways; the performance is planned to return by way of the libwcwidth
C11 library.

Two generator fixes come along.  Measurements carrying 'delta_ypos' or an
out-of-range width are now discarded instead of becoming override widths --
iterm2 reports -88 for five Burmese clusters.  And the shared grapheme override
tables are named by a codepoint digest rather than by hashing repr() of the
cluster, which escapes characters the *running* interpreter's unicodedata
considers unassigned and so made the generated _known_* filenames depend on
which Python ran the generator.  All _known_* files are renamed once.

Unicode 18.0 is still pre-release, so bin/update-tables.py grows a --draft
argument to fetch the draft data files.  CI does not pass it; instead, a
GraphemeBreakTest.txt fixture older than our tables now skips those tests
rather than failing them.
jquast added a commit that referenced this pull request Sep 15, 2026
Implements C Python wrapper of our C11 iter_graphemes(), previous to
#238 we used std python's
iter_graphemes, but, as i wrote:

> I can't say for sure if or when standard python's
> unicodedata.iter_graphemes will be fixed for the
> InCB/Indic_Conjunct_Break rules changed in unicode 18, so this is
> expected to degrade performance for any grapheme-related functions.

And so it seems we should implement a C Python shim to our own
iter_graphemes(), so that we support Unicode 18 on all python versions
equally by our own up-to-date impl.
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.98%. Comparing base (1042e5f) to head (36f165b).

Additional details and impacted files
@@                 Coverage Diff                  @@
##           jq/libwcwidth-rc     #238      +/-   ##
====================================================
- Coverage            100.00%   98.98%   -1.02%     
====================================================
  Files                    27       27              
  Lines                  1999     1975      -24     
  Branches                467      460       -7     
====================================================
- Hits                   1999     1955      -44     
- Misses                    0       11      +11     
- Partials                  0        9       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant