Skip to content

perf(editor): cache root TextSpan in QueryaHighlightController to eliminate allocations on cursor blinks #1544

Closed

Description

@ZhuchkaTriplesix

Problem

In QueryaHighlightController.buildTextSpan (lib/core/editor/querya_highlight_controller.dart), buildTextSpan is invoked twice per second for cursor blink repaints.
Even on a full cache hit (_cachedText == text), it unconditionally allocates a new wrapper TextSpan and a new List<TextSpan> (TextSpan(style: style, children: [_cachedSpan!])).
Over a session, background cursor blinks across multiple open editor tabs churn unnecessary garbage collection cycles.

Solution

  • Cache the outer root TextSpan directly (_cachedRootSpan), invalidating it only when the text content, base style, or theme brightness changes.
  • Return the cached root span directly on cache hits without allocating a new wrapper TextSpan or children list.

Benefits

  • 0 allocations per cursor blink cycle on idle editors.
  • Reduced GC pressure when multiple SQL editor tabs are active.

Activity

  1. added
    performanceTheme parser epic label: performance
    enhancementNew feature or request
    editorCode/SQL editor
    coreCore library logic and services
    on Oct 12, 2026
  2. added this to the 0.5.0 milestone on Oct 12, 2026
  3. ZhuchkaTriplesix commented on Oct 17, 2026

    @ZhuchkaTriplesix
    MemberAuthor

    Closing as not worth doing. Checked against the code and the Flutter sources:

    • The premise is off: the cursor blink does not call buildTextSpan. EditableText blinks by changing the cursor opacity on the RenderEditable; it does not rebuild. buildTextSpan runs on rebuilds such as text, selection and focus changes.
    • On those rebuilds the new root TextSpan costs one object and a one-element list. RenderEditable.text compares it with ==. TextSpan == compares the children with listEquals, and the only child is the cached _cachedSpan itself, so the comparison ends at identical in O(1) and no relayout follows.

    Caching the root would save those two small allocations per rebuild and nothing measurable. #1522 is the same proposal and is closed with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority / Parity & RefactoringcoreCore library logic and serviceseditorCode/SQL editorenhancementNew feature or requestperformanceTheme parser epic label: performance

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions