highlighter: Bound non-combined injection parsing (#2689)
Closes #2688 ## Description Markdown fenced code blocks use non-combined Tree-sitter injections, so every registered fence is parsed into a separate retained tree. Without a document-wide limit, a document with thousands of small fences can create one parser, tree, and injection layer per fence. This change adds a limit of 512 non-combined injection parse attempts per document. The budget is consumed before parsing so failed or timed-out parses cannot bypass it. Once exhausted, traversal continues so later combined injections, including `markdown_inline`, can still be collected. The limit preserves the existing test coverage for 384 fenced blocks while placing a finite bound on independently parsed and retained injection trees. Fences beyond the limit continue to receive the host Markdown highlighting but not injected-language token highlighting. No public API or update return semantics are changed. ## Performance The public `SyntaxHighlighter::update` and `SyntaxHighlighter::styles` APIs were measured with 20,000 identical 12-byte fences. `rs` is a registered Rust alias; `zz` is an unregistered control. Release timing runs used separate processes pinned to one CPU with main/fix order alternated. The table reports medians from 20 `rs` runs and 30 `zz` runs; RSS medians are from three fresh-process runs. | Source tree | Info string | `update` | Process RSS increase | 1,000 end-of-document highlight queries | | :---------- | :---------- | -------: | -------------------: | ---------------------------------------: | | `main@46576ae7` | `rs` | 502.5 ms | 62.7 MiB | 13.449 ms | | This change | `rs` | 119.9 ms | 41.1 MiB | 1.373 ms | | `main@46576ae7` | `zz` | 101.1 ms | 38.7 MiB | 1.120 ms | | This change | `zz` | 102.6 ms | 38.8 MiB | 1.125 ms | With this change, the 20,000-fence reproduction retains 512 Rust injection layers instead of 20,000. The small `zz` update-time difference is the bounded per-match budget check; no parser or injection tree is created for that case, and its query time remains unchanged. ## How to Test ```sh cargo test --locked --offline -p gpui-component \ --features tree-sitter-languages \ highlighter::highlighter::tests cargo fmt --all -- --check ``` The highlighter test suite passes, including: - the existing test proving all 384 fenced Rust blocks are highlighted; - a new 640-fence boundary test proving exactly 512 Rust layers are retained; - an assertion that the combined `markdown_inline` layer is not starved after the non-combined budget is exhausted. Temporarily increasing the new boundary test to 20,000 fences also passes and retains exactly 512 Rust layers. ## AI Assistance AI assisted with implementation, regression-test scaffolding, and benchmark documentation. The final diff and test evidence are included for contributor review. ## Checklist - [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and followed the guidelines. - [X] Reviewed the changes in this PR and confirmed AI generated code (if any) is accurate. - [x] `cargo run` story verification is not applicable; no UI or story behavior changed. - [x] Cross-platform performance testing is not applicable; no platform-specific path changed. --------- Co-authored-by: Jason Lee <huacnlee@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
L
luren committed
7033423d0b91ef74ce8ddbe3c64bca828dffb417
Parent: da736c9
Committed by GitHub <noreply@github.com>
on 8/13/2026, 3:14:16 PM