gpui/keymap/
collection.rs1use crate::{Action, DummyKeyboardMapper, KeyBinding, KeyBindingContextPredicate, SharedString};
2use std::{any::TypeId, collections::HashMap};
3
4#[derive(Default)]
16pub struct ActionBindingCollection {
17 entries: HashMap<TypeId, ActionBindingEntry>,
18}
19
20#[derive(Debug)]
21struct ActionBindingEntry {
22 action: Box<dyn Action>,
24 keystrokes: Vec<SharedString>,
27}
28
29impl ActionBindingCollection {
30 pub fn with<A: Action + Default>(mut self, keystrokes: impl Into<SharedString>) -> Self {
32 let action_id = TypeId::of::<A>();
33 let entry = self.entries.entry(action_id);
34 let entry = entry.or_insert_with(|| ActionBindingEntry {
35 action: Box::new(A::default()),
36 keystrokes: Vec::with_capacity(5),
37 });
38 entry.keystrokes.push(keystrokes.into());
39 self
40 }
41
42 pub fn as_keybindings(&self, context: Option<&str>) -> impl Iterator<Item = KeyBinding> {
45 let context_predicate =
46 context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
47 let iter = self.entries.iter();
48 let iter = iter.map(move |(_, entry)| {
49 let action = &entry.action;
50 let context_predicate = context_predicate.clone();
51 entry.keystrokes.iter().map(move |keystrokes| {
52 let keybinding = KeyBinding::load(
53 keystrokes.as_str(),
54 action.boxed_clone(),
55 context_predicate.clone(),
56 false,
57 None,
58 &DummyKeyboardMapper,
59 );
60 keybinding.expect("failed to load keybinding")
61 })
62 });
63 iter.flatten()
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::TestAppContext;
71
72 actions!([TestAction]);
73
74 #[gpui::test]
75 fn test_default_empty(_cx: &mut TestAppContext) {
76 assert!(ActionBindingCollection::default().entries.is_empty());
77 }
78
79 #[gpui::test]
80 fn test_single_binding(_cx: &mut TestAppContext) {
81 let collection = ActionBindingCollection::default();
82 let collection = collection.with::<TestAction>("enter");
83
84 let entry = collection.entries.get(&TypeId::of::<TestAction>());
85 let entry = entry.map(|entry| &entry.keystrokes);
86 assert_eq!(entry, Some(&vec![SharedString::new_static("enter")]));
87
88 let out_bindings = collection.as_keybindings(None).collect::<Vec<_>>();
89 assert_eq!(out_bindings.len(), 1);
90 }
91
92 #[gpui::test]
93 fn test_single_action_many_keys(_cx: &mut TestAppContext) {
94 let collection = ActionBindingCollection::default();
95 let collection = collection.with::<TestAction>("enter");
96 let collection = collection.with::<TestAction>("escape");
97
98 let entry = collection.entries.get(&TypeId::of::<TestAction>());
99 let entry = entry.map(|entry| &entry.keystrokes);
100 assert_eq!(
101 entry,
102 Some(&vec![
103 SharedString::new_static("enter"),
104 SharedString::new_static("escape")
105 ])
106 );
107
108 let out_bindings = collection.as_keybindings(None).collect::<Vec<_>>();
109 assert_eq!(out_bindings.len(), 2);
110 }
111
112 #[gpui::test]
113 fn test_single_action_many_keys_context(_cx: &mut TestAppContext) {
114 let collection = ActionBindingCollection::default();
115 let collection = collection.with::<TestAction>("enter");
116 let collection = collection.with::<TestAction>("escape");
117 let out_bindings = collection
118 .as_keybindings(Some("test_content"))
119 .collect::<Vec<_>>();
120 assert_eq!(out_bindings.len(), 2);
121 }
122}