diff --git a/frontends/rioterm/src/grid_emit.rs b/frontends/rioterm/src/grid_emit.rs index 6a134b92..993f5c55 100644 --- a/frontends/rioterm/src/grid_emit.rs +++ b/frontends/rioterm/src/grid_emit.rs @@ -1402,6 +1402,13 @@ pub fn build_row_fg( row_hints: &[RowHint], font_library: &FontLibrary, route_id: usize, + // Column of the cursor on this row, or `None` if the cursor isn't + // on this row (different row, or hidden). When `Some`, the + // run-extension loop breaks the run around the cursor cell so + // partial ligature lookahead (e.g. `grap` waiting for `h` to form + // `graph`) can't make pre-cursor cells visually disappear while + // the user is mid-typing. + cursor_col_for_row: Option, fg_scratch: &mut Vec, ) { fg_scratch.clear(); @@ -1594,6 +1601,24 @@ pub fn build_row_fg( if is_run_breaker(sq2) { break; } + // Cursor break: keep the cursor cell in its own one-cell + // run so OpenType lookahead can't leave a pre-cursor span + // visually empty while waiting for substitution candidates + // to resolve (e.g. typing `paragraph` mid-row used to make + // `grap` blank until `h` arrived). Skipped on grapheme + // cells so emoji-ZWJ / combining-mark clusters stay whole + // when the cursor lands on them. + if !sq2.has_grapheme() { + if let Some(cursor_x) = cursor_col_for_row { + let cursor_x = cursor_x as usize; + if run_start == cursor_x && end == run_start + 1 { + break; + } + if run_start < cursor_x && end == cursor_x { + break; + } + } + } let style2_id = sq2.style_id(); if style2_id != prev_style_id { let f = (style_set.get(style2_id).flags.bits() & SHAPING_FLAG_MASK) as u8; diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index 5ef264c6..85872885 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -3828,6 +3828,14 @@ impl Screen<'_> { &hint_scratch, &mut bg_scratch, ); + let cursor_col_for_row = if p.cursor_visible + && (y as u16) == p.cursor_row + && p.cursor_shape != rio_backend::ansi::CursorShape::Hidden + { + Some(p.cursor_col) + } else { + None + }; crate::grid_emit::build_row_fg( row, cols, @@ -3844,6 +3852,7 @@ impl Screen<'_> { &hint_scratch, &font_library, p.route_id, + cursor_col_for_row, &mut fg_scratch, ); grid.write_row(y as u32, &bg_scratch, &fg_scratch);