wcwidth 0.9.0__tar.gz → 0.9.2__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.0 → wcwidth-0.9.2}/.gitignore +4 -0
  2. {wcwidth-0.9.0 → wcwidth-0.9.2}/PKG-INFO +6 -5
  3. {wcwidth-0.9.0 → wcwidth-0.9.2}/README.rst +5 -4
  4. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/update-docs.py +38 -15
  5. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/developing.rst +6 -0
  6. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/history.rst +21 -2
  7. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/intro.rst +5 -4
  8. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/libwcwidth.rst +36 -12
  9. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/specs.rst +60 -45
  10. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/align.h +5 -0
  11. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/utf8.h +9 -2
  12. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/wcwidth_config.h +2 -2
  13. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/textwrap.c +10 -0
  14. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/utf8.c +8 -18
  15. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/wcstwidth.c +1 -1
  16. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/width.c +47 -20
  17. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_align.c +29 -0
  18. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_clip.c +51 -0
  19. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_sgr.c +50 -0
  20. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_text_sizing.c +13 -0
  21. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_textwrap.c +71 -0
  22. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_utf8.c +29 -0
  23. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_wcswidth.c +7 -0
  24. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_width.c +69 -1
  25. {wcwidth-0.9.0 → wcwidth-0.9.2}/pyproject.toml +4 -5
  26. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-update.txt +1 -1
  27. wcwidth-0.9.2/requirements-wheels.in +4 -0
  28. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-wheels.txt +4 -11
  29. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_benchmarks.py +10 -0
  30. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_check_release.py +1 -1
  31. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_clip.py +28 -0
  32. wcwidth-0.9.2/tests/test_fastcall_args.py +118 -0
  33. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_term_overrides.py +32 -0
  34. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_textwrap.py +21 -0
  35. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_width.py +10 -0
  36. {wcwidth-0.9.0 → wcwidth-0.9.2}/tox.ini +58 -9
  37. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/__init__.py +1 -1
  38. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_clip.py +9 -2
  39. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_wcswidth.py +1 -1
  40. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_wcwidth_c.c +724 -107
  41. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_width.py +1 -1
  42. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/align.py +61 -3
  43. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/textwrap.py +9 -0
  44. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth.egg-info/PKG-INFO +6 -5
  45. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth.egg-info/SOURCES.txt +1 -1
  46. wcwidth-0.9.0/docs/api_c.rst +0 -1042
  47. wcwidth-0.9.0/requirements-wheels.in +0 -3
  48. {wcwidth-0.9.0 → wcwidth-0.9.2}/.pylintrc +0 -0
  49. {wcwidth-0.9.0 → wcwidth-0.9.2}/LICENSE +0 -0
  50. {wcwidth-0.9.0 → wcwidth-0.9.2}/MANIFEST.in +0 -0
  51. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/check-release.py +0 -0
  52. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/update-tables.py +0 -0
  53. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/verify-table-integrity.py +0 -0
  54. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/wcwidth-browser.py +0 -0
  55. {wcwidth-0.9.0 → wcwidth-0.9.2}/bin/wcwidth-libc-comparator.py +0 -0
  56. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/api_c.rst.j2 +0 -0
  57. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/c_gcb_class.c.j2 +0 -0
  58. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/c_table.c.j2 +0 -0
  59. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/c_term_programs.c.j2 +0 -0
  60. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/c_terminal_overrides.c.j2 +0 -0
  61. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/grapheme_override_per_terminal.py.j2 +0 -0
  62. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/grapheme_override_table.py.j2 +0 -0
  63. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/grapheme_registry.py.j2 +0 -0
  64. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/grapheme_table.c.j2 +0 -0
  65. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/grapheme_table.py.j2 +0 -0
  66. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/python_table.py.j2 +0 -0
  67. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/table_overrides.py.j2 +0 -0
  68. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/tables.h.j2 +0 -0
  69. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/term_programs.py.j2 +0 -0
  70. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/unicode_version.rst.j2 +0 -0
  71. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/unicode_versions.py.j2 +0 -0
  72. {wcwidth-0.9.0 → wcwidth-0.9.2}/code_templates/wcwidth_config.h.j2 +0 -0
  73. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/api.rst +0 -0
  74. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/conf.py +0 -0
  75. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/index.rst +0 -0
  76. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/related.rst +0 -0
  77. {wcwidth-0.9.0 → wcwidth-0.9.2}/docs/unicode_version.rst +0 -0
  78. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/.clang-format +0 -0
  79. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/CMakeLists.txt +0 -0
  80. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/LICENSE +0 -0
  81. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/Makefile +0 -0
  82. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/README.rst +0 -0
  83. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/cmake/libwcwidth-config.cmake.in +0 -0
  84. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/examples/align.c +0 -0
  85. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/examples/textwrap.c +0 -0
  86. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/examples/width.c +0 -0
  87. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/clip.h +0 -0
  88. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/escape.h +0 -0
  89. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/grapheme.h +0 -0
  90. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/sgr.h +0 -0
  91. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/table_types.h +0 -0
  92. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/tables.h +0 -0
  93. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/terminal_override.h +0 -0
  94. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/text_sizing.h +0 -0
  95. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/textwrap.h +0 -0
  96. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/unicode.h +0 -0
  97. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/wcwidth.h +0 -0
  98. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/include/wcwidth/width.h +0 -0
  99. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/align.c +0 -0
  100. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/bisearch.c +0 -0
  101. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/clip.c +0 -0
  102. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/escape.c +0 -0
  103. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/grapheme.c +0 -0
  104. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/sgr.c +0 -0
  105. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_ambiguous.c +0 -0
  106. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_gcb_class.c +0 -0
  107. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_grapheme.c +0 -0
  108. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_mc.c +0 -0
  109. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_term_programs.c +0 -0
  110. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_terminal_overrides.c +0 -0
  111. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_vs15.c +0 -0
  112. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_vs16.c +0 -0
  113. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_wide.c +0 -0
  114. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/tables/table_zero.c +0 -0
  115. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/terminal_override.c +0 -0
  116. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/text_sizing.c +0 -0
  117. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/wcswidth.c +0 -0
  118. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/src/wcwidth.c +0 -0
  119. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_common.h +0 -0
  120. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_escape.c +0 -0
  121. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_gcb_class.c +0 -0
  122. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_grapheme.c +0 -0
  123. {wcwidth-0.9.0 → wcwidth-0.9.2}/libwcwidth/tests/test_wcwidth.c +0 -0
  124. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-develop.txt +0 -0
  125. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-docs.in +0 -0
  126. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-tests39.in +0 -0
  127. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-tests39.txt +0 -0
  128. {wcwidth-0.9.0 → wcwidth-0.9.2}/requirements-update.in +0 -0
  129. {wcwidth-0.9.0 → wcwidth-0.9.2}/setup.cfg +0 -0
  130. {wcwidth-0.9.0 → wcwidth-0.9.2}/setup.py +0 -0
  131. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/__init__.py +0 -0
  132. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/conftest.py +0 -0
  133. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_ambiguous.py +0 -0
  134. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_clip_cjk_emoji.py +0 -0
  135. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_clip_overtyping.py +0 -0
  136. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_core.py +0 -0
  137. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_emojis.py +0 -0
  138. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_grapheme.py +0 -0
  139. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_hyperlink.py +0 -0
  140. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_justify.py +0 -0
  141. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_setup.py +0 -0
  142. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_sgr_state.py +0 -0
  143. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_text_sizing.py +0 -0
  144. {wcwidth-0.9.0 → wcwidth-0.9.2}/tests/test_ucslevel.py +0 -0
  145. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_constants.py +0 -0
  146. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_wcwidth.py +0 -0
  147. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/_wcwidth_c.pyi +0 -0
  148. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/bisearch.py +0 -0
  149. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/control_codes.py +0 -0
  150. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/escape_sequences.py +0 -0
  151. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/grapheme.py +0 -0
  152. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/hyperlink.py +0 -0
  153. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/py.typed +0 -0
  154. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/sgr_state.py +0 -0
  155. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_ambiguous.py +0 -0
  156. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme.py +0 -0
  157. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/__init__.py +0 -0
  158. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_058c7585.py +0 -0
  159. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_2148ff29.py +0 -0
  160. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_244dc88b.py +0 -0
  161. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_336a71a1.py +0 -0
  162. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_33ac73f2.py +0 -0
  163. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_3a1895d5.py +0 -0
  164. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_59180a0d.py +0 -0
  165. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_65c6beb5.py +0 -0
  166. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_78f828e8.py +0 -0
  167. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_8c2ae14d.py +0 -0
  168. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_900356bf.py +0 -0
  169. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_97904ece.py +0 -0
  170. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_a5880eeb.py +0 -0
  171. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_b6d33cf4.py +0 -0
  172. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_c57c295b.py +0 -0
  173. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_cf02297d.py +0 -0
  174. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_e18ac6a7.py +0 -0
  175. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_e2b75ee3.py +0 -0
  176. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_fb5fd79b.py +0 -0
  177. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_known_fdc99132.py +0 -0
  178. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_grapheme_overrides/_registry.py +0 -0
  179. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_mc.py +0 -0
  180. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_overrides.py +0 -0
  181. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_term_programs.py +0 -0
  182. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_vs15.py +0 -0
  183. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_vs16.py +0 -0
  184. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_wide.py +0 -0
  185. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/table_zero.py +0 -0
  186. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/text_sizing.py +0 -0
  187. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/unicode_versions.py +0 -0
  188. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth/wcwidth.py +0 -0
  189. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth.egg-info/dependency_links.txt +0 -0
  190. {wcwidth-0.9.0 → wcwidth-0.9.2}/wcwidth.egg-info/top_level.txt +0 -0
@@ -14,7 +14,11 @@ wheelhouse
14
14
  *.so
15
15
  dist
16
16
  docs/_build
17
+ # generated from the C headers by bin/update-docs.py, not committed
18
+ docs/api_c.rst
17
19
  htmlcov
20
+ htmlcov-c
21
+ coverage-c.lcov
18
22
  .coveralls.yml
19
23
  data
20
24
  .DS_Store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcwidth
3
- Version: 0.9.0
3
+ Version: 0.9.2
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
@@ -6,7 +6,9 @@ from __future__ import annotations
6
6
  # std imports
7
7
  import os
8
8
  import re
9
+ import sys
9
10
  import glob
11
+ import argparse
10
12
  import textwrap
11
13
  import importlib.util
12
14
 
@@ -21,7 +23,7 @@ import jinja2
21
23
  # docs/unicode_version.rst, docs/libwcwidth.rst, and the list_term_programs()
22
24
  # example in README.rst.
23
25
 
24
- PATH_UP = os.path.relpath(os.path.join(os.path.dirname(__file__), os.path.pardir))
26
+ PATH_UP = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25
27
  JINJA_ENV = jinja2.Environment(
26
28
  loader=jinja2.FileSystemLoader(os.path.join(PATH_UP, 'code_templates')),
27
29
  keep_trailing_newline=True,
@@ -458,21 +460,29 @@ def report(label: str, changed: bool) -> None:
458
460
  print(f'{label}: {"updated" if changed else "up-to-date"}')
459
461
 
460
462
 
461
- def write_if_changed(path: str, content: str, label: str) -> None:
462
- """Write *content* to *path* only if it differs, reporting the result."""
463
+ def write_if_changed(path: str, content: str, label: str, check: bool = False) -> bool:
464
+ """
465
+ Write *content* to *path* only if it differs, reporting and returning whether it did.
466
+
467
+ With *check*, report the difference without writing, so a stale file fails the run.
468
+ """
463
469
  try:
464
470
  with open(path, encoding='utf-8') as fin:
465
471
  original = fin.read()
466
472
  except FileNotFoundError:
467
473
  original = ''
468
- if content != original:
469
- new_path = path + '.new'
470
- with open(new_path, 'w', encoding='utf-8', newline='\n') as fout:
471
- fout.write(content)
472
- os.replace(new_path, path)
473
- print(f'{label}: updated')
474
- else:
474
+ if content == original:
475
475
  print(f'{label}: up-to-date')
476
+ return False
477
+ if check:
478
+ print(f'{label}: out of date, run bin/update-docs.py')
479
+ return True
480
+ new_path = path + '.new'
481
+ with open(new_path, 'w', encoding='utf-8', newline='\n') as fout:
482
+ fout.write(content)
483
+ os.replace(new_path, path)
484
+ print(f'{label}: updated')
485
+ return True
476
486
 
477
487
 
478
488
  def update_libwcwidth_term_programs() -> bool:
@@ -545,18 +555,31 @@ def unicode_version_page() -> str:
545
555
  source_headers=unicode_source_headers())
546
556
 
547
557
 
548
- def main() -> None:
549
- """Regenerate all generated documentation files."""
550
- write_if_changed(API_C_OUTPUT, render_api_doc(), 'docs/api_c.rst')
558
+ def main(api_only: bool = False, check: bool = False) -> int:
559
+ """Regenerate all generated documentation files, or only docs/api_c.rst."""
560
+ stale = write_if_changed(API_C_OUTPUT, render_api_doc(), 'docs/api_c.rst', check)
561
+ if api_only:
562
+ # api_c.rst is parsed from the C headers, but everything below reads the
563
+ # Unicode sources under data/, which only `tox -e fetch` downloads.
564
+ return int(check and stale)
551
565
  write_if_changed(os.path.join(PATH_DOCS, 'unicode_version.rst'),
552
- unicode_version_page(), 'docs/unicode_version.rst')
566
+ unicode_version_page(), 'docs/unicode_version.rst', check)
553
567
  report('docs/libwcwidth.rst: canonical terminal names',
554
568
  update_libwcwidth_term_programs())
555
569
  report('docs/libwcwidth.rst: Unicode version',
556
570
  update_libwcwidth_unicode_version())
557
571
  report('README.rst: list_term_programs() example',
558
572
  update_readme_term_programs())
573
+ return int(check and stale)
559
574
 
560
575
 
561
576
  if __name__ == '__main__':
562
- main()
577
+ parser = argparse.ArgumentParser(description=__doc__)
578
+ parser.add_argument('--api-only', action='store_true',
579
+ help='regenerate docs/api_c.rst only, which needs no Unicode data')
580
+ parser.add_argument('--check', action='store_true',
581
+ help='report stale output instead of writing it, for CI')
582
+ args = parser.parse_args()
583
+ if args.check and not args.api_only:
584
+ parser.error('--check needs --api-only: the other outputs read the fetched Unicode data')
585
+ sys.exit(main(api_only=args.api_only, check=args.check))
@@ -18,6 +18,12 @@ To run tests with detailed coverage reporting showing missing lines::
18
18
 
19
19
  tox -epy314 -- --cov-report=term-missing
20
20
 
21
+ The C11 library's coverage is measured separately, since coverage.py cannot see compiled
22
+ code. This merges two instrumented runs, the C11 test suite and the Python suite driving
23
+ the C extension, and writes ``coverage-c.lcov`` and ``htmlcov-c/index.html``::
24
+
25
+ tox -e c_coverage
26
+
21
27
  Updating Unicode Version
22
28
  ------------------------
23
29
 
@@ -2,7 +2,20 @@
2
2
  History
3
3
  =======
4
4
 
5
- 0.9.0 *unreleased* ('jq/libwcwidth-shim-wheels' branch)
5
+ 0.9.2 *2026-10-04*
6
+ * **Bugfix** `wrap()`_ should omit an empty line as output, like `textwrap.wrap()`_ `PR #291`_.
7
+ * **Bugfix** `clip()`_ when using non-default argument, ``tabsize=0``, `PR #288`_.
8
+ * **Bugfix** regional-indicator pairs (flags) when corrected for terminals that show lone regional
9
+ indicators as narrow (xterm, wezterm, VTE, and others) in `wcstwidth()`_ and `width()`_, `PR
10
+ #284`.
11
+
12
+ 0.9.1 *2026-09-23*
13
+ * **Bugfix** `width()`_ in Python and libwcwidth mis-measured CSI sequences over 64 bytes, a 0.9.0
14
+ release regression! `PR #271`_.
15
+ * **Performance** improvement of ~15% for many Python API functions when using default arguments
16
+ like ``ambiguous_width`` and ``term_program``. `PR #273`_
17
+
18
+ 0.9.0 *2026-09-23*
6
19
  * **New** Optional CPython extension using libwcwidth_, improving performance 10-58x `PR #248`_.
7
20
  * **Drop Support** for Python 3.8, `PR #248`_.
8
21
 
@@ -233,7 +246,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
233
246
  * for any purpose and without fee is hereby granted. The author
234
247
  * disclaims all warranties with regard to this software.
235
248
 
236
- .. _`Spacing Combining Mark`: https://www.unicode.org/versions/latest/ch04.pdf#G134153
249
+ .. _`Spacing Combining Mark`: https://www.unicode.org/versions/latest/core-spec/chapter-4/#G134153
237
250
  .. _`specification`: https://wcwidth.readthedocs.io/en/latest/specs.html
238
251
  .. _`prospector`: https://github.com/landscapeio/prospector
239
252
  .. _`combining`: https://en.wikipedia.org/wiki/Combining_character
@@ -299,6 +312,12 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
299
312
  .. _`PR #253`: https://github.com/jquast/wcwidth/pull/253
300
313
  .. _`PR #259`: https://github.com/jquast/wcwidth/pull/259
301
314
  .. _`PR #262`: https://github.com/jquast/wcwidth/pull/262
315
+ .. _`PR #271`: https://github.com/jquast/wcwidth/pull/271
316
+ .. _`PR #273`: https://github.com/jquast/wcwidth/pull/273
317
+ .. _`PR #281`: https://github.com/jquast/wcwidth/pull/281
318
+ .. _`PR #284`: https://github.com/jquast/wcwidth/pull/284
319
+ .. _`PR #288`: https://github.com/jquast/wcwidth/pull/288
320
+ .. _`PR #291`: https://github.com/jquast/wcwidth/pull/291
302
321
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
303
322
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
304
323
  .. _`Issue #211`: https://github.com/jquast/wcwidth/issues/211
@@ -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
@@ -10,16 +10,20 @@ The Python documentation_ closely matches this C library. The C API adds UTF-8
10
10
  array interfaces.
11
11
 
12
12
  The lowest-level functions are derived from POSIX.1-2001 and POSIX.1-2008 `wcwidth(3)`_ and
13
- `wcswidth(3)`_, which this library implements as `wcwidth_u32()`_ and `wcswidth_u32()`_. These
14
- functions return -1 when C0 and C1 control codes other than NUL are present; NUL measures as
15
- zero-width. They do not parse terminal escape sequences: any escape sequence contains control
16
- codes, so these functions return -1 for it.
13
+ `wcswidth(3)`_, which this library implements as :ref:`wcwidth_u32() <libwcwidth-wcwidth>` and
14
+ :ref:`wcswidth_u32() <libwcwidth-wcswidth>`. These functions return -1 when C0 and C1 control
15
+ codes other than NUL are present; NUL measures as zero-width.
16
+ :ref:`wcstwidth_u8() <libwcwidth-wcstwidth>` applies corrections for a specific terminal program,
17
+ as described in the Python Corrections_ documentation.
17
18
 
18
- `wcwidth_width_u8()`_ is a higher-level wrapper of `wcswidth_u8()`_ that also measures terminal
19
- control sequences, like colors, bold, tabstops, and horizontal cursor movement.
19
+ :ref:`wcwidth_width_u8() <libwcwidth-width>` is a higher-level wrapper of
20
+ :ref:`wcswidth_u8() <libwcwidth-wcswidth>` that also measures terminal control sequences, like
21
+ colors, bold, tabstops, and horizontal cursor movement.
20
22
 
21
- `wcstwidth_u8()`_ applies corrections for a specific terminal program and version, as described
22
- in the Python Corrections_ documentation.
23
+ The text transforms have their own sections:
24
+ :ref:`wcwidth_ljust_u8() <libwcwidth-align>` with ``rjust`` and ``center``,
25
+ :ref:`wcwidth_clip_u8() <libwcwidth-clip>`, :ref:`wcwidth_wrap_u8() <libwcwidth-wrap>`, and
26
+ :ref:`wcwidth_escape_strip() <libwcwidth-escape-strip>`.
23
27
 
24
28
  Quick Start
25
29
  -----------
@@ -133,16 +137,15 @@ address:
133
137
  String length conventions
134
138
  ~~~~~~~~~~~~~~~~~~~~~~~~~
135
139
 
136
- Every string function takes an explicit length and reads exactly that many units:
140
+ Every string function reads exactly the length you pass:
137
141
 
138
142
  ``_u32`` functions
139
143
  count codepoints in the array,
140
144
  ``_u8`` functions
141
145
  count bytes.
142
146
 
143
- There is no NUL-terminated sentinel form; pass ``strlen(text)`` when the text is a C string. The
144
- length is authoritative: a NUL is an ordinary zero-width character that may appear anywhere, and
145
- 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``.
146
149
 
147
150
  Alternate encodings
148
151
  ~~~~~~~~~~~~~~~~~~~
@@ -187,6 +190,8 @@ throughout, or use the ``_u32`` forms and re-encode the result. `wcwidth_encode
187
190
  Re-encoding to a legacy charset is the caller's iconv(3) or ICU (``ucnv_*``) call; a byte cast
188
191
  works when every codepoint fits the target, and iconv reports ``EILSEQ`` when one does not.
189
192
 
193
+ .. _libwcwidth-wcwidth:
194
+
190
195
  wcwidth_u32()
191
196
  ~~~~~~~~~~~~~
192
197
 
@@ -206,6 +211,8 @@ U+2640 above is Ambiguous, so it answers to the second argument, while U+2630 is
206
211
  2 under either setting. A single codepoint needs no ``_u8`` variant; use `wcswidth_u8()`_ to
207
212
  measure text.
208
213
 
214
+ .. _libwcwidth-wcswidth:
215
+
209
216
  wcswidth_u32() and wcswidth_u8()
210
217
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
211
218
 
@@ -219,6 +226,8 @@ when any control code other than NUL is present:
219
226
  wcswidth_u32(family, 5, 1) /* man ZWJ woman ZWJ girl */ 2
220
227
  wcswidth_u8("café", 5, 1) 4
221
228
 
229
+ .. _libwcwidth-wcstwidth:
230
+
222
231
  wcstwidth_u32() and wcstwidth_u8()
223
232
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224
233
 
@@ -230,6 +239,8 @@ argument applies terminal-specific corrections:
230
239
  wcswidth_u8("☰", 3, 1) /* U+2630, wide */ 2
231
240
  wcstwidth_u8("☰", 3, 1, "vte") 1
232
241
 
242
+ .. _libwcwidth-width:
243
+
233
244
  wcwidth_width_u32() and wcwidth_width_u8()
234
245
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235
246
 
@@ -261,6 +272,8 @@ codes:
261
272
  /* err is WCWIDTH_ERROR_VERTICAL_CTRL */
262
273
  }
263
274
 
275
+ .. _libwcwidth-align:
276
+
264
277
  wcwidth_ljust_u8(), wcwidth_rjust_u8(), and wcwidth_center_u8()
265
278
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
266
279
 
@@ -288,6 +301,13 @@ returns a ``malloc``\ 'd NUL-terminated string the caller must ``free``:
288
301
  /* "*café*" */
289
302
  free(out);
290
303
 
304
+ Tabs are measured as though text begins at column 0. ``wcwidth_rjust_u8()`` and
305
+ ``wcwidth_center_u8()`` insert padding to the left of the ``text`` which move tabstops and return
306
+ an incorrect result, always expand tabs or call `wcwidth_clip_u8()`_ to ``opts->dest_width`` first,
307
+ which expands tabs before using these alignment functions.
308
+
309
+ .. _libwcwidth-clip:
310
+
291
311
  wcwidth_clip_u8()
292
312
  ~~~~~~~~~~~~~~~~~
293
313
 
@@ -344,6 +364,8 @@ Movement is reported wherever it appears; the OSC sequences only where the clip
344
364
  `wcwidth_clip_u32()`_ is the codepoint-array form, returning a ``malloc``\ 'd array of ``*out_len``
345
365
  codepoints.
346
366
 
367
+ .. _libwcwidth-wrap:
368
+
347
369
  wcwidth_wrap_u8() and wcwidth_wrap_u8_text()
348
370
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
349
371
 
@@ -383,6 +405,8 @@ unbreakable unit, so a line is never broken inside one, even at a space or hyphe
383
405
  text. Python's `wrap()`_ behaves the same way. `wcwidth_wrap_u32()`_ and `wcwidth_wrap_u32_text()`_ are the
384
406
  codepoint-array forms.
385
407
 
408
+ .. _libwcwidth-escape-strip:
409
+
386
410
  wcwidth_escape_strip()
387
411
  ~~~~~~~~~~~~~~~~~~~~~~
388
412
 
@@ -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
@@ -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);