Skip to main content

Module keymap_insert

Module keymap_insert 

Source
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_open gate).

§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:

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 shapenormalisation
Special(_) + ALT/SUPERstrip ALT, SUPER
Char(_) without CTRLstrip ALT, SUPER
Char(_) with CTRLstrip 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 returned EnterMode(Normal); new returns None (chord (Esc, SHIFT) has no entry; SHIFT is preserved on specials).
  • <C-Esc> (CONTROL + Esc): legacy returned EnterMode(Normal); new returns None.
  • <S-Tab> as KeyCode::Tab + SHIFT (rare; usually arrives as KeyCode::BackTab instead): legacy returned Insert("\t"); new returns SnippetPrevPlaceholder if the snippet layer is pushed, else None. KeyCode::BackTab (the common path) is unaffected – KeyChord::from_event normalises 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_layer when the popup opens; popped when the popup closes.
completion_popup_mode_id
K.1.b (2026-05-30): canonical ModeId for the completion-popup minor-mode keymap layer. Used both by completion_popup_layer_bindings (the per-binding provenance tag at build time) and by App::sync_keymap_overlays (the push site). Centralised here so the two stay in lockstep — drift would surface as :describe-key showing the wrong mode name.
dispatch_insert
Dispatch a key event in Insert mode through the layered keymap registry. Replaces the legacy input::translate_insert plus the translate_insert_completion_popup and translate_active_snippet overlay branches at the top of input::translate.
register_insert_bindings
Register every chord the legacy input::translate_insert recognised into the supplied handle’s Builtin layer under BindingMode::Insert. Called at App startup.