1use crate::{Action, KeyBindingContextPredicate, KeyMatch, Keystroke};
2use anyhow::Result;
3use smallvec::SmallVec;
4
5/// A keybinding and its associated metadata, from the keymap.
6pub struct KeyBinding {
7 pub(crate) action: Box<dyn Action>,
8 pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
9 pub(crate) context_predicate: Option<KeyBindingContextPredicate>,
10}
11
12impl Clone for KeyBinding {
13 fn clone(&self) -> Self {
14 KeyBinding {
15 action: self.action.boxed_clone(),
16 keystrokes: self.keystrokes.clone(),
17 context_predicate: self.context_predicate.clone(),
18 }
19 }
20}
21
22impl KeyBinding {
23 /// Construct a new keybinding from the given data.
24 pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
25 Self::load(keystrokes, Box::new(action), context_predicate).unwrap()
26 }
27
28 /// Load a keybinding from the given raw data.
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(KeyBindingContextPredicate::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 /// Check if the given keystrokes match this binding.
49 pub fn match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch {
50 if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
51 // If the binding is completed, push it onto the matches list
52 if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
53 KeyMatch::Matched
54 } else {
55 KeyMatch::Pending
56 }
57 } else {
58 KeyMatch::None
59 }
60 }
61
62 /// Get the keystrokes associated with this binding
63 pub fn keystrokes(&self) -> &[Keystroke] {
64 self.keystrokes.as_slice()
65 }
66
67 /// Get the action associated with this binding
68 pub fn action(&self) -> &dyn Action {
69 self.action.as_ref()
70 }
71}
72
73impl std::fmt::Debug for KeyBinding {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("KeyBinding")
76 .field("keystrokes", &self.keystrokes)
77 .field("context_predicate", &self.context_predicate)
78 .field("action", &self.action.name())
79 .finish()
80 }
81}