pub fn resolve_layered<'a, A, L>(layers: L, input: &KeyInput) -> Option<&'a A>where
L: IntoIterator<Item = &'a Keymap<A>>,
A: 'a,Expand description
Resolves input against an ordered list of keymap layers, returning the
first match (earlier layers win).
This is how context-dependent bindings are expressed without the library
knowing anything about “contexts”: the caller decides, from its own state,
which layers are active and in what priority order, then passes them here.
A binding present only in a high-priority layer overrides the base; a binding
only in the base falls through and still resolves. The library never sees a
mode or context type — only an ordered sequence of plain Keymaps.
This is a lexical scope chain (block → panel → global, innermost-first):
the caller flattens its context tree into this ordered list per event, and
the list stays fixed during resolution, so the resolver is state-free. A
miss in every layer (None) is the “pass through” signal — in a terminal
multiplexer that is the key flowing to the PTY, which lives past the end of
the chain as a sink, never as a layer within it.
use keymap_core::{resolve_layered, Key, KeyInput, Keymap, Modifiers};
#[derive(PartialEq, Debug)]
enum Action { Save, Split, Quit }
let ctrl = |c| KeyInput::new(Key::Char(c), Modifiers::CTRL);
let mut base = Keymap::new();
base.bind(ctrl('s'), Action::Save);
base.bind(ctrl('q'), Action::Quit);
let mut panel = Keymap::new();
panel.bind(ctrl('s'), Action::Split);
// In "panel" context, the panel layer overrides ctrl+s …
assert_eq!(resolve_layered([&panel, &base], &ctrl('s')), Some(&Action::Split));
// … but ctrl+q falls through to the base layer.
assert_eq!(resolve_layered([&panel, &base], &ctrl('q')), Some(&Action::Quit));
// In "editor" context, only the base layer is active.
assert_eq!(resolve_layered([&base], &ctrl('s')), Some(&Action::Save));Examples found in repository?
37fn main() {
38 // Base layer: shared bindings live here once.
39 let mut base = Keymap::new();
40 base.bind(ctrl('s'), Action::Save);
41 base.bind(ctrl('q'), Action::Quit);
42
43 // Panel layer: only the overrides for panel context.
44 let mut panel = Keymap::new();
45 panel.bind(ctrl('s'), Action::SplitPanel);
46
47 // The app maps its context to an ordered layer stack. This `match` is the
48 // only place "context" exists — entirely on the application side.
49 let layers_for = |ctx: &Context| -> Vec<&Keymap<Action>> {
50 match ctx {
51 Context::Editor => vec![&base],
52 Context::Panel => vec![&panel, &base],
53 }
54 };
55
56 for ctx in [Context::Editor, Context::Panel] {
57 let name = match ctx {
58 Context::Editor => "editor",
59 Context::Panel => "panel",
60 };
61 let layers = layers_for(&ctx);
62 for key in [ctrl('s'), ctrl('q')] {
63 match resolve_layered(layers.iter().copied(), &key) {
64 Some(action) => println!("[{name:>6}] {key:>6} -> {action:?}"),
65 None => println!("[{name:>6}] {key:>6} -> pass through"),
66 }
67 }
68 }
69}