Expand description
Pure geometry helpers for host-driven mouse translation.
These helpers are host-agnostic: they operate on doc-space coordinates (row/col in chars) and tab-expanded visual columns. The TUI host and any future GUI host use them independently after doing their own pixel-or-cell → visual-column conversion.
§Cell-width semantics
Both helpers measure a char with exactly the rule hjkl-buffer-tui’s
paint_row uses to advance the cursor across the terminal, because being
consistent with the cell the glyph was actually painted in is the entire
point of these functions:
| char | cells | why |
|---|---|---|
\t | to the next tab_width stop, measured from the visual column | tab stops are screen positions, so a preceding wide char shifts them |
width() == Some(w) | w | the unicode-width table — 2 for CJK and most emoji, 1 for ASCII |
width() == Some(0) | 0 | combining marks and variation selectors compose onto the preceding cell |
width() == None | 1 | control characters; see below |
Zero-width chars (combining marks, U+FE0F) advance nothing and are
never a cursor landing site — visual_col_to_char_col skips them, so a
click or a j into that column lands on the base char they compose onto
(or, past the composed cell, on the next real char). Verified against
neovim 0.12.4: on "ae\u{301}b", virtcol reports the mark and the e at
the same column, and j from column 3 of an ASCII line lands on the b,
never on the mark.
Control chars (width() == None) count as one cell. This
deliberately diverges from vim, which renders ^A in two: paint_row
resolves them through sanitize_control, which maps every C0/C1 control to
a single-width Control Pictures glyph (U+0001 → ␁), and then advances
by ch.width().unwrap_or(1) — one cell. Matching the renderer is the
contract here; matching vim’s ^X notation would put the cursor one cell
left of its own glyph on every line containing a control char.
Known divergence from vim: emoji presentation sequences. vim widens
U+2764 U+FE0F (“❤️”) to two cells because it resolves the sequence as a
grapheme cluster. unicode-width is consulted per char here (and in
paint_row), so U+2764 measures 1 (East Asian Ambiguous) and the
variation selector 0 — one cell total. The two sides of hjkl agree with
each other, which keeps the cursor on its glyph; they are both one cell
narrower than vim would be. Fixing that means grapheme segmentation in the
renderer first.
Functions§
- char_
col_ to_ visual_ col - Map a character index in
lineto its starting visual column (the screen-cell offset from line start of the char’s FIRST cell). The inverse ofvisual_col_to_char_col. - visual_
col_ to_ char_ col - Inverse of
char_col_to_visual_col.