Measure the width of text in the terminal and build simple layouts!
0

Configure Feed

Select the types of activity you want to include in your feed.

string_width / internals.md
5.0 kB 112 lines
1# Comparison 2 3Compared to many other libraries in this space, the main focus of this one is 4measuring the real width of things when printed to terminal emulators. This 5library does **not** try to guess what font shaping (especially ligatures) would 6do, and cares **less** about the way things look like in other monospace contexts, 7such as using a modern rendering pipeline (e.g. in a browser) but with a monospace 8font. I encourage you to use your font rendering library directly, or find a way 9to layout things without needing to know the size of your text. 10 11# Unicode version 12 13The data files are taken from the Unicode 16 release (Sep 2024). 14 15# Algorithm 16 17Components (codepoints, control sequences and grapheme clusters) are classified 18into 4 catgories: 19 20- **zero-width:** these do not advance the current column 21- **narrow:** these advance the cursor by one column 22- **wide:** these advance the cursor by two columns 23- **ambiguous:** these advance the cursor either by one or two columns, depending 24 on the `ambiguous_as_wide` option. 25 26Control characters and ansi escape sequences are ignored and reported as zero-width, 27**except**: 28 29 - Newlines (`\n` and `\r\n`) increase the current row, while resetting the column. 30 - Tabs (`\t`) increase the current column to the next tab stop. 31 32For the remaining string, 3 different modes are supported: `wcwidth`, `mode_2027` 33and `mode2027_ext`. 34 35## Default mode (`wcwidth`) 36 37By default, this library tries to match the output of the `wcwidth` function from 38glibc. This function is still widely used by the majority of terminal emulators. 39 40In this mode, the width of a string is the sum of the widths of its individual 41code points. (Extended) grapheme cluster boundaries are ignored. 42 43## (Extended) Mode 2027 44 45_Mode 2027_ is a proposed mode for terminal emulators that applications can 46request. When active and supported, the terminal emulator is supposed to follow 47the [terminal unicode core spec](https://github.com/contour-terminal/terminal-unicode-core). 48This mode is supported by some terminals, and even the default and only 49behaviour in some others. 50 51None of the terminals I tested implement the proposed spec fully, and the exact 52behaviour is subject to ongoing discussion in the freedesktop.org terminal 53working group. Support for this mode should therefore be considered best-effort 54at the moment, especially when support for Brahmic, Arabic, and some east asian 55scripts is required. If you just want your emoji family to be 2 columns wide, 56this mode works well enough right now. 57 58This library does the following: 59 60- Extended grapheme clusters are segmented based on the `pop_grapheme` function. 61 This function uses native target code, and is therefore not guaranteed to match 62 the unicode version of this library (at the time of writing, 63 Erlang uses 15.0, and Node.JS uses 15.1). 64- The width of a grapheme cluster is the _maximum_ width of all codepoints 65 that make it up. 66- If a grapheme cluster contains U+FE0F Variant Selector 16 in addition to 67 another non-zero-width codepoint, its width is wide. 68 69Additionally, in the extended mode, the following rules apply: 70 71- If a grapheme cluster contains two or more non-spacing non-zero-width 72 codepoints, its width is wide. "Spacing" codepoints in this context are all 73 codepoints with a `General_Category` of `Spacing_Mark`. 74 75This rule makes the width reported by this library across Erlang and Nodejs more 76stable (Unicode version differences, see above), as well as closer match the 77behaviour of actual terminals with mode 2027 support. Starting with Unicode 15.1, 78some sequences that ocupy multiple columns are now segmented into single 79grapheme clusters. 80 81 82## Width of a single codepoint 83 84The following codepoints are classified as **zero-with:** 85 86- All codepoints with the `Default_Ignorable_Code_Point` property, **except** for U+115F Hangul Choseong Filler. 87- All codepoints with a `General_Category` of `Control`, `Enclosing_Mark`, `Nonspacing_Mark`, `Paragraph_Separator`, or `Line_Separator`. 88- All codepoints with `Hangul_Syllable_Type` of Vowel or Trailing Jamo. 89- All codepoints marked as `Prepended_Concatenation_Mark` 90- `U+fff9..U+fffb`, interlinear annotation format characters 91- `U+13430..U+13440`, egyptian hieroglyph format characters 92 93The following codepoints are classified as **wide:** 94 95- All codepoints with the `Emoji_Presentation` property set. 96- All codepoints with an `East_Asian_Width` of Wide or Fullwidth. 97 98The following codepoints are classified as **ambiguous:** 99 100- All codepoints with an `East_Asian_Width` of Ambiguous. 101 102All other codepoints are **narrow**. 103 104# Testing 105 106I currently test against VTE (mainly gnome-terminal), Windows Terminal, Kitty, 107and foot for mode 2027 support. I also test on Contour since they originally 108proposed the mode 2027 spec; however, they use a custom Unicode library that I 109don't trust fully. 110 111When reporting a mismatch, please include which terminal and the escaped 112codepoints. Gitlab/Discord sometimes like to strip certain modifiers.