Expand description
Insert-mode binding registration + drift-test helpers.
Audit slice 8.f. Third mode migrated off input::translate’s
hand-rolled match table. Insert is bigger than Replace / Visual
because two minor-mode overlays ride on top of base Insert
(architecture doc §5.3):
- Completion popup (
App.insert_completion = Some(...)): the popup claims a fixed set of CTRL-bearing chords plus<Tab>/<CR>/<Esc>plus a bare-char wildcard (“commit-then-insert”); other chords fall through to base Insert. - Active snippet (
App.active_snippet = Some(...)): the snippet claims<Tab>/<S-Tab>/<Esc>for placeholder navigation; other chords fall through to base Insert. Popup wins when both overlays are active (legacy&& !ctx.insert_completion_opengate).
§Layer model
Each overlay is registered as a KeymapLayer::MinorMode
layer pushed onto the registry when the overlay activates and
popped when it deactivates. Push order is enforced by
App::sync_keymap_overlays: snippet first, popup second, so
popup’s LayerId is higher and popup wins on overlapping
chords (preserving the legacy “popup precedes snippet”
gating).
§Base Insert bindings
Registered directly into KeymapLayer::Builtin +
BindingMode::Insert by register_insert_bindings:
<Esc>->Action::EnterMode(Normal)<BS>->Action::DeleteCharBackward<CR>->Action::Insert("\n")<Tab>->Action::Insert("\t")<C-Space>->Action::CompletionTrigger[<C-x>, <C-o>]->Action::CompletionTrigger(omni-completion)
SN.3c.1 (2026-06-14): [<C-x>, <C-s>] (snippet-expand) is no
longer a Builtin binding — it lives on snippet-mode’s keymap()
(KeymapLayer::MinorMode("snippet-mode")). <C-x> stays a partial
prefix because that mode’s layer (boot-pushed) provides the
<C-x><C-s> terminal.
<C-x> itself is a partial trie node (no terminal binding;
children only). Lookup at [<C-x>] returns
LookupResult::Partial; dispatch_insert translates that
into Action::SetPending(Pending::AfterCtrlX). The next
keystroke arrives with pending = AfterCtrlX and the
dispatcher reconstructs the two-chord sequence
[<C-x>, current_chord] for the lookup.
§Literal-text fall-through
Per the architecture doc §9 / slice 8.f bullet, “type any
printable char that has no binding” stays a dispatcher default
rather than a registered char wildcard. Lookup at an
unmodified Char(c) returns LookupResult::Unbound in base
Insert; the dispatcher’s private literal_text_fallback returns
Action::Insert(c.to_string()) (suppressing CONTROL-bearing
chars to match legacy semantics). When the popup layer is
pushed, its char-wildcard wins, so literal typing routes
through CompletionAcceptThenInsert(c) instead – the popup
handler in App decides whether to accept the focused candidate
or fall back to plain insertion.
§Modifier transparency (drift caveats)
Legacy translate_insert matched on event.code alone for
<Esc> / <BS> / <CR> / <Tab> (modifiers ignored), and
short-circuited only CONTROL on the Char(c) arm. The trie
is precise: (Esc, NONE) and (Esc, CONTROL) are distinct
chords. To bridge, dispatch_insert normalizes per the table
below – but see OS.0b just after it before assuming this strip
is unconditional:
| chord shape | normalisation |
|---|---|
Special(_) + ALT/SUPER | strip ALT, SUPER |
Char(_) without CTRL | strip ALT, SUPER |
Char(_) with CTRL | strip ALT, SUPER |
SHIFT is preserved on specials so the snippet layer can
distinguish <S-Tab> from <Tab>. SHIFT is preserved on
CTRL+letter so <C-S-c> stays distinct from <C-c>. SHIFT
is preserved on bare letters too (the chord normalisation in
[KeyChord::from_event] already strips redundant SHIFT for
bare ASCII letters where case carries the bit).
OS.0b (2026-09-06): the strip is a fallback, not a precondition.
No BUILTIN Insert binding (base or overlay) uses ALT or SUPER, so
this table’s normalized form is exactly what every builtin chord
still resolves to. But the modes WIT seam makes no such promise to
a plugin or host mode registering its OWN Insert-mode binding
(binding-mode: insert), and nothing in registration rejects an
ALT/SUPER-bearing Insert chord – so a mode or plugin CAN bind one.
Stripping the modifiers before every lookup, unconditionally, used
to mean such a binding registered correctly and then could never
fire: real keypresses were normalized away before reaching it. Every
lookup site now tries the chord AS PRESSED first
([lookup_insert_chord]) and falls back to this table’s normalized
form only when the raw lookup finds nothing – so a deliberately
ALT/SUPER-bearing binding is reachable, and a chord that was never
going to match either way still costs exactly one lookup.
Three documented drift cases vs. legacy (acceptable per the drift test’s allow-list – terminals don’t emit these in practice):
<S-Esc>(SHIFT + Esc): legacy returnedEnterMode(Normal); new returnsNone(chord(Esc, SHIFT)has no entry; SHIFT is preserved on specials).<C-Esc>(CONTROL + Esc): legacy returnedEnterMode(Normal); new returnsNone.<S-Tab>asKeyCode::Tab + SHIFT(rare; usually arrives asKeyCode::BackTabinstead): legacy returnedInsert("\t"); new returnsSnippetPrevPlaceholderif the snippet layer is pushed, elseNone.KeyCode::BackTab(the common path) is unaffected –KeyChord::from_eventnormalises BackTab to(Tab, SHIFT), identical handling.
Functions§
- completion_
popup_ layer_ bindings - Build the completion-popup minor-mode layer’s binding set.
Wrapped into the registry by
App::push_completion_popup_layerwhen the popup opens; popped when the popup closes. - completion_
popup_ mode_ id - K.1.b (2026-05-30): canonical
ModeIdfor the completion-popup minor-mode keymap layer. Used both bycompletion_popup_layer_bindings(the per-binding provenance tag at build time) and byApp::sync_keymap_overlays(the push site). Centralised here so the two stay in lockstep — drift would surface as:describe-keyshowing the wrong mode name. - dispatch_
insert - Dispatch a key event in Insert mode through the layered
keymap registry. Replaces the legacy
input::translate_insertplus thetranslate_insert_completion_popupandtranslate_active_snippetoverlay branches at the top ofinput::translate. - register_
insert_ bindings - Register every chord the legacy
input::translate_insertrecognised into the supplied handle’sBuiltinlayer underBindingMode::Insert. Called at App startup.