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