Module: Charming::UI::Width
- Defined in:
- lib/charming/presentation/ui/width.rb
Overview
Width is a namespace for measuring and normalising the visual width of strings that may contain ANSI escape sequences. It delegates to ‘Unicode::DisplayWidth` while automatically stripping formatting codes so layout primitives can calculate exact character positions.
Constant Summary collapse
- ANSI_PATTERN =
Matches OSC sequences (e.g. OSC 8 hyperlinks, terminated by BEL or ST), CSI sequences (SGR colors/attributes, cursor movement), and single-character Fe escapes. The OSC branch must come first and the Fe class must exclude “[” and “]”, or “e]” would match as a bare Fe escape and leave the OSC payload counted as visible text.
/\e(?:\][^\a]*?(?:\a|\e\\)|\[[0-9;?]*[@-~]|[@-Z\\^_])/- EMOJI_PRESENTATION =
A grapheme cluster containing either codepoint renders as a single emoji (double-width) cell in a terminal: U+200D ZWJ joins a multi-glyph emoji sequence, and U+FE0F (VS16) requests emoji presentation. The unicode-display_width tables disagree with terminals here — e.g. “⚔️” measures 1 and “🧙♂️” measures 3 — so we pin such clusters to 2.
/[\u200D\uFE0F]/- GRAPHEME =
Onig’s X matches one extended grapheme cluster, keeping multi-codepoint emoji together so each is measured (and later sliced) as one unit.
/\X/
Class Method Summary collapse
Class Method Details
.cluster_width(cluster) ⇒ Object
38 39 40 41 42 |
# File 'lib/charming/presentation/ui/width.rb', line 38 def cluster_width(cluster) return 2 if cluster.match?(EMOJI_PRESENTATION) Unicode::DisplayWidth.of(cluster) end |
.measure(value) ⇒ Object
31 32 33 34 35 36 |
# File 'lib/charming/presentation/ui/width.rb', line 31 def measure(value) stripped = strip_ansi(value.to_s) return Unicode::DisplayWidth.of(stripped) unless stripped.match?(EMOJI_PRESENTATION) stripped.scan(GRAPHEME).sum { |cluster| cluster_width(cluster) } end |
.strip_ansi(value) ⇒ Object
44 45 46 |
# File 'lib/charming/presentation/ui/width.rb', line 44 def strip_ansi(value) value.to_s.gsub(ANSI_PATTERN, "") end |