SIGN IN SIGN UP

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