1use std::rc::Rc;
2
3use collections::HashMap;
4
5use crate::{Action, InvalidKeystrokeError, KeyBindingContextPredicate, Keystroke, SharedString};
6use smallvec::SmallVec;
7
8/// A keybinding and its associated metadata, from the keymap.
9pub struct KeyBinding {
10 pub(crate) action: Box<dyn Action>,
11 pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
12 pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
13 pub(crate) meta: Option<KeyBindingMetaIndex>,
14 /// The json input string used when building the keybinding, if any
15 pub(crate) action_input: Option<SharedString>,
16}
17
18impl Clone for KeyBinding {
19 fn clone(&self) -> Self {
20 KeyBinding {
21 action: self.action.boxed_clone(),
22 keystrokes: self.keystrokes.clone(),
23 context_predicate: self.context_predicate.clone(),
24 meta: self.meta,
25 action_input: self.action_input.clone(),
26 }
27 }
28}
29
30impl KeyBinding {
31 /// Construct a new keybinding from the given data. Panics on parse error.
32 pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
33 let context_predicate = if let Some(context) = context {
34 Some(KeyBindingContextPredicate::parse(context).unwrap().into())
35 } else {
36 None
37 };
38 Self::load(keystrokes, Box::new(action), context_predicate, None, None).unwrap()
39 }
40
41 /// Load a keybinding from the given raw data.
42 pub fn load(
43 keystrokes: &str,
44 action: Box<dyn Action>,
45 context_predicate: Option<Rc<KeyBindingContextPredicate>>,
46 key_equivalents: Option<&HashMap<char, char>>,
47 action_input: Option<SharedString>,
48 ) -> std::result::Result<Self, InvalidKeystrokeError> {
49 let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
50 .split_whitespace()
51 .map(Keystroke::parse)
52 .collect::<std::result::Result<_, _>>()?;
53
54 if let Some(equivalents) = key_equivalents {
55 for keystroke in keystrokes.iter_mut() {
56 if keystroke.key.chars().count() == 1
57 && let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap()) {
58 keystroke.key = key.to_string();
59 }
60 }
61 }
62
63 Ok(Self {
64 keystrokes,
65 action,
66 context_predicate,
67 meta: None,
68 action_input,
69 })
70 }
71
72 /// Set the metadata for this binding.
73 pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self {
74 self.meta = Some(meta);
75 self
76 }
77
78 /// Set the metadata for this binding.
79 pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) {
80 self.meta = Some(meta);
81 }
82
83 /// Check if the given keystrokes match this binding.
84 pub fn match_keystrokes(&self, typed: &[Keystroke]) -> Option<bool> {
85 if self.keystrokes.len() < typed.len() {
86 return None;
87 }
88
89 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
90 if !typed.should_match(target) {
91 return None;
92 }
93 }
94
95 Some(self.keystrokes.len() > typed.len())
96 }
97
98 /// Get the keystrokes associated with this binding
99 pub fn keystrokes(&self) -> &[Keystroke] {
100 self.keystrokes.as_slice()
101 }
102
103 /// Get the action associated with this binding
104 pub fn action(&self) -> &dyn Action {
105 self.action.as_ref()
106 }
107
108 /// Get the predicate used to match this binding
109 pub fn predicate(&self) -> Option<Rc<KeyBindingContextPredicate>> {
110 self.context_predicate.as_ref().map(|rc| rc.clone())
111 }
112
113 /// Get the metadata for this binding
114 pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
115 self.meta
116 }
117
118 /// Get the action input associated with the action for this binding
119 pub fn action_input(&self) -> Option<SharedString> {
120 self.action_input.clone()
121 }
122}
123
124impl std::fmt::Debug for KeyBinding {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 f.debug_struct("KeyBinding")
127 .field("keystrokes", &self.keystrokes)
128 .field("context_predicate", &self.context_predicate)
129 .field("action", &self.action.name())
130 .finish()
131 }
132}
133
134/// A unique identifier for retrieval of metadata associated with a key binding.
135/// Intended to be used as an index or key into a user-defined store of metadata
136/// associated with the binding, such as the source of the binding.
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138pub struct KeyBindingMetaIndex(pub u32);