keymap.rs

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