1use collections::HashMap;
2
3use crate::{Action, KeyBindingContextPredicate, Keystroke};
4use anyhow::Result;
5use smallvec::SmallVec;
6
7/// A keybinding and its associated metadata, from the keymap.
8pub struct KeyBinding {
9 pub(crate) action: Box<dyn Action>,
10 pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
11 pub(crate) context_predicate: Option<KeyBindingContextPredicate>,
12}
13
14impl Clone for KeyBinding {
15 fn clone(&self) -> Self {
16 KeyBinding {
17 action: self.action.boxed_clone(),
18 keystrokes: self.keystrokes.clone(),
19 context_predicate: self.context_predicate.clone(),
20 }
21 }
22}
23
24impl KeyBinding {
25 /// Construct a new keybinding from the given data.
26 pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
27 Self::load(keystrokes, Box::new(action), context_predicate, None).unwrap()
28 }
29
30 /// Load a keybinding from the given raw data.
31 pub fn load(
32 keystrokes: &str,
33 action: Box<dyn Action>,
34 context: Option<&str>,
35 key_equivalents: Option<&HashMap<char, char>>,
36 ) -> Result<Self> {
37 let context = if let Some(context) = context {
38 Some(KeyBindingContextPredicate::parse(context)?)
39 } else {
40 None
41 };
42
43 let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
44 .split_whitespace()
45 .map(Keystroke::parse)
46 .collect::<Result<_>>()?;
47
48 if let Some(equivalents) = key_equivalents {
49 for keystroke in keystrokes.iter_mut() {
50 if keystroke.key.chars().count() == 1 {
51 if let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap()) {
52 keystroke.key = key.to_string();
53 }
54 }
55 }
56 }
57
58 Ok(Self {
59 keystrokes,
60 action,
61 context_predicate: context,
62 })
63 }
64
65 /// Check if the given keystrokes match this binding.
66 pub fn match_keystrokes(&self, typed: &[Keystroke]) -> Option<bool> {
67 if self.keystrokes.len() < typed.len() {
68 return None;
69 }
70
71 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
72 if !typed.should_match(target) {
73 return None;
74 }
75 }
76
77 Some(self.keystrokes.len() > typed.len())
78 }
79
80 /// Get the keystrokes associated with this binding
81 pub fn keystrokes(&self) -> &[Keystroke] {
82 self.keystrokes.as_slice()
83 }
84
85 /// Get the action associated with this binding
86 pub fn action(&self) -> &dyn Action {
87 self.action.as_ref()
88 }
89
90 /// Get the predicate used to match this binding
91 pub fn predicate(&self) -> Option<&KeyBindingContextPredicate> {
92 self.context_predicate.as_ref()
93 }
94}
95
96impl std::fmt::Debug for KeyBinding {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.debug_struct("KeyBinding")
99 .field("keystrokes", &self.keystrokes)
100 .field("context_predicate", &self.context_predicate)
101 .field("action", &self.action.name())
102 .finish()
103 }
104}