1use crate::{Action, KeyBindingContextPredicate, 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 match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch {
46 if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
47 // If the binding is completed, push it onto the matches list
48 if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
49 KeyMatch::Some(vec![self.action.boxed_clone()])
50 } else {
51 KeyMatch::Pending
52 }
53 } else {
54 KeyMatch::None
55 }
56 }
57
58 pub fn keystrokes(&self) -> &[Keystroke] {
59 self.keystrokes.as_slice()
60 }
61
62 pub fn action(&self) -> &dyn Action {
63 self.action.as_ref()
64 }
65}
66
67impl std::fmt::Debug for KeyBinding {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 f.debug_struct("KeyBinding")
70 .field("keystrokes", &self.keystrokes)
71 .field("context_predicate", &self.context_predicate)
72 .field("action", &self.action.name())
73 .finish()
74 }
75}