Skip to content

Editor rendering: add per-line visual cache with document-change invalidation#54

Closed
Copilot wants to merge 3 commits into
developfrom
copilot/add-visual-line-cache-invalidation
Closed

Editor rendering: add per-line visual cache with document-change invalidation#54
Copilot wants to merge 3 commits into
developfrom
copilot/add-visual-line-cache-invalidation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 11, 2026

Editor.BuildVisualLine was rebuilding visual lines for every visible row on every frame, driving high allocation churn during navigation/scroll. This change adds a per-line cache and selective invalidation so unchanged lines are reused and only affected lines are rebuilt.

  • What this PR changes

    • Introduces a visual-line cache in Editor keyed by DocumentLine.
    • Reuses cached CellVisualLine instances on cache-eligible build paths (no selection/styled segments/transformers).
    • Invalidates cache entries from the first affected line onward on Document.Changed (covers insert/remove/replace and downstream line-number shifts).
    • Clears cache when rendering-affecting editor options/document wiring change (Document, SyntaxHighlighter, SyntaxLanguage, IndentationSize, ShowTabs).
  • Safety/consistency details

    • Cache entries store source line number metadata and validate line identity before reuse.
    • End-of-document change offsets are handled safely during invalidation (no stale/invalid DocumentLine access).
  • Tests added

    • Cache reuse for unchanged lines.
    • Invalidation of changed line and following lines after edits.
if (canUseCache
    && _visualLineCache.TryGetValue (line, out CachedVisualLine? cached)
    && cached.LineNumber == line.LineNumber
    && cached.NormalAttribute == resolvedNormal
    && cached.SelectedAttribute == resolvedSelected)
{
    return cached.Line;
}

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • www.jetbrains.com
    • Triggering command: /usr/share/dotnet/dotnet /usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/jetbrains.resharper.globaltools/2025.2.4/tools/netcoreapp3.1/any/cleanupcode.unix.runtimeconfig.json /home/REDACTED/.nuget/packages/jetbrains.resharper.globaltools/2025.2.4/tools/netcoreapp3.1/any/cleanupcode.exe Terminal.Gui.Text.slnx --profile=Full Cleanup (dns block)
    • Triggering command: /usr/share/dotnet/dotnet /usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/jetbrains.resharper.globaltools/2025.2.4/tools/netcoreapp3.1/any/cleanupcode.unix.runtimeconfig.json /home/REDACTED/.nuget/packages/jetbrains.resharper.globaltools/2025.2.4/tools/netcoreapp3.1/any/cleanupcode.exe Terminal.Gui.Text.slnx --profile=Built-in: Full Cleanup _TOKEN"; }; f (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Add visual-line cache and invalidation for rendering Editor rendering: add per-line visual cache with document-change invalidation May 11, 2026
Copilot AI requested a review from tig May 11, 2026 13:59
Copilot finished work on behalf of tig May 11, 2026 13:59
tig added a commit that referenced this pull request May 11, 2026
Cherry-picked the cache-the-draw-path idea from PR #54. Adds a second
narrow cache (_drawVisualLineCache) keyed by DocumentLine.LineNumber
that the draw path consults via GetOrBuildDrawVisualLine when:

  - no syntax-highlighter segments
  - no selection
  - no LineTransformers

It's kept separate from _defaultVisualLineCache so the caret/mouse path
(Attribute.Default) and the draw path (editor role attributes) don't
overwrite each other on alternating reads. Both caches share the same
invalidation logic: ranged drop from first affected line down on
OnDocumentChanged, wholesale clear on Document swap / IndentationSize /
ShowTabs change.

What this helps:
  - A consumer that doesn't enable syntax highlighting now reuses
    cached visual lines across consecutive frames during static
    scrolling, instead of rebuilding every visible line each frame.

What it doesn't help:
  - ted, because TedApp sets a TextMateSyntaxHighlighter; segments is
    always non-null and the eligibility predicate falls through. That's
    fine — the same predicate gracefully degrades, and the cache will
    light up automatically once syntax-colorizer (#28/#32) replaces
    the per-frame segments computation with a transformer.

Tests added (EditorVisualLineCacheTests.DrawCache nested class):
  - Repeated draw with same attributes returns same instance
  - Different attributes bypass cache
  - Selection bypasses cache
  - Document edit drops affected line from draw cache

CI perf gate unchanged: VisualLineBuild_Short ratio 1.00× pre vs.
post-cherry-pick (5.331 → 5.356 µs on local Windows). The bench
doesn't go through Editor.BuildVisualLine, so this commit has no
effect on the gated metric — it's a feature-gain commit, not a perf
fix for the existing gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tig tig closed this May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rendering: add visual-line cache and invalidation (B1 follow-up)

2 participants