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        let should_debug = self.keystrokes.len() == 1
48            && self.keystrokes[0].key == "p"
49            && self.keystrokes[0].modifiers.command == true
50            && self.keystrokes[0].modifiers.shift == true;
51
52        if false && should_debug {
53            dbg!(
54                &self.keystrokes,
55                &pending_keystrokes,
56                &contexts,
57                &self.matches_context(contexts)
58            );
59        }
60        if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
61            && self.matches_context(contexts)
62        {
63            // If the binding is completed, push it onto the matches list
64            if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
65                KeyMatch::Some(self.action.boxed_clone())
66            } else {
67                KeyMatch::Pending
68            }
69        } else {
70            KeyMatch::None
71        }
72    }
73
74    pub fn keystrokes_for_action(
75        &self,
76        action: &dyn Action,
77        contexts: &[&DispatchContext],
78    ) -> Option<SmallVec<[Keystroke; 2]>> {
79        if self.action.partial_eq(action) && self.matches_context(contexts) {
80            Some(self.keystrokes.clone())
81        } else {
82            None
83        }
84    }
85
86    pub fn keystrokes(&self) -> &[Keystroke] {
87        self.keystrokes.as_slice()
88    }
89
90    pub fn action(&self) -> &dyn Action {
91        self.action.as_ref()
92    }
93}