keymap.rs

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