Terminal::WCWidth
WorksHow many columns a character really takes up on a terminal — two for CJK, none for a combining mark, and a refusal for a control code — so that padded columns survive text that is not ASCII.
- Version
0.1.5zef:raku-community-modules- Depends
- nothing outside the core
- License
- MIT
- Its own test suite
- 1 file, green
- Checked
- 2026-09-14 against Raku++ 3.28.0 and Rakudo 2026.08
Install it #
$ rakupp install Terminal::WCWidthzef install Terminal::WCWidth writes the same store; either installer leaves the module usable by both engines.
What it is for #
.chars counts characters and a terminal draws columns, and the two numbers stop agreeing the moment the text leaves ASCII. 世 is one character and two columns wide. A combining acute accent is one character and no columns at all — it is drawn on top of the letter before it. Pad a column to width with .chars and every row containing either of them comes out crooked.
This distribution is the C wcwidth(3) table, transcribed and kept as Raku data: one function for a single codepoint, one for a whole string.
Two subs, one question #
wcwidth takes a codepoint as an Int — note that it wants .ord, not a character — and wcswidth takes the Str:
use Terminal::WCWidth;
say wcwidth('A'.ord);
say wcwidth('世'.ord);
say wcwidth("\c[COMBINING ACUTE ACCENT]".ord);
say wcwidth(7);
say wcswidth('hello 世界');
say 'hello 世界'.chars;1
2
0
-1
10
8The last pair is the whole point: eight characters, ten columns. Feed wcswidth into the padding instead of .chars and the columns line up:
use Terminal::WCWidth;
sub pad(Str $s, Int $w) { $s ~ ' ' x ($w - wcswidth($s)) }
for 'apple', '世界', 'café' -> $label {
say '|', pad($label, 8), '| ', $label.chars, ' chars';
}|apple | 5 chars
|世界 | 2 chars
|café | 4 charsThe one thing to know #
-1 is not a width, it is an error, and it is contagious. A control character has no sensible column count, so wcwidth answers -1 for one — and wcswidth answers -1 for the whole string if it contains one anywhere:
use Terminal::WCWidth;
say wcswidth("plain");
say wcswidth("a\tb");5
-1A tab is enough to trigger it. Since the natural thing to write is $w - wcswidth($s), an un-sanitised string turns a padding calculation into a number one larger than you wanted, silently. Check for the negative before you subtract, or strip control characters on the way in.