Expand description
KeymapRegistry – the public, layered keymap engine the
input dispatcher consults. Audit slice 8.c of the M3
refactor; see docs/dev/architecture/keymap-architecture.md for the design.
§Five-layer model (DESIGN.md §5.2.3)
Bindings live in five layers, in priority order
(Builtin < MajorMode < MinorMode(_) < User < Buffer):
- Builtin – the default vim keymap, registered at
startup from the existing
KeymapEntrycatalog. - MajorMode – per-major-mode (rust, markdown, …) additions / overrides.
- MinorMode – pushed/popped layers
(active-snippet, completion-popup, picker, chord-capture).
One layer per
ModeId: re-pushing the same mode replaces that layer’s bindings rather than stacking a sibling. - User – compiled
init.rsbindings. - Buffer – per-buffer ad-hoc bindings (
:nmap <buffer>).
§Wait-free reads, mailbox-style writes (in spirit)
Reads (lookup) walk one merged trie per BindingMode –
the layers are physically merged into the read structure on
every write. Read cost is one ArcSwap::load + the trie
walk (audit slice 8.b: ~17ns single-chord, ~43ns three-chord).
Writes (bind / unbind / push_layer / pop_layer)
take a brief mutex on the layer stack, mutate the affected
per-mode tries, rebuild the merged structure for every mode
that changed, and ArcSwap::store it. The mutex covers
pure in-memory work (no I/O); typical write completes in
sub-millisecond per the slice 8.b merge bench (~444 ns per
layer-merge × 6 modes = ~3 µs worst case).
Writes are infrequent (startup catalog enumeration; minor-
mode push/pop on UI events; user :bind / :unmap); the
brief lock has no correctness exposure to the keystroke
path because reads never touch it.
§Gating: always-on vs. mode layers
Builtin, User and Buffer are always on. MajorMode(id) and
MinorMode(id) layers fire only when the caller names id in the
active_modes slice passed to KeymapHandle::lookup_with_context
(active major first, then minors in activation order). A gated layer
overlays the always-on merge, so an active mode’s chord wins even over
a User rebind of the same chord.
§Examples
use lattice_grammar::{CommandId, CommandInvocation, SourceLocation};
use lattice_keymap::{BindingMode, KeymapHandle, KeymapLayer, LookupResult, ModeId};
use lattice_keymap::{KeymapCapability, KeymapError};
use lattice_protocol::KeyChord;
let keymap = KeymapHandle::new();
let cap = KeymapCapability::Full;
let src = || SourceLocation::synthetic("doc");
let (builtin_w, diff_w) = (CommandId::new(1), CommandId::new(2));
keymap.try_bind_chord_string(cap, KeymapLayer::Builtin, BindingMode::Normal, "w",
CommandInvocation::of(builtin_w), src()).unwrap();
let diff = ModeId::new("diff-mode");
keymap.try_bind_chord_string(cap, KeymapLayer::MinorMode(diff), BindingMode::Normal, "w",
CommandInvocation::of(diff_w), src()).unwrap();
let fired = |active: &[ModeId]| match keymap.lookup_with_context(
BindingMode::Normal, &[KeyChord::char('w')], active,
) {
LookupResult::Bound { command, .. } => Some(command.command.command),
_ => None,
};
assert_eq!(fired(&[]), Some(builtin_w)); // diff-mode not active here
assert_eq!(fired(&[diff]), Some(diff_w)); // active mode shadows the builtin
// Capabilities scope writes: user config cannot touch the builtin layer.
let denied = keymap.try_bind_chord_string(KeymapCapability::User, KeymapLayer::Builtin,
BindingMode::Normal, "x", CommandInvocation::of(builtin_w), src());
assert!(matches!(denied, Err(KeymapError::CapabilityDenied { .. })));Structs§
- Keymap
Handle - Editor-facing handle to the keymap registry.
- Keymap
Registry - Keymap registry. Cheap to clone (
Arc-backed); every caller (App, plugins, the future WIT host) holds aKeymapHandlethat wraps the same underlying registry. - LayerId
- Stable id for a registered layer. Issued by
KeymapHandle::push_layer(minor-mode overlays, per-buffer bindings); the caller passes it toKeymapHandle::pop_layerto remove the layer. Layers created implicitly by abindalso get one internally, but it is never handed out — remove those withKeymapHandle::remove_layer.
Enums§
- Keymap
Capability - Privilege bundle a writer presents when calling
capability-gated bind APIs (slice 8.h). Mirrors the WIT
keymap-writecapability variants in DESIGN.md §5.5: the host hands one of these to every caller of the registry – built-in startup, the user’s compiledinit.rs, each loaded plugin – and the registry enforces the layer scope before committing any write. - Keymap
Error - Errors returned by the capability-gated bind APIs. Slice 8.h.
- Push
Layer Kind - What kind of runtime-pushed layer to install.
Constants§
- DEFAULT_
LEADER - OM.2b: what
<leader>expands to unless the host sets otherwise.
Functions§
- expand_
leader - Expand every
<leader>/<Leader>token inchord_strtoleader. - overtypes_
in_ select - Does
chordovertype a Select-mode selection when nothing binds it?