Expand description
RenderState — the renderer’s read contract with the host.
Phase 5.8.AF.5 / Slice 3a.
§Why this exists
Per paramount goal #4 (CLAUDE.md):
Nothing blocks the UI — enforced architecturally, not by discipline.
Renderers must not read Editor fields directly during
render — that read path needs the same &Editor reference
the dispatcher holds for mutation, which couples render
latency to whatever happens to be mutating Editor at the
moment. RenderState is the wait-free read seam: dispatch
publishes a fresh snapshot into an ArcSwap<RenderState>
at the end of every tick; the renderer loads it once per
frame and reads everything it needs from there.
This is the substrate for two follow-on slices:
- Slice 3b moves every drain in
run_tick_pendinginto per-subsystem background tasks. Each task writes through the same publication path; the renderer never sees a half-built mutation. - Slice 3c moves
Editorto its own thread. Channels replace&mut Editorreferences; the renderer becomes a pureRenderStatereader.
Both slices preserve the read contract this slice
establishes — RenderState doesn’t change shape, just who
produces it and on which thread.
§Per-subsystem sub-states
RenderState is split into 11 sub-state structs, one per
UI-visible subsystem. Each is Arc-wrapped so a subsystem
whose backing state didn’t change between publications can
share its sub-state Arc across frames (identity-preserved).
In Slice 3b, subsystem background tasks publish their own
sub-state directly without re-snapshotting unrelated
domains.
The active-buffer hot-path state (cursor, scroll, viewport,
modal, visual selection, snapshot pointer) lives in its own
ActiveDocumentRenderState — separate from the buffer registry
(BuffersRenderState) because the read frequencies differ
by orders of magnitude: the active-buffer state churns on
every motion/edit (per-frame critical), the registry churns
only on :b / :e / :bd. Splitting lets Slice 3b
republish them on independent cadences.
For Slice 3a only DiagnosticsRenderState carries real
data — that’s the proof-of-life migration path. The other
sub-states are deliberately empty placeholders so the shape
is in place when their backing fields migrate in later
slices.
Structs§
- Active
Document Render State - Active buffer’s hot-path render-side projection.
- Buffer
Locals Render State - Buffer-locals per buffer. Slice 3c.final.B.9 — drops the
per-frame
read_editor(|e| e.buffer_locals.get(&buf).and_then(...))chain in the modeline, help-render, file-tree, and oil paint paths to a wait-free Arc-bump lookup off the published snapshot. - Buffers
Render State - Buffer registry’s render-side projection — the list of buffers the editor knows about, independent of which one is currently active.
- Cells
Render State - Cell-grid renderer substrate state. Mirror of
SyntaxRenderStatein shape (wait-free output cell + read inputs); replaces the per-frameshape_linepath for code-class buffers. - Completion
Render State - Insert-completion + cmdline-completion popup state.
- Diagnostics
Render State - Diagnostics — the proof-of-life sub-state for Slice 3a.
- Diff
Render State - D.3.d.1 (2026-05-29): renderer-side projection of the
active document’s diff overlay state. Carries the
DiffSignMapfor the gutter-sign column; future D.3.e (line tints) reads through the same map. - Excerpt
Syntax - D.4.d.1.a (2026-05-29): per-visible-Document-pane build
inputs for the cell-builder worker. Mirrors the shape of
the top-level
CellsRenderStateactive-doc fields but keyed by(pane_id, buffer_id)so the worker can resolve each entry’s matrix Arc viacrate::editor::Editor::cells_matrix_forat publish time and rebuild per visible buffer. - Inlay
Hint Row - One inlay-hint row published on
SyntaxRenderState::inlay_hints. - Lifecycle
Render State - Renderer lifecycle flags published per tick.
- LspRender
State - LSP feature data the renderer reads beyond diagnostics.
- Messages
Render State *messages*buffer + echo line state.- Modeline
Render State - Modeline status (cmdline text, search indicator, mode hints).
- Modes
Render State - Active modes per buffer. Slice 3c.final.B.11 — drops the
per-frame
read_editor(|e| e.active_modes.get(&buf))call in the modelineis_messages_buffercheck to a wait-free Arc-bump lookup off the published snapshot. - Notifications
Render State - NOTIF.1b: corner-anchored notifications, as the renderers see them.
- Options
Render State - Typed-options registry handle. Slice 3c.final.B.10 — drops the
per-frame
read_editor(|e| e.config.get_typed::<X>())calls inpicker_display_is_minibufferand elsewhere to a wait-free Arc bump off the published snapshot. The innerConfigRegistryis already Arc-shared, so a publish here is one Arc clone. - Pane
Cells Inputs last_editisSome(delta)only for the active pane (edits land on the active document; the publish pathtake()sEditor::last_edit_for_cellsexactly once); non-active panes always carryNoneso the worker conservatively full-rebuilds on text-version bumps for those buffers. That is correct today because non-active buffers don’t take edits in normal use; LSP-driven edits to non-active buffers also flush throughapply_editwhich clears the slot.- Panes
Render State - Pane tree’s render-side projection.
- Picker
Render State - Active picker’s render-side projection.
- Popup
Render State - Help / hover / signature popup’s render-side projection.
- Publish
Cache - Perf plan B.4: identity-preserving sub-state cache.
- Render
State - The renderer’s read contract with the host. Built fresh by
[
crate::dispatch::Editor::build_render_state] at the end of every dispatch tick and stored into the editor’sArcSwap<RenderState>. Renderers load witheditor.render_state.load_full()once per frame. - Resolved
Options Render State - PI.4: per-buffer mode-resolved options, published so BOTH renderer
peers resolve a buffer’s options (Number, Wrap, CursorLine, …) through
ONE renderer-agnostic seam —
RenderState::resolved_option_for— instead of the TUI reading the live editor and GPUI reading the active document’soption_cache. Mirror of the host’sEditor::resolved_options; a buffer absent from the map falls back to the global typed-option default viaOptionsRenderState::config. - RowOverlay
Quad - Perf plan B.2: one pre-bucketed static-overlay quad inside a
row of
StaticOverlayQuads. Coordinates are in source utf-8 byte space — the byte offsets into the SOURCE line text (not into any combined / inlay-spliced row text). - Signs
Render State - SG.2b — everything a renderer needs to paint a
GutterDecoration::Sign, resolved on the actor thread so the render path does neither a string hash nor a theme lookup by name. - Static
Overlay Quads - Perf plan B.2: worker-published per-row pre-bucketed static-overlay quads for the active pane’s visible window.
- Syntax
Render State - Tree-sitter syntax inputs + static-overlay bucket cache.
- TabRender
Item - Tabs
Render State - Issue #29 (2026-05-22): published per-frame tab snapshot.
Carries the user-visible label for each tab + the active
index + the resolved visibility decision (
auto⇒ Multi- or-zero already evaluated by the publisher). - Translator
Render State - Translator inputs for the renderer’s input loop.
- Virtual
Rows Render State - D.3.b.1 (2026-05-29): renderer-side projection of the
virtual-rows worker’s published
VirtualRowMatrix. Carries the matrix so the TUI / GPUI renderer can interleave virtual rows between document rows when painting visible content. - Visible
Highlights Key - Cache key identifying the inputs that produced a particular
StaticOverlayQuads. The overlay worker compares the current inputs againstStaticOverlayQuads::computed_for_keyto short-circuit re-bucketing on a no-op tick (cursor blink, unchanged scroll/viewport/folds).
Enums§
- Overlay
Layer - Perf plan B.2: overlay layer tag carried on each per-row
pre-bucketed quad in
StaticOverlayQuads. The renderer uses the tag to interleave cursor-coupled layers (current_match,visual_range) at the right precedence at prepaint time: - RowRun
- One coloured run within a display row’s combined text.
Functions§
- cached_
or_ build - Perf plan B.4: cache-or-build helper for the sub-state Arc
memoisation in
build_render_state. Returns the cached Arc whencurrent_versionmatches the version stored inslot; otherwise callsbuild, stores the result, and returns it. - inlay_
hints_ version - Perf plan A.2 slice A.2b.2: content hash of a flattened inlay-
hint list. Stable per-payload (same vec → same hash) so it can
drive
VisibleHighlightsKey::inlay_versionfor the worker’s row-cache invalidation. Empty list hashes to 0 — matches theinlay_version: 0default and keeps the steady-state no-hint path on a single cheap branch. - static_
overlay_ state_ version - Perf plan B.2: content hash of the three static-overlay layer
payloads. Drives
VisibleHighlightsKey::static_overlay_versionfor the worker’s overlay-bucket invalidation. All-empty payloads hash to 0 so the steady-state no-overlay path stays on a single cheap branch (matches thestatic_overlay_version: 0default).