Expand description
Renderer-neutral action dispatch.
Phase 5.5 / slice 5.5.A scaffolding. After Phase 5.4 closed the
input side (every KeyEvent → KeyChord → Action path lives in
lattice-host), this module is the seam for the output side
that 5.5 fills in: Action → state mutation. The renderer’s
App::apply body (today ~2.6k LoC of match action { ... }
in lattice-ui-tui::app::dispatch) will relocate here
sub-slice by sub-slice as 5.5.B → 5.5.H land.
§Why this module exists today
5.5.A defines the surface (Editor::dispatch +
DispatchOutcome + RendererSignal) but leaves the body
empty. Renderer App structs keep doing all the work in their
own apply paths; the stub is a no-op. Fixing the public shape
up front means subsequent sub-slices are mechanical moves
rather than design decisions, and the future lattice-ui-gpui
has a stable function signature to compose against from day one.
Sub-slices populate the stub:
- 5.5.B – macro-recording capture, partial-chord clear, read-only-help guard (the preamble).
- 5.5.C – simplest
match action { ... }arms (mutateeditorfields directly with no helper call). First emission ofRendererSignal::Quit. - 5.5.D – pure-editor-mutation helpers (
clamp_cursor_to_buffer,ensure_cursor_visible,dismiss_popup, …). - 5.5.E – ex-command effect handlers (the ~60-variant
apply_effecttable + thedo_*family). First emission ofRendererSignal::ThemeChanged(fromEffect::SetOptiononui.*keys). 5.5.E.1 scaffoldedEditor::handle_effectand migrated the three helper-free arms (Effect::None,Effect::ClearSearchHighlight,Effect::Echo); 5.5.E.2 adds the echo-only ex-command listers (Effect::EchoMarks->Editor::do_list_marks,Effect::EchoRegisters->Editor::do_list_registers) and co-moves thepreview_registerfree fn; 5.5.E.3 migratesstore_yanktoEditor::store_yank, unlockingEffect::Yank; 5.5.E.4 migratesset_selections_blocking+publish_selections_changedtoEditorandvisual_kind_to_modeto a host-side free fn, unlockingEffect::SelectionChange. Subsequent E.* sub-slices land the remaining helper-bearing arms as theirdo_*bodies move host-side; theapply_edit_blocking/handle_editscluster is gated on the render-coupledshift_highlights_for_editcache and follows the visible-highlights slice (out of scope per 5.5 design doc). - 5.5.F – mode-lifecycle helpers
(
do_open_file_tree/do_open_oil/do_open_hover/ …). - 5.5.G – final remnants;
App::applycollapses to the dispatch call + signal-handling wrapper. - 5.5.H – render-coupled cleanup; removes now-vestigial
Appmethods.
Focused design doc:
docs/dev/architecture/phase-5-dispatch-extraction.md.
Structs§
- Dispatch
Outcome - Result of
Editor::dispatch. Carries the renderer-side side-effects the caller must surface after the host-side state mutation completes. - Display
Buffer Request - 5.5.F.1: payload for
RendererSignal::DisplayBuffer. Carries thelattice_help::HelpContentthe host built + the category the renderer should dispatch it under. The renderer resolves the category to alattice_core::ui::display::BufferDisplay(via its existingresolve_display) and routes through the matching surface. Why a struct instead of inline variant fields: keeps theRendererSignalvariant size constant when more host-sidedo_*arms migrate over and want to attach side-channel data (e.g. a buffer-id token the renderer should mirror into its registry on completion); subsequent slices can add fields here without churning everyRendererSignalmatch arm in every renderer. - Effective
Completion Config - Effective insert-completion config for a given language.
Materialised by
Editor::effective_completion_forfrom the per-language overrides + global typed options + spec fallbacks. Phase 5.8.AD.4: hoisted from TUI App. - Fused
Dispatch - Slice I.7 — result of
Editor::dispatch_fused, the one-round-trip keystroke apply. - Host
Display Resolver - VM.3g-2: the display geometry
gj/gk/g0/g$read. Owned, because it crosses the actor — so it carries the wrap width plus row counts for a BOUNDED window of lines around the cursor, never the whole buffer. A display motion walks at mostcountlines, so that window is all it can reach; a line outside answers 1, which is what an unwrapped line occupies anyway. - Host
Fold Resolver - VM.3i:
zj/zk’s view of the fold table for one dispatch. - Plugin
Meta - PI.2: prose / short labels for a plugin-API interface’s world-derived
direction and host-authored capability, rendered by the
:describe-plugin-api/:list-plugin-apisbuilders. PI.4: resolved metadata for one loaded plugin. Thedocis the plugin’s OWN documentation (its embedded WIT world doc-comment, or its manifestdocfield) — both fixed at the plugin’s build/package time, so it is extracted ONCE at load and kept here, never re-fetched per:describe-plugin(a component’s embedded WIT is immutable at editor runtime; only a reload re-extracts, PH7.12). - Plugin
Meta Registry - PI.3/PI.4: host-side plugin-id →
PluginMetamap. Backs both provenance display (SourceLayer::Plugin(id)renders as the manifest name, else<plugin:id>) and the loaded-plugin introspection surfaces (:describe-plugin/:list-plugins). A newtype (not a bareRwLock<HashMap>) so theServiceRegistryTypeIdcan’t collide. Registered empty at boot; the Phase-8 plugin loader is the populator (none exists yet — the seam is ready).RwLockgives the interior mutability a post-boot populate needs behind the sharedArc<ServiceRegistry>. - Snapshot
Indent Resolver - IN.7: answers “how deep should line N sit” for the
=operator, over a syntax snapshot captured at dispatch time.
Enums§
- Completion
Compute Error - 5.5.G.23.cmdline: error variants for
Editor::compute_completion_state. Routed throughrefresh_completion_popupto decide whether to keep the popup open with zero candidates (NoMatches) or drop it entirely (other variants — slot moved to a no-completion region). - DoEdit
Outcome - Outcome of
Editor::do_edit. Renderer peers translate each variant into the appropriate side-effect: - External
Format Outcome - IN.8b: what an external formatter run produced, carried back to the editor thread.
- Indent
Source - IN.2: where predictive indent gets its answer for one keystroke.
- Renderer
Signal - Host-to-renderer side-effect signal.
Constants§
- POSITION_
HISTORY_ CAP - 5.5.F.4.2: position-history ring cap, co-located with
Editor::push_position_history. - SNIPPET_
COMPLETION_ KIND_ ID Extension::kind_iddiscriminant for snippet-sourced candidates (Phase 4.2.g.4). Sidecar metadata decoded byEditor::snippet_meta_for. Phase 5.8.AD.4: hoisted from TUI App.
Functions§
- accept_
action_ to_ outcome - Slice
3c.unify.picker-registry-cutover(7d.0). Translate a unifiedlattice_completion::AcceptActioninto the host-internallattice_picker::PickerAcceptOutcomefor dispatch. The two enums encode the same concept; this fn is the bridge until 7e+ retires PickerAcceptOutcome entirely. - accept_
action_ to_ routing_ payload - Slice
3c.unify.picker-registry-cutover(7d.1). Inverse ofaccept_action_to_outcome: build aRoutingPayloadthat encodes the MRU identity of anAcceptAction. Used when seating an engine-shapeSourceRegistration(PreSupplied) into the Picker’s pair-based candidate store — the picker needs ONE routing per candidate for MRU bonus computation, even though the 7d.0 accept dispatch readsaccept_actiondirectly. - action_
escapes_ focused_ popup - PIC.2: actions that escape / reconfigure the world BEHIND a focused
popup overlay — tab navigation and pane-tree management. A focused
popup (State B) is an exclusive overlay surface, not a pane you can
navigate away from; these chords are consumed while it holds focus so
a stray
gt/<C-w>…doesn’t refocus or reshape the buffer behind it (and, forgt, drag the single-slot popup overlay onto the new tab). Motions / scroll / search / dismiss / follow-link are NOT here — they keep operating on the popup’s own cursor. - action_
is_ document_ mutation - doesn’t fall through onto the underlying document.
- action_
mutates_ fold_ state - Returns
truefor actions that mutate the document and therefore must no-op when a read-only help buffer holds focus. - autoread_
watch_ fingerprint - AR.3: order-independent fingerprint of a desired watch set, used as the
cheap “did the set change since the last sync?” gate. Sorting the dirs
and each dir’s basenames makes the hash independent of
HashMapiteration order. - bound_
watch_ set - AR.3: bound a desired watch set to at most
capdirectories. The active buffer’s directory is always kept (the file you’re looking at must be watched); the remainder fill deterministically (sorted) up tocap. Excess dirs are dropped — their buffers rely on the on-activatestatfallback. Pure, so the cap logic is unit-testable without a filesystem. - choose_
indent_ source - The staleness decision (design §5), as a pure function so the policy is testable without racing the reparse worker.
- dedup_
rendered_ by_ text - 5.5.G.23.insert-prep: cross-source visual dedup for the
insert-completion popup (Phase 4.2.g.7 polish). Keeps the FIRST
occurrence of each
raw.text; subsequent rows with the same text drop out. Called after the ranker has sorted descending by score, so the surviving row is the highest-ranked entry per text — the buffer-words copy ofouteroutranks the tree-sitter copy at the spec’s 100/80 priority split, so the popup row foroutercarries the buffer-words tag. - effect_
mutates - 5.5.G.23: True if the Effect produced a buffer mutation. Used by
dot-repeat to decide whether to record the invocation — yank-only
invocations (vim’s
y) are NOT eligible for., only changes. - effect_
mutates_ or_ yanks - 5.5.G.23: True if the Effect indicates an operator-class action (the buffer changed or content was yanked). Used by Visual mode to decide whether to auto-exit after the dispatch — motions in Visual should not exit; d / y / c should.
- flatten_
workspace_ edit - Flatten a
WorkspaceEditinto a per-fileVec<(Uri, Vec<TextEdit>)>. Handles both the legacychangesmap and the moderndocument_changesshape (create/rename/delete ops skipped in v1; rename returns plain text edits for ~100% of identifier rewrites). - folding_
range_ to_ fold - Translate an LSP
FoldingRangeinto the renderer-neutralFoldshape. Hashes (start_line, end_line, kind) for stable fold identity across re-fetches. Phase 5.8.AA.i: hoisted fromlattice-ui-tui::app::folding_range_to_fold. - normalize_
user_ path - Tilde-expand + absolutise a user-typed path.
~/rest→$HOME/rest;~alone →$HOME; relative paths join the current working directory. Phase 5.8.AA.j: hoisted fromlattice-ui-tui::app::normalize_user_pathso the host-sidedo_editmigration can reach it without dragging in the App. - normalize_
user_ path_ with_ cwd - Like
normalize_user_pathbut uses the providedcwdfor relative path resolution instead ofstd::env::current_dir(). - picker_
action_ for - Translate a first-party source id into the
PickerActiontag. Phase 5.8.AA.n: hoisted from TUI App. - picker_
buffer_ entry - Translate a host-side
crate::buffer_registry::BufferEntryinto the picker’s renderer-agnosticlattice_picker::BufferEntry. Pure function on the input + buffer-locals map; called byEditor::build_picker_contextfor every registry entry. - picker_
source_ for - Translate a first-party source id into the
PickerSourcetag the picker primitive stores. Phase 5.8.AA.n: hoisted from TUI App; pure constant mapping. - prefer_
aliases_ for_ command_ candidates - 5.5.G.23.cmdline: rewrite command candidates from canonical names
to user-facing aliases. The parser accepts both forms; this is a
pure UX rewrite. Public so the App-side test module’s existing
prefer_aliases_*tests can call it through the re-export. - preview_
register - Render a register’s content into a one-line preview (truncated
and with newlines escaped). Used by
Editor::do_list_registers(:reg) and the picker-source register listing. Moved here fromlattice-ui-tui::appin 5.5.E.2. - raw_
buffer_ candidates - Build the buffer-picker candidate set: every entry in the
registry, with the active buffer floated to the bottom and
tagged
(current)in marginalia. Free function because the picker module is renderer-agnostic; this composes bothRawCandidate+RoutingPayloadshapes against aBufferRegistryborrow. Phase 5.8.AC.1: hoisted from TUI App. - visual_
kind_ to_ mode - Project a grammar
VisualKindonto the protocol-sideVisualMode. Used when constructing aSelectionfrom a modal-state visual mode so the document actor’s selection set carries the visual flavour. Moved here fromlattice-ui-tui::app::visualin 5.5.E.4 alongside theEffect::SelectionChangearm.