Expand description
Pure application state and transitions.
The state machine is intentionally separated from the IO loop so it can be
unit-tested without spinning up a terminal. Each input keystroke becomes
an Action; App::apply consumes the action, dispatching motion / edit
work through lattice_grammar::execute() where appropriate.
Slice 3c.final.E.swap aftermath: many App-side delegate methods +
free helper functions in this file are only reachable from
#[cfg(test)] mod tests blocks; the production paint / dispatch
paths route through the mutate_editor / read_editor seam or
the published RS sub-states (ad(), panes(), popup(), …).
The #![allow(dead_code)] below acknowledges that test-only state
without restructuring 30+ methods into #[cfg(test)] impl App
blocks. A follow-up cleanup slice (3c.final.E.cleanup) can
tighten this if the lint signal becomes load-bearing for catching
genuine future dead code.
§Module layout
This file holds the App struct definition, the cross-feature data
types it carries (Action, the LSP outcome enums + structs,
OptionCache, PositionEntry, LspNavKind, CompletionState,
Fold, EchoMessage, EchoLevel, SearchLine, …), the
cross-module free helpers (line_byte_len, is_word_char_byte,
word_under_cursor, etc.), and a mod tests block of cross-
feature integration tests. Per-feature App methods live in
app/<feature>.rs submodules – see docs/dev/notes/ui-tui-refactor.md
for the full per-module catalog. The R.1.x slice sequence
(R.1.0 – R.1.98) split the App’s monolithic impl block apart.
§Where to look for App methods
app/dispatch.rs–apply/apply_effect/apply_app_effect/handle_edits/dispatch_blocking/run_*_invocation/execute_ex_line/ Effect classifiers.app/lsp.rs– everylattice-lspconsumer (requests, drains, log buffers, hover, navigation, references, signature help, completion, rename, code action, format, symbols, trace, on-type formatting, trigger chars, workspace/applyEdit + workspace/configuration drains).app/lifecycle.rs–:e/:w/:q/:bn/:ls/<C-l>, save family, help-buffer adoption, document swap, buffer-state hooks, pane snapshot, event publishers.app/completion.rs– popup state machine, ranker, ghost text, snippet expansion, refilter,EffectiveCompletionConfig.app/edit.rs– actor-bridge mutation wrappers, yank / paste / register store, Insert+Replace primitives,:d, block-insert.app/motions.rs– bracket match, history walkers, mark jump, viewport / scroll, cursor clamp, viewport sizing, active-buffer accessors.app/folds.rs– fold compute / open / close / auto-open.app/search.rs–/,?,:s,:%s, find family.app/options.rs–:set, typed options, customize, per-language overrides, typed-option getters.app/cmdline.rs–:minibuffer + completion.app/help.rs–:help,:describe-*,:apropos,:keymap,do_help_follow_link.app/highlights.rs– tree-sitter highlight cache + per-frame refresh + post-edit shift.app/visual.rs– charwise / linewise / blockwise selection state,set_selections_blocking.app/picker.rs– picker state machine + candidate builders.app/boot.rs–App::new,build_lsp_subsystem,load_persistent_config,sync_keymap_overlays,sync_theme_from_config.app/file_tree.rs– file-tree buffer ops.app/oil.rs– oil buffer ops.app/macros.rs–qrecording /@replay.app/mode.rs–modal_label+enter_mode+activate_major_for_buffer_kind.app/syntax.rs–maybe_reparse_syntax.app/test_helpers.rs– shared test fixtures.
Structs§
- App
- Code
Action Row - One row of a code-action picker. Carries the action title,
kind glyph, and the original
CodeActionpayload (or its rawCommand-only form). Action items survive on the App across the request → picker accept gap so the resolve / apply path can read them by index. - Command
Invocation - One call of one command, with every grammar slot vim can fill: the
unified call type every front-end produces and
executeconsumes (DESIGN.md §5.2.1). - Completion
Item Row - One row of an LSP completion picker. Carries the item
label, kind glyph, optional detail blurb, and the insert
text.
replace_rangeis the byte range in the active line to splice the insert text over. - Completion
Resolve Outcome - Drain payload for
completionItem/resolve(Phase 4.2.g.3). CSM.8b.5:meta_indexis now the index of the fired candidate within state.raw’s LSP-row sequence; the drain decodes that row’s payload, applies the resolved fields, re-encodes in place. Multiple resolves in flight (selection change → cancel prior → fire new) cancel via the supplied token. - Completion
State - Cmdline completion popup state: candidates and selection. Renderer-agnostic; the renderer reads this via &App.
- Decoded
Semantic Token - 4.4.h: one decoded LSP semantic token, expanded from the
server’s relative-position varint encoding into absolute
positions.
token_typeis the canonical name from the server’s legend (e.g."keyword","function") so the renderer can pick a style without looking up the index.lengthis in utf-16 code units per the LSP spec. - Document
Highlight Cache - 4.4.e: cached
textDocument/documentHighlightresult anchored at a specific buffer + cursor position. The renderer readshighlightsto paint a soft overlay; the pump comparescursorto the live cursor to decide whether to invalidate and re-request. - Echo
Message - Single-line message rendered in the echo area below the mode
line (DESIGN.md §5.9.10). Replaced by the next call to
App::set_message(no timeout-based fade yet). - Fold
- One open completion popup (DESIGN.md §5.11.3 vertico-style
rendering). Built by
Action::CommandLineCompleteOrAdvancewhen the user presses Tab; consumed by accept / dismiss / scroll actions. One contiguous fold range in a document buffer. - InFlight
Live Query - Spawned
on_query_changedfuture / stream paired with the query it was launched against. The drain compareslaunched_for_queryagainst the picker’s live query; if they differ, the user has kept typing and a newer fire is already in flight (or coming via debounce) – discard the stale result. - Last
Find - The last
f/F/t/Tthe user ran, which is all;and,need to repeat it. - Last
Search - VM.3d-2: the last completed
//?/*/#search, which is allnandNneed to repeat it. Moved here from the host (which re-exports the name, so no call site moved) for the reasonLastFindwas:nis a motion now, and the state it repeats has to reach the grammar. - Last
Visual - Most-recently-completed visual selection. Used by
gvto reselect. - Live
Picker Query State - Live-picker query state. Installed when
open_pickerresolves a source whosespec().liveis true; survives until the picker is dismissed. - LspCode
Lens Cache - 4.5.d: per-buffer cache of
textDocument/codeLensresults. Filled by the pump on doc-version change or afterworkspace/codeLens/refreshevicts the entry. The:lsp-code-lenspicker reads the cache; accept routes the chosen lens’scommandthroughworkspace/executeCommand. - LspCompletion
Meta - CSM.8b:
LspCompletionMeta+LSP_COMPLETION_KIND_IDmoved intolattice-lsp::completionso the type lives in the crate that owns lsp-types. The host imports them via this re-export – candidate payloads carry the serde- encoded form directly (encode_meta/decode_meta) so the parallelApp.insert_completion_lsp_metasidecar is gone; the candidate IS the metadata. LSP-sourced insert-completion candidate metadata. Carried inside theRawCandidate’sCandidateData::Extensionpayload as a JSON blob (seeencode_meta/decode_meta). Replaces the pre-CSM.8b host-side sidecar – the candidate IS the metadata; the host decodes on demand. - LspDocument
Color Cache - 4.5.e: per-buffer cache of color literals + their resolved
values. Filled by the per-tick pump on document-version
change; consumed by
:lsp-color-presentation. Renderer swatch overlay queued – today the cache only feeds the picker. - LspDocument
Links Cache - 4.5.c: per-buffer cache of
textDocument/documentLinkresponses. Filled by the pump on document-version change;gxwalks the entries looking for the first link whose range covers the cursor and follows it. Cache invalidates when the version changes. - LspFolds
Cache - 4.4.f: cached
textDocument/foldingRangeresponse for one buffer.document_versionis the buffer’slattice_core::Documentversion at the time the request was issued; the pump compares it against the live version to decide whether to refresh. - LspInlay
Hint Cache - 4.4.g: cached
textDocument/inlayHintresponse for one buffer. Keyed on(BufferId, document_version); pump invalidates when the version changes.hintsare sorted by position so the renderer can stop scanning once it walks past the current line. - LspPull
Diagnostics Cache - 4.4.j: cached
textDocument/diagnosticstate per buffer.result_idis what the server issued on the previous response; threading it back inprevious_result_idlets the server answerUnchangedwhen nothing moved. - LspSelection
Chain - 4.4.e: cached
textDocument/selectionRangechain anchored at a specific buffer + cursor position. FlatVec<Range>(innermost first) instead of the LSP linked-list shape so the operator can index into it in O(1). Captured once on the first:lsp-expand-regionafter a fresh cursor; reused across subsequent expand/shrink steps until the cursor moves outsideranges[0](the innermost) or the buffer changes. - LspSemantic
Tokens Cache - 4.4.h: cached decoded
textDocument/semanticTokens/fullresponse for one buffer. Same shape as the other LSP per-buffer caches: keyed on(BufferId, document_version), invalidated by the pump when the version changes. - Macro
Recording - In-progress macro recording.
q<reg>starts;qagain stops and persists into the register table. - Message
Pushed - Typed event published on the editor’s event bus whenever
set_messageruns. Carries the appended record (cloned from the ring). Subscribers see every echo in arrival order; the renderer’s*messages*buffer live tail, the plugin host’s WIT bridge, future telemetry hooks, etc. are all peer subscribers with no privileged path. - Message
Record - One historical minibuffer echo. The renderer’s
*messages*buffer renders these in chronological order; eachset_messagecall appends one record. Cheap to clone (bounded text) – producers fan the same record out to every subscriber on the typed event bus. - Messages
Ring - Bounded chronological ring of every echo the editor has
emitted. Push on every
set_message; snapshot on demand for:messagesopen and live refresh. Capacity is fixed at construction; once full, the oldest entry drops to make room for the newest. - Option
Cache - Hot-path option cache. Mirrors the typed-options registry’s
resolved values for the active buffer; reads on this struct
fire on every render tick, so the cache exists to skip a
HashMap lookup per option. Repopulated by
App::rebuild_option_cacheafter every:set. - Pending
Block Insert - In-flight blockwise-visual insert (
IorA). - Pending
Picker Init - In-flight async picker init. The future from
PickerSourceGenerator::initis spawned on the LSP runtime; its resolved batch lands here viarx. Thecanceltoken lets a subsequent:picker <source>drop the predecessor before it completes. - Position
Entry - One entry in the unified position history (DESIGN.md §5.1.1).
- Prev
Pane State - Snapshot of the active pane’s state captured just before help
took it over. Used by
dismiss_popupto restore the user to the buffer + cursor + scroll they came from. The same struct serves both display modes (in-pane and popup-overlay). - Replace
Entry - One replace-mode entry – the byte that was at
atbefore the overwrite, so<BS>can restore it.original = Nonemeans the overwrite extended the line (the position was past EOL);<BS>deletes the inserted char rather than restoring a byte. - Search
Line - In-progress
/or?search state (MB.5a). The pattern text is NOT stored here — it lives in the focused*search-line*buffer (single source of truth, read viaEditor::search_pattern), the same way the:line reads*command-line*. This struct is the “search is active” marker + the metadata the buffer can’t carry: the direction (/vs?) and theorigincursor preserved so<Esc>restores it and incremental preview anchors its search. - Snippet
Candidate Meta - Sidecar metadata for snippet candidates in the popup. The
host renders the snippet body on accept and starts an
ActiveSnippet; this struct carries the parsed body + the display fields the popup row uses. - Substitute
Preview - Snapshot of an in-progress
:s/pat/repl/...preview. Refreshed on every cmdline keystroke while the input parses as a substitute; consumed by the renderer to overlay match ranges (and the typed replacement, when present) on the target buffer. - Symbol
Row - One row of a document-symbol / workspace-symbol picker.
Carries the symbol’s name, kind, depth (for in-document
hierarchy indent), and the location to jump to. Built
host-side from the LSP
DocumentSymbolResponse/Vec<SymbolInformation>so the picker doesn’t depend on lsp-types. - TagStack
Entry - One entry on the vim-style tag stack. Pushed by
gd(and the goto-* family) at the pre-jump cursor; popped by<C-t>to walk back. Distinct from the jump list because the user’s mental model for<C-t>is “undo the drill-down chain”, not “step through every cursor jump in chronological order”. - Unnamed
Register - The unnamed register’s payload. v1 uses a single global slot;
the full vim register zoo (
"a-z,"+,"*, etc.) lands later.
Enums§
- Action
- Async
Completion Outcome - OR.7: drain payload for the async completion fan-out — every
AsyncCompletionSourcethe active modes contribute, not only LSP’s. - Code
Action Outcome - Outcome of a
:code-actionsrequest. Drained per frame. - Code
Lens Outcome - 4.5.d: outcome of an in-flight
textDocument/codeLensrequest. Carries the server id so the eviction-on-refresh path can match by server. - Completion
Outcome - Outcome of a
textDocument/completionrequest. - Document
Color Outcome - 4.5.e: outcome of an in-flight
documentColorrequest. - Document
Highlight Outcome - 4.4.e: in-flight
documentHighlightrequest outcome. - Document
Links Outcome - 4.5.c: outcome of an in-flight
textDocument/documentLinkrequest.Emptyfor server responses that returned no links (still updates the cache so we don’t keep re-issuing for the same version). - Echo
Level - Renderer-side display level for echo messages. Mirrors
lattice_grammar::EchoLevel(wire-typed) but kept separate so renderers can adopt their own display semantics around the shared wire levels. - Find
Kind f/F/t/T— which direction, and whether the target character is included.- Fold
Method - One open completion popup (DESIGN.md §5.11.3 vertico-style
rendering). Built by
Action::CommandLineCompleteOrAdvancewhen the user presses Tab; consumed by accept / dismiss / scroll actions.:set foldmethod=...(DESIGN.md §15:18, C.2;docs/user/folding.md). Decides which provider feeds the per-buffer fold list. - Folding
Range Outcome - 4.4.f: in-flight
foldingRangerequest outcome. - Format
Outcome - Outcome of a
:format/:format-rangerequest. Drained per frame; the App applies the edits as one undo unit or echoes the appropriate failure / no-op state. - Hover
Outcome - Result of a
K(LSP hover) request, sent from the spawned task to the App’s main thread. Carrying the no-result variants explicitly (instead of just dropping the channel send) lets the drain echo a clear message so the user always gets feedback onK. - Inlay
Hint Outcome - 4.4.g: in-flight
inlayHintrequest outcome. Therequested_*_linepair rides through so the drain can seat the cache with the range that actually produced these hints (matters for the viewport pump – subsequent scrolls reuse the cache only while the viewport sits inside that range). - LspNav
Kind - Which navigation request flavour produced an in-flight nav
response (Phase 4.2.c). All four share the same dispatch
shape (per-server
Vec<Location>merge + dedup + jump-or- list) – the kind only changes the LSP method called and the user-facing “no X found” echo. - Modal
State - The vim modal state of a buffer — which grammar a keystroke is read in.
- Pane
Direction <C-w>h/j/k/lcardinal navigation. Geometry-aware: walks the tree to find the spatial neighbour of the active pane.- Position
Source - Pull
Diagnostics Outcome - 4.4.j: in-flight
textDocument/diagnosticrequest outcome.Fullmeans “here are the diagnostics” (apply to the layer);Unchangedmeans “nothing moved since the previousresult_id” (no-op on the layer, just refresh the cache’s version).Emptyis the “no server / cancelled / error” path – still seats a cache entry with the current version so the pump doesn’t re-fire on the next tick without an actual edit. - References
Outcome - Result of a
gr(LSP references) request. Carries the symbol-under-cursor verbatim so the rendered help buffer’s title readsReferences for "foo"and the user has confirmation of what they searched for. - Rename
Outcome - Outcome of a
:renamerequest. The success arm pre-flattens the WorkspaceEdit into a per-fileVec<TextEdit>map so the App-side apply path doesn’t have to walk lsp-types’ enum shapes.NoProviderechoes when no attached server advertisesrenameProvider;NotRenameablewhen prepareRename refused;Emptywhen the rename succeeded but the server returned no edits. - Scroll
Pos - Vim’s
zz/zt/zbpost-scroll cursor target. Re-export of thelattice_grammardefinition (seeViewportPosabove for rationale). Vim’szz/zt/zbtarget positions: where in the viewport the cursor’s current line should sit after the scroll. App-side concept hosted alongsideViewportPosfor the same dependency reason. Slice 8.i.2.c hoist. - Selection
Range Outcome - 4.4.e: outcome of an in-flight
textDocument/selectionRangerequest. The drain consumes one of these per response and either seats the chain intoApp::lsp_selection_chainor surfaces an error echo.pending_stepcarries whether the triggering invocation was:lsp-expand-regionor the first step of:lsp-shrink-region(the latter is rare – shrink without an existing chain is a user error – but the drain handles it uniformly). - Selection
Range Step - Semantic
Tokens Outcome - 4.4.h: in-flight
semanticTokens/fullrequest outcome. 4.4.i extends with theDeltavariant for thefull/deltapath. - Signature
Help Outcome - Outcome of a
textDocument/signatureHelprequest. The response carries multiple signatures (one per overload) plus the active signature/parameter indices. We collapse to the active overload + parameter highlight for the popup body. - Symbols
Outcome - Outcome of a document-symbol / workspace-symbol request – drained per frame and either opens a picker or echoes.
- Viewport
Pos - Vim’s
H/M/Lcursor target within the visible viewport. Slice 8.i.2.c hoisted the type intolattice_grammar::app_effectsoAppEffect::JumpViewportcan carry it; this is a re-export so existingcrate::app::ViewportPoscallers stay compiling. Vim’sH/M/Ltarget positions: where in the visible viewport the cursor lands. App-side concept hosted here soAppEffect::JumpViewport(ViewportPos)can carry the typed payload withoutlattice-ui-tuihaving to dance through a dependency cycle. Slice 8.i.2.c hoist; the App’s previouscrate::app::ViewportPosbecomes apub usere-export of this type.
Constants§
- LIVE_
PICKER_ DEBOUNCE - Debounce window before a live picker’s query change
fires
on_query_changed. Telescope uses ~150ms; chosen so that burst keystrokes coalesce into one source call without feeling laggy. Constant lives here (not inlattice-picker) because debounce is host policy – the picker primitive is renderer- and timer-agnostic. - LSP_
COMPLETION_ KIND_ ID - CSM.8b:
LspCompletionMeta+LSP_COMPLETION_KIND_IDmoved intolattice-lsp::completionso the type lives in the crate that owns lsp-types. The host imports them via this re-export – candidate payloads carry the serde- encoded form directly (encode_meta/decode_meta) so the parallelApp.insert_completion_lsp_metasidecar is gone; the candidate IS the metadata.Extension::kind_iddiscriminant for LSP-sourced candidates. Values 0-99 reserved for first-party host data (snippet uses 2); plugins use 1000+. The host’s decoder checks this id before attemptingdecode_meta. - SNIPPET_
COMPLETION_ KIND_ ID Extension::kind_iddiscriminant for snippet-sourced candidates (Phase 4.2.g.4). Sidecar metadata lives inApp.insert_completion_snippet_meta.
Functions§
- apply_
semantic_ token_ edits - 4.4.i: apply a server-issued
SemanticTokensEditscript toraw_datain place. Each edit specifies a start index (into the previous token vec), a count to delete, and a replacement slice. Edits are applied in order; the server constructs them against the index space of the input vec. - decode_
meta - CSM.8b:
LspCompletionMeta+LSP_COMPLETION_KIND_IDmoved intolattice-lsp::completionso the type lives in the crate that owns lsp-types. The host imports them via this re-export – candidate payloads carry the serde- encoded form directly (encode_meta/decode_meta) so the parallelApp.insert_completion_lsp_metasidecar is gone; the candidate IS the metadata. Decode a payload produced byencode_meta. ReturnsNonewhen the bytes don’t deserialise asLspCompletionMeta– the candidate’skind_idmay not be ours (plugin source colliding on the kind, or a stray payload), or the wire format may have drifted across versions. - decode_
semantic_ tokens - 4.4.h: decode the LSP semantic-tokens stream into
absolute-position tokens. Format per LSP §3.17.6: each
token’s
delta_lineis relative to the previous token’s line;delta_startis relative to the previous token’s start when on the same line, otherwise relative to column 0.token_types/token_modifiersare the server’s legend slices – indexes outside the legend are dropped (defense-in-depth; real servers don’t emit out-of-range). - encode_
meta - CSM.8b:
LspCompletionMeta+LSP_COMPLETION_KIND_IDmoved intolattice-lsp::completionso the type lives in the crate that owns lsp-types. The host imports them via this re-export – candidate payloads carry the serde- encoded form directly (encode_meta/decode_meta) so the parallelApp.insert_completion_lsp_metasidecar is gone; the candidate IS the metadata. Encodemetato a JSON byte blob suitable for embedding in aRawCandidate’sCandidateData::Extension::payload. Errors are caller-handled by panicking in the source’s produce path – a serialize failure on lsp-types output indicates a malformed item, which the supervisor should have rejected before we got here.