grid: writing over half of a wide cluster loses text #11

Closed
opened 2026-10-01 12:49:49 +09:00 by natsukium · 0 comments
Owner

Several grid paths recognize only a bare Grapheme::Char as the owner of a wide pair, never a Grapheme::Cluster, so a multi-codepoint wide grapheme (VS16 emoji, flag, ZWJ sequence, CJK + combining mark) is not cleaned up when half of it is overwritten or erased. kitty, WezTerm and Alacritty render all repros below correctly.

Repros (8-column grid, · = Empty, _ = Spacer):

  • Redrawing an unchanged line (a prompt repaint): printf '❄️ x\r❄️ x' → ·| |x|x, cursor col 3; expected ❄️|_| |x, cursor col 4. Same for 🇯🇵, ❤️, keycaps. widen_cluster_if_needed (crates/felis-grid/src/editing.rs ~523) only claims an Empty trailing cell, so the stale Spacer blocks the widen, the cursor is not advanced, and the next print evicts the emoji.
  • Overwriting the left half: printf '👩‍💻z\rXY' → ·|Y|z; expected X|Y|z. Same with CUP onto the cluster.
  • Erasing the left half: ECH, DECERA (\e[1;1;1;1$z), DECFRA leave ·|_.
  • REP after a cluster repeats only the base scalar: ❤️\e[2b → narrow ❤❤ (last_printed is not set on a fold).

Where: evict_wide_partner_at / needs_right_evict (editing.rs ~660), store_ascii_run (sink.rs ~82), range_blank_inner to - 1 check (editing.rs ~715).

Knock-on: push_cell_text emits nothing for a Spacer, so capture, search and selection drop a column over the orphan.

Several grid paths recognize only a bare `Grapheme::Char` as the owner of a wide pair, never a `Grapheme::Cluster`, so a multi-codepoint wide grapheme (VS16 emoji, flag, ZWJ sequence, CJK + combining mark) is not cleaned up when half of it is overwritten or erased. kitty, WezTerm and Alacritty render all repros below correctly. **Repros** (8-column grid, `·` = Empty, `_` = Spacer): - Redrawing an unchanged line (a prompt repaint): `printf '❄️ x\r❄️ x'` → `·| |x|x`, cursor col 3; expected `❄️|_| |x`, cursor col 4. Same for `🇯🇵`, `❤️`, keycaps. `widen_cluster_if_needed` (`crates/felis-grid/src/editing.rs` ~523) only claims an `Empty` trailing cell, so the stale Spacer blocks the widen, the cursor is not advanced, and the next print evicts the emoji. - Overwriting the left half: `printf '👩‍💻z\rXY'` → `·|Y|z`; expected `X|Y|z`. Same with CUP onto the cluster. - Erasing the left half: ECH, DECERA (`\e[1;1;1;1$z`), DECFRA leave `·|_`. - REP after a cluster repeats only the base scalar: `❤️\e[2b` → narrow `❤❤` (`last_printed` is not set on a fold). **Where:** `evict_wide_partner_at` / `needs_right_evict` (`editing.rs` ~660), `store_ascii_run` (`sink.rs` ~82), `range_blank_inner` `to - 1` check (`editing.rs` ~715). **Knock-on:** `push_cell_text` emits nothing for a Spacer, so capture, search and selection drop a column over the orphan.
natsukium changed title from probe to grid: writing over half of a wide cluster loses text 2026-10-01 12:50:19 +09:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
felis-terminal/felis#11
No description provided.