SIGN IN SIGN UP

input: Keep highlight runs on char boundaries of the shaped text (#2777)

## Summary

Fixes a crash in the editor when highlight run boundaries drift off the
UTF-8 char boundaries of the line being shaped. The platform text system
slices the line by run lengths (`text.split_at(run.len)` in
`MacTextSystem::layout_line`) and panics inside a multi-byte character:

```
end byte index 9 is not a char boundary; it is inside '量' (bytes 8..11 of string)
```

Observed in Longbridge's quant script editor (Navi scripts with Chinese
comments), crashing the whole app during prepaint.

## Root causes and fixes

Two real sources of drift, plus a last line of defense:

1. **Unclipped diagnostics shift the runs coordinate space.**
`DiagnosticSet::range` returns an entry straddling the viewport edge
(its seek target matches any entry *containing* the offset) — and any
range inside a folded region also lands between the visible line groups.
`combine_highlights` then emits coverage outside the visible lines,
breaking `layout_lines`' invariant that the runs vec concatenates
exactly the visible lines' bytes; every later run boundary shifts by the
excess byte count. `highlight_lines` now records the flushed line-group
ranges and clips the composed styles back to them
(`clip_styles_to_ranges`).

2. **Stale-tree styles.** While a background reparse is pending (2ms
sync-parse timeout, or the >256KB `edit_tree` path),
`SyntaxHighlighter::styles` serves node offsets from a stale tree; they
can fall inside multi-byte characters of the *current* text. Ranges are
now snapped to char boundaries with `Rope::clip_offset` (same approach
as #2700 for selections).

3. **Defense in depth.** `layout_lines` snaps run boundaries to char
boundaries of the exact sub-line being shaped
(`align_runs_to_char_boundaries`, zero-cost when already aligned) before
every `shape_line` call, so any remaining mis-aligned source degrades to
slightly-off colors for a frame instead of a panic.

## Test plan

- [x] `cargo test -p gpui-base --lib` (378 tests) — includes new unit
tests for `align_runs_to_char_boundaries` and `clip_styles_to_ranges`
- [x] `cargo test -p gpui-component --features tree-sitter-languages
--lib` (486 tests) — includes
`test_stale_tree_styles_snap_to_char_boundaries`, verified to fail
without the fix (`style range 2..13 is not on char boundaries`)
- [x] Manual repro (`crates/story/examples/cjk_diagnostic_panic.rs`,
untracked): on `main` it panics right after the window opens (`end byte
index 70 is not a char boundary; it is inside '例'`); on this branch it
renders normally with the wavy underlines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Y
Ylin committed
e5b8a3f496e4c812b1548229dc043ac1f72cee1f
Parent: 61f299b
Committed by GitHub <noreply@github.com> on 8/20/2026, 8:14:43 AM