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