binding.rs

 1use crate::{Action, DispatchContext, DispatchContextPredicate, KeyMatch, Keystroke};
 2use anyhow::Result;
 3use smallvec::SmallVec;
 4
 5pub struct KeyBinding {
 6    action: Box<dyn Action>,
 7    pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
 8    pub(super) context_predicate: Option<DispatchContextPredicate>,
 9}
10
11impl KeyBinding {
12    pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
13        Self::load(keystrokes, Box::new(action), context_predicate).unwrap()
14    }
15
16    pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
17        let context = if let Some(context) = context {
18            Some(DispatchContextPredicate::parse(context)?)
19        } else {
20            None
21        };
22
23        let keystrokes = keystrokes
24            .split_whitespace()
25            .map(Keystroke::parse)
26            .collect::<Result<_>>()?;
27
28        Ok(Self {
29            keystrokes,
30            action,
31            context_predicate: context,
32        })
33    }
34
35    pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool {
36        self.context_predicate
37            .as_ref()
38            .map(|predicate| predicate.eval(contexts))
39            .unwrap_or(true)
40    }
41
42    pub fn match_keystrokes(
43        &self,
44        pending_keystrokes: &[Keystroke],
45        contexts: &[&DispatchContext],
46    ) -> KeyMatch {
47        if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
48            && self.matches_context(contexts)
49        {
50            // If the binding is completed, push it onto the matches list
51            if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
52                KeyMatch::Some(self.action.boxed_clone())
53            } else {
54                KeyMatch::Pending
55            }
56        } else {
57            KeyMatch::None
58        }
59    }
60
61    pub fn keystrokes_for_action(
62        &self,
63        action: &dyn Action,
64        contexts: &[&DispatchContext],
65    ) -> Option<SmallVec<[Keystroke; 2]>> {
66        if self.action.partial_eq(action) && self.matches_context(contexts) {
67            Some(self.keystrokes.clone())
68        } else {
69            None
70        }
71    }
72
73    pub fn keystrokes(&self) -> &[Keystroke] {
74        self.keystrokes.as_slice()
75    }
76
77    pub fn action(&self) -> &dyn Action {
78        self.action.as_ref()
79    }
80}