1use crate::{KeyBinding, KeyBindingContextPredicate, Keystroke, NoAction};
2use collections::HashSet;
3use smallvec::SmallVec;
4use std::{
5 any::{Any, TypeId},
6 collections::HashMap,
7};
8
9#[derive(Copy, Clone, Eq, PartialEq, Default)]
10pub struct KeymapVersion(usize);
11
12#[derive(Default)]
13pub struct Keymap {
14 bindings: Vec<KeyBinding>,
15 binding_indices_by_action_id: HashMap<TypeId, SmallVec<[usize; 3]>>,
16 disabled_keystrokes:
17 HashMap<SmallVec<[Keystroke; 2]>, HashSet<Option<KeyBindingContextPredicate>>>,
18 version: KeymapVersion,
19}
20
21impl Keymap {
22 pub fn new(bindings: Vec<KeyBinding>) -> Self {
23 let mut this = Self::default();
24 this.add_bindings(bindings);
25 this
26 }
27
28 pub fn version(&self) -> KeymapVersion {
29 self.version
30 }
31
32 pub fn bindings_for_action(&self, action_id: TypeId) -> impl Iterator<Item = &'_ KeyBinding> {
33 self.binding_indices_by_action_id
34 .get(&action_id)
35 .map(SmallVec::as_slice)
36 .unwrap_or(&[])
37 .iter()
38 .map(|ix| &self.bindings[*ix])
39 .filter(|binding| !self.binding_disabled(binding))
40 }
41
42 pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
43 let no_action_id = &(NoAction {}).type_id();
44 let mut new_bindings = Vec::new();
45 let mut has_new_disabled_keystrokes = false;
46 for binding in bindings {
47 if binding.action.type_id() == *no_action_id {
48 has_new_disabled_keystrokes |= self
49 .disabled_keystrokes
50 .entry(binding.keystrokes)
51 .or_default()
52 .insert(binding.context_predicate);
53 } else {
54 new_bindings.push(binding);
55 }
56 }
57
58 if has_new_disabled_keystrokes {
59 self.binding_indices_by_action_id.retain(|_, indices| {
60 indices.retain(|ix| {
61 let binding = &self.bindings[*ix];
62 match self.disabled_keystrokes.get(&binding.keystrokes) {
63 Some(disabled_predicates) => {
64 !disabled_predicates.contains(&binding.context_predicate)
65 }
66 None => true,
67 }
68 });
69 !indices.is_empty()
70 });
71 }
72
73 for new_binding in new_bindings {
74 if !self.binding_disabled(&new_binding) {
75 self.binding_indices_by_action_id
76 .entry(new_binding.action().as_any().type_id())
77 .or_default()
78 .push(self.bindings.len());
79 self.bindings.push(new_binding);
80 }
81 }
82
83 self.version.0 += 1;
84 }
85
86 pub fn clear(&mut self) {
87 self.bindings.clear();
88 self.binding_indices_by_action_id.clear();
89 self.disabled_keystrokes.clear();
90 self.version.0 += 1;
91 }
92
93 pub fn bindings(&self) -> Vec<&KeyBinding> {
94 self.bindings
95 .iter()
96 .filter(|binding| !self.binding_disabled(binding))
97 .collect()
98 }
99
100 fn binding_disabled(&self, binding: &KeyBinding) -> bool {
101 match self.disabled_keystrokes.get(&binding.keystrokes) {
102 Some(disabled_predicates) => disabled_predicates.contains(&binding.context_predicate),
103 None => false,
104 }
105 }
106}
107
108// #[cfg(test)]
109// mod tests {
110// use crate::actions;
111
112// use super::*;
113
114// actions!(
115// keymap_test,
116// [Present1, Present2, Present3, Duplicate, Missing]
117// );
118
119// #[test]
120// fn regular_keymap() {
121// let present_1 = Binding::new("ctrl-q", Present1 {}, None);
122// let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
123// let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
124// let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
125// let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
126// let missing = Binding::new("ctrl-r", Missing {}, None);
127// let all_bindings = [
128// &present_1,
129// &present_2,
130// &present_3,
131// &keystroke_duplicate_to_1,
132// &full_duplicate_to_2,
133// &missing,
134// ];
135
136// let mut keymap = Keymap::default();
137// assert_absent(&keymap, &all_bindings);
138// assert!(keymap.bindings().is_empty());
139
140// keymap.add_bindings([present_1.clone(), present_2.clone(), present_3.clone()]);
141// assert_absent(&keymap, &[&keystroke_duplicate_to_1, &missing]);
142// assert_present(
143// &keymap,
144// &[(&present_1, "q"), (&present_2, "w"), (&present_3, "e")],
145// );
146
147// keymap.add_bindings([
148// keystroke_duplicate_to_1.clone(),
149// full_duplicate_to_2.clone(),
150// ]);
151// assert_absent(&keymap, &[&missing]);
152// assert!(
153// !keymap.binding_disabled(&keystroke_duplicate_to_1),
154// "Duplicate binding 1 was added and should not be disabled"
155// );
156// assert!(
157// !keymap.binding_disabled(&full_duplicate_to_2),
158// "Duplicate binding 2 was added and should not be disabled"
159// );
160
161// assert_eq!(
162// keymap
163// .bindings_for_action(keystroke_duplicate_to_1.action().id())
164// .map(|binding| &binding.keystrokes)
165// .flatten()
166// .collect::<Vec<_>>(),
167// vec![&Keystroke {
168// ctrl: true,
169// alt: false,
170// shift: false,
171// cmd: false,
172// function: false,
173// key: "q".to_string(),
174// ime_key: None,
175// }],
176// "{keystroke_duplicate_to_1:?} should have the expected keystroke in the keymap"
177// );
178// assert_eq!(
179// keymap
180// .bindings_for_action(full_duplicate_to_2.action().id())
181// .map(|binding| &binding.keystrokes)
182// .flatten()
183// .collect::<Vec<_>>(),
184// vec![
185// &Keystroke {
186// ctrl: true,
187// alt: false,
188// shift: false,
189// cmd: false,
190// function: false,
191// key: "w".to_string(),
192// ime_key: None,
193// },
194// &Keystroke {
195// ctrl: true,
196// alt: false,
197// shift: false,
198// cmd: false,
199// function: false,
200// key: "w".to_string(),
201// ime_key: None,
202// }
203// ],
204// "{full_duplicate_to_2:?} should have a duplicated keystroke in the keymap"
205// );
206
207// let updated_bindings = keymap.bindings();
208// let expected_updated_bindings = vec![
209// &present_1,
210// &present_2,
211// &present_3,
212// &keystroke_duplicate_to_1,
213// &full_duplicate_to_2,
214// ];
215// assert_eq!(
216// updated_bindings.len(),
217// expected_updated_bindings.len(),
218// "Unexpected updated keymap bindings {updated_bindings:?}"
219// );
220// for (i, expected) in expected_updated_bindings.iter().enumerate() {
221// let keymap_binding = &updated_bindings[i];
222// assert_eq!(
223// keymap_binding.context_predicate, expected.context_predicate,
224// "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
225// );
226// assert_eq!(
227// keymap_binding.keystrokes, expected.keystrokes,
228// "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
229// );
230// }
231
232// keymap.clear();
233// assert_absent(&keymap, &all_bindings);
234// assert!(keymap.bindings().is_empty());
235// }
236
237// #[test]
238// fn keymap_with_ignored() {
239// let present_1 = Binding::new("ctrl-q", Present1 {}, None);
240// let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
241// let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
242// let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
243// let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
244// let ignored_1 = Binding::new("ctrl-q", NoAction {}, None);
245// let ignored_2 = Binding::new("ctrl-w", NoAction {}, Some("pane"));
246// let ignored_3_with_other_context =
247// Binding::new("ctrl-e", NoAction {}, Some("other_context"));
248
249// let mut keymap = Keymap::default();
250
251// keymap.add_bindings([
252// ignored_1.clone(),
253// ignored_2.clone(),
254// ignored_3_with_other_context.clone(),
255// ]);
256// assert_absent(&keymap, &[&present_3]);
257// assert_disabled(
258// &keymap,
259// &[
260// &present_1,
261// &present_2,
262// &ignored_1,
263// &ignored_2,
264// &ignored_3_with_other_context,
265// ],
266// );
267// assert!(keymap.bindings().is_empty());
268// keymap.clear();
269
270// keymap.add_bindings([
271// present_1.clone(),
272// present_2.clone(),
273// present_3.clone(),
274// ignored_1.clone(),
275// ignored_2.clone(),
276// ignored_3_with_other_context.clone(),
277// ]);
278// assert_present(&keymap, &[(&present_3, "e")]);
279// assert_disabled(
280// &keymap,
281// &[
282// &present_1,
283// &present_2,
284// &ignored_1,
285// &ignored_2,
286// &ignored_3_with_other_context,
287// ],
288// );
289// keymap.clear();
290
291// keymap.add_bindings([
292// present_1.clone(),
293// present_2.clone(),
294// present_3.clone(),
295// ignored_1.clone(),
296// ]);
297// assert_present(&keymap, &[(&present_2, "w"), (&present_3, "e")]);
298// assert_disabled(&keymap, &[&present_1, &ignored_1]);
299// assert_absent(&keymap, &[&ignored_2, &ignored_3_with_other_context]);
300// keymap.clear();
301
302// keymap.add_bindings([
303// present_1.clone(),
304// present_2.clone(),
305// present_3.clone(),
306// keystroke_duplicate_to_1.clone(),
307// full_duplicate_to_2.clone(),
308// ignored_1.clone(),
309// ignored_2.clone(),
310// ignored_3_with_other_context.clone(),
311// ]);
312// assert_present(&keymap, &[(&present_3, "e")]);
313// assert_disabled(
314// &keymap,
315// &[
316// &present_1,
317// &present_2,
318// &keystroke_duplicate_to_1,
319// &full_duplicate_to_2,
320// &ignored_1,
321// &ignored_2,
322// &ignored_3_with_other_context,
323// ],
324// );
325// keymap.clear();
326// }
327
328// #[track_caller]
329// fn assert_present(keymap: &Keymap, expected_bindings: &[(&Binding, &str)]) {
330// let keymap_bindings = keymap.bindings();
331// assert_eq!(
332// expected_bindings.len(),
333// keymap_bindings.len(),
334// "Unexpected keymap bindings {keymap_bindings:?}"
335// );
336// for (i, (expected, expected_key)) in expected_bindings.iter().enumerate() {
337// assert!(
338// !keymap.binding_disabled(expected),
339// "{expected:?} should not be disabled as it was added into keymap for element {i}"
340// );
341// assert_eq!(
342// keymap
343// .bindings_for_action(expected.action().id())
344// .map(|binding| &binding.keystrokes)
345// .flatten()
346// .collect::<Vec<_>>(),
347// vec![&Keystroke {
348// ctrl: true,
349// alt: false,
350// shift: false,
351// cmd: false,
352// function: false,
353// key: expected_key.to_string(),
354// ime_key: None,
355// }],
356// "{expected:?} should have the expected keystroke with key '{expected_key}' in the keymap for element {i}"
357// );
358
359// let keymap_binding = &keymap_bindings[i];
360// assert_eq!(
361// keymap_binding.context_predicate, expected.context_predicate,
362// "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
363// );
364// assert_eq!(
365// keymap_binding.keystrokes, expected.keystrokes,
366// "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
367// );
368// }
369// }
370
371// #[track_caller]
372// fn assert_absent(keymap: &Keymap, bindings: &[&Binding]) {
373// for binding in bindings.iter() {
374// assert!(
375// !keymap.binding_disabled(binding),
376// "{binding:?} should not be disabled in the keymap where was not added"
377// );
378// assert_eq!(
379// keymap.bindings_for_action(binding.action().id()).count(),
380// 0,
381// "{binding:?} should have no actions in the keymap where was not added"
382// );
383// }
384// }
385
386// #[track_caller]
387// fn assert_disabled(keymap: &Keymap, bindings: &[&Binding]) {
388// for binding in bindings.iter() {
389// assert!(
390// keymap.binding_disabled(binding),
391// "{binding:?} should be disabled in the keymap"
392// );
393// assert_eq!(
394// keymap.bindings_for_action(binding.action().id()).count(),
395// 0,
396// "{binding:?} should have no actions in the keymap where it was disabled"
397// );
398// }
399// }
400// }