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