wcwidth 0.6.0__tar.gz → 0.8.0__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.
- {wcwidth-0.6.0 → wcwidth-0.8.0}/.pylintrc +2 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/PKG-INFO +289 -39
- wcwidth-0.8.0/bin/show-sequences +23 -0
- wcwidth-0.8.0/bin/strip-sequences +18 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/bin/update-tables.py +603 -57
- {wcwidth-0.6.0 → wcwidth-0.8.0}/bin/verify-table-integrity.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/bin/wcwidth-browser.py +9 -9
- {wcwidth-0.6.0 → wcwidth-0.8.0}/bin/wcwidth-libc-comparator.py +1 -0
- wcwidth-0.8.0/code_templates/grapheme_override_per_terminal.py.j2 +13 -0
- wcwidth-0.8.0/code_templates/grapheme_override_table.py.j2 +18 -0
- wcwidth-0.8.0/code_templates/grapheme_registry.py.j2 +13 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/code_templates/grapheme_table.py.j2 +1 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/code_templates/python_table.py.j2 +1 -1
- wcwidth-0.8.0/code_templates/table_overrides.py.j2 +37 -0
- wcwidth-0.8.0/code_templates/term_programs.py.j2 +22 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/code_templates/unicode_versions.py.j2 +1 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/api.rst +14 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/intro.rst +287 -38
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/requirements.txt +10 -11
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/specs.rst +60 -25
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/unicode_version.rst +15 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/pyproject.toml +5 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-tests38.in +1 -2
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-tests38.txt +4 -9
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-tests39.in +2 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-tests39.txt +8 -12
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-update.in +2 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-update.txt +10 -8
- wcwidth-0.8.0/tests/conftest.py +38 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_ambiguous.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_benchmarks.py +256 -7
- wcwidth-0.8.0/tests/test_clip.py +454 -0
- wcwidth-0.8.0/tests/test_clip_cjk_emoji.py +47 -0
- wcwidth-0.8.0/tests/test_clip_overtyping.py +159 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_core.py +74 -21
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_emojis.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_grapheme.py +1 -0
- wcwidth-0.8.0/tests/test_hyperlink.py +75 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_justify.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_sgr_state.py +1 -0
- wcwidth-0.8.0/tests/test_term_overrides.py +493 -0
- wcwidth-0.8.0/tests/test_text_sizing.py +328 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_textwrap.py +39 -69
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_ucslevel.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/test_width.py +154 -22
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tox.ini +29 -27
- wcwidth-0.8.0/ucs-detect/data/AbsoluteTelnetSSH.yaml +789 -0
- wcwidth-0.8.0/ucs-detect/data/_linuxfbdev.yaml +50 -0
- wcwidth-0.8.0/ucs-detect/data/_syncterm.yaml +38 -0
- wcwidth-0.8.0/ucs-detect/data/alacritty.yaml +8755 -0
- wcwidth-0.8.0/ucs-detect/data/bobcat.yaml +10780 -0
- wcwidth-0.8.0/ucs-detect/data/conemu.exe.yaml +29154 -0
- wcwidth-0.8.0/ucs-detect/data/contour.yaml +16580 -0
- wcwidth-0.8.0/ucs-detect/data/coolretroterm.yaml +10905 -0
- wcwidth-0.8.0/ucs-detect/data/extraterm.yaml +27161 -0
- wcwidth-0.8.0/ucs-detect/data/foot.yaml +8336 -0
- wcwidth-0.8.0/ucs-detect/data/ghostty.yaml +1001 -0
- wcwidth-0.8.0/ucs-detect/data/gnometerminal.yaml +13095 -0
- wcwidth-0.8.0/ucs-detect/data/hyper.yaml +12481 -0
- wcwidth-0.8.0/ucs-detect/data/iterm2.yaml +6576 -0
- wcwidth-0.8.0/ucs-detect/data/kitty.yaml +15571 -0
- wcwidth-0.8.0/ucs-detect/data/konsole.yaml +6187 -0
- wcwidth-0.8.0/ucs-detect/data/libvterm.yaml +11631 -0
- wcwidth-0.8.0/ucs-detect/data/lxterminal.yaml +12887 -0
- wcwidth-0.8.0/ucs-detect/data/mintty.yaml +27871 -0
- wcwidth-0.8.0/ucs-detect/data/mlterm.yaml +14706 -0
- wcwidth-0.8.0/ucs-detect/data/putty.yaml +10658 -0
- wcwidth-0.8.0/ucs-detect/data/qterminal.yaml +8394 -0
- wcwidth-0.8.0/ucs-detect/data/rio.yaml +9924 -0
- wcwidth-0.8.0/ucs-detect/data/rxvtunicode.yaml +10826 -0
- wcwidth-0.8.0/ucs-detect/data/screen.yaml +18026 -0
- wcwidth-0.8.0/ucs-detect/data/securecrt.yaml +16450 -0
- wcwidth-0.8.0/ucs-detect/data/st.yaml +10759 -0
- wcwidth-0.8.0/ucs-detect/data/tabby.yaml +12135 -0
- wcwidth-0.8.0/ucs-detect/data/teraterm.yaml +6678 -0
- wcwidth-0.8.0/ucs-detect/data/terminalapp.yaml +10895 -0
- wcwidth-0.8.0/ucs-detect/data/terminalexe.yaml +5649 -0
- wcwidth-0.8.0/ucs-detect/data/terminator.yaml +13065 -0
- wcwidth-0.8.0/ucs-detect/data/terminology.yaml +26742 -0
- wcwidth-0.8.0/ucs-detect/data/tmux.yaml +6895 -0
- wcwidth-0.8.0/ucs-detect/data/vscodeterminal.yaml +11720 -0
- wcwidth-0.8.0/ucs-detect/data/warp.yaml +11038 -0
- wcwidth-0.8.0/ucs-detect/data/westonterminal.yaml +26416 -0
- wcwidth-0.8.0/ucs-detect/data/wezterm.yaml +10565 -0
- wcwidth-0.8.0/ucs-detect/data/xfce4terminal.yaml +13020 -0
- wcwidth-0.8.0/ucs-detect/data/xterm.yaml +10881 -0
- wcwidth-0.8.0/ucs-detect/data/zellij.yaml +12096 -0
- wcwidth-0.8.0/ucs-detect/data/zutty.yaml +10797 -0
- wcwidth-0.8.0/wcwidth/__init__.py +81 -0
- wcwidth-0.8.0/wcwidth/_clip.py +835 -0
- wcwidth-0.8.0/wcwidth/_constants.py +151 -0
- wcwidth-0.8.0/wcwidth/_wcswidth.py +427 -0
- wcwidth-0.8.0/wcwidth/_wcwidth.py +162 -0
- wcwidth-0.8.0/wcwidth/_width.py +503 -0
- wcwidth-0.8.0/wcwidth/align.py +165 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/bisearch.py +3 -2
- wcwidth-0.8.0/wcwidth/escape_sequences.py +194 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/grapheme.py +72 -7
- wcwidth-0.8.0/wcwidth/hyperlink.py +142 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/sgr_state.py +2 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_ambiguous.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_grapheme.py +54 -1
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/__init__.py +37 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_27e0693f.py +2677 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_45d92e98.py +1969 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_4cdf59ce.py +42 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_50bf0759.py +2571 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_529fbb4a.py +1799 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_5bfac390.py +2571 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_813fee16.py +2570 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_8589765c.py +1310 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_8f94b404.py +2581 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_970dbe10.py +2839 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c0a2cdbf.py +3617 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c0d5dc9e.py +6427 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c2157f7e.py +1153 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c3db41c0.py +3401 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_da9ceb0a.py +1910 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_e08bd75e.py +2641 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_e22030f3.py +6396 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_fcc05a0f.py +6648 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_fd9d4c44.py +3671 -0
- wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_registry.py +32 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_mc.py +1 -1
- wcwidth-0.8.0/wcwidth/table_overrides.py +714 -0
- wcwidth-0.8.0/wcwidth/table_term_programs.py +49 -0
- wcwidth-0.8.0/wcwidth/table_vs15.py +104 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_vs16.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_wide.py +3 -3
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/table_zero.py +1 -1
- wcwidth-0.8.0/wcwidth/text_sizing.py +202 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/textwrap.py +54 -54
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/unicode_versions.py +1 -1
- wcwidth-0.8.0/wcwidth/wcwidth.py +88 -0
- wcwidth-0.6.0/bin/new-wide-by-version.py +0 -48
- wcwidth-0.6.0/tests/conftest.py +0 -15
- wcwidth-0.6.0/tests/test_clip.py +0 -262
- wcwidth-0.6.0/wcwidth/__init__.py +0 -43
- wcwidth-0.6.0/wcwidth/escape_sequences.py +0 -69
- wcwidth-0.6.0/wcwidth/wcwidth.py +0 -1030
- {wcwidth-0.6.0 → wcwidth-0.8.0}/.gitignore +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/LICENSE +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/README.rst +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/code_templates/unicode_version.rst.j2 +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/conf.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/docs/index.rst +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-develop.txt +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/requirements-docs.in +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/tests/__init__.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/control_codes.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.8.0}/wcwidth/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wcwidth
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Measures the displayed width of unicode strings in a terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/jquast/wcwidth
|
|
6
6
|
Author-email: Jeff Quast <contact@jeffquast.com>
|
|
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
24
|
Classifier: Topic :: Software Development :: Internationalization
|
|
24
25
|
Classifier: Topic :: Software Development :: Libraries
|
|
25
26
|
Classifier: Topic :: Software Development :: Localization
|
|
@@ -28,6 +29,7 @@ Classifier: Typing :: Typed
|
|
|
28
29
|
Requires-Python: >=3.8
|
|
29
30
|
Description-Content-Type: text/x-rst
|
|
30
31
|
|
|
32
|
+
|
|
31
33
|
|pypi_downloads| |codecov| |license|
|
|
32
34
|
|
|
33
35
|
============
|
|
@@ -65,33 +67,49 @@ Some examples of **incorrect results**:
|
|
|
65
67
|
Solution
|
|
66
68
|
--------
|
|
67
69
|
|
|
68
|
-
The lowest-level functions in this library are
|
|
69
|
-
`wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
|
|
70
|
-
These functions return -1 when C0 and C1 control codes are present.
|
|
70
|
+
The lowest-level functions in this library are derived from POSIX.1-2001 and POSIX.1-2008
|
|
71
|
+
`wcwidth(3)`_ and `wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
|
|
72
|
+
and `wcswidth()`_. These functions return -1 when C0 and C1 control codes are present.
|
|
71
73
|
|
|
72
74
|
An easy-to-use `width()`_ function is provided as a wrapper of `wcswidth()`_ that is also capable of
|
|
73
75
|
measuring most terminal control codes and sequences, like colors, bold, tabstops, and horizontal
|
|
74
|
-
cursor movement.
|
|
76
|
+
cursor movement. `width()`_ argument ``term_program`` may provide more accurate terminal measurement
|
|
77
|
+
Corrections_ as a wrapper of `wcstwidth()`_.
|
|
78
|
+
|
|
79
|
+
Text-justification is solved by the sequence-aware functions `ljust()`_, `rjust()`_, `center()`_,
|
|
80
|
+
and the grapheme-aware function `wrap()`_, serving as drop-in replacements to python standard
|
|
81
|
+
functions.
|
|
75
82
|
|
|
76
|
-
|
|
77
|
-
`
|
|
78
|
-
of the same names.
|
|
83
|
+
The `clip()`_ function extracts substrings by their displayed column positions, and
|
|
84
|
+
`strip_sequences()`_ removes terminal escape sequences from text altogether.
|
|
79
85
|
|
|
80
86
|
The iterator functions `iter_graphemes()`_ and `iter_sequences()`_ allow for careful navigation of
|
|
81
|
-
grapheme and terminal control sequence boundaries
|
|
82
|
-
`grapheme_boundary_before()`_ are
|
|
83
|
-
|
|
84
|
-
terminal escape sequences from text altogether.
|
|
87
|
+
grapheme and terminal control sequence boundaries as required by editors or REPLs with cursor
|
|
88
|
+
control. `iter_graphemes_reverse()`_ and `grapheme_boundary_before()`_ are necessary for backward
|
|
89
|
+
cursor control over complex unicode.
|
|
85
90
|
|
|
86
91
|
Discrepancies
|
|
87
92
|
-------------
|
|
88
93
|
|
|
89
94
|
You may find that support *varies* for complex unicode sequences or codepoints.
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
This library may be considered to presume the terminal is enabled for DEC Private Mode 2027
|
|
97
|
+
("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
|
|
98
|
+
is often the default mode for those terminals that support it: Windows Terminal, WezTerm, ghostty,
|
|
99
|
+
contour, and foot. This library does support any specific "legacy width" measurement, but does
|
|
100
|
+
provide Corrections_ for those terminals without grapheme support.
|
|
101
|
+
|
|
102
|
+
See also:
|
|
103
|
+
|
|
104
|
+
- `Grapheme Clusters and Terminal Emulators`_
|
|
105
|
+
- `terminal-unicode-core.tex`_
|
|
106
|
+
- `State of Terminal Emulators in 2025`_
|
|
107
|
+
- `Report of terminals supporting Graphemes (2027)`_
|
|
108
|
+
|
|
109
|
+
The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
|
|
110
|
+
character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
|
|
93
111
|
variations, and regional indicator (flags) as a `General Tabulated Summary`_ by terminal emulator
|
|
94
|
-
software and version.
|
|
112
|
+
software and version. The results of the ucs-detect project create our correction tables.
|
|
95
113
|
|
|
96
114
|
========
|
|
97
115
|
Overview
|
|
@@ -138,40 +156,95 @@ Use function `wcswidth()`_ to determine the length of many, a *string of unicode
|
|
|
138
156
|
See specification_ of character measurements. Note that ``-1`` is returned if control codes occurs
|
|
139
157
|
anywhere in the string.
|
|
140
158
|
|
|
159
|
+
wcstwidth()
|
|
160
|
+
-----------
|
|
161
|
+
|
|
162
|
+
Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
|
|
163
|
+
``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
|
|
164
|
+
XTVERSION_ or ENQ_ response:
|
|
165
|
+
|
|
166
|
+
.. code-block:: python
|
|
167
|
+
|
|
168
|
+
>>> # '♀️' emoji w/vs-16, uncorrected:
|
|
169
|
+
>>> wcwidth.wcswidth('\u2640\ufe0f')
|
|
170
|
+
2
|
|
171
|
+
>>> # corrected,
|
|
172
|
+
>>> wcwidth.wcstwidth('\u2640\ufe0f', term_program='vte')
|
|
173
|
+
1
|
|
174
|
+
|
|
141
175
|
width()
|
|
142
176
|
-------
|
|
143
177
|
|
|
144
|
-
Use function `width()`_ to measure a string with improved handling of ``control_codes
|
|
178
|
+
Use function `width()`_ to measure a string with improved handling of ``control_codes`` and
|
|
179
|
+
measurement Corrections_ through ``term_program``:.
|
|
145
180
|
|
|
146
181
|
.. code-block:: python
|
|
147
182
|
|
|
148
183
|
>>> # same support as wcswidth(), eg. regional indicator flag:
|
|
149
184
|
>>> wcwidth.width('\U0001F1FF\U0001F1FC')
|
|
150
185
|
2
|
|
151
|
-
>>> #
|
|
186
|
+
>>> # set term_program=True to use wcstwidth()
|
|
187
|
+
>>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program=True)
|
|
188
|
+
1
|
|
189
|
+
>>> # or set term_program for measurement of a specific terminal
|
|
190
|
+
>>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program='contour')
|
|
191
|
+
2
|
|
192
|
+
>>> # but also supports sequences, like SGR colored text, "WARN", followed by reset
|
|
152
193
|
>>> wcwidth.width('\x1b[38;2;255;150;100mWARN\x1b[0m')
|
|
153
194
|
4
|
|
154
|
-
>>> # tabs,
|
|
195
|
+
>>> # tabs are measured as though the string begins at a tabstop,
|
|
155
196
|
>>> wcwidth.width('\t', tabsize=4)
|
|
156
197
|
4
|
|
157
|
-
>>> # or,
|
|
158
|
-
>>> wcwidth.width('\t', control_codes='ignore')
|
|
198
|
+
>>> # or, all control characters can be ignored (including tab)
|
|
199
|
+
>>> wcwidth.width('\t\n\a\r', control_codes='ignore')
|
|
159
200
|
0
|
|
160
|
-
>>> # "
|
|
161
|
-
>>> wcwidth.width('\n')
|
|
162
|
-
0
|
|
163
|
-
>>> # as well as sequences with "indeterminate" effects like Home + Clear
|
|
201
|
+
>>> # sequences with "indeterminate" effects like Home + Clear are zero-width
|
|
164
202
|
>>> wcwidth.width('\x1b[H\x1b[2J')
|
|
165
203
|
0
|
|
204
|
+
>>> # horizontal cursor movements are parsed,
|
|
205
|
+
>>> wcwidth.width('hello\b\b\b\b\bworld')
|
|
206
|
+
5
|
|
207
|
+
>>> wcwidth.width('hello\x1b[5Dworld')
|
|
208
|
+
5
|
|
209
|
+
>>> # or ignored,
|
|
210
|
+
>>> wcwidth.width('hello\x1b[5Dworld', control_codes='ignore')
|
|
211
|
+
10
|
|
212
|
+
>>> # Measure width of text using kitty text sizing protocol (OSC 66),
|
|
213
|
+
>>> width('\x1b]66;w=2;XY\x07')
|
|
214
|
+
2
|
|
215
|
+
>>> # Scaled text sizing: each grapheme occupies 'scale' cells
|
|
216
|
+
>>> width('\x1b]66;s=2;ABC\x07')
|
|
217
|
+
6
|
|
218
|
+
|
|
219
|
+
Use ``control_codes='ignore'`` when the input is known not to contain any control characters or
|
|
220
|
+
terminal sequences for slightly improved performance. Note that TAB (``'\t'``) is a control
|
|
221
|
+
character and is also ignored, you may want to use `str.expandtabs()`_, first.
|
|
222
|
+
|
|
223
|
+
Use ``control_codes='strict'`` when input is known to contain some control sequences, such as
|
|
224
|
+
SGR color, bold, hyperlinks and cursor movement. Any sequence that cannot be accurately parsed
|
|
225
|
+
for horizontal measurement, such as clearing the screen, vertical, or absolute cursor movement will
|
|
226
|
+
raise ``ValueError``:
|
|
227
|
+
|
|
228
|
+
.. code-block:: python
|
|
229
|
+
|
|
166
230
|
>>> # or, raise ValueError for "indeterminate" effects using control_codes='strict'
|
|
167
231
|
>>> wcwidth.width('\n', control_codes='strict')
|
|
168
232
|
Traceback (most recent call last):
|
|
169
233
|
...
|
|
170
234
|
ValueError: Vertical movement character 0xa at position 0
|
|
171
235
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
236
|
+
|
|
237
|
+
>>> wcwidth.width('\x1b[H\x1b[2J', control_codes='strict')
|
|
238
|
+
Traceback (most recent call last):
|
|
239
|
+
...
|
|
240
|
+
ValueError: Indeterminate cursor sequence at position 0, '\x1b[H'
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
>>> # cursor left movement beyond string start raises in strict mode,
|
|
244
|
+
>>> wcwidth.width('a\x1b[5Da', control_codes='strict')
|
|
245
|
+
Traceback (most recent call last):
|
|
246
|
+
...
|
|
247
|
+
ValueError: Cursor left movement at position 1 would move 5 cells left from column 1, exceeding string start
|
|
175
248
|
|
|
176
249
|
iter_sequences()
|
|
177
250
|
----------------
|
|
@@ -290,9 +363,29 @@ Use `clip()`_ to extract a substring by column positions, preserving terminal se
|
|
|
290
363
|
>>> clip('\x1b[1;31mHello world\x1b[0m', 6, 11)
|
|
291
364
|
'\x1b[1;31mworld\x1b[0m'
|
|
292
365
|
|
|
293
|
-
>>> # Disable SGR propagation to preserve
|
|
294
|
-
>>> clip('\x1b[31m中文\x1b[
|
|
295
|
-
'\x1b[31m中 \x1b[
|
|
366
|
+
>>> # Disable SGR propagation to preserve sequence order outside of clip boundary
|
|
367
|
+
>>> clip('\x1b[31m中文\x1b[32m', 0, 3, propagate_sgr=False)
|
|
368
|
+
'\x1b[31m中 \x1b[32m'
|
|
369
|
+
|
|
370
|
+
>>> # Cursor-left overwrites previous text (painter's algorithm)
|
|
371
|
+
>>> clip('hello\x1b[2DXY', 0, 5)
|
|
372
|
+
'helXY'
|
|
373
|
+
>>> # Carriage return resets to column 0, overwriting earlier cells
|
|
374
|
+
>>> clip('abc\rXY', 0, 5)
|
|
375
|
+
'XYc'
|
|
376
|
+
|
|
377
|
+
>>> # even OSC 8 hyperlink text may be clipped, 'Click This link' -> 'is link' !
|
|
378
|
+
>>> clip('\x1b]8;;http://example.com\x07Click This link\x1b]8;;\x07', 8, 15)
|
|
379
|
+
'\x1b]8;;http://example.com\x07is link\x1b]8;;\x07'
|
|
380
|
+
|
|
381
|
+
>>> # and OSC 66 kitty text sizing, supporting width and scale, 'Look' -> '...ook'
|
|
382
|
+
>>> clip('\x1b]66;w=4:s=4;Look\x07', 1, 16, fillchar='.')
|
|
383
|
+
'...\x1b]66;s=4:w=3;ook\x07'
|
|
384
|
+
|
|
385
|
+
Use ``overtyping=False`` when the input is known not to contain any cursor movement characters
|
|
386
|
+
(``\b``, ``\r``, ``CSI C``, ``CSI D``, ``CSI G``) for improved performance. When
|
|
387
|
+
``overtyping=None`` (default), a slower "Painter's algorithm" may be used after testing for the
|
|
388
|
+
presence of these characters. ``overtyping`` has no effect when ``control_codes='ignore'``.
|
|
296
389
|
|
|
297
390
|
strip_sequences()
|
|
298
391
|
-----------------
|
|
@@ -307,7 +400,7 @@ Use `strip_sequences()`_ to remove all terminal escape sequences from text.
|
|
|
307
400
|
|
|
308
401
|
.. _ambiguous_width:
|
|
309
402
|
|
|
310
|
-
|
|
403
|
+
Ambiguous Width
|
|
311
404
|
---------------
|
|
312
405
|
|
|
313
406
|
Some Unicode characters have "East Asian Ambiguous" (A) width. These characters display as 1 cell by
|
|
@@ -315,6 +408,9 @@ default, matching Western terminal contexts, but many CJK (Chinese, Japanese, Ko
|
|
|
315
408
|
may have a preference for 2 cells. This is often found as boolean option, "Ambiguous width as wide"
|
|
316
409
|
in Terminal Emulator software preferences.
|
|
317
410
|
|
|
411
|
+
The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
|
|
412
|
+
`wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
|
|
413
|
+
|
|
318
414
|
By default, wcwidth treats ambiguous characters as narrow (width 1). For CJK environments where your
|
|
319
415
|
terminal is configured to display ambiguous characters as double-width, pass ``ambiguous_width=2``:
|
|
320
416
|
|
|
@@ -326,9 +422,6 @@ terminal is configured to display ambiguous characters as double-width, pass ``a
|
|
|
326
422
|
>>> wcwidth.width('\u2460', ambiguous_width=2)
|
|
327
423
|
2
|
|
328
424
|
|
|
329
|
-
The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
|
|
330
|
-
`wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
|
|
331
|
-
|
|
332
425
|
**Terminal Detection**
|
|
333
426
|
|
|
334
427
|
The most reliable method to detect whether a terminal profile is set for "Ambiguous width as wide"
|
|
@@ -336,7 +429,7 @@ mode is to display an ambiguous character surrounded by a pair of Cursor Positio
|
|
|
336
429
|
queries with a terminal in cooked or raw mode, and to parse the responses for their ``(y, x)``
|
|
337
430
|
locations and measure the difference ``x``.
|
|
338
431
|
|
|
339
|
-
This code should also be careful check whether it is attached to a terminal and be careful of
|
|
432
|
+
This code should also be careful to check whether it is attached to a terminal and be careful of
|
|
340
433
|
possible timeout, slow network, or non-response when working with "dumb terminals" like a CI build.
|
|
341
434
|
|
|
342
435
|
`jquast/blessed`_ library provides such a helping `Terminal.detect_ambiguous_width()`_ method:
|
|
@@ -352,6 +445,90 @@ possible timeout, slow network, or non-response when working with "dumb terminal
|
|
|
352
445
|
>>> awidth('\u2460')
|
|
353
446
|
1
|
|
354
447
|
|
|
448
|
+
Corrections
|
|
449
|
+
-----------
|
|
450
|
+
|
|
451
|
+
Corrections are automatically applied depending on detected or given terminal software name
|
|
452
|
+
beginning with wcwidth release 0.8.0. This allows to correct widths for terminal software that
|
|
453
|
+
differs from the standard. These corrections are sourced from the `jquast/ucs-detect`_ project.
|
|
454
|
+
|
|
455
|
+
The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
|
|
456
|
+
`width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
|
|
457
|
+
|
|
458
|
+
`wcstwidth()`_ defaults to ``term_program=True``, auto-detecting the terminal from the
|
|
459
|
+
``TERM_PROGRAM`` or ``TERM`` environment variable. All other functions default to
|
|
460
|
+
``term_program=False``, disabling corrections. Use ``term_program=True`` for automatic
|
|
461
|
+
detection by environment values of ``TERM`` and ``TERM_PROGRAM``.
|
|
462
|
+
|
|
463
|
+
.. code-block:: python
|
|
464
|
+
|
|
465
|
+
# VTE terminals (Gnome Terminal Et al.) still render trigrams as narrow (1 cell), but their
|
|
466
|
+
# definition was changed to wide in Unicode 16 (September 2024).
|
|
467
|
+
>>> wcwidth.wcswidth('\u2630')
|
|
468
|
+
2
|
|
469
|
+
>>> wcwidth.wcstwidth('\u2630', term_program='vte')
|
|
470
|
+
1
|
|
471
|
+
|
|
472
|
+
# account for Alacritty non-support of emoji ZWJ:
|
|
473
|
+
# man + ZWJ + woman + ZWJ + girl + ZWJ + boy
|
|
474
|
+
>>> family = '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'
|
|
475
|
+
>>> wcwidth.wcswidth(family)
|
|
476
|
+
2
|
|
477
|
+
>>> wcwidth.wcstwidth(family, term_program='alacritty')
|
|
478
|
+
8
|
|
479
|
+
|
|
480
|
+
Only detectable_ terminals are included: those that identify themselves by XTVERSION_, ENQ_, any
|
|
481
|
+
``TERM_PROGRAM`` or a unique ``TERM`` environment value. For the most accurate correction tables,
|
|
482
|
+
query the terminal's software version via XTVERSION_ (``CSI > q``) using a higher-level interactive
|
|
483
|
+
terminal library like `jquast/blessed`_:
|
|
484
|
+
|
|
485
|
+
.. code-block:: python
|
|
486
|
+
|
|
487
|
+
>>> import blessed, wcwidth
|
|
488
|
+
>>> term = blessed.Terminal()
|
|
489
|
+
>>> sw_ver = term.get_software_version()
|
|
490
|
+
>>> print(sw_ver)
|
|
491
|
+
SoftwareVersion(name='VTE', version='7600')
|
|
492
|
+
>>> wcwidth.width('\u2630', term_program=sw_ver.name)
|
|
493
|
+
1
|
|
494
|
+
|
|
495
|
+
This is important because ``TERM_PROGRAM`` is not forwarded for remote hosts, like SSH, and many
|
|
496
|
+
terminals may only be identified using XTVERSION_ or ENQ_. Use `list_term_programs()`_ to see all
|
|
497
|
+
recognized names:
|
|
498
|
+
|
|
499
|
+
.. BEGIN_LIST_TERM_PROGRAMS
|
|
500
|
+
.. code-block:: python
|
|
501
|
+
|
|
502
|
+
>>> wcwidth.list_term_programs()
|
|
503
|
+
('alacritty', 'apple_terminal', 'bobcat', 'contour', 'extraterm', 'foot',
|
|
504
|
+
'ghostty', 'hyper', 'iterm.app', 'iterm2', 'kitty', 'konsole', 'mintty',
|
|
505
|
+
'mlterm', 'pterm', 'putty', 'rio', 'rxvt', 'rxvt-unicode-256color', 'st',
|
|
506
|
+
'st-256color', 'tabby', 'terminology', 'urxvt', 'vscode', 'vte', 'warp',
|
|
507
|
+
'warpterminal', 'wezterm', 'xterm', 'xterm-ghostty', 'xterm-kitty',
|
|
508
|
+
'xterm.js')
|
|
509
|
+
|
|
510
|
+
.. END_LIST_TERM_PROGRAMS
|
|
511
|
+
|
|
512
|
+
``term_program=False`` (the default for `width()`_, `ljust()`_, `rjust()`_, `center()`_,
|
|
513
|
+
`wrap()`_, and `clip()`_) disables terminal corrections. `wcstwidth()`_ defaults to
|
|
514
|
+
``term_program=True`` for auto-detection.
|
|
515
|
+
|
|
516
|
+
For automatic tests and other purposes that require cross-environment consistency, set static values
|
|
517
|
+
or unset ``TERM`` and ``TERM_PROGRAM`` environment values, such as in ``conftest.py`` with pytest:
|
|
518
|
+
|
|
519
|
+
.. code-block:: python
|
|
520
|
+
|
|
521
|
+
@pytest.fixture(autouse=True)
|
|
522
|
+
def _clear_term_program():
|
|
523
|
+
"""unset TERM/TERM_PROGRAM before each test."""
|
|
524
|
+
saved_term = os.environ.pop('TERM', None)
|
|
525
|
+
saved_tprog = os.environ.pop('TERM_PROGRAM', None)
|
|
526
|
+
yield
|
|
527
|
+
if saved_term is not None:
|
|
528
|
+
os.environ['TERM'] = saved_term
|
|
529
|
+
if saved_tprog is not None:
|
|
530
|
+
os.environ['TERM_PROGRAM'] = saved_tprog
|
|
531
|
+
|
|
355
532
|
==========
|
|
356
533
|
Developing
|
|
357
534
|
==========
|
|
@@ -429,9 +606,18 @@ This library is used in:
|
|
|
429
606
|
- `jquast/blessed`_: a thin, practical wrapper around terminal capabilities in
|
|
430
607
|
Python.
|
|
431
608
|
|
|
609
|
+
- `jquast/telix`_: A Modern telnet client especially designed for BBSs and MUDs.
|
|
610
|
+
|
|
432
611
|
- `prompt-toolkit/python-prompt-toolkit`_: a Library for building powerful
|
|
433
612
|
interactive command lines in Python.
|
|
434
613
|
|
|
614
|
+
- `urwid/urwid`_: Console user interface library for Python
|
|
615
|
+
|
|
616
|
+
- `prettytable/prettytable`_: Display tabular data in a visually appealing ASCII table format
|
|
617
|
+
|
|
618
|
+
- `leviathan0992/Pylsy`_: Pylsy is a simple python library draw tables in the Terminal. Just two
|
|
619
|
+
lines of code.
|
|
620
|
+
|
|
435
621
|
- `dbcli/pgcli`_: Postgres CLI with autocompletion and syntax highlighting.
|
|
436
622
|
|
|
437
623
|
- `thomasballinger/curtsies`_: a Curses-like terminal wrapper with a display
|
|
@@ -448,8 +634,8 @@ This library is used in:
|
|
|
448
634
|
- `nbedos/termtosvg`_: Terminal recorder that renders sessions as SVG
|
|
449
635
|
animations.
|
|
450
636
|
|
|
451
|
-
- `peterbrittain/asciimatics`_:
|
|
452
|
-
UIs
|
|
637
|
+
- `peterbrittain/asciimatics`_: A cross platform package to do curses-like operations, plus higher
|
|
638
|
+
level APIs and widgets to create text UIs and ASCII art animations
|
|
453
639
|
|
|
454
640
|
- `python-cmd2/cmd2`_: A tool for building interactive command line apps
|
|
455
641
|
|
|
@@ -466,9 +652,18 @@ This library is used in:
|
|
|
466
652
|
Other Languages
|
|
467
653
|
===============
|
|
468
654
|
|
|
469
|
-
|
|
470
|
-
|
|
655
|
+
The following libraries provide grapheme and emoji support and closely align with our
|
|
656
|
+
specification_:
|
|
657
|
+
|
|
658
|
+
- `jacobsandlund/uucode`_ Zig
|
|
659
|
+
- `contour-terminal/libunicode`_ C++20
|
|
471
660
|
|
|
661
|
+
There are similar implementations of at least the `wcwidth()`_ and `wcswidth()`_ functions in other
|
|
662
|
+
languages:
|
|
663
|
+
|
|
664
|
+
- `ridiculousfish/widecharwidth`_: Python
|
|
665
|
+
- `termux/wcwidth`_: C
|
|
666
|
+
- `powerman/wcwidth-icons`_: C
|
|
472
667
|
- `timoxley/wcwidth`_: JavaScript
|
|
473
668
|
- `janlelis/unicode-display_width`_: Ruby
|
|
474
669
|
- `alecrabbit/php-wcwidth`_: PHP
|
|
@@ -478,6 +673,9 @@ languages.
|
|
|
478
673
|
- `grepsuzette/wcwidth`_: Haxe
|
|
479
674
|
- `aperezdc/lua-wcwidth`_: Lua
|
|
480
675
|
- `joachimschmidt557/zig-wcwidth`_: Zig
|
|
676
|
+
- `mycoboco/wcwidth.js`_: JavaScript
|
|
677
|
+
- `ainame/swift-displaywidth`_: Swift
|
|
678
|
+
- `pmonks/clj-wcwidth`_: Clojure
|
|
481
679
|
- `fumiyas/wcwidth-cjk`_: `LD_PRELOAD` override
|
|
482
680
|
- `joshuarubin/wcwidth9`_: Unicode version 9 in C
|
|
483
681
|
- `spectreconsole/wcwidth`_: C#
|
|
@@ -486,6 +684,29 @@ languages.
|
|
|
486
684
|
History
|
|
487
685
|
=======
|
|
488
686
|
|
|
687
|
+
0.8.0 *(unreleased)*
|
|
688
|
+
* **New** support for Variation Selector 15 Emojis as narrow, `Issue #211`_.
|
|
689
|
+
* **New** argument, ``term_program`` for `wcstwidth()`_, `width()`_, `clip()`_, `wrap()`_,
|
|
690
|
+
`ljust()`_, `rjust()`_, and `center()`_. ``False`` disables corrections; ``True``
|
|
691
|
+
auto-detects by ``TERM_PROGRAM`` or ``TERM``; string values accept canonical names matching
|
|
692
|
+
`list_term_programs()`_. `wcstwidth()`_ defaults to ``True``; all other functions
|
|
693
|
+
default to ``False``.
|
|
694
|
+
* **Improved** performance on Python 3.15 using standard library iter_graphemes() `PR #206`_.
|
|
695
|
+
* **Improved** memory usage and import time for Python 3.15 using lazy imports `PR #221`_.
|
|
696
|
+
* **Bugfix** Invisible_Stacker viramas now form conjuncts (Burmese, Khmer, etc.) and
|
|
697
|
+
change some Virama width calculations to match `jacobsandlund/uucode`_ (ghostty) `PR #223`_.
|
|
698
|
+
* **Updated** graphemes width maximum now 2, matching Ghostty, foot, and Windows Terminal `PR
|
|
699
|
+
#224`_.
|
|
700
|
+
|
|
701
|
+
0.7.0 *2026-05-02*
|
|
702
|
+
* **New** support for `kitty text sizing protocol`_ (OSC 66) in `width()`_ and `clip()`_.
|
|
703
|
+
* **New** `clip()`_ parameter ``control_codes='parse'``, ``'ignore'``, and ``'strict'``. `clip()`_
|
|
704
|
+
is now able to clip OSC 8 hyperlinks and OSC 66 text sizing sequences.
|
|
705
|
+
* **Improved** `clip()`_ and `width()`_ to support horizontal cursor sequences (``cub``, ``cuf``,
|
|
706
|
+
``hpa``). Cursor-left (``cub``) or backspace (``\b``) now overwrites text. ``column_address``
|
|
707
|
+
(``hpa``) and carriage return (``\r``) are now parsed, and more values conditionally raise
|
|
708
|
+
``ValueError`` when ``control_codes='strict'``.
|
|
709
|
+
|
|
489
710
|
0.6.0 *2026-02-06*
|
|
490
711
|
* **New** Parameters ``expand_tabs``, ``replace_whitespace``, ``fix_sentence_endings``,
|
|
491
712
|
``drop_whitespace``, ``max_lines``, and ``placeholder`` for `wrap()`_, completing stdlib
|
|
@@ -707,10 +928,16 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
707
928
|
.. _`PR #200`: https://github.com/jquast/wcwidth/pull/200
|
|
708
929
|
.. _`PR #202`: https://github.com/jquast/wcwidth/pull/202
|
|
709
930
|
.. _`PR #204`: https://github.com/jquast/wcwidth/pull/204
|
|
931
|
+
.. _`PR #206`: https://github.com/jquast/wcwidth/pull/206
|
|
932
|
+
.. _`PR #221`: https://github.com/jquast/wcwidth/pull/221
|
|
933
|
+
.. _`PR #223`: https://github.com/jquast/wcwidth/pull/223
|
|
934
|
+
.. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
|
|
710
935
|
.. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
|
|
711
936
|
.. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
|
|
712
937
|
.. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
|
|
938
|
+
.. _`Issue #211`: https://github.com/jquast/wcwidth/issues/211
|
|
713
939
|
.. _`jquast/blessed`: https://github.com/jquast/blessed
|
|
940
|
+
.. _`jquast/telix`: https://github.com/jquast/telix
|
|
714
941
|
.. _`selectel/pyte`: https://github.com/selectel/pyte
|
|
715
942
|
.. _`thomasballinger/curtsies`: https://github.com/thomasballinger/curtsies
|
|
716
943
|
.. _`dbcli/pgcli`: https://github.com/dbcli/pgcli
|
|
@@ -735,10 +962,21 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
735
962
|
.. _`fumiyas/wcwidth-cjk`: https://github.com/fumiyas/wcwidth-cjk
|
|
736
963
|
.. _`joshuarubin/wcwidth9`: https://github.com/joshuarubin/wcwidth9
|
|
737
964
|
.. _`spectreconsole/wcwidth`: https://github.com/spectreconsole/wcwidth
|
|
965
|
+
.. _`contour-terminal/libunicode`: https://github.com/contour-terminal/libunicode
|
|
966
|
+
.. _`jacobsandlund/uucode`: https://github.com/jacobsandlund/uucode
|
|
967
|
+
.. _`ridiculousfish/widecharwidth`: https://github.com/ridiculousfish/widecharwidth
|
|
968
|
+
.. _`termux/wcwidth`: https://github.com/termux/wcwidth
|
|
969
|
+
.. _`powerman/wcwidth-icons`: https://github.com/powerman/wcwidth-icons
|
|
970
|
+
.. _`mycoboco/wcwidth.js`: https://github.com/mycoboco/wcwidth.js
|
|
971
|
+
.. _`ainame/swift-displaywidth`: https://github.com/ainame/swift-displaywidth
|
|
972
|
+
.. _`pmonks/clj-wcwidth`: https://github.com/pmonks/clj-wcwidth
|
|
738
973
|
.. _`python-cmd2/cmd2`: https://github.com/python-cmd2/cmd2
|
|
739
974
|
.. _`stratis-storage/stratis-cli`: https://github.com/stratis-storage/stratis-cli
|
|
740
975
|
.. _`ihabunek/toot`: https://github.com/ihabunek/toot
|
|
741
976
|
.. _`saulpw/visidata`: https://github.com/saulpw/visidata
|
|
977
|
+
.. _`urwid/urwid`: https://github.com/urwid/urwid
|
|
978
|
+
.. _`prettytable/prettytable`: https://github.com/prettytable/prettytable
|
|
979
|
+
.. _`leviathan0992/Pylsy`: https://github.com/leviathan0992/Pylsy
|
|
742
980
|
.. _`pip-tools`: https://pip-tools.readthedocs.io/
|
|
743
981
|
.. _`sphinx`: https://www.sphinx-doc.org/
|
|
744
982
|
.. _`textwrap.wrap()`: https://docs.python.org/3/library/textwrap.html#textwrap.wrap
|
|
@@ -749,6 +987,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
749
987
|
.. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
|
|
750
988
|
.. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
|
|
751
989
|
.. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
|
|
990
|
+
.. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
|
|
752
991
|
.. _`width()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.width
|
|
753
992
|
.. _`iter_graphemes()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes
|
|
754
993
|
.. _`iter_graphemes_reverse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes_reverse
|
|
@@ -760,11 +999,22 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
760
999
|
.. _`clip()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.clip
|
|
761
1000
|
.. _`strip_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.strip_sequences
|
|
762
1001
|
.. _`propagate_sgr()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.propagate_sgr
|
|
1002
|
+
.. _`TextSizing`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizing
|
|
1003
|
+
.. _`TextSizingParams`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizingParams
|
|
763
1004
|
.. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
|
|
764
1005
|
.. _`list_versions()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_versions
|
|
1006
|
+
.. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
|
|
765
1007
|
.. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
|
|
766
1008
|
.. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
|
|
767
1009
|
.. _`parity padding`: https://jazcap53.github.io/pythons-eccentric-strcenter.html
|
|
1010
|
+
.. _`kitty text sizing protocol`: https://sw.kovidgoyal.net/kitty/text-sizing-protocol/
|
|
1011
|
+
.. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
|
|
1012
|
+
.. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
|
|
1013
|
+
.. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
|
|
1014
|
+
.. _`Report of terminals supporting Graphemes (2027)`: https://ucs-detect.readthedocs.io/results.html#terminal-features
|
|
1015
|
+
.. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
|
|
1016
|
+
.. _ENQ: https://documentation.help/PuTTY/config-answerback.html
|
|
1017
|
+
.. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
|
|
768
1018
|
.. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
|
|
769
1019
|
:alt: Downloads
|
|
770
1020
|
:target: https://pypi.org/project/wcwidth/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Show terminal escape sequences in a file or stdin as repr() literals."""
|
|
3
|
+
# std imports
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# local
|
|
7
|
+
from wcwidth import iter_sequences
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _read_input():
|
|
11
|
+
# some care is taken regarding '\r\n' because this program is meant to show
|
|
12
|
+
# raw terminal data to terminals that are *not* in raw mode, so different
|
|
13
|
+
# interpretation of '\r\n' is needed.
|
|
14
|
+
if len(sys.argv) > 1:
|
|
15
|
+
with open(sys.argv[1], "rb") as f:
|
|
16
|
+
return f.read().replace(b"\r\n", b"\n").decode()
|
|
17
|
+
return sys.stdin.buffer.read().replace(b"\r\n", b"\n").decode()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
for line in _read_input().split("\n"):
|
|
21
|
+
for item, is_seq in iter_sequences(line):
|
|
22
|
+
print(repr(item) if is_seq else item.replace("\r", repr("\r")), end="")
|
|
23
|
+
print("", flush=True)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Strip terminal escape sequences from a file or stdin."""
|
|
3
|
+
# std imports
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# local
|
|
7
|
+
from wcwidth import strip_sequences
|
|
8
|
+
|
|
9
|
+
# some care is taken regarding '\r\n' because this program is meant to show
|
|
10
|
+
# raw terminal data to terminals that are *not* in raw mode, so different
|
|
11
|
+
# interpretation of '\r\n' is needed.
|
|
12
|
+
|
|
13
|
+
if len(sys.argv) > 1:
|
|
14
|
+
with open(sys.argv[1], "rb") as f:
|
|
15
|
+
text = f.read().replace(b"\r\n", b"\n").replace(b"\r", b"").decode()
|
|
16
|
+
else:
|
|
17
|
+
text = sys.stdin.buffer.read().replace(b"\r\n", b"\n").replace(b"\r", b"").decode()
|
|
18
|
+
sys.stdout.write(strip_sequences(text))
|