Skip to main content

Module registry

Module registry 

Source
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):

  1. Builtin – the default vim keymap, registered at startup from the existing KeymapEntry catalog.
  2. MajorMode – per-major-mode (rust, markdown, …) additions / overrides.
  3. 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.
  4. User – compiled init.rs bindings.
  5. 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§

KeymapHandle
Editor-facing handle to the keymap registry.
KeymapRegistry
Keymap registry. Cheap to clone (Arc-backed); every caller (App, plugins, the future WIT host) holds a KeymapHandle that 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 to KeymapHandle::pop_layer to remove the layer. Layers created implicitly by a bind also get one internally, but it is never handed out — remove those with KeymapHandle::remove_layer.

Enums§

KeymapCapability
Privilege bundle a writer presents when calling capability-gated bind APIs (slice 8.h). Mirrors the WIT keymap-write capability variants in DESIGN.md §5.5: the host hands one of these to every caller of the registry – built-in startup, the user’s compiled init.rs, each loaded plugin – and the registry enforces the layer scope before committing any write.
KeymapError
Errors returned by the capability-gated bind APIs. Slice 8.h.
PushLayerKind
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 in chord_str to leader.
overtypes_in_select
Does chord overtype a Select-mode selection when nothing binds it?