Skip to main content

verso/ui/keymap/
table.rs

1use super::{
2    keys::{parse_sequence, Key},
3    Action,
4};
5use std::str::FromStr;
6
7#[derive(Debug, PartialEq)]
8pub enum Dispatch {
9    Fire(Action),
10    Pending,
11    Unbound,
12}
13
14#[derive(Debug)]
15pub struct Keymap {
16    rules: Vec<(Vec<Key>, Action)>,
17    buffer: std::cell::RefCell<Vec<Key>>,
18}
19
20impl Keymap {
21    pub fn from_config(entries: &[(String, Vec<String>)]) -> anyhow::Result<Self> {
22        let mut rules: Vec<(Vec<Key>, Action)> = Vec::new();
23        for (action_str, seqs) in entries {
24            let action = Action::from_str(action_str).map_err(|e| anyhow::anyhow!(e))?;
25            for s in seqs {
26                rules.push((parse_sequence(s)?, action));
27            }
28        }
29        // Prefix check: no sequence can be a strict prefix of another.
30        for i in 0..rules.len() {
31            for j in 0..rules.len() {
32                if i == j {
33                    continue;
34                }
35                let (a, _) = &rules[i];
36                let (b, _) = &rules[j];
37                if b.len() > a.len() && &b[..a.len()] == a.as_slice() {
38                    return Err(anyhow::anyhow!(
39                        "keymap prefix collision: {:?} is a prefix of {:?}",
40                        a,
41                        b
42                    ));
43                }
44            }
45        }
46        Ok(Self {
47            rules,
48            buffer: std::cell::RefCell::new(Vec::new()),
49        })
50    }
51
52    /// Feed one user keystroke. Returns what to do.
53    pub fn feed(&self, raw: &str) -> Dispatch {
54        let keys = parse_sequence(raw).unwrap_or_default();
55        let mut buf = self.buffer.borrow_mut();
56        buf.extend(keys);
57
58        // Exact full match?
59        for (seq, action) in &self.rules {
60            if *seq == *buf {
61                buf.clear();
62                return Dispatch::Fire(*action);
63            }
64        }
65        // Proper prefix of some rule?
66        for (seq, _) in &self.rules {
67            if seq.len() > buf.len() && seq.starts_with(buf.as_slice()) {
68                return Dispatch::Pending;
69            }
70        }
71        buf.clear();
72        Dispatch::Unbound
73    }
74}