1use crate::{Action, KeyBindingContextPredicate, 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, typed: &[Keystroke]) -> Option<bool> {
50 if self.keystrokes.len() < typed.len() {
51 return None;
52 }
53
54 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
55 if !typed.should_match(target) {
56 return None;
57 }
58 }
59
60 Some(self.keystrokes.len() > typed.len())
61 }
62
63 /// Get the keystrokes associated with this binding
64 pub fn keystrokes(&self) -> &[Keystroke] {
65 self.keystrokes.as_slice()
66 }
67
68 /// Get the action associated with this binding
69 pub fn action(&self) -> &dyn Action {
70 self.action.as_ref()
71 }
72
73 /// Get the predicate used to match this binding
74 pub fn predicate(&self) -> Option<&KeyBindingContextPredicate> {
75 self.context_predicate.as_ref()
76 }
77}
78
79impl std::fmt::Debug for KeyBinding {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 f.debug_struct("KeyBinding")
82 .field("keystrokes", &self.keystrokes)
83 .field("context_predicate", &self.context_predicate)
84 .field("action", &self.action.name())
85 .finish()
86 }
87}