binding.rs

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