pub enum AppEffect {
Show 166 variants
Quit,
Cancel,
FormatRange {
intent: FormatIntent,
start_line: u32,
end_line: u32,
},
MatchBracket,
ToggleCaseAtCursor,
OpenLineBelow,
OpenLineAbove,
SearchNext,
SearchPrevious,
JumpHistoryBack,
JumpHistoryForward,
PaneHistoryBack,
PaneHistoryForward,
WalkMarkHistoryBack,
WalkMarkHistoryForward,
TagStackPop,
OpenFoldAtCursor,
CloseFoldAtCursor,
ToggleFoldAtCursor,
OpenAllFolds,
CloseAllFolds,
CycleFoldAtCursor,
CycleFoldsGlobal,
GotoParentFold,
DeleteFoldAtCursor,
OpenFoldsRecursively,
CloseFoldsRecursively,
DeleteFoldsRecursively,
GotoNextFold,
GotoPrevFold,
ToggleFoldEnable,
Undo,
Redo,
RepeatLastChange,
PageDown,
HalfPageDown,
HalfPageUp,
PageUp,
ScrollLineUp,
ScrollLineDown,
RedrawScreen,
OpenCommandPicker,
OpenHistoryPicker,
OpenSearchHistoryPicker,
EnterCommandLine,
CommandLineSubmit,
CommandLineCancel,
CommandLineHistoryPrev,
CommandLineHistoryNext,
CommandLineComplete,
CommandLineCompletePrev,
CommandLineDescribeUnderCursor,
CommandLineToggleExpand,
SearchLineSubmit,
SearchLineCancel,
SearchLineBackspace,
SearchLineHistoryPrev,
SearchLineHistoryNext,
SearchLineToggleExpand,
PromptLineSubmit,
PromptLineCancel,
OilNavigateUp,
ReselectLastVisual,
SwapVisualEnds,
PasteAfter,
PasteBefore,
EnterAppend,
EnterInsertFirstNonBlank,
EnterAppendEndOfLine,
DisplayLineDown,
DisplayLineUp,
DisplayLineStart,
DisplayLineEnd,
CreateFoldFromVisual,
CreateFold {
start_line: u32,
end_line: u32,
},
DeleteCharBackward,
InsertLineEdit(InsertLineEdit),
CompletionTrigger,
ExitVisual,
ReplaceUndoLast,
EnterMode(ModalState),
EnterVisual(VisualKind),
EnterSelect(VisualKind),
EnterSearch(SearchDirection),
SearchWordUnderCursor(SearchDirection),
JumpViewport(ViewportPos),
ScrollCursorTo(ScrollPos),
HorizontalScroll(HScroll),
JoinLines {
with_space: bool,
},
FindRepeat {
reverse: bool,
},
InsertNewline,
InsertTab,
OverwriteChar(char),
SetMark(char),
JumpToMarkLine(char),
JumpToMarkExact(char),
SelectRegister(Register),
StartMacroRecord(char),
PlayMacro(char),
PlayLastMacro,
AbsorbOperatorPrefix(OperatorId),
SplitPaneHorizontal,
SplitPaneVertical,
ClosePane,
OnlyPane,
ToggleZoomPane,
NavigatePane(PaneDirection),
NextPane,
PrevPane,
NextTab,
PrevTab,
GoToTab(u32),
NewTab,
NewTabAt(String),
TerminalSpawn(Option<String>),
TerminalSpawnInNewTab(Option<String>),
MovePaneToNewTab,
CloseTab,
OnlyTab,
MoveTab(u32),
PickerAcceptInSplit,
PickerAcceptInVSplit,
PickerAcceptInTab,
EqualizePanes,
GrowPaneHeight,
ShrinkPaneHeight,
GrowPaneWidth,
ShrinkPaneWidth,
CompletionNext,
CompletionPrev,
CompletionAccept,
CompletionCancel,
CompletionCancelAndExitInsert,
CompletionToggleDocs,
CompletionDocsScrollDown,
CompletionDocsScrollUp,
CompletionAcceptThenInsert(char),
InsertRegister(char),
OpenYankPicker,
OpenArgPicker,
SnippetNextPlaceholder,
SnippetPrevPlaceholder,
CompletionFilterToSource(String),
CompletionFilterClear,
DiffGet,
DiffPut,
TutorAdvance,
TutorRetreat,
MultibufferExpand {
delta: i32,
},
NarrowTrigger {
range: Option<Range>,
},
NarrowWiden,
NarrowLines {
start_line: u32,
end_line: u32,
},
SearchTrigger {
query: String,
},
SearchRefresh,
CompileRun {
cmdline: Option<String>,
},
CompileJumpToLocation {
path: PathBuf,
line: u32,
col: u32,
},
ErrorNav {
target: ErrorTarget,
},
SetErrorList {
source: ErrorSource,
write: ErrorWrite,
entries: Vec<ErrorEntry>,
},
CompilationGutterSet {
buffer: u32,
entries: Vec<(u32, ErrorSeverity)>,
},
CompilationLocationLines {
buffer: u32,
lines: Vec<(u32, u32, u32)>,
},
CompilationThemeColors {
bg: u32,
fg: u32,
},
CompilationKill,
ProblemsOpen,
ProblemsClose,
ProblemsRefresh,
OpenProviderView {
provider: String,
args: Args,
},
}Expand description
Variants are added incrementally during slice 8.i as each
historical Action variant is promoted from the legacy
bind_legacy bridge to a typed CommandInvocation.
Eq was dropped in PV.1 (2026-08-12), deliberately.
Self::OpenProviderView carries an Args so a
provider’s trigger keeps the typed multi-argument shape the : line
and transient rows already produce — and Args reaches
ArgValue::Invocation(Box<CommandInvocation>), which is PartialEq
but not Eq. The alternative (flattening the payload to a
Option<String>) would force every provider to re-encode structured
arguments as strings, which is the stringly-typed weakness the seam
exists to avoid. Nothing keys a map or set on AppEffect; PartialEq
is what the round-trip tests use.
Variants§
Quit
Graceful editor shutdown. The App’s apply_effect arm
publishes Event::BeforeQuit and sets the should_quit
flag the runtime polls between frames; matches today’s
Action::Quit semantics exactly. (8.i.0 smoke variant.)
CM.3d (2026-07-22) unbound the <C-c> → quit hatch that
used to be hardcoded in input.rs; action:quit is now
reachable only through :q and user bindings.
Cancel
CG.1: foreground cancellation. Flips the token of the
in-flight user-initiated async op (search scan, LSP command,
plugin call) and snaps the editor back to a stable Normal
state. Bound to <C-c> at the Builtin layer in every mode,
and to <C-g> by emacs-keys-mode.
Idempotent: with no op armed it degrades to the mode reset
alone, which is why it is safe as a universal binding.
See docs/dev/architecture/cancellation.md.
FormatRange
RF.5b: an operator resolved its range and the buffer’s chain for that intent says a non-native provider owns it.
The operator cannot run one itself — an LSP round-trip and a
process spawn are both asynchronous, and the grammar layer has
neither client nor runtime. So it returns the range and the host
resolves the chain, exactly as SearchTrigger hands back a query
rather than running a search.
Emitted only when the winning rung is not native, so the
default configuration never produces one and the common path is
unchanged.
Fields
intent: FormatIntentWhich kind of formatting the operator asked for (= indent,
gq reflow, g= reformat); selects the provider chain.
MatchBracket
Vim’s %. Jumps to the bracket / brace / paren matching
the one at-or-after the cursor on the current line.
Promoted from Action::MatchBracket in slice 8.i.1.a.
ToggleCaseAtCursor
Vim’s ~. Toggles the case of the char at the cursor and
advances by one byte. Promoted from
Action::ToggleCaseAtCursor in slice 8.i.1.a.
OpenLineBelow
Vim’s o. Opens a new line below the current line and
enters Insert. Promoted from Action::OpenLineBelow in
slice 8.i.1.a.
OpenLineAbove
Vim’s O. Opens a new line above the current line and
enters Insert. Promoted from Action::OpenLineAbove in
slice 8.i.1.a.
SearchNext
Vim’s n. Re-runs the last search forward. Promoted from
Action::SearchNext in slice 8.i.1.b.
SearchPrevious
Vim’s N. Re-runs the last search in the reverse
direction. Promoted from Action::SearchPrevious in
slice 8.i.1.b.
JumpHistoryBack
Vim’s <C-o>. Walk one step backward through the position
history (DESIGN.md §5.1.1). Promoted from
Action::JumpHistoryBack in slice 8.i.1.b.
JumpHistoryForward
Vim’s <C-i> / <Tab>. Walk one step forward through the
position history. Promoted from Action::JumpHistoryForward
in slice 8.i.1.b.
PaneHistoryBack
PBH.3: <C-6>. Walk one step back through the active
pane’s buffer trail — the buffers this pane has shown.
Distinct from Self::JumpHistoryBack (<C-o>), which walks
the global position ring at position granularity. This one is
per-pane and moves whole buffers. See
docs/dev/architecture/pane-buffer-history.md.
PaneHistoryForward
PBH.3: <C-7>. Walk one step forward through the active
pane’s buffer trail.
WalkMarkHistoryBack
Vim’s g;. Walk one step backward through the
mark history (oldest -> newest cursor positions in this
buffer). Promoted from Action::WalkMarkHistoryBack in
slice 8.i.1.b.
WalkMarkHistoryForward
Vim’s g,. Walk one step forward through the mark
history. Promoted from Action::WalkMarkHistoryForward in
slice 8.i.1.b.
TagStackPop
Vim’s <C-t>. Pop one entry off the tag stack and jump
back to where the previous gd / <C-]> originated.
Promoted from Action::TagStackPop in slice 8.i.1.b.
OpenFoldAtCursor
Vim’s zo. Open the fold containing the cursor.
Promoted from Action::OpenFoldAtCursor in slice 8.i.1.c.
CloseFoldAtCursor
Vim’s zc. Close the fold containing the cursor.
Promoted from Action::CloseFoldAtCursor in slice 8.i.1.c.
ToggleFoldAtCursor
Vim’s za. Toggle the fold containing the cursor.
Promoted from Action::ToggleFoldAtCursor in slice 8.i.1.c.
OpenAllFolds
Vim’s zR. Open every fold in the buffer.
Promoted from Action::OpenAllFolds in slice 8.i.1.c.
CloseAllFolds
Vim’s zM. Close every fold in the buffer.
Promoted from Action::CloseAllFolds in slice 8.i.1.c.
CycleFoldAtCursor
org-cycle z<Space> / :fold-cycle. Cycle the heading/fold under
the cursor through emacs org-mode’s local states
FOLDED → CHILDREN → SUBTREE.
CycleFoldsGlobal
org-cycle z<Tab> / :fold-cycle-global. Cycle the WHOLE buffer
through OVERVIEW → CONTENTS → SHOW-ALL.
GotoParentFold
zp / :fold-goto-parent. Move the cursor to the parent heading
(one level up the fold hierarchy) — emacs outline-up-heading.
DeleteFoldAtCursor
Vim’s zd. Delete the fold containing the cursor (drop
it from the manual fold table; structure-driven folds
reappear on the next reparse). Promoted from
Action::DeleteFoldAtCursor in slice 8.i.1.c.
OpenFoldsRecursively
VM.3h: vim’s zO. Open every fold containing the cursor line and every
fold nested inside those. In Visual, the same over each selected line.
CloseFoldsRecursively
VM.3h: vim’s zC. Close every fold containing the cursor line, and
nothing nested below the cursor. In Visual, every fold containing a
selected line, enclosing ones included.
DeleteFoldsRecursively
VM.3h: vim’s zD. Delete the innermost fold containing the cursor line
and every fold nested inside it. In Visual, the folds inside the
selection, not the ones that merely enclose it.
GotoNextFold
Vim’s zj. Move cursor to the start of the next fold.
Promoted from Action::GotoNextFold in slice 8.i.1.c.
GotoPrevFold
Vim’s zk. Move cursor to the end of the previous fold.
Promoted from Action::GotoPrevFold in slice 8.i.1.c.
ToggleFoldEnable
Vim’s zi. Toggle the foldenable option (when off,
every line renders flat regardless of any closed flag).
Promoted from Action::ToggleFoldEnable in slice 8.i.1.c.
Undo
Vim’s u. Undo the last buffer change. Promoted from
Action::Undo in slice 8.i.1.d.
Redo
Vim’s <C-r>. Redo the last undone change. Promoted from
Action::Redo in slice 8.i.1.d.
RepeatLastChange
Vim’s .. Repeat the last change (operator + motion +
register + count). Promoted from Action::RepeatLastChange
in slice 8.i.1.d.
PageDown
Vim’s <C-f>. Page-down: scroll the viewport down one
page. Promoted from Action::PageDown in slice 8.i.1.d.
HalfPageDown
VM.3j-2: vim’s <C-d> / <C-u> — scroll the view and the cursor
together by scroll lines (half the window by default). SCROLL
commands, not motions: vim composes no operator with them.
HalfPageUp
Vim’s <C-u>; the upward twin of Self::HalfPageDown.
PageUp
Vim’s <C-b>. Page-up: scroll the viewport up one page.
Promoted from Action::PageUp in slice 8.i.1.d.
ScrollLineUp
Vim’s <C-y>. Scroll viewport up one line (cursor
stays at the same screen position when possible).
Promoted from Action::ScrollLineUp in slice 8.i.1.d.
ScrollLineDown
Vim’s <C-e>. Scroll viewport down one line. Promoted
from Action::ScrollLineDown in slice 8.i.1.d.
RedrawScreen
Vim’s <C-l>. Force a full screen redraw. Promoted from
Action::RedrawScreen in slice 8.i.1.e.
OpenCommandPicker
Vim’s : / Emacs’ M-x. Open the command picker over
all registered ex-commands. If the chosen command has a
required first argument the picker arms the cmdline so
the user can supply it; otherwise executes immediately.
OpenHistoryPicker
MB.3: Vim’s q:. Open the command-line history picker
over command_history. <CR> loads the chosen command
into the : line WITHOUT executing (the user tweaks /
<C-x><C-e> expands, then <CR>s). Fired from an ordinary
buffer’s Normal mode or the expanded tier-2 band’s Normal
mode (where it seeds the picker filter with the in-progress
command-line text).
OpenSearchHistoryPicker
MB.5: q/ / q? — open the search-line history picker.
Accept loads the chosen term into the / search line.
EnterCommandLine
Vim’s :. Enter the command-line minibuffer. Promoted
from Action::EnterCommandLine in slice 8.i.1.e.
CommandLineSubmit
MB.1: <CR> in command-line-mode — submit (or accept the
open completion candidate). Drives the rewired
Editor::do_command_line_submit.
CommandLineCancel
MB.1: <Esc> / <C-c> in command-line-mode — cancel (or
dismiss the open completion popup first).
CommandLineHistoryPrev
MB.1: <C-p> / <Up> in command-line-mode — walk history
backward (or previous completion candidate when the popup is open).
CommandLineHistoryNext
MB.1: <C-n> / <Down> in command-line-mode — walk history
forward (or next completion candidate when the popup is open).
CommandLineComplete
MB.1: <Tab> in command-line-mode — open the completion popup
or advance the selection.
CommandLineCompletePrev
MB.1: <S-Tab> in command-line-mode — previous completion
candidate.
CommandLineDescribeUnderCursor
MB.1: <C-h> in command-line-mode — describe the command / arg
under the cursor.
CommandLineToggleExpand
MB.2: <C-x><C-e> in command-line-mode — toggle the : line’s
expanded tier-2 mini-buffer band (full modal editing in place),
or collapse it back to the one-row readline line for review.
SearchLineSubmit
MB.5a: <CR> on the /·? search line — submit the search
pattern. Resolved from search-line-mode’s Insert keymap.
SearchLineCancel
MB.5a: <Esc> / <C-c> on the /·? search line — cancel
the search and restore the prior editing buffer.
SearchLineBackspace
<BS> on the /·? line: delete a char, or cancel when empty.
SearchLineHistoryPrev
MB.5b: <C-p> / <Up> on the /·? search line — walk to an
older entry in search_history.
SearchLineHistoryNext
MB.5b: <C-n> / <Down> on the /·? search line — walk to a
newer entry in search_history.
SearchLineToggleExpand
MB.5c: <C-x><C-e> on the /·? search line — toggle the
expanded tier-2 mini-buffer band.
PromptLineSubmit
<CR> in prompt-line-mode (Effect::OpenPrompt’s generic
one-line text prompt) — submit the typed text to whichever
action:* handler the caller named as on_submit_action.
Drives Editor::do_prompt_line_submit.
PromptLineCancel
<Esc> / <C-c> in prompt-line-mode — cancel without
firing anything, restore the prior editing buffer.
Lattice’s -. Open / step up in the oil-style directory
view (DESIGN.md §5.9.4). Promoted from
Action::OilNavigateUp in slice 8.i.1.e.
ReselectLastVisual
Vim’s gv. Reselect the last Visual selection (same
kind, anchor, head). Promoted from
Action::ReselectLastVisual in slice 8.i.1.e.
SwapVisualEnds
Vim’s o in Visual mode – swap the cursor (head) to the
other end of the selection (and back). Anchor and head trade
places so subsequent motions / text objects grow or shrink the
selection at the end the cursor now sits on.
PasteAfter
Vim’s p. Paste the unnamed register’s contents after
the cursor. Promoted from Action::PasteAfter in slice
8.i.1.e.
PasteBefore
Vim’s P. Paste the unnamed register’s contents before
the cursor. Promoted from Action::PasteBefore in slice
8.i.1.e.
EnterAppend
Vim’s a. Move cursor one byte right (clamped) and enter
Insert. Promoted from Action::EnterAppend in slice
8.i.1.g.
EnterInsertFirstNonBlank
Vim’s I: move cursor to first non-blank column of the
current line and enter Insert.
EnterAppendEndOfLine
Vim’s A: move cursor to end of the current line and enter
Insert.
DisplayLineDown
Vim’s gj: move down one display line (wrap segment).
Degrades to j when wrapping is off.
DisplayLineUp
Vim’s gk: move up one display line (wrap segment).
Degrades to k when wrapping is off.
DisplayLineStart
Vim’s g0: move to the first byte of the current display segment.
Degrades to 0 when wrapping is off.
DisplayLineEnd
Vim’s g$: move to the last byte of the current display segment.
Degrades to $ when wrapping is off.
CreateFoldFromVisual
Vim’s zf. Create a fold from the most recent Visual
selection. Promoted from Action::CreateFoldFromVisual
in slice 8.i.1.g.
CreateFold
VM.3h: vim’s zf operator (zf{motion}, {Visual}zf). A CLOSED fold
over the pre-resolved inclusive 0-based lines. Carries the span, so it
crosses WIT like NarrowLines.
Fields
DeleteCharBackward
Insert mode’s <BS>. Delete the byte before the cursor.
Promoted from Action::DeleteCharBackward in slice
8.i.1.g.
InsertLineEdit(InsertLineEdit)
Insert-mode line editing — the readline/vim chords (<C-a>, <C-e>,
<C-w>, <C-u>, <C-k>, <C-t>, <C-d>, …) available in every
buffer. One grouped effect keyed by InsertLineEdit so the whole
family shares a single host handler.
CompletionTrigger
Insert mode’s <C-Space> and <C-x><C-o>. Trigger the
completion popup (omni-completion alias). Promoted from
Action::CompletionTrigger in slice 8.i.1.g. The
completion-popup minor-mode layer’s <C-Space> binding
keeps its legacy bind_action registration until that
helper picks up CommandInvocation (separate scope).
ExitVisual
Visual mode’s <Esc> / v / V. Exit Visual to Normal,
collapsing the selection. Promoted from Action::ExitVisual
in slice 8.i.1.h.
ReplaceUndoLast
Replace mode’s <BS>. Undo the last overwritten char.
Promoted from Action::ReplaceUndoLast in slice 8.i.1.h.
EnterMode(ModalState)
Vim’s i / R / <Esc> (from Insert / Replace) – enter
the named modal state. Promoted from
Action::EnterMode(_) in slice 8.i.2.a. Each chord in the
keymap binds a distinct CommandId whose ActionSpec
returns the right AppEffect::EnterMode(state) constant
– the ModalState rides in the AppEffect rather than in
CommandInvocation::args so the App’s apply_app_effect
matches a single EnterMode(state) arm instead of N
param-flat variants.
EnterVisual(VisualKind)
Vim’s v / V / <C-v> – enter Visual with the named
kind anchored at the current cursor. Promoted from
Action::EnterVisual(_) in slice 8.i.2.a. Same encoding
as Self::EnterMode: distinct CommandId per kind,
payload rides in the AppEffect.
EnterSelect(VisualKind)
Vim’s gh / gH / g<C-h> – enter Select mode (SN.3d) with
the named kind, anchored at the current cursor. Same encoding as
Self::EnterVisual: distinct CommandId per kind, payload
rides in the AppEffect. The host handler (do_enter_select)
anchors a zero-width selection like do_enter_visual does —
typing then overtypes it. Programmatic entry (snippets) instead
uses EnterMode(Select(k)) with an explicit selection already
set; see docs/dev/architecture/select-mode.md §3.
EnterSearch(SearchDirection)
Vim’s / / ? – enter the Search minibuffer in the
named direction. Promoted from Action::EnterSearch(_)
in slice 8.i.2.b. Same encoding as Self::EnterMode:
distinct CommandId per direction.
SearchWordUnderCursor(SearchDirection)
Vim’s * / # – search for the word under the cursor
in the named direction. Promoted from
Action::SearchWordUnderCursor(_) in slice 8.i.2.b.
JumpViewport(ViewportPos)
Vim’s H / M / L – jump cursor to the named
position within the visible viewport. Promoted from
Action::JumpViewport(_) in slice 8.i.2.c.
ScrollCursorTo(ScrollPos)
Vim’s zz / zt / zb – scroll the viewport so the
cursor’s current line sits at the named position.
Promoted from Action::ScrollCursorTo(_) in slice 8.i.2.c.
HorizontalScroll(HScroll)
HS.2: vim z{l,h,L,H,s,e} manual horizontal scroll.
JoinLines
Vim’s J (with-space) / gJ (no-space). Joins the
current line with the next, replacing the joining
newline with a single space (with_space: true) or
nothing (false). Promoted from Action::JoinLines in
slice 8.i.2.d. Bool payload rides in the AppEffect:
distinct CommandId per binding (J -> with-space=true,
gJ -> with-space=false).
FindRepeat
Vim’s ; (forward) / , (reverse). Repeat the most
recent f / F / t / T find on the current line
in the originally-typed direction (reverse: false) or
the opposite direction (reverse: true). Promoted from
Action::FindRepeat in slice 8.i.2.d.
Fields
InsertNewline
Insert / Replace mode’s <CR>. Inserts a literal newline
at the cursor. Promoted from
Action::Insert("\n".into()) in slice 8.i.2.e. Distinct
flat variant rather than a String-payload Insert(_)
because the keymap-bound forms always pin a fixed
literal; the wildcard “type any printable char” path
stays on Action::Insert(c.to_string()) since it isn’t
keymap-bound.
InsertTab
Insert mode’s <Tab>. Inserts a literal tab at the
cursor. Promoted from Action::Insert("\t".into()) in
slice 8.i.2.e.
OverwriteChar(char)
Replace mode’s bare-printable wildcard. Overwrites the
byte at the cursor with the captured char. Promoted from
Action::OverwriteChar(c) in slice 8.i.3.
SetMark(char)
Vim’s m<X>. Sets mark <X> at the cursor’s current
position. Promoted from Action::SetMark(c) in slice
8.i.3. The bound ActionSpec validates that <X> is
[a-zA-Z0-9] – invalid chars dispatch to Effect::None
(effectively a no-op; App::apply clears the pending
state on every Invoke).
JumpToMarkLine(char)
Vim’s '<X>. Jumps the cursor to the line of mark <X>.
Promoted from Action::JumpToMarkLine(c) in slice 8.i.3.
JumpToMarkExact(char)
Vim’s `<X>. Jumps the cursor to the exact position
(line + byte) of mark <X>. Promoted from
Action::JumpToMarkExact(c) in slice 8.i.3.
SelectRegister(Register)
Vim’s "<X>. Selects the named register for the next
yank / paste / delete. Promoted from
Action::SelectRegister(_) in slice 8.i.3. The bound
ActionSpec validates the captured char via
Register::from_input_char; chars that don’t name a
register dispatch to Effect::None.
StartMacroRecord(char)
Vim’s q<X>. Starts recording a macro into register
<X>. Promoted from Action::StartMacroRecord(c) in
slice 8.i.3.
PlayMacro(char)
Vim’s @<X> for <X> alphanumeric. Plays the macro
stored in register <X>. Promoted from
Action::PlayMacro(c) in slice 8.i.3. The @@ chord is
dispatched to Self::PlayLastMacro from the same
play-macro action (the spec branches on the captured
char).
PlayLastMacro
Vim’s @@. Replays the most recently played macro.
Promoted from Action::PlayLastMacro in slice 8.i.3.
Shares its bind site (@<CharLiteral>) with
Self::PlayMacro; the play-macro action’s apply
closure picks one or the other based on the captured
char.
AbsorbOperatorPrefix(OperatorId)
Slice 8.i.4.c: arm an operator-pending state via the
partial_chord mechanism. The App handler does two
things atomically:
- Latch
pending_countintoop_count(vim’s<count>op<motion>count multiplication; without this2ddwould get a count of 1). - Push the operator’s chord prefix into
App::partial_chordso the next keystroke routes throughlookup_normal_with_prefixand resolves[op, motion]/[op, i/a, text-object]/[op, f/F/t/T, char]to the boundInvoke.
Replaces the Action::SetPending(Pending::AfterOperator(_))
flow that did the same two things split across the
keymap (which fired SetPending) and App::apply
(which latched op_count inside the SetPending arm).
The 8 operator prefixes – d, c, y, >, <,
gU, gu, g~ – bind to typed actions whose
ApplySpec returns this variant.
SplitPaneHorizontal
Vim’s <C-w>s: split the active pane horizontally.
Promoted from Action::SplitPaneHorizontal in slice 8.i.4.d.
SplitPaneVertical
Vim’s <C-w>v: split the active pane vertically.
Promoted from Action::SplitPaneVertical in slice 8.i.4.d.
ClosePane
Vim’s <C-w>c / <C-w>q: close the active pane.
Promoted from Action::ClosePane in slice 8.i.4.d.
OnlyPane
Vim’s <C-w>o / :only / emacs C-x 1: close every pane
except the active one (collapse the tree to the active leaf).
No-op when only one pane is open. S3b (2026-06-22).
ToggleZoomPane
ZP.2: <C-w>z / <C-w><C-z> / :zoom-pane — toggle
tmux-style zoom on the active pane. Non-destructive, unlike
Self::OnlyPane: the split layout is preserved and the
second toggle restores it verbatim. No-op on a single-pane
tab. See docs/dev/architecture/pane-zoom.md.
Vim’s <C-w>h/j/k/l (and arrow / <BS> aliases): move
focus to the pane in the named direction. Promoted from
Action::NavigatePane(_) in slice 8.i.4.d.
NextPane
Vim’s <C-w>w / <C-w><Tab>: cycle focus to the next
pane. Promoted from Action::NextPane in slice 8.i.4.d.
PrevPane
Vim’s <C-w>W / <C-w><S-Tab>: cycle focus to the
previous pane. Promoted from Action::PrevPane in
slice 8.i.4.d.
NextTab
Issue #29 (2026-05-22): vim’s gt — next tab.
PrevTab
Vim’s gT — previous tab.
GoToTab(u32)
Vim’s {N}gt — switch to tab N (1-indexed; clamped).
NewTab
:tabnew — new empty tab.
NewTabAt(String)
:tabnew <path> — new tab opening path.
TerminalSpawn(Option<String>)
Issue #40 / Terminal-mode T1 (2026-05-22):
:terminal [cmd] — spawn a PTY-backed shell.
TerminalSpawnInNewTab(Option<String>)
T4 (2026-05-25): :tabterminal [cmd] — open a fresh
tab and spawn a PTY-backed shell in it. Handler does
do_new_tab then do_terminal_spawn(cmd).
MovePaneToNewTab
T4 (2026-05-25): <C-w>T — move the active pane to a
fresh tab. Handler in Editor::do_move_pane_to_new_tab.
CloseTab
:tabclose — close active tab.
OnlyTab
:tabonly — close every tab except the active one.
MoveTab(u32)
:tabmove [N] — move active tab to position N (1-indexed).
PickerAcceptInSplit
Issue #32 (2026-05-22): picker open-target overrides.
<C-s> — accept selected candidate in horizontal split.
PickerAcceptInVSplit
<C-v> — accept selected candidate in vertical split.
PickerAcceptInTab
<C-t> — accept selected candidate in new tab.
EqualizePanes
Issue #28 (2026-05-22): <C-w>= — reset every split’s
ratio to 0.5.
GrowPaneHeight
<C-w>+ — grow the active pane vertically.
ShrinkPaneHeight
<C-w>- — shrink the active pane vertically.
GrowPaneWidth
<C-w>> — grow the active pane horizontally.
ShrinkPaneWidth
<C-w>< — shrink the active pane horizontally.
CompletionNext
Completion-popup overlay: focus the next entry. Promoted
from Action::CompletionNext in slice 8.i.4.e.
CompletionPrev
Completion-popup overlay: focus the previous entry.
Promoted from Action::CompletionPrev in slice 8.i.4.e.
CompletionAccept
Completion-popup overlay: accept the focused candidate.
Promoted from Action::CompletionAccept in slice
8.i.4.e.
CompletionCancel
Completion-popup overlay: cancel the popup, stay in
Insert. Promoted from Action::CompletionCancel in
slice 8.i.4.e.
CompletionCancelAndExitInsert
Completion-popup overlay: cancel the popup and exit
Insert. Promoted from
Action::CompletionCancelAndExitInsert in slice 8.i.4.e.
CompletionToggleDocs
Completion-popup overlay: toggle the doc-popup
(<C-d>). Promoted from Action::CompletionToggleDocs
in slice 8.i.4.e.
CompletionDocsScrollDown
Completion-popup overlay: scroll the doc-popup down
(<C-f>). Promoted from
Action::CompletionDocsScrollDown in slice 8.i.4.e.
CompletionDocsScrollUp
Completion-popup overlay: scroll the doc-popup up
(<C-b>). Promoted from
Action::CompletionDocsScrollUp in slice 8.i.4.e.
CompletionAcceptThenInsert(char)
Completion-popup overlay: bare-printable wildcard.
Accept the focused candidate, then insert the captured
char (so the user can finish typing through a
confirmed prefix). Promoted from
Action::CompletionAcceptThenInsert(c) in slice
8.i.4.e.
InsertRegister(char)
YR.5: vim’s insert-register — <C-r> then a register char,
inserting that register’s contents at the cursor.
OpenYankPicker
YR.5: <C-r><C-r> — open the yank-ring picker, filling whichever
surface it was opened from.
OpenArgPicker
YR.6: open the picker registered for the :-line argument under
the cursor (ArgSpec.picker), filling the pick back into it.
SnippetNextPlaceholder
Active-snippet overlay: jump to the next placeholder
(<Tab>). Promoted from
Action::SnippetNextPlaceholder in slice 8.i.4.e.
SnippetPrevPlaceholder
Active-snippet overlay: jump to the previous placeholder
(<S-Tab>). Promoted from
Action::SnippetPrevPlaceholder in slice 8.i.4.e.
CompletionFilterToSource(String)
Completion-popup overlay: restrict candidates to a single
source. The string is the SourceId (e.g.
"gen:buffer-words", "gen:lsp-completion"). Bound to
the popup-mode filter chords (<C-b>, <C-o>, <C-f>,
<C-t>, …) introduced in CSM.K2.
CompletionFilterClear
Completion-popup overlay: clear the active source filter
(<C-Space>). Restores the mixed merged candidate list.
DiffGet
D.5.b (2026-05-30): diff-mode do (diff-get) operator.
CR.1 (2026-06-24): do is now mode-owned
(DiffMode::action_handlers() → Effect::ApplyEdit); this
AppEffect is retained only as the FALLBACK the action:diff-get
CommandSpec emits when no handler is registered. The host’s
apply_app_effect arm is emptied to a silent no-op (the
Action::DiffGet variant it used to push is deleted).
DiffPut
D.5.c (2026-05-30): diff-mode dp (diff-put) operator.
CR.1: mirror of Self::DiffGet — mode-owned now; retained as
the emptied action:diff-put fallback shell.
TutorAdvance
T.3: tutor-mode <CR> / :tutor-next. Advance to the
next exercise; advance to the next lesson when the
current one is complete. No-op when tutor-mode is not
active on the current buffer.
TutorRetreat
T.3: tutor-mode :tutor-prev. Retreat to the previous
exercise. No-op at exercise 0.
MultibufferExpand
M.5 (2026-06-01): :multibuffer-expand [n] /
:multibuffer-contract [n] ex-commands. delta is the
signed row count (positive expands, negative contracts).
Routed to the active multibuffer view’s expand_excerpt_at
from the dispatch path. No-op when the active buffer
isn’t a multibuffer.
Fields
NarrowTrigger
N.1.1 (2026-06-10): :narrow [{range}] ex-command. The host
arm resolves range against the active document to a line
span, fetches the active buffer’s Arc<dyn Document>, and
calls lattice_multibuffer::providers::narrow::create_narrow_view
— opening a one-excerpt multibuffer focused on that region.
range == None (bare :narrow) narrows the current paragraph
(blank-line delimited, vim’s ip).
Fields
range: Option<Range>The unresolved : range, resolved by the host against cursor,
last Visual selection and marks (see crate::Range for which
forms resolve); None = current paragraph.
NarrowWiden
N.1.1 (2026-06-10): :widen ex-command. The host arm closes
the active narrow view (an editable one-excerpt multibuffer),
restoring the full source buffer. No-op + echo when the
active buffer isn’t a narrow view.
NarrowLines
N.1.3 (2026-06-10): the zn narrow operator emits this once
the operator-pending machinery has resolved its motion / text
object to a line span. Carries pre-resolved inclusive 0-based
[start_line, end_line] (unlike NarrowTrigger, which carries
an unresolved Range); the host arm narrows the active buffer
to that span via the same create_narrow_view sink.
Fields
SearchTrigger
M.6 (2026-06-01): :search <query> ex-command. M.10.6
(2026-06-03) inlined the work into the host’s
apply_effect arm — it calls
lattice_multibuffer::providers::search::project_search
against the active editor as the activator. No longer
trampolines through Action::SearchTrigger /
Editor::do_search (both deleted).
SearchRefresh
M.6.1 (2026-06-01): gr chord in project-search-mode.
Re-runs the scan with the view’s current query. M.10.5
(2026-06-03) made this mode-owned: the search mode’s
on_activate registers a closure that intercepts via
ActionHandlerRegistry before this AppEffect arm runs.
The arm is a no-op marker; the work happens in the
mode’s closure (reading state, clearing excerpts,
spawning a fresh scan task).
CompileRun
CM.1 (2026-07-21): :compile <cmd> / :recompile / :make.
The host arm creates the read-only synthetic *compilation*
buffer host-side (Editor::ensure_named_synthetic_document,
activating compilation-mode) and kicks off the pipe-captured
off-thread run via the CompilationServiceHandle, whose output
streams into that buffer. Same
shape as SearchTrigger: the substrate crate owns the work;
the host arm is generic apply-effect routing. cmdline:
Some(cmd) for :compile; None for :recompile / bare
:make (reuse the last command).
CompileJumpToLocation
CM.3b (2026-07-22): <CR> on a location line in the
*compilation* buffer. The compilation-mode action handler
parses the cursor line’s text (parse_location_line) into a
source location and emits this; the host arm calls
Editor::jump_to_file_line_col(&path, line, col) (records the
hop in position history) and syncs the error list index to the
matching entry. line / col are 0-based. The location rides
in the AppEffect because the jump target is computed off the
buffer text in the mode’s closure, but the error list index is
core/host state — so the host owns the apply.
Fields
CM.2 (2026-07-22): :cnext/:cprev/:cc [N]/:cfirst/
:clast and the Builtin ]q/[q chords. The host arm calls
Editor::do_error_nav, which walks the core error list
(recording each hop in position history via
jump_to_file_line_col). On an empty list every target echoes
“no error list” and moves nothing – there is no fallback to
diagnostic hopping (]d / [d own that).
SetErrorList
CM.3a (2026-07-22): parsed error entries from the compilation
stderr reader — the off-thread → host-state seam. The reader
accumulates entries and sends the FULL list through the
compilation inbound bus; this handler maps each send here, and
the host arm calls Editor::set_error_list(entries)
(replace-semantics — the growing list stays visible). An empty
vec (sent on a new run / :recompile) clears the stale list.
The parser (below-host lattice-compilation) and this payload
share the lattice_protocol::error_list::ErrorEntry type.
Fields
source: ErrorSourceEP.1 (2026-08-10): which producer this run came from. The host arm replaces only this source’s slice, so a language server republishing on edit-debounce cannot wipe a compile run’s entries while the user walks them.
write: ErrorWriteEP.2: new run (reset the index) vs live refresh (re-anchor it). Declared by the producer — not inferrable from the entries.
CompilationGutterSet
CM.3c (2026-07-22): the per-buffer severity gutter index for the
*compilation* buffer — the off-thread → host-state seam for
in-buffer severity marks (twin of SetErrorList, which feeds the
cross-file error list). The compilation drain scans each
streamed line for a severity keyword, accumulates the FULL
per-buffer index of (line, severity), and sends it through the
compilation inbound bus; this handler maps each send here, and the
host arm converts the severities to GutterSeverityLevel and writes
the render_state compilation-severity slot for buffer. The
renderer reads that slot and injects it into the mode’s
gutter_decorations via CompilationSeverityData. An empty vec
(sent on Reset / a new run) clears the buffer’s marks.
buffer is the raw BufferId.0 (BufferId is process-local and
not Serialize, so the wire form is the u32; the host arm
reconstructs BufferId). Severity rides as the parser-native
ErrorSeverity (already shared with SetErrorList) — the single
map to the renderer-facing GutterSeverityLevel happens host-side,
avoiding a lossy GutterSeverityLevel↔ErrorSeverity round-trip
and keeping lattice-grammar free of a lattice-mode dependency.
Fields
CompilationLocationLines
CM.3c (2026-07-22): per-buffer compilation location-line
index for theme-based highlighting of navigable lines in
the *compilation* buffer. Twin of CompilationGutterSet:
the off-thread compilation drain scans each chunk for
location-bearing lines (via parse_location_line) and
ships the full list through an inbound bus; this effect
writes the render_state compilation-location slot for
buffer. An empty vec (sent on Reset / a new run)
clears the buffer’s location-line set.
Fields
CompilationThemeColors
CM.3d (2026-07-22): resolved compilation location theme colours
— published by the mode during activation so the renderer
reads compilation.location bg/fg from the theme rather than
hardcoding RGB values.
Stored editor-wide (not per buffer); the latest send wins.
Fields
CompilationKill
CM.3d (2026-07-22): kill the running compilation child
process. The host arm calls CompilationService::kill().
ProblemsOpen
CM.4 (2026-07-22): :copen. The host arm reads the core
error list and calls
lattice_multibuffer::providers::problems::create_problems_view,
opening the *problems* multibuffer — the error entries
grouped as editable source excerpts by file. Same shape as
SearchTrigger: the substrate crate owns view-creation; the
host arm is generic apply-effect routing. Echoes “no error
list” when the list is empty.
ProblemsClose
CM.4 (2026-07-22): :cclose. The host arm closes the active
*problems* view (an editable multibuffer with
ProblemsMinorMode), leaving the source buffers open — the
NarrowWiden close shape, guarded to problems views. No-op +
echo when the active buffer isn’t a problems view.
ProblemsRefresh
RV.3 (2026-08-10): gr in a *problems* view — rebuild its
excerpts from the current error list, in place.
Deliberately NOT a re-fire of Self::ProblemsOpen:
create_multibuffer_view mints a fresh BufferId on every
call, so re-opening would leave the old view behind and add a
second *problems* buffer rather than refreshing the one the
user is looking at. The host arm instead re-reads the error
list and hands it to
lattice_multibuffer::providers::problems::refresh_problems_view,
which swaps sources + excerpts atomically via
replace_excerpts. Same division as ProblemsOpen: the
substrate crate owns the rebuild, the host arm is generic glue.
OpenProviderView
PV.1 (2026-08-12): open the multibuffer view a provider owns — the generic replacement for the per-provider trigger variant.
A multibuffer view can only be created through ModeActivator,
which is &mut-backed and therefore host-only, so every provider
trigger has to round-trip through an effect. SearchTrigger /
ProblemsOpen each spent a variant here plus a host match arm
plus a plugin-boundary arm on exactly that — three crates touched
per provider, against a design whose acid test is that a provider
crate touches none.
Here the host arm is provider-agnostic: look provider up in the
ProviderViewRegistry, call the registered opener with the host
as the activator and args verbatim, then apply the generic
outcome (activate + echo, or echo the refusal). What the opener
computes, and how it words its own success and refusal, stays in
the provider’s crate.
args is the trigger’s parameters as the : line or transient
row produced them — the same Args shape a command handler
receives, so one opener serves both front-ends.
:narrow / zn deliberately do NOT route through here; see
Self::NarrowTrigger and lattice_mode::provider_view’s module
docs for why a range resolved against cursor / Visual / marks is
not a provider parameter.