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}