Skip to main content

resolve_layered

Function resolve_layered 

Source
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?
examples/modal_keymap.rs (line 63)
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}