Repository navigation
fix(native-chat): keep a huge unbroken paste from freezing the chat (#32355) - #36031
Conversation
A long run with no whitespace ("xxxx…", "0000…", "abab…") wrapped with
kerning makes Chromium reshape it at every line, quadratic in run length:
128 KiB stalls 2.4 s, 1 MiB 19 s, 5 MiB never finishes. ProseMirror forces
that layout on paste and on each keystroke, so the composer freezes in
every terminal-backed chat, including paired servers and WSL. A sent
message stalls the transcript the same way: its fold clips the text but
still lays it all out (128 KiB: 2.5 s).
Runs over 256 characters now render with font-kerning: none, so prose of
any length and Shift+Enter lines keep kerning:
- composer: inline decorations, updated only for the lines a transaction
touches (a keystroke beside 8,000 decorated lines ~1 ms, not ~190 ms)
- user messages in the transcript and expanded queued cards: the whole
text when it holds such a run
Text, drafts and what is sent are unchanged. After: 1 MiB single line
84 ms, 5 MiB 312 ms (keystroke 248 ms), 1 MiB of 4000-char lines 50 ms
(was 2.3 s), transcript 128 KiB 80 ms. Worst case left kerned, 256-char
runs: 1 MiB 242 ms, 5 MiB 1.2 s, like 1 MiB of short lines (291 ms).
Fixes #32355
Co-authored-by: Manan Chataut <mananchataut@gmail.com>
These scripts have no spaces but Chromium can break between their characters, so a long paragraph is not one unbreakable run. Count ideographs, kana, Hangul and Thai as break opportunities so such text keeps kerning. Co-authored-by: Manan Chataut <mananchataut@gmail.com>
…itions Splitting one run inside a block of thousands replaced every decoration in that block, and ProseMirror's removal is quadratic: 1,411 ms for one space in a paragraph of 8,000 long runs. A change now rescans only from the run edge before it to the run edge after it, and removes only the decorations that overlap: ~1 ms for the same edit. Decorations are only mapped while an IME composition is in progress, so the composing text node is not re-rendered. The deferred range is rescanned with the next non-composing change, or on compositionend when none follows. The keystroke-timing test is replaced by deterministic ones: unaffected decorations are reused and the result equals a full scan; a composition defers the rescan and compositionend runs it. Co-authored-by: Manan Chataut <mananchataut@gmail.com>
A supplementary ideograph (U+20000-U+3FFFF) broke a run at its high surrogate only, so the decoration began on the low surrogate and split the pair across elements. Both the run scan and the leftward edge scan now treat the pair as one break. NBSP joins words for Chromium's line breaker (UAX #14 class GL), so an NBSP-joined paste is one unbreakable run and must lose kerning; drop it from the break set. No other non-breaking characters were in it. Drop a composer test whose coverage the decoration reuse test already has. Co-authored-by: Manan Chataut <mananchataut@gmail.com>
Two lone-surrogate edge cases made the incremental rescan disagree with a full scan: - an edit completing a pair at the window start (a lone high surrogate, then a pasted low surrogate and run) began the decoration on the low surrogate; the window now widens past the whole pair - a lone low surrogate after any break counted as a pair end, dropping it from the run; that now requires a preceding high surrogate A seeded fuzz also found inclusive decoration edges mapping across a joined block's boundary, where ProseMirror cannot remove them; the decorations are no longer inclusive. Co-authored-by: Manan Chataut <mananchataut@gmail.com>
[Medium impact] The PR appears safe to merge; no actionable defects were established. SummaryThis PR turns off kerning for long unbroken text without changing the saved text.
No actionable defects were established. Reviews (1) · Last reviewed commit: "fix(native-chat): classify surrogate pai..." · Reviewed by Greptile |
|
No actionable comments were generated in the recent review.
|
Fixes #32355 (Linear STA-12531). Takeover of community PR #34501 by @thefrikidude — thank you; co-author credit on every commit.
Cause: not a regression from a specific Orca PR. Chromium re-shapes kerned text for every wrapped line of a long run with no break opportunity, so a single huge unbroken line (JSON, logs, base64) froze the composer: 1 MiB took ~19–23s.
Fix: turn off
font-kerningonly on runs that have no line-break opportunity and are longer than 256 characters. The freeze drops to ~0.2s, the text and DOM are unchanged, and the loss of kerning on such runs isn't visible.Unlike #34501, a long paste stays inline as text rather than being converted to a file.
Verification: adversarial review over 5 rounds by an Opus sub-agent and Codex (gpt-6.1-sol, xhigh); both final verdicts are MERGE. In independent seeded fuzzing (~65k transactions, plus 462k Unicode boundary cases), the incremental decorations matched a fresh full scan every time.