Files
opentui/packages
Simon Klee 07225659bb native: snap vertical cursor moves to the start of a wide character (#1596)
Up and Down kept the desired display column and committed it as is. When
the target line had a wide character (CJK, emoji) or a tab at that
column, the cursor landed on its second cell: the logical moves
(`moveUp`/`moveDown`), the visual moves that Textarea's arrow keys use
(`moveUpVisual`/`moveDownVisual`, also Shift+Up/Down), and
`setViewport(..., moveCursor = true)`. The terminal cursor was drawn in
the middle of the character, and typed text went in front of it while
the cursor moved on to the next mid-character column.

Each of these now passes its column through `EditBuffer.snapColumn`,
which returns the start of the cursor unit under the column. Left/Right
and mouse clicks already snap to that start. The desired column stays
unsnapped, so the cursor returns to it on the next line that has a
boundary there. A cursor unit never spans two virtual lines, so the
snapped column stays on the target visual row.

Source additions: `snapColumn` is the one shared snap (3 call sites);
`moveToRow` replaces the two copies of the move body in `moveUp` and
`moveDown`; `makeCursorVisible` keeps an unsnapped desired column in
its own variable.

Tests: one table in `editor-view_test.zig` replaces seven vertical-move
tests (each old assertion is a row, most now exact) and adds CJK, tab,
skin-tone and ZWJ emoji rows for both width methods, the
https://github.com/anomalyco/opentui/issues/1289 lines,
a wrapped https://github.com/anomalyco/opentui/issues/1289 line, and
`setViewport(moveCursor)`. The seeded
Textarea model test presses Up/Down and requires a grapheme boundary.
The Textarea Shift+Up/Down tests become one table with the
https://github.com/anomalyco/opentui/issues/1289 rows
in both selection occupancies. One `editor-view.test.ts` assertion
pinned the mid-emoji column 8; it is now 7.

Issue: 001
Fixes https://github.com/anomalyco/opentui/issues/1289
2026-10-09 14:01:59 +02:00
..