Expand description
Visual-mode binding registration + drift-test helpers.
Audit slice 8.e. The second mode migrated off input::translate’s
hand-rolled match table; follows the slice 8.d
template (register_<mode>_bindings +
dispatch_<mode> + drift test against a frozen reference body
of the legacy translator).
§Surface
Visual mode is the same chord table for charwise / linewise /
blockwise – the kind only changes how Range::Selection
resolves at operator-dispatch time (see
lattice-grammar::dispatcher §5.2.3). Two block-only
exceptions land before the trie lookup:
I->Action::EnterBlockVisualInsert(blockwise only)A->Action::EnterBlockVisualAppend(blockwise only)
These are pre-dispatch overrides rather than a separate
BindingMode::VisualBlock – the architecture’s eventual model
is a minor-mode layer pushed at blockwise entry / popped at
exit (see docs/dev/architecture/keymap-architecture.md §5.3); slice 8.e keeps
the surgical pre-check until that layer machinery lands. The
drift test below pins the kind branch so a future graduation
to push_layer is mechanical.
Common-to-all-kinds bindings registered by
register_visual_bindings:
- Exits:
<Esc>/v/V->ExitVisual. - Motions (extend the selection):
h/<Left>/j/<Down>/k/<Up>/l/<Right>/0/<Home>/$/<End>/^/w/b/e/W/B/E/}/{/)/(/G. Each binds toCommandInvocation::of(motion.0)– the non-legacy_actionpath; the dispatcher returnsAction::Invoke(command.command.clone()). - Operators on selection: an operator acts on the active
selection BY DESIGN, so its Visual binding is generated once at
operator registration, NOT hand-listed here. Every operator
(
crate::keymap_normal::register_operator_bindings– builtind/c/y/>/<, casegU/gu/g~, contributedzn, …) binds its trigger chord toCommandInvocation::of(op.0).with_range(Range::Selection); the operator dispatcher’s range walker resolvesRange::Selectionagainst the active visual selection. The ONLY operator entries that remain inregister_visual_bindingsare the two Visual-only ALIASESx-> delete ands-> change (in Normalx/sare different commands, so they are not the operators’ trigger chord and cannot come from the registration). - Text objects (set the selection to the object’s span):
i<obj>/a<obj>for every object in the SHARED [crate::keymap_normal::text_object_rows] table –viw,vaw,vi{,vap,vaf(function),vac(class),vaC(comment), … These are TWO-key chords resolved by the same partial-chord machinery Normal mode uses (i/aabsorbs into the host’spartial_chord, the object char resolves the pair); seedispatch_visual. Each binds to a bareCommandInvocation::of(tobj.0), which the grammar’sexecute_text_objectturns into anEffect::SelectionChangespanning the object. There is ZERO per-object code: the binder iterates the shared table, so Visual and the Normal operator-pending resolver can never drift.
Slice 8.e’s net win: every motion / operator binding moves
off the legacy_action bridge and onto a real
CommandInvocation. Only the three ExitVisual shapes plus
the two block-only Enter* paths still carry a legacy_action
– they don’t have a CommandInvocation peer today.
Functions§
- dispatch_
visual - Dispatch a Visual-mode key event through the keymap registry.
- register_
visual_ bindings - Register every chord the legacy
input::translate_visualrecognised into the supplied handle’sBuiltinlayer underBindingMode::Visual. Called at App startup; the registration capturesbuiltins’s motion / operator ids by value, so the resultingBoundCommands never re-resolve at lookup time.