gpui/keymap/
collection.rs1use crate::{Action, DummyKeyboardMapper, KeyBinding, KeyBindingContextPredicate, SharedString};
2use std::{any::TypeId, collections::HashMap};
3
4#[derive(Default)]
28pub struct ActionBindingCollection {
29 entries: HashMap<TypeId, ActionBindingEntry>,
30}
31
32#[derive(Debug)]
33struct ActionBindingEntry {
34 action: Box<dyn Action>,
36 keystrokes: Vec<SharedString>,
39}
40
41impl ActionBindingCollection {
42 pub fn with<A: Action + Default>(mut self, keystrokes: impl Into<SharedString>) -> Self {
49 let action_id = TypeId::of::<A>();
50 let entry = self.entries.entry(action_id);
51 let entry = entry.or_insert_with(|| ActionBindingEntry {
52 action: Box::new(A::default()),
53 keystrokes: Vec::with_capacity(5),
54 });
55 entry.keystrokes.push(keystrokes.into());
56 self
57 }
58
59 pub fn as_keybindings(&self, context: Option<&str>) -> impl Iterator<Item = KeyBinding> {
72 let context_predicate =
73 context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
74 let iter = self.entries.iter();
75 let iter = iter.map(move |(_, entry)| {
76 let action = &entry.action;
77 let context_predicate = context_predicate.clone();
78 entry.keystrokes.iter().map(move |keystrokes| {
79 let keybinding = KeyBinding::load(
80 keystrokes.as_str(),
81 action.boxed_clone(),
82 context_predicate.clone(),
83 false,
84 None,
85 &DummyKeyboardMapper,
86 );
87 keybinding.expect("failed to load keybinding")
88 })
89 });
90 iter.flatten()
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::TestAppContext;
98
99 actions!([TestAction]);
100
101 #[gpui::test]
102 fn test_default_empty(_cx: &mut TestAppContext) {
103 assert!(ActionBindingCollection::default().entries.is_empty());
104 }
105
106 #[gpui::test]
107 fn test_single_binding(_cx: &mut TestAppContext) {
108 let collection = ActionBindingCollection::default();
109 let collection = collection.with::<TestAction>("enter");
110
111 let entry = collection.entries.get(&TypeId::of::<TestAction>());
112 let entry = entry.map(|entry| &entry.keystrokes);
113 assert_eq!(entry, Some(&vec![SharedString::new_static("enter")]));
114
115 let out_bindings = collection.as_keybindings(None).collect::<Vec<_>>();
116 assert_eq!(out_bindings.len(), 1);
117 }
118
119 #[gpui::test]
120 fn test_single_action_many_keys(_cx: &mut TestAppContext) {
121 let collection = ActionBindingCollection::default();
122 let collection = collection.with::<TestAction>("enter");
123 let collection = collection.with::<TestAction>("escape");
124
125 let entry = collection.entries.get(&TypeId::of::<TestAction>());
126 let entry = entry.map(|entry| &entry.keystrokes);
127 assert_eq!(
128 entry,
129 Some(&vec![
130 SharedString::new_static("enter"),
131 SharedString::new_static("escape")
132 ])
133 );
134
135 let out_bindings = collection.as_keybindings(None).collect::<Vec<_>>();
136 assert_eq!(out_bindings.len(), 2);
137 }
138
139 #[gpui::test]
140 fn test_single_action_many_keys_context(_cx: &mut TestAppContext) {
141 let collection = ActionBindingCollection::default();
142 let collection = collection.with::<TestAction>("enter");
143 let collection = collection.with::<TestAction>("escape");
144 let out_bindings = collection
145 .as_keybindings(Some("test_content"))
146 .collect::<Vec<_>>();
147 assert_eq!(out_bindings.len(), 2);
148 }
149}