pub struct KeymapTrie { /* private fields */ }Expand description
One layer’s worth of bindings, indexed for
O(prefix_length) lookup.
A plain value type: no locking, no layering. The
KeymapRegistry keeps one per
(layer, BindingMode) and merges them with Self::merge_over;
callers build one directly to hand a whole layer to
KeymapHandle::push_layer.
§Examples
use std::sync::Arc;
use lattice_grammar::{CommandId, CommandInvocation, SourceLocation};
use lattice_keymap::{BoundCommand, ChordPattern, KeymapLayer, KeymapTrie, LookupResult};
use lattice_protocol::KeyChord;
let bound = |id| Arc::new(BoundCommand::from_invocation(
CommandInvocation::of(CommandId::new(id)),
SourceLocation::synthetic("doc"),
KeymapLayer::Builtin,
));
let lit = |c| ChordPattern::Literal(KeyChord::char(c));
let mut trie = KeymapTrie::new();
trie.insert(&[lit('g'), lit('g')], bound(1)); // gg
trie.insert(&[lit('f'), ChordPattern::CharLiteral], bound(2)); // f{char}
let k = KeyChord::char;
assert!(matches!(trie.lookup(&[k('g')]), LookupResult::Partial));
assert!(matches!(trie.lookup(&[k('g'), k('g')]), LookupResult::Bound { .. }));
assert!(matches!(trie.lookup(&[k('q')]), LookupResult::Unbound));
// The wildcard captures the typed char...
match trie.lookup(&[k('f'), k('x')]) {
LookupResult::Bound { captured, .. } => assert_eq!(captured, vec!['x']),
other => panic!("{other:?}"),
}
// ...but never a modified chord.
assert!(matches!(trie.lookup(&[k('f'), KeyChord::ctrl('x')]), LookupResult::Unbound));
assert_eq!(trie.binding_count(), 2);Implementations§
Source§impl KeymapTrie
impl KeymapTrie
Sourcepub fn new() -> KeymapTrie
pub fn new() -> KeymapTrie
Empty trie. Use insert to populate.
Sourcepub fn insert(&mut self, path: &[ChordPattern], bound: Arc<BoundCommand>)
pub fn insert(&mut self, path: &[ChordPattern], bound: Arc<BoundCommand>)
Register bound at the chord path path. Replaces any
existing binding at the same path (last-bind-wins within
a single trie / layer). Empty path is a no-op (no
“bind nothing”).
Sourcepub fn remove(&mut self, path: &[ChordPattern]) -> Option<Arc<BoundCommand>>
pub fn remove(&mut self, path: &[ChordPattern]) -> Option<Arc<BoundCommand>>
Remove the binding at path if present. Returns the
dropped Arc<BoundCommand> for callers that want to
surface “what was unbound” in echo messages. Does not
prune empty intermediate nodes – a future bind at the
same prefix should reuse them, and the wasted nodes are
bounded by the trie’s lifetime.
Sourcepub fn get(&self, path: &[ChordPattern]) -> Option<&Arc<BoundCommand>>
pub fn get(&self, path: &[ChordPattern]) -> Option<&Arc<BoundCommand>>
VM.4: the binding registered at EXACTLY path, if any.
Not Self::lookup, and the difference is why this exists. lookup
answers “what does this sequence of PRESSED chords resolve to”, and
falls back to the {char} wildcard when no exact child matches, so it
can’t tell the registration [f, {char}] from [f, x], and it can’t
be asked about a pattern path at all. The motion mirror needs “is this
exact registration slot occupied”, which is a question about PATTERNS.
Here a CharLiteral segment walks the wildcard slot and only that slot,
never as a fallback.
Sourcepub fn lookup(&self, chords: &[KeyChord]) -> LookupResult
pub fn lookup(&self, chords: &[KeyChord]) -> LookupResult
Walk the input chord sequence and return what we found.
Lookup precedence at each depth: exact children match
first; if absent, fall back to char_wildcard when the
input chord is a bare printable char (no modifiers).
Modifier-bearing chords (<C-x>) never match the
wildcard – the wildcard’s job is “any single typed
char” for marks / registers / find-char.
Sourcepub fn walk_continuations<F>(&self, prefix: &[KeyChord], f: F)
pub fn walk_continuations<F>(&self, prefix: &[KeyChord], f: F)
DK.4: walk every binding registered strictly BELOW prefix,
invoking f with the SUFFIX path (the chords that follow
prefix) and the binding at that path.
This is the subtree :describe-key renders when the chord it
was asked about is a prefix rather than a binding — the answer
“<C-c><C-x> is not bound” is true and useless, “it is a prefix
with eight continuations” is the answer the user came for.
A binding sitting exactly AT prefix is not emitted; that is
the Self::lookup answer and is reported separately.
O(subtree) walk. Telemetry path; never on the keystroke path.
Sourcepub fn node_view(&self, prefix: &[KeyChord]) -> Option<NodeView>
pub fn node_view(&self, prefix: &[KeyChord]) -> Option<NodeView>
WK.1: the immediate children of the node prefix names, as a
NodeView. None when the prefix leaves the trie — which-key
shows nothing for a chord the keymap does not know.
Distinct from Self::walk_continuations, which flattens the
whole subtree for :describe-key. Which-key wants one row per
next keystroke, so it needs depth one plus a count of what hangs
off each child.
O(children + subtree) — the descendant counts walk each child’s subtree once. Runs on the actor thread after the idle delay, never on the keystroke path.
Sourcepub fn merge_over(&mut self, other: &KeymapTrie)
pub fn merge_over(&mut self, other: &KeymapTrie)
Overlay other on top of self. other’s bindings win
on conflict; the merge is structural so paths in other
that don’t conflict simply add to self’s tree.
§Examples
use std::sync::Arc;
use lattice_grammar::{CommandId, CommandInvocation, SourceLocation};
use lattice_keymap::{BoundCommand, ChordPattern, KeymapLayer, KeymapTrie};
use lattice_protocol::KeyChord;
let bound = |id, layer| Arc::new(BoundCommand::from_invocation(
CommandInvocation::of(CommandId::new(id)), SourceLocation::synthetic("doc"), layer,
));
let x = [ChordPattern::Literal(KeyChord::char('x'))];
let y = [ChordPattern::Literal(KeyChord::char('y'))];
let mut base = KeymapTrie::new();
base.insert(&x, bound(1, KeymapLayer::Builtin));
base.insert(&y, bound(2, KeymapLayer::Builtin));
let mut user = KeymapTrie::new();
user.insert(&x, bound(3, KeymapLayer::User));
base.merge_over(&user);
assert_eq!(base.get(&x).unwrap().layer, KeymapLayer::User); // overridden
assert_eq!(base.get(&y).unwrap().layer, KeymapLayer::Builtin); // keptThe registry’s layer-stack collapse calls this in
priority order (lowest first) so the highest-priority
layer’s bindings end up authoritative. See
docs/dev/architecture/keymap-architecture.md §2 + §4 (layer-merge on
write, not on read).
Sourcepub fn binding_count(&self) -> usize
pub fn binding_count(&self) -> usize
Number of terminal bindings in the trie. O(N) walk; useful for tests + registry telemetry, not on the hot path.
Sourcepub fn walk_bindings<F>(&self, f: F)
pub fn walk_bindings<F>(&self, f: F)
MARG.2 (2026-06-03): walk every terminal binding,
invoking f with the chord path that reaches it and
the Arc<BoundCommand> at that path. Used by the
reverse-keymap-cache builder in KeymapRegistry to
produce a command_name → Vec<KeyChord> map for the
keybinding annotator (see
docs/dev/architecture/marginalia.md §6).
Path slice is borrowed; the closure must capture-by-
clone if it wants to retain the chord sequence. O(N)
over the bound-chord count — same cost class as
Self::binding_count, not on the hot path.
Trait Implementations§
Source§impl Clone for KeymapTrie
impl Clone for KeymapTrie
Source§fn clone(&self) -> KeymapTrie
fn clone(&self) -> KeymapTrie
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for KeymapTrie
impl Debug for KeymapTrie
Source§impl Default for KeymapTrie
impl Default for KeymapTrie
Source§fn default() -> KeymapTrie
fn default() -> KeymapTrie
Auto Trait Implementations§
impl Freeze for KeymapTrie
impl RefUnwindSafe for KeymapTrie
impl Send for KeymapTrie
impl Sync for KeymapTrie
impl Unpin for KeymapTrie
impl UnsafeUnpin for KeymapTrie
impl UnwindSafe for KeymapTrie
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more