wcwidth 0.9.1__tar.gz → 0.9.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. {wcwidth-0.9.1 → wcwidth-0.9.3}/PKG-INFO +6 -5
  2. {wcwidth-0.9.1 → wcwidth-0.9.3}/README.rst +5 -4
  3. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/history.rst +28 -6
  4. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/intro.rst +5 -4
  5. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/libwcwidth.rst +16 -4
  6. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/specs.rst +60 -45
  7. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/Makefile +2 -2
  8. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/align.h +5 -0
  9. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/utf8.h +23 -4
  10. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/wcwidth_config.h +2 -2
  11. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/align.c +13 -3
  12. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/textwrap.c +10 -0
  13. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/utf8.c +27 -24
  14. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/wcstwidth.c +1 -1
  15. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/width.c +5 -4
  16. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_align.c +36 -0
  17. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_clip.c +13 -0
  18. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_textwrap.c +21 -0
  19. wcwidth-0.9.3/libwcwidth/tests/test_utf8.c +177 -0
  20. {wcwidth-0.9.1 → wcwidth-0.9.3}/pyproject.toml +4 -5
  21. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-update.txt +1 -1
  22. wcwidth-0.9.3/requirements-wheels.in +4 -0
  23. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-wheels.txt +7 -16
  24. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_check_release.py +1 -1
  25. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_clip.py +28 -0
  26. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_hyperlink.py +6 -0
  27. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_term_overrides.py +26 -0
  28. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_textwrap.py +32 -0
  29. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_width.py +5 -0
  30. {wcwidth-0.9.1 → wcwidth-0.9.3}/tox.ini +8 -7
  31. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/__init__.py +1 -1
  32. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_clip.py +9 -2
  33. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_wcswidth.py +1 -1
  34. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_width.py +1 -1
  35. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/align.py +61 -3
  36. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/hyperlink.py +1 -1
  37. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/textwrap.py +22 -17
  38. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth.egg-info/PKG-INFO +6 -5
  39. wcwidth-0.9.1/libwcwidth/tests/test_utf8.c +0 -76
  40. wcwidth-0.9.1/requirements-wheels.in +0 -3
  41. {wcwidth-0.9.1 → wcwidth-0.9.3}/.gitignore +0 -0
  42. {wcwidth-0.9.1 → wcwidth-0.9.3}/.pylintrc +0 -0
  43. {wcwidth-0.9.1 → wcwidth-0.9.3}/LICENSE +0 -0
  44. {wcwidth-0.9.1 → wcwidth-0.9.3}/MANIFEST.in +0 -0
  45. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/check-release.py +0 -0
  46. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/update-docs.py +0 -0
  47. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/update-tables.py +0 -0
  48. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/verify-table-integrity.py +0 -0
  49. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/wcwidth-browser.py +0 -0
  50. {wcwidth-0.9.1 → wcwidth-0.9.3}/bin/wcwidth-libc-comparator.py +0 -0
  51. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/api_c.rst.j2 +0 -0
  52. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/c_gcb_class.c.j2 +0 -0
  53. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/c_table.c.j2 +0 -0
  54. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/c_term_programs.c.j2 +0 -0
  55. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/c_terminal_overrides.c.j2 +0 -0
  56. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/grapheme_override_per_terminal.py.j2 +0 -0
  57. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/grapheme_override_table.py.j2 +0 -0
  58. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/grapheme_registry.py.j2 +0 -0
  59. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/grapheme_table.c.j2 +0 -0
  60. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/grapheme_table.py.j2 +0 -0
  61. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/python_table.py.j2 +0 -0
  62. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/table_overrides.py.j2 +0 -0
  63. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/tables.h.j2 +0 -0
  64. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/term_programs.py.j2 +0 -0
  65. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/unicode_version.rst.j2 +0 -0
  66. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/unicode_versions.py.j2 +0 -0
  67. {wcwidth-0.9.1 → wcwidth-0.9.3}/code_templates/wcwidth_config.h.j2 +0 -0
  68. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/api.rst +0 -0
  69. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/conf.py +0 -0
  70. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/developing.rst +0 -0
  71. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/index.rst +0 -0
  72. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/related.rst +0 -0
  73. {wcwidth-0.9.1 → wcwidth-0.9.3}/docs/unicode_version.rst +0 -0
  74. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/.clang-format +0 -0
  75. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/CMakeLists.txt +0 -0
  76. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/LICENSE +0 -0
  77. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/README.rst +0 -0
  78. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/cmake/libwcwidth-config.cmake.in +0 -0
  79. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/examples/align.c +0 -0
  80. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/examples/textwrap.c +0 -0
  81. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/examples/width.c +0 -0
  82. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/clip.h +0 -0
  83. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/escape.h +0 -0
  84. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/grapheme.h +0 -0
  85. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/sgr.h +0 -0
  86. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/table_types.h +0 -0
  87. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/tables.h +0 -0
  88. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/terminal_override.h +0 -0
  89. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/text_sizing.h +0 -0
  90. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/textwrap.h +0 -0
  91. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/unicode.h +0 -0
  92. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/wcwidth.h +0 -0
  93. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/include/wcwidth/width.h +0 -0
  94. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/bisearch.c +0 -0
  95. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/clip.c +0 -0
  96. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/escape.c +0 -0
  97. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/grapheme.c +0 -0
  98. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/sgr.c +0 -0
  99. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_ambiguous.c +0 -0
  100. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_gcb_class.c +0 -0
  101. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_grapheme.c +0 -0
  102. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_mc.c +0 -0
  103. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_term_programs.c +0 -0
  104. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_terminal_overrides.c +0 -0
  105. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_vs15.c +0 -0
  106. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_vs16.c +0 -0
  107. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_wide.c +0 -0
  108. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/tables/table_zero.c +0 -0
  109. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/terminal_override.c +0 -0
  110. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/text_sizing.c +0 -0
  111. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/wcswidth.c +0 -0
  112. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/src/wcwidth.c +0 -0
  113. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_common.h +0 -0
  114. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_escape.c +0 -0
  115. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_gcb_class.c +0 -0
  116. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_grapheme.c +0 -0
  117. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_sgr.c +0 -0
  118. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_text_sizing.c +0 -0
  119. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_wcswidth.c +0 -0
  120. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_wcwidth.c +0 -0
  121. {wcwidth-0.9.1 → wcwidth-0.9.3}/libwcwidth/tests/test_width.c +0 -0
  122. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-develop.txt +0 -0
  123. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-docs.in +0 -0
  124. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-tests39.in +0 -0
  125. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-tests39.txt +0 -0
  126. {wcwidth-0.9.1 → wcwidth-0.9.3}/requirements-update.in +0 -0
  127. {wcwidth-0.9.1 → wcwidth-0.9.3}/setup.cfg +0 -0
  128. {wcwidth-0.9.1 → wcwidth-0.9.3}/setup.py +0 -0
  129. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/__init__.py +0 -0
  130. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/conftest.py +0 -0
  131. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_ambiguous.py +0 -0
  132. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_benchmarks.py +0 -0
  133. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_clip_cjk_emoji.py +0 -0
  134. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_clip_overtyping.py +0 -0
  135. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_core.py +0 -0
  136. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_emojis.py +0 -0
  137. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_fastcall_args.py +0 -0
  138. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_grapheme.py +0 -0
  139. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_justify.py +0 -0
  140. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_setup.py +0 -0
  141. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_sgr_state.py +0 -0
  142. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_text_sizing.py +0 -0
  143. {wcwidth-0.9.1 → wcwidth-0.9.3}/tests/test_ucslevel.py +0 -0
  144. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_constants.py +0 -0
  145. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_wcwidth.py +0 -0
  146. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_wcwidth_c.c +0 -0
  147. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/_wcwidth_c.pyi +0 -0
  148. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/bisearch.py +0 -0
  149. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/control_codes.py +0 -0
  150. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/escape_sequences.py +0 -0
  151. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/grapheme.py +0 -0
  152. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/py.typed +0 -0
  153. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/sgr_state.py +0 -0
  154. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_ambiguous.py +0 -0
  155. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme.py +0 -0
  156. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/__init__.py +0 -0
  157. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_058c7585.py +0 -0
  158. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_2148ff29.py +0 -0
  159. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_244dc88b.py +0 -0
  160. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_336a71a1.py +0 -0
  161. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_33ac73f2.py +0 -0
  162. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_3a1895d5.py +0 -0
  163. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_59180a0d.py +0 -0
  164. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_65c6beb5.py +0 -0
  165. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_78f828e8.py +0 -0
  166. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_8c2ae14d.py +0 -0
  167. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_900356bf.py +0 -0
  168. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_97904ece.py +0 -0
  169. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_a5880eeb.py +0 -0
  170. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_b6d33cf4.py +0 -0
  171. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_c57c295b.py +0 -0
  172. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_cf02297d.py +0 -0
  173. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_e18ac6a7.py +0 -0
  174. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_e2b75ee3.py +0 -0
  175. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_fb5fd79b.py +0 -0
  176. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_known_fdc99132.py +0 -0
  177. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_grapheme_overrides/_registry.py +0 -0
  178. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_mc.py +0 -0
  179. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_overrides.py +0 -0
  180. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_term_programs.py +0 -0
  181. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_vs15.py +0 -0
  182. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_vs16.py +0 -0
  183. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_wide.py +0 -0
  184. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/table_zero.py +0 -0
  185. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/text_sizing.py +0 -0
  186. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/unicode_versions.py +0 -0
  187. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth/wcwidth.py +0 -0
  188. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth.egg-info/SOURCES.txt +0 -0
  189. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth.egg-info/dependency_links.txt +0 -0
  190. {wcwidth-0.9.1 → wcwidth-0.9.3}/wcwidth.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcwidth
3
- Version: 0.9.1
3
+ Version: 0.9.3
4
4
  Summary: Measures the displayed width of unicode strings in a terminal
5
5
  Author-email: Jeff Quast <contact@jeffquast.com>
6
6
  Project-URL: Homepage, https://github.com/jquast/wcwidth
@@ -97,8 +97,9 @@ This library may be considered to presume the terminal is enabled for DEC Privat
97
97
  ("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
98
98
  is often the default mode for those terminals that support it.
99
99
 
100
- This library does support any specific "legacy width" measurement by API, but it does provide
101
- Corrections_ for those terminals without grapheme support.
100
+ This library does not support any specific "legacy width" measurement by API, but it does provide
101
+ Corrections_ for terminals using "legacy width" measurements or are without basic emoji or grapheme
102
+ support.
102
103
 
103
104
  See also:
104
105
 
@@ -154,7 +155,7 @@ anywhere in the string.
154
155
  wcstwidth()
155
156
  -----------
156
157
 
157
- Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
158
+ `wcstwidth()`_ is a variant of `wcswidth()`_ that provides terminal-specific Corrections_, reading
158
159
  ``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
159
160
  XTVERSION_ or ENQ_ response:
160
161
 
@@ -550,7 +551,7 @@ https://wcwidth.readthedocs.io/en/latest/related.html
550
551
  .. _`str.rjust()`: https://docs.python.org/3/library/stdtypes.html#str.rjust
551
552
  .. _`str.center()`: https://docs.python.org/3/library/stdtypes.html#str.center
552
553
  .. _`str.expandtabs()`: https://docs.python.org/3/library/stdtypes.html#str.expandtabs
553
- .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
554
+ .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#general-tabulated-summary
554
555
  .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
555
556
  .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
556
557
  .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
@@ -66,8 +66,9 @@ This library may be considered to presume the terminal is enabled for DEC Privat
66
66
  ("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
67
67
  is often the default mode for those terminals that support it.
68
68
 
69
- This library does support any specific "legacy width" measurement by API, but it does provide
70
- Corrections_ for those terminals without grapheme support.
69
+ This library does not support any specific "legacy width" measurement by API, but it does provide
70
+ Corrections_ for terminals using "legacy width" measurements or are without basic emoji or grapheme
71
+ support.
71
72
 
72
73
  See also:
73
74
 
@@ -123,7 +124,7 @@ anywhere in the string.
123
124
  wcstwidth()
124
125
  -----------
125
126
 
126
- Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
127
+ `wcstwidth()`_ is a variant of `wcswidth()`_ that provides terminal-specific Corrections_, reading
127
128
  ``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
128
129
  XTVERSION_ or ENQ_ response:
129
130
 
@@ -519,7 +520,7 @@ https://wcwidth.readthedocs.io/en/latest/related.html
519
520
  .. _`str.rjust()`: https://docs.python.org/3/library/stdtypes.html#str.rjust
520
521
  .. _`str.center()`: https://docs.python.org/3/library/stdtypes.html#str.center
521
522
  .. _`str.expandtabs()`: https://docs.python.org/3/library/stdtypes.html#str.expandtabs
522
- .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
523
+ .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#general-tabulated-summary
523
524
  .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
524
525
  .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
525
526
  .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
@@ -2,11 +2,24 @@
2
2
  History
3
3
  =======
4
4
 
5
+ 0.9.3 *unreleased*
6
+ * **Bugfix** libwcwidth no longer has undefined behavior on NULL or empty input in the UTF-8 and
7
+ alignment functions, `PR #293`_, `PR #294`_.
8
+ * **Changed** `HyperlinkParams.parse()`_ now rejects trailing text, `PR #296`_.
9
+ * **Bugfix** `wrap()`_ should keep a link's own OSC 8 params across a line break, `PR #299`_.
10
+
11
+ 0.9.2 *2026-10-04*
12
+ * **Bugfix** `wrap()`_ should omit an empty line as output, like `textwrap.wrap()`_ `PR #291`_.
13
+ * **Bugfix** `clip()`_ when using non-default argument, ``tabsize=0``, `PR #288`_.
14
+ * **Bugfix** regional-indicator pairs (flags) when corrected for terminals that show lone regional
15
+ indicators as narrow (xterm, wezterm, VTE, and others) in `wcstwidth()`_ and `width()`_, `PR
16
+ #284`.
17
+
5
18
  0.9.1 *2026-09-23*
6
- * **Bugfix** a regression in 0.9.0 Python and libwcwidth_ `width()`_ mis-measured CSI sequences
7
- over 64 bytes, `PR #271`_.
8
- * **Performance** improvement of ~15% for 11 many Python API functions when using
9
- default ``ambiguous_width`` and ``term_program`` arguments. `PR #273`_
19
+ * **Bugfix** `width()`_ in Python and libwcwidth mis-measured CSI sequences over 64 bytes, a 0.9.0
20
+ release regression! `PR #272`_.
21
+ * **Performance** improvement of ~15% for many Python API functions when using default arguments
22
+ like ``ambiguous_width`` and ``term_program``. `PR #273`_
10
23
 
11
24
  0.9.0 *2026-09-23*
12
25
  * **New** Optional CPython extension using libwcwidth_, improving performance 10-58x `PR #248`_.
@@ -239,7 +252,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
239
252
  * for any purpose and without fee is hereby granted. The author
240
253
  * disclaims all warranties with regard to this software.
241
254
 
242
- .. _`Spacing Combining Mark`: https://www.unicode.org/versions/latest/ch04.pdf#G134153
255
+ .. _`Spacing Combining Mark`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
243
256
  .. _`specification`: https://wcwidth.readthedocs.io/en/latest/specs.html
244
257
  .. _`prospector`: https://github.com/landscapeio/prospector
245
258
  .. _`combining`: https://en.wikipedia.org/wiki/Combining_character
@@ -305,8 +318,16 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
305
318
  .. _`PR #253`: https://github.com/jquast/wcwidth/pull/253
306
319
  .. _`PR #259`: https://github.com/jquast/wcwidth/pull/259
307
320
  .. _`PR #262`: https://github.com/jquast/wcwidth/pull/262
308
- .. _`PR #271`: https://github.com/jquast/wcwidth/pull/271
321
+ .. _`PR #272`: https://github.com/jquast/wcwidth/pull/272
309
322
  .. _`PR #273`: https://github.com/jquast/wcwidth/pull/273
323
+ .. _`PR #281`: https://github.com/jquast/wcwidth/pull/281
324
+ .. _`PR #284`: https://github.com/jquast/wcwidth/pull/284
325
+ .. _`PR #288`: https://github.com/jquast/wcwidth/pull/288
326
+ .. _`PR #291`: https://github.com/jquast/wcwidth/pull/291
327
+ .. _`PR #293`: https://github.com/jquast/wcwidth/pull/293
328
+ .. _`PR #294`: https://github.com/jquast/wcwidth/pull/294
329
+ .. _`PR #296`: https://github.com/jquast/wcwidth/pull/296
330
+ .. _`PR #299`: https://github.com/jquast/wcwidth/pull/299
310
331
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
311
332
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
312
333
  .. _`Issue #211`: https://github.com/jquast/wcwidth/issues/211
@@ -332,6 +353,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
332
353
  .. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
333
354
  .. _`list_versions()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_versions
334
355
  .. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
356
+ .. _`HyperlinkParams.parse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.HyperlinkParams
335
357
  .. _`parity padding`: https://jazcap53.github.io/pythons-eccentric-strcenter.html
336
358
  .. _`kitty text sizing protocol`: https://sw.kovidgoyal.net/kitty/text-sizing-protocol/
337
359
  .. _Corrections: https://wcwidth.readthedocs.io/en/latest/intro.html#corrections
@@ -66,8 +66,9 @@ This library may be considered to presume the terminal is enabled for DEC Privat
66
66
  ("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
67
67
  is often the default mode for those terminals that support it.
68
68
 
69
- This library does support any specific "legacy width" measurement by API, but it does provide
70
- Corrections_ for those terminals without grapheme support.
69
+ This library does not support any specific "legacy width" measurement by API, but it does provide
70
+ Corrections_ for terminals using "legacy width" measurements or are without basic emoji or grapheme
71
+ support.
71
72
 
72
73
  See also:
73
74
 
@@ -123,7 +124,7 @@ anywhere in the string.
123
124
  wcstwidth()
124
125
  -----------
125
126
 
126
- Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
127
+ `wcstwidth()`_ is a variant of `wcswidth()`_ that provides terminal-specific Corrections_, reading
127
128
  ``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
128
129
  XTVERSION_ or ENQ_ response:
129
130
 
@@ -519,7 +520,7 @@ https://wcwidth.readthedocs.io/en/latest/related.html
519
520
  .. _`str.rjust()`: https://docs.python.org/3/library/stdtypes.html#str.rjust
520
521
  .. _`str.center()`: https://docs.python.org/3/library/stdtypes.html#str.center
521
522
  .. _`str.expandtabs()`: https://docs.python.org/3/library/stdtypes.html#str.expandtabs
522
- .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
523
+ .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#general-tabulated-summary
523
524
  .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
524
525
  .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
525
526
  .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
@@ -137,16 +137,23 @@ address:
137
137
  String length conventions
138
138
  ~~~~~~~~~~~~~~~~~~~~~~~~~
139
139
 
140
- Every string function takes an explicit length and reads exactly that many units:
140
+ Every string function reads exactly the length you pass:
141
141
 
142
142
  ``_u32`` functions
143
143
  count codepoints in the array,
144
144
  ``_u8`` functions
145
145
  count bytes.
146
146
 
147
- There is no NUL-terminated sentinel form; pass ``strlen(text)`` when the text is a C string. The
148
- length is authoritative: a NUL is an ordinary zero-width character that may appear anywhere, and
149
- it survives into transform output, whose ``*out_len`` is the true length.
147
+ Pass ``strlen(text)`` when the text is a C string. A NUL counts as an ordinary zero-width character
148
+ in any position. Transform functions copy it through and report the full length in ``*out_len``.
149
+
150
+ Pointer contract
151
+ ~~~~~~~~~~~~~~~~
152
+
153
+ Input pointers and the ``count`` / ``out_len`` out-length pointers must not be NULL.
154
+
155
+ ``stack`` accepts NULL to select the heap path. ``wcwidth_utf8_decode_single()`` accepts
156
+ NULL as ``cp_out``, reporting the length of the next character.
150
157
 
151
158
  Alternate encodings
152
159
  ~~~~~~~~~~~~~~~~~~~
@@ -302,6 +309,11 @@ returns a ``malloc``\ 'd NUL-terminated string the caller must ``free``:
302
309
  /* "*café*" */
303
310
  free(out);
304
311
 
312
+ Tabs are measured as though text begins at column 0. ``wcwidth_rjust_u8()`` and
313
+ ``wcwidth_center_u8()`` insert padding to the left of the ``text`` which move tabstops and return
314
+ an incorrect result, always expand tabs or call `wcwidth_clip_u8()`_ to ``opts->dest_width`` first,
315
+ which expands tabs before using these alignment functions.
316
+
305
317
  .. _libwcwidth-clip:
306
318
 
307
319
  wcwidth_clip_u8()
@@ -4,10 +4,11 @@
4
4
  Specification
5
5
  =============
6
6
 
7
- This document defines how this Python wcwidth library measures the printable width of characters of
8
- a string. This is not meant to an official standard, but as a terse description of the lowest level
9
- API functions :func:`wcwidth.wcwidth` and :func:`wcwidth.wcswidth` and its relation to higher level
10
- functions :func:`wcwidth.width` and :func:`wcwidth.iter_graphemes`.
7
+ This document defines how the Python wcwidth and C11 libwcwidth_ libraries measure the printable
8
+ width of characters of a string. This is not meant to be an official standard, but as a terse
9
+ description of the lowest level API functions :func:`wcwidth.wcwidth` and :func:`wcwidth.wcswidth`
10
+ and their relation to higher level functions :func:`wcwidth.width`, :func:`wcwidth.wcstwidth` and
11
+ :func:`wcwidth.iter_graphemes`.
11
12
 
12
13
  Scope
13
14
  -----
@@ -19,10 +20,19 @@ its docstring and specifications of related control codes, `XTerm Control Sequen
19
20
  Text Sizing Protocol`_.
20
21
 
21
22
  Each string yielded by :func:`wcwidth.iter_graphemes` may be mapped to :func:`wcwidth.wcswidth` to
22
- accurately measure the width of a grapheme. Although :func:`wcwidth.iter_graphemes` matches
23
- behavior of Python 3.15 `uncodedata.iter_graphemes()`_ it differs in its return value,
24
- :func:`wcwidth.iter_graphemes` yields only strings, while :func:`unicodedata.iter_graphemes` yields
25
- ``unicodedata.Segment`` class objects.
23
+ accurately measure the width of a grapheme. :func:`wcwidth.iter_graphemes` implements the `Unicode
24
+ Standard Annex #29`_ rules of Unicode 18.0, the version of this library's tables, while
25
+ `unicodedata.iter_graphemes()`_, new in Python 3.15, implements Unicode 17.0. They also differ in
26
+ return value: :func:`wcwidth.iter_graphemes` yields only strings, while
27
+ :func:`unicodedata.iter_graphemes` yields ``unicodedata.Segment`` class objects.
28
+
29
+ Tamil, Kannada and Sinhala text is measured differently than it is segmented, so
30
+ :func:`wcwidth.clip` and :func:`wcwidth.wrap` do not measure these scripts correctly. See `Virama
31
+ Conjunct Formation`_.
32
+
33
+ The C11 library libwcwidth_ implements this specification through parallel entry points:
34
+ ``wcswidth_u32()`` and ``wcswidth_u8()`` for :func:`wcwidth.wcswidth`, ``wcstwidth_u8()`` for
35
+ :func:`wcwidth.wcstwidth`, and ``wcwidth_width_u8()`` for :func:`wcwidth.width`; see the `C11 API`_.
26
36
 
27
37
  Width of -1
28
38
  -----------
@@ -30,39 +40,38 @@ Width of -1
30
40
  The following have a column width of -1 for function :func:`wcwidth.wcwidth`
31
41
 
32
42
  - ``C0`` control characters (`U+0001`_ through `U+001F`_).
33
- - ``C1`` control characters and ``DEL`` (`U+007F`_ through `U+00A0`_).
43
+ - ``C1`` control characters and ``DEL`` (`U+007F`_ through `U+009F`_).
34
44
 
35
45
  If any character in sequence contains ``C0`` or ``C1`` control characters, the final
36
- return value of :func:`wcwidth.wcswidth` is -1.
46
+ return value of :func:`wcwidth.wcswidth` is -1, except a control character immediately following
47
+ ZWJ (`U+200D`_), which `Width of 0`_ consumes.
37
48
 
38
49
  Width of 0
39
50
  ----------
40
51
 
41
52
  Any characters with the `Default_Ignorable_Code_Point`_ property in
42
- `DerivedCoreProperties.txt`_ files, 4,174 characters, excluding `U+00AD`_ SOFT HYPHEN
43
- (width 1) and `U+115F`_ HANGUL CHOSEONG FILLER (width 2).
53
+ `DerivedCoreProperties.txt`_, excluding `U+00AD`_ SOFT HYPHEN (width 1) and
54
+ `U+115F`_ HANGUL CHOSEONG FILLER (width 2).
44
55
 
45
- Any characters defined by `General Category`_ codes in `DerivedGeneralCategory.txt`_ files:
56
+ Any characters defined by `General Category`_ codes in `DerivedGeneralCategory.txt`_:
46
57
 
47
- - 'Me': `Enclosing Mark`_, aprox. 13 characters.
48
- - 'Mn': `Nonspacing Mark`_, aprox. 1,839 characters.
58
+ - 'Me': `Enclosing Mark`_.
59
+ - 'Mn': `Nonspacing Mark`_.
49
60
  - 'Cf': `Format`_ control characters excluding `U+00AD`_ SOFT HYPHEN and
50
- `Prepended_Concatenation_Mark`_ characters, aprox. 147 characters.
61
+ `Prepended_Concatenation_Mark`_ characters.
51
62
  - 'Zl': `U+2028`_ LINE SEPARATOR only
52
63
  - 'Zp': `U+2029`_ PARAGRAPH SEPARATOR only
53
- - 'Sk': `Modifier Symbol`_, aprox. 1 character with ``'FULLWIDTH'`` in comment
54
- of `UnicodeData.txt`_ (see `Width of 2`_). `Emoji Modifier`_ Fitzpatrick
55
- symbols (`U+1F3FB`_ through `U+1F3FF`_) are zero-width only when following
56
- an emoji base character in sequence; see `Width of 2`_ for standalone.
64
+
65
+ `Emoji Modifier`_ Fitzpatrick symbols (`U+1F3FB`_ through `U+1F3FF`_) are zero-width
66
+ only when following an emoji base character in sequence; see `Width of 2`_ for standalone.
57
67
 
58
68
  The NULL character (`U+0000`_).
59
69
 
60
- Any character following ZWJ (`U+200D`_) when preceded by an emoji
61
- (`Extended_Pictographic`_ property) or `Regional Indicator`_ in sequence by
62
- function :func:`wcwidth.wcswidth`, following grapheme cluster boundary rules
63
- of `Unicode Standard Annex #29`_. When ZWJ follows a non-emoji character
64
- (including CJK), only the ZWJ itself is zero-width; the following character
65
- is measured normally.
70
+ ZWJ (`U+200D`_) is zero-width, and in sequence by :func:`wcwidth.wcswidth` or
71
+ :func:`wcwidth.width` the codepoint immediately following it is consumed as zero-width.
72
+ For emoji (`Extended_Pictographic`_ property) and `Regional Indicator`_ sequences this follows the
73
+ grapheme cluster boundary rules of `Unicode Standard Annex #29`_. After a virama, the following
74
+ consonant continues the conjunct, see `Virama Conjunct Formation`_.
66
75
 
67
76
  The second `Regional Indicator`_ symbol (`U+1F1E6`_ through `U+1F1FF`_) in a
68
77
  consecutive pair, when measured in sequence by :func:`wcwidth.wcswidth` or
@@ -71,9 +80,9 @@ consecutive pair, when measured in sequence by :func:`wcwidth.wcswidth` or
71
80
  `Hangul Jamo`_ Jungseong and "Extended-B" code blocks, `U+1160`_ through
72
81
  `U+11FF`_ and `U+D7B0`_ through `U+D7FF`_.
73
82
 
74
- Any characters of category ``Mc`` (`Spacing Combining Mark`_), aprox. 443
75
- characters, for the single-character function :func:`wcwidth.wcwidth`.
76
- When measured in sequence by :func:`wcwidth.wcswidth`, see `Width of 2`_.
83
+ Any characters of category ``Mc`` (`Spacing Combining Mark`_), for the
84
+ single-character function :func:`wcwidth.wcwidth`. When measured in sequence by
85
+ :func:`wcwidth.wcswidth`, see `Width of 2`_.
77
86
 
78
87
  Width of 1
79
88
  ----------
@@ -93,8 +102,9 @@ Width of 2
93
102
  ----------
94
103
 
95
104
  Any character defined by `East Asian`_ (`Unicode Standard Annex #11`_) Fullwidth
96
- (``F``) or Wide (``W``) properties in `EastAsianWidth.txt`_ files, except those
97
- that are defined by the Category code of `Nonspacing Mark`_ (``Mn``).
105
+ (``F``) or Wide (``W``) properties in `EastAsianWidth.txt`_, except those that
106
+ are defined by the Category codes of `Nonspacing Mark`_ (``Mn``) or `Spacing
107
+ Combining Mark`_ (``Mc``).
98
108
 
99
109
  `Regional Indicator`_ symbols (`U+1F1E6`_ through `U+1F1FF`_). Though
100
110
  classified as Neutral in `EastAsianWidth.txt`_, terminals universally render
@@ -106,7 +116,7 @@ measured standalone (not following an emoji base character). When following
106
116
  an emoji base, they combine with the base and add 0 to total width.
107
117
 
108
118
  Any characters of `Modifier Symbol`_ category, ``'Sk'`` where ``'FULLWIDTH'`` is
109
- present in comment of `UnicodeData.txt`_, aprox. 3 characters.
119
+ present in comment of `UnicodeData.txt`_.
110
120
 
111
121
  Any character with `U+FE0F`_ (Variation Selector 16) defined as ``emoji style``
112
122
  in `emoji-variation-sequences.txt`_, per `UTS #51`_ and `Unicode Standard
@@ -116,13 +126,14 @@ making the pair width 2. Wide characters are unchanged.
116
126
  Any character of non-zero width followed by an ``Mc`` (`Spacing Combining Mark`_)
117
127
  character when measured in sequence by :func:`wcwidth.wcswidth` or
118
128
  :func:`wcwidth.width`. The ``Mc`` character caps the cluster width at 2,
119
- reflecting its *positive advance width* as defined in `General Category`_
120
- (Table 4-4). Zero-width combining marks (``Mn``) between the base character
121
- and the ``Mc`` do not break the association. For example, a consonant followed
129
+ reflecting its *positive advance width* as defined in the `General Category Values`_
130
+ table. Zero-width combining marks (``Mn``) between the base character and the ``Mc``
131
+ do not break the association. For example, a consonant followed
122
132
  by a Nukta (``Mn``) and then a vowel sign (``Mc``) is measured as a cluster of
123
133
  width 2.
124
134
 
125
- Any grapheme cluster width is limited to 2 cells since 0.8.0, `PR #224`_.
135
+ Any grapheme cluster width is limited to 2 cells since 0.8.0, `PR #224`_, which prevents the ``Mc``
136
+ rule and the virama conjunct sum from exceeding 2 cells.
126
137
 
127
138
  Virama Conjunct Formation
128
139
  -------------------------
@@ -145,16 +156,19 @@ described in the Virama section header).
145
156
  - ZWJ (`U+200D`_) after a virama is consumed without breaking conjunct state,
146
157
  supporting explicit half-form requests (virama + ZWJ + consonant).
147
158
 
148
- See also: `L2/2023/23107`_ "Proper Complex Script Support in Text Terminals".
159
+ See also: `L2/23-107`_ "Proper Complex Script Support in Text Terminals", the 2023 project
160
+ proposal that records the missing terminal-width standard, and `L2/25-263`_ for the Text Terminal
161
+ Working Group's latest report.
149
162
 
150
- .. _`Hyperlinks in Terminal Emulators`: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
151
163
  .. _`Kitty Text Sizing Protocol`: https://sw.kovidgoyal.net/kitty/text-sizing-protocol/
152
164
  .. _`XTerm Control Sequences`: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
165
+ .. _`C11 API`: https://wcwidth.readthedocs.io/en/latest/api_c.html
166
+ .. _libwcwidth: https://wcwidth.readthedocs.io/en/latest/libwcwidth.html
153
167
  .. _`U+0000`: https://codepoints.net/U+0000
154
168
  .. _`U+0001`: https://codepoints.net/U+0001
155
169
  .. _`U+001F`: https://codepoints.net/U+001F
156
170
  .. _`U+007F`: https://codepoints.net/U+007F
157
- .. _`U+00A0`: https://codepoints.net/U+00A0
171
+ .. _`U+009F`: https://codepoints.net/U+009F
158
172
  .. _`U+00AD`: https://codepoints.net/U+00AD
159
173
  .. _`U+1160`: https://codepoints.net/U+1160
160
174
  .. _`U+11FF`: https://codepoints.net/U+11FF
@@ -172,12 +186,13 @@ See also: `L2/2023/23107`_ "Proper Complex Script Support in Text Terminals".
172
186
  .. _`Prepended_Concatenation_Mark`: https://www.unicode.org/reports/tr44/#Prepended_Concatenation_Mark
173
187
  .. _`Default_Ignorable_Code_Point`: https://www.unicode.org/reports/tr44/#Default_Ignorable_Code_Point
174
188
  .. _`General Category`: https://www.unicode.org/reports/tr44/#General_Category
189
+ .. _`General Category Values`: https://www.unicode.org/reports/tr44/#GC_Values_Table
175
190
  .. _`Spacing Combining Mark`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
176
191
  .. _`Enclosing Mark`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
177
192
  .. _`Format`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
178
193
  .. _`Modifier Symbol`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
179
194
  .. _`Hangul Jamo`: https://www.unicode.org/charts/PDF/U1100.pdf
180
- .. _`U+D7FF`: https://codepoints.net/U+D7FF
195
+ .. _`U+D7FF`: https://www.unicode.org/charts/PDF/UD7B0.pdf
181
196
  .. _`UnicodeData.txt`: https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
182
197
  .. _`East Asian`: https://www.unicode.org/reports/tr11/
183
198
  .. _`Unicode Standard Annex #11`: https://www.unicode.org/reports/tr11/
@@ -187,18 +202,18 @@ See also: `L2/2023/23107`_ "Proper Complex Script Support in Text Terminals".
187
202
  .. _`U+1F3FF`: https://codepoints.net/U+1F3FF
188
203
  .. _`Regional Indicator`: https://www.unicode.org/charts/PDF/U1F100.pdf
189
204
  .. _`Emoji Modifier`: https://unicode.org/reports/tr51/#Emoji_Modifiers
190
- .. _`Extended_Pictographic`: https://www.unicode.org/reports/tr51/#def_extended_pictographic
205
+ .. _`Extended_Pictographic`: https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt
191
206
  .. _`UTS #51`: https://www.unicode.org/reports/tr51/
192
207
  .. _`Nonspacing Mark`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
193
208
  .. _`IndicSyllabicCategory.txt`: https://www.unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt
194
- .. _`Indic_Syllabic_Category`: https://www.unicode.org/reports/tr44/#Indic_Syllabic_Category
195
209
  .. _`Invisible_Stacker`: https://www.unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt
196
210
  .. _`Brahmic scripts`: https://en.wikipedia.org/wiki/Brahmic_scripts
197
211
  .. _`Virama`: https://www.unicode.org/glossary/#virama
198
212
  .. _`conjunct`: https://www.unicode.org/glossary/#consonant_conjunct
199
213
  .. _`aksara`: https://www.unicode.org/glossary/#aksara
200
- .. _`L2/2023/23107`: https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
214
+ .. _`L2/23-107`: https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
215
+ .. _`L2/25-263`: https://www.unicode.org/L2/L2025/25263-ttwg-report-utc185.pdf
201
216
  .. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
202
- .. _`Unicode Standard Section 23.4`: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-23/#G77993
203
- .. _`uncodedata.iter_graphemes()`: https://docs.python.org/3.15/library/unicodedata.html#unicodedata.iter_graphemes
217
+ .. _`Unicode Standard Section 23.4`: https://www.unicode.org/versions/latest/core-spec/chapter-23/#G19053
218
+ .. _`unicodedata.iter_graphemes()`: https://docs.python.org/3.15/library/unicodedata.html#unicodedata.iter_graphemes
204
219
  .. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
@@ -13,8 +13,8 @@ CFLAGS ?= -std=c11 -Wall -Wextra -O2 -g
13
13
  INCLUDE = -Iinclude
14
14
  BUILD = build
15
15
 
16
- ASAN_CFLAGS = -std=c11 -Wall -Wextra -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer
17
- UBSAN_CFLAGS = -std=c11 -Wall -Wextra -O2 -g -fsanitize=undefined -fno-omit-frame-pointer
16
+ ASAN_CFLAGS = -std=c11 -Wall -Wextra -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer
17
+ UBSAN_CFLAGS = -std=c11 -Wall -Wextra -O2 -g -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer
18
18
 
19
19
  LIB_SRCS = $(wildcard src/*.c) $(wildcard src/tables/*.c)
20
20
  LIB_OBJS = $(patsubst %.c,$(BUILD)/%.o,$(LIB_SRCS))
@@ -48,6 +48,11 @@ extern const wcwidth_align_opts_t WCWIDTH_ALIGN_OPTS_DEFAULT;
48
48
  * opts: alignment options, or NULL for defaults.
49
49
  * out_len: output: byte length of result (excluding NUL); may be NULL.
50
50
  * error: output: wcwidth_error_t, WCWIDTH_ERROR_NONE on success.
51
+ *
52
+ * Tabs are measured as though text begins at column 0. 'rjust' and 'center' operations insert
53
+ * padding to the left of the ``text`` which move tabstops and return an incorrect result, always
54
+ * expand tabs or clip to ``opts->dest_width``, which expands tabs before using those alignment
55
+ * functions.
51
56
  */
52
57
  char *wcwidth_ljust_u8(const char *text, size_t text_len, wcwidth_control_mode_t mode,
53
58
  const wcwidth_align_opts_t *opts, size_t *out_len, int *error);
@@ -17,6 +17,9 @@ extern "C" {
17
17
  * U+FFFD and the return value is the number of bytes to skip (1 for a bad
18
18
  * leading byte, the remaining length for a truncated sequence, or 0 when
19
19
  * *len* is 0).
20
+ *
21
+ * *s* must not be NULL. *cp_out* may be NULL to consume the sequence and
22
+ * query the length of the next character.
20
23
  */
21
24
  size_t wcwidth_utf8_decode_single(const char *s, size_t len, uint32_t *cp_out);
22
25
 
@@ -28,15 +31,26 @@ size_t wcwidth_utf8_decode_single(const char *s, size_t len, uint32_t *cp_out);
28
31
  * is undefined behavior. Sets *count* and returns NULL on allocation
29
32
  * failure.
30
33
  *
34
+ * *utf8* and *count* must not be NULL; *count* is required because the
35
+ * returned array carries no terminator. A *stack* of NULL forces the heap
36
+ * path. When *n* is 0 and *stack* is NULL, the result is a freeable array
37
+ * with *count* set to 0.
38
+ *
31
39
  * The result type allows a plain free() release, matching wcwidth_encode_u32()
32
40
  * below. Treat the contents as read-only.
41
+ *
42
+ * Decoding reads exactly *n* bytes. A NUL byte counts as an ordinary
43
+ * zero-width codepoint in any position; pass strlen(utf8) for a C string.
33
44
  */
34
45
  uint32_t *wcwidth_decode_u32(const char *utf8, size_t n, uint32_t *stack, size_t stack_cap,
35
46
  size_t *count);
36
47
 
37
48
  /*
38
49
  * Decode *utf8* into a malloc'd codepoint array (one free()). Returns the
39
- * array and sets *count*, or NULL on allocation failure.
50
+ * array and sets *count*, or NULL on allocation failure. An *n* of 0 returns
51
+ * a freeable array with *count* set to 0.
52
+ *
53
+ * Follows the length and NUL semantics of wcwidth_decode_u32().
40
54
  */
41
55
  uint32_t *wcwidth_decode_u32_heap(const char *utf8, size_t n, size_t *count);
42
56
 
@@ -46,10 +60,15 @@ uint32_t *wcwidth_decode_u32_heap(const char *utf8, size_t n, size_t *count);
46
60
  * in *stack* (no allocation), otherwise a heap buffer is allocated. The
47
61
  * caller must free() the result only when it is not *stack*; freeing *stack*
48
62
  * is undefined behavior. Sets *out_len* and returns NULL on allocation
49
- * failure.
63
+ * failure. An *n* of 0 returns a freeable buffer with *out_len* set to 0.
64
+ *
65
+ * *codepoints* and *out_len* must not be NULL; *out_len* is required because
66
+ * the returned bytes carry no terminator. A *stack* of NULL forces the heap
67
+ * path.
50
68
  *
51
- * The result is not NUL-terminated. Invalid codepoints (lone surrogates and
52
- * values above U+10FFFF) are encoded as U+FFFD.
69
+ * Encoding writes exactly *n* codepoints: a U+0000 becomes a NUL byte, so the
70
+ * output can contain embedded NUL bytes. Invalid codepoints (lone surrogates
71
+ * and values above U+10FFFF) are encoded as U+FFFD.
53
72
  */
54
73
  char *wcwidth_encode_u32(const uint32_t *codepoints, size_t n, char *stack, size_t stack_cap,
55
74
  size_t *out_len);
@@ -18,8 +18,8 @@ extern "C" {
18
18
 
19
19
  #define WCWIDTH_VERSION_MAJOR 0
20
20
  #define WCWIDTH_VERSION_MINOR 9
21
- #define WCWIDTH_VERSION_PATCH 1
22
- #define WCWIDTH_VERSION "0.9.1"
21
+ #define WCWIDTH_VERSION_PATCH 3
22
+ #define WCWIDTH_VERSION "0.9.3"
23
23
 
24
24
  /* Unicode version the library tables were generated from. */
25
25
  #define WCWIDTH_UNICODE_VERSION "18.0.0"
@@ -87,6 +87,10 @@ fill_repeat(char *dst, const char *fillchar, size_t fillchar_len, size_t count)
87
87
  {
88
88
  size_t i;
89
89
 
90
+ if (fillchar_len == 0) {
91
+ return dst;
92
+ }
93
+
90
94
  for (i = 0; i < count; i++) {
91
95
  memcpy(dst, fillchar, fillchar_len);
92
96
  dst += fillchar_len;
@@ -133,7 +137,9 @@ ljust_impl(const char *text, size_t text_len, size_t dest_width, const char *fil
133
137
  return NULL;
134
138
  }
135
139
 
136
- memcpy(result, text, text_len);
140
+ if (text_len > 0) {
141
+ memcpy(result, text, text_len);
142
+ }
137
143
  fill_repeat(result + text_len, fillchar, fillchar_len, padding);
138
144
  result[total] = '\0';
139
145
 
@@ -184,7 +190,9 @@ rjust_impl(const char *text, size_t text_len, size_t dest_width, const char *fil
184
190
 
185
191
  fill_repeat(result, fillchar, fillchar_len, padding);
186
192
  /* add_fill_bytes() above already proved this product cannot wrap. */
187
- memcpy(result + padding * fillchar_len, text, text_len);
193
+ if (text_len > 0) {
194
+ memcpy(result + padding * fillchar_len, text, text_len);
195
+ }
188
196
  result[total] = '\0';
189
197
 
190
198
  if (out_len != NULL) {
@@ -246,7 +254,9 @@ center_impl(const char *text, size_t text_len, size_t dest_width, const char *fi
246
254
 
247
255
  {
248
256
  char *dst = fill_repeat(result, fillchar, fillchar_len, left_pad);
249
- memcpy(dst, text, text_len);
257
+ if (text_len > 0) {
258
+ memcpy(dst, text, text_len);
259
+ }
250
260
  fill_repeat(dst + text_len, fillchar, fillchar_len, right_pad);
251
261
  }
252
262
  result[total] = '\0';
@@ -806,6 +806,16 @@ _wrap_chunks(chunklist_t *chunks, const wcwidth_wrap_opts_t *opts, lines_t *line
806
806
  lc.len = rstrip_len(lc.data, lc.len);
807
807
  if (lc.data != NULL)
808
808
  lc.data[lc.len] = '\0';
809
+ if (lc.len == 0) {
810
+ /*
811
+ * The line held only whitespace, which dropping has now emptied. Skip
812
+ * it and leave `first` unchanged so initial_indent still applies to the
813
+ * first line with content, as stdlib textwrap and wcwidth.textwrap do.
814
+ */
815
+ sbuf_free(&lc);
816
+ cl_free(&cur);
817
+ continue;
818
+ }
809
819
  }
810
820
  sbuf_t fl;
811
821
  sbuf_init(&fl);