Skip to main content

Module app

Module app 

Source
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 – every lattice-lsp consumer (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 – q recording / @ 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
CodeActionRow
One row of a code-action picker. Carries the action title, kind glyph, and the original CodeAction payload (or its raw Command-only form). Action items survive on the App across the request → picker accept gap so the resolve / apply path can read them by index.
CommandInvocation
One call of one command, with every grammar slot vim can fill: the unified call type every front-end produces and execute consumes (DESIGN.md §5.2.1).
CompletionItemRow
One row of an LSP completion picker. Carries the item label, kind glyph, optional detail blurb, and the insert text. replace_range is the byte range in the active line to splice the insert text over.
CompletionResolveOutcome
Drain payload for completionItem/resolve (Phase 4.2.g.3). CSM.8b.5: meta_index is 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.
CompletionState
Cmdline completion popup state: candidates and selection. Renderer-agnostic; the renderer reads this via &App.
DecodedSemanticToken
4.4.h: one decoded LSP semantic token, expanded from the server’s relative-position varint encoding into absolute positions. token_type is the canonical name from the server’s legend (e.g. "keyword", "function") so the renderer can pick a style without looking up the index. length is in utf-16 code units per the LSP spec.
DocumentHighlightCache
4.4.e: cached textDocument/documentHighlight result anchored at a specific buffer + cursor position. The renderer reads highlights to paint a soft overlay; the pump compares cursor to the live cursor to decide whether to invalidate and re-request.
EchoMessage
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::CommandLineCompleteOrAdvance when the user presses Tab; consumed by accept / dismiss / scroll actions. One contiguous fold range in a document buffer.
InFlightLiveQuery
Spawned on_query_changed future / stream paired with the query it was launched against. The drain compares launched_for_query against 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.
LastFind
The last f / F / t / T the user ran, which is all ; and , need to repeat it.
LastSearch
VM.3d-2: the last completed / / ? / * / # search, which is all n and N need to repeat it. Moved here from the host (which re-exports the name, so no call site moved) for the reason LastFind was: n is a motion now, and the state it repeats has to reach the grammar.
LastVisual
Most-recently-completed visual selection. Used by gv to reselect.
LivePickerQueryState
Live-picker query state. Installed when open_picker resolves a source whose spec().live is true; survives until the picker is dismissed.
LspCodeLensCache
4.5.d: per-buffer cache of textDocument/codeLens results. Filled by the pump on doc-version change or after workspace/codeLens/refresh evicts the entry. The :lsp-code-lens picker reads the cache; accept routes the chosen lens’s command through workspace/executeCommand.
LspCompletionMeta
CSM.8b: LspCompletionMeta + LSP_COMPLETION_KIND_ID moved into lattice-lsp::completion so 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 parallel App.insert_completion_lsp_meta sidecar is gone; the candidate IS the metadata. LSP-sourced insert-completion candidate metadata. Carried inside the RawCandidate’s CandidateData::Extension payload as a JSON blob (see encode_meta / decode_meta). Replaces the pre-CSM.8b host-side sidecar – the candidate IS the metadata; the host decodes on demand.
LspDocumentColorCache
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.
LspDocumentLinksCache
4.5.c: per-buffer cache of textDocument/documentLink responses. Filled by the pump on document-version change; gx walks the entries looking for the first link whose range covers the cursor and follows it. Cache invalidates when the version changes.
LspFoldsCache
4.4.f: cached textDocument/foldingRange response for one buffer. document_version is the buffer’s lattice_core::Document version at the time the request was issued; the pump compares it against the live version to decide whether to refresh.
LspInlayHintCache
4.4.g: cached textDocument/inlayHint response for one buffer. Keyed on (BufferId, document_version); pump invalidates when the version changes. hints are sorted by position so the renderer can stop scanning once it walks past the current line.
LspPullDiagnosticsCache
4.4.j: cached textDocument/diagnostic state per buffer. result_id is what the server issued on the previous response; threading it back in previous_result_id lets the server answer Unchanged when nothing moved.
LspSelectionChain
4.4.e: cached textDocument/selectionRange chain anchored at a specific buffer + cursor position. Flat Vec<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-region after a fresh cursor; reused across subsequent expand/shrink steps until the cursor moves outside ranges[0] (the innermost) or the buffer changes.
LspSemanticTokensCache
4.4.h: cached decoded textDocument/semanticTokens/full response 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.
MacroRecording
In-progress macro recording. q<reg> starts; q again stops and persists into the register table.
MessagePushed
Typed event published on the editor’s event bus whenever set_message runs. 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.
MessageRecord
One historical minibuffer echo. The renderer’s *messages* buffer renders these in chronological order; each set_message call appends one record. Cheap to clone (bounded text) – producers fan the same record out to every subscriber on the typed event bus.
MessagesRing
Bounded chronological ring of every echo the editor has emitted. Push on every set_message; snapshot on demand for :messages open and live refresh. Capacity is fixed at construction; once full, the oldest entry drops to make room for the newest.
OptionCache
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_cache after every :set.
PendingBlockInsert
In-flight blockwise-visual insert (I or A).
PendingPickerInit
In-flight async picker init. The future from PickerSourceGenerator::init is spawned on the LSP runtime; its resolved batch lands here via rx. The cancel token lets a subsequent :picker <source> drop the predecessor before it completes.
PositionEntry
One entry in the unified position history (DESIGN.md §5.1.1).
PrevPaneState
Snapshot of the active pane’s state captured just before help took it over. Used by dismiss_popup to restore the user to the buffer + cursor + scroll they came from. The same struct serves both display modes (in-pane and popup-overlay).
ReplaceEntry
One replace-mode entry – the byte that was at at before the overwrite, so <BS> can restore it. original = None means the overwrite extended the line (the position was past EOL); <BS> deletes the inserted char rather than restoring a byte.
SearchLine
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 via Editor::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 the origin cursor preserved so <Esc> restores it and incremental preview anchors its search.
SnippetCandidateMeta
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.
SubstitutePreview
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.
SymbolRow
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.
TagStackEntry
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”.
UnnamedRegister
The unnamed register’s payload. v1 uses a single global slot; the full vim register zoo ("a-z, "+, "*, etc.) lands later.

Enums§

Action
AsyncCompletionOutcome
OR.7: drain payload for the async completion fan-out — every AsyncCompletionSource the active modes contribute, not only LSP’s.
CodeActionOutcome
Outcome of a :code-actions request. Drained per frame.
CodeLensOutcome
4.5.d: outcome of an in-flight textDocument/codeLens request. Carries the server id so the eviction-on-refresh path can match by server.
CompletionOutcome
Outcome of a textDocument/completion request.
DocumentColorOutcome
4.5.e: outcome of an in-flight documentColor request.
DocumentHighlightOutcome
4.4.e: in-flight documentHighlight request outcome.
DocumentLinksOutcome
4.5.c: outcome of an in-flight textDocument/documentLink request. Empty for server responses that returned no links (still updates the cache so we don’t keep re-issuing for the same version).
EchoLevel
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.
FindKind
f / F / t / T — which direction, and whether the target character is included.
FoldMethod
One open completion popup (DESIGN.md §5.11.3 vertico-style rendering). Built by Action::CommandLineCompleteOrAdvance when 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.
FoldingRangeOutcome
4.4.f: in-flight foldingRange request outcome.
FormatOutcome
Outcome of a :format / :format-range request. Drained per frame; the App applies the edits as one undo unit or echoes the appropriate failure / no-op state.
HoverOutcome
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 on K.
InlayHintOutcome
4.4.g: in-flight inlayHint request outcome. The requested_*_line pair 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).
LspNavKind
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.
ModalState
The vim modal state of a buffer — which grammar a keystroke is read in.
PaneDirection
<C-w>h/j/k/l cardinal navigation. Geometry-aware: walks the tree to find the spatial neighbour of the active pane.
PositionSource
PullDiagnosticsOutcome
4.4.j: in-flight textDocument/diagnostic request outcome. Full means “here are the diagnostics” (apply to the layer); Unchanged means “nothing moved since the previous result_id” (no-op on the layer, just refresh the cache’s version). Empty is 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.
ReferencesOutcome
Result of a gr (LSP references) request. Carries the symbol-under-cursor verbatim so the rendered help buffer’s title reads References for "foo" and the user has confirmation of what they searched for.
RenameOutcome
Outcome of a :rename request. The success arm pre-flattens the WorkspaceEdit into a per-file Vec<TextEdit> map so the App-side apply path doesn’t have to walk lsp-types’ enum shapes. NoProvider echoes when no attached server advertises renameProvider; NotRenameable when prepareRename refused; Empty when the rename succeeded but the server returned no edits.
ScrollPos
Vim’s zz / zt / zb post-scroll cursor target. Re-export of the lattice_grammar definition (see ViewportPos above for rationale). Vim’s zz / zt / zb target positions: where in the viewport the cursor’s current line should sit after the scroll. App-side concept hosted alongside ViewportPos for the same dependency reason. Slice 8.i.2.c hoist.
SelectionRangeOutcome
4.4.e: outcome of an in-flight textDocument/selectionRange request. The drain consumes one of these per response and either seats the chain into App::lsp_selection_chain or surfaces an error echo. pending_step carries whether the triggering invocation was :lsp-expand-region or 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).
SelectionRangeStep
SemanticTokensOutcome
4.4.h: in-flight semanticTokens/full request outcome. 4.4.i extends with the Delta variant for the full/delta path.
SignatureHelpOutcome
Outcome of a textDocument/signatureHelp request. 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.
SymbolsOutcome
Outcome of a document-symbol / workspace-symbol request – drained per frame and either opens a picker or echoes.
ViewportPos
Vim’s H / M / L cursor target within the visible viewport. Slice 8.i.2.c hoisted the type into lattice_grammar::app_effect so AppEffect::JumpViewport can carry it; this is a re-export so existing crate::app::ViewportPos callers stay compiling. Vim’s H / M / L target positions: where in the visible viewport the cursor lands. App-side concept hosted here so AppEffect::JumpViewport(ViewportPos) can carry the typed payload without lattice-ui-tui having to dance through a dependency cycle. Slice 8.i.2.c hoist; the App’s previous crate::app::ViewportPos becomes a pub use re-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 in lattice-picker) because debounce is host policy – the picker primitive is renderer- and timer-agnostic.
LSP_COMPLETION_KIND_ID
CSM.8b: LspCompletionMeta + LSP_COMPLETION_KIND_ID moved into lattice-lsp::completion so 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 parallel App.insert_completion_lsp_meta sidecar is gone; the candidate IS the metadata. Extension::kind_id discriminant 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 attempting decode_meta.
SNIPPET_COMPLETION_KIND_ID
Extension::kind_id discriminant for snippet-sourced candidates (Phase 4.2.g.4). Sidecar metadata lives in App.insert_completion_snippet_meta.

Functions§

apply_semantic_token_edits
4.4.i: apply a server-issued SemanticTokensEdit script to raw_data in 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_ID moved into lattice-lsp::completion so 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 parallel App.insert_completion_lsp_meta sidecar is gone; the candidate IS the metadata. Decode a payload produced by encode_meta. Returns None when the bytes don’t deserialise as LspCompletionMeta – the candidate’s kind_id may 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_line is relative to the previous token’s line; delta_start is relative to the previous token’s start when on the same line, otherwise relative to column 0. token_types / token_modifiers are 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_ID moved into lattice-lsp::completion so 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 parallel App.insert_completion_lsp_meta sidecar is gone; the candidate IS the metadata. Encode meta to a JSON byte blob suitable for embedding in a RawCandidate’s CandidateData::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.