keymap.rs

  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// }