1mod binding;
2mod keymap;
3mod keymap_context;
4mod keystroke;
5
6use std::{any::TypeId, fmt::Debug};
7
8use collections::HashMap;
9use serde::Deserialize;
10use smallvec::SmallVec;
11
12use crate::{impl_actions, Action};
13
14pub use binding::{Binding, BindingMatchResult};
15pub use keymap::Keymap;
16pub use keymap_context::{KeymapContext, KeymapContextPredicate};
17pub use keystroke::Keystroke;
18
19#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize)]
20pub struct KeyPressed {
21 #[serde(default)]
22 pub keystroke: Keystroke,
23}
24
25impl_actions!(gpui, [KeyPressed]);
26
27pub struct KeymapMatcher {
28 pending_views: HashMap<usize, KeymapContext>,
29 pending_keystrokes: Vec<Keystroke>,
30 keymap: Keymap,
31}
32
33impl KeymapMatcher {
34 pub fn new(keymap: Keymap) -> Self {
35 Self {
36 pending_views: Default::default(),
37 pending_keystrokes: Vec::new(),
38 keymap,
39 }
40 }
41
42 pub fn set_keymap(&mut self, keymap: Keymap) {
43 self.clear_pending();
44 self.keymap = keymap;
45 }
46
47 pub fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
48 self.clear_pending();
49 self.keymap.add_bindings(bindings);
50 }
51
52 pub fn clear_bindings(&mut self) {
53 self.clear_pending();
54 self.keymap.clear();
55 }
56
57 pub fn bindings_for_action_type(&self, action_type: TypeId) -> impl Iterator<Item = &Binding> {
58 self.keymap.bindings_for_action_type(action_type)
59 }
60
61 pub fn clear_pending(&mut self) {
62 self.pending_keystrokes.clear();
63 self.pending_views.clear();
64 }
65
66 pub fn has_pending_keystrokes(&self) -> bool {
67 !self.pending_keystrokes.is_empty()
68 }
69
70 pub fn push_keystroke(
71 &mut self,
72 keystroke: Keystroke,
73 dispatch_path: Vec<(usize, KeymapContext)>,
74 ) -> MatchResult {
75 let mut any_pending = false;
76 let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Vec::new();
77
78 let first_keystroke = self.pending_keystrokes.is_empty();
79 self.pending_keystrokes.push(keystroke.clone());
80
81 for (view_id, context) in dispatch_path {
82 // Don't require pending view entry if there are no pending keystrokes
83 if !first_keystroke && !self.pending_views.contains_key(&view_id) {
84 continue;
85 }
86
87 // If there is a previous view context, invalidate that view if it
88 // has changed
89 if let Some(previous_view_context) = self.pending_views.remove(&view_id) {
90 if previous_view_context != context {
91 continue;
92 }
93 }
94
95 // Find the bindings which map the pending keystrokes and current context
96 for binding in self.keymap.bindings().iter().rev() {
97 match binding.match_keys_and_context(&self.pending_keystrokes, &context) {
98 BindingMatchResult::Complete(mut action) => {
99 // Swap in keystroke for special KeyPressed action
100 if action.name() == "KeyPressed" && action.namespace() == "gpui" {
101 action = Box::new(KeyPressed {
102 keystroke: keystroke.clone(),
103 });
104 }
105 matched_bindings.push((view_id, action))
106 }
107 BindingMatchResult::Partial => {
108 self.pending_views.insert(view_id, context.clone());
109 any_pending = true;
110 }
111 _ => {}
112 }
113 }
114 }
115
116 if !any_pending {
117 self.clear_pending();
118 }
119
120 if !matched_bindings.is_empty() {
121 MatchResult::Matches(matched_bindings)
122 } else if any_pending {
123 MatchResult::Pending
124 } else {
125 MatchResult::None
126 }
127 }
128
129 pub fn keystrokes_for_action(
130 &self,
131 action: &dyn Action,
132 context: &KeymapContext,
133 ) -> Option<SmallVec<[Keystroke; 2]>> {
134 self.keymap
135 .bindings()
136 .iter()
137 .rev()
138 .find_map(|binding| binding.keystrokes_for_action(action, context))
139 }
140}
141
142impl Default for KeymapMatcher {
143 fn default() -> Self {
144 Self::new(Keymap::default())
145 }
146}
147
148pub enum MatchResult {
149 None,
150 Pending,
151 Matches(Vec<(usize, Box<dyn Action>)>),
152}
153
154impl Debug for MatchResult {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 MatchResult::None => f.debug_struct("MatchResult::None").finish(),
158 MatchResult::Pending => f.debug_struct("MatchResult::Pending").finish(),
159 MatchResult::Matches(matches) => f
160 .debug_list()
161 .entries(
162 matches
163 .iter()
164 .map(|(view_id, action)| format!("{view_id}, {}", action.name())),
165 )
166 .finish(),
167 }
168 }
169}
170
171impl PartialEq for MatchResult {
172 fn eq(&self, other: &Self) -> bool {
173 match (self, other) {
174 (MatchResult::None, MatchResult::None) => true,
175 (MatchResult::Pending, MatchResult::Pending) => true,
176 (MatchResult::Matches(matches), MatchResult::Matches(other_matches)) => {
177 matches.len() == other_matches.len()
178 && matches.iter().zip(other_matches.iter()).all(
179 |((view_id, action), (other_view_id, other_action))| {
180 view_id == other_view_id && action.eq(other_action.as_ref())
181 },
182 )
183 }
184 _ => false,
185 }
186 }
187}
188
189impl Eq for MatchResult {}
190
191impl Clone for MatchResult {
192 fn clone(&self) -> Self {
193 match self {
194 MatchResult::None => MatchResult::None,
195 MatchResult::Pending => MatchResult::Pending,
196 MatchResult::Matches(matches) => MatchResult::Matches(
197 matches
198 .iter()
199 .map(|(view_id, action)| (*view_id, Action::boxed_clone(action.as_ref())))
200 .collect(),
201 ),
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use anyhow::Result;
209 use serde::Deserialize;
210
211 use crate::{actions, impl_actions, keymap_matcher::KeymapContext};
212
213 use super::*;
214
215 #[test]
216 fn test_push_keystroke() -> Result<()> {
217 actions!(test, [B, AB, C, D, DA]);
218
219 let mut context1 = KeymapContext::default();
220 context1.set.insert("1".into());
221
222 let mut context2 = KeymapContext::default();
223 context2.set.insert("2".into());
224
225 let dispatch_path = vec![(2, context2), (1, context1)];
226
227 let keymap = Keymap::new(vec![
228 Binding::new("a b", AB, Some("1")),
229 Binding::new("b", B, Some("2")),
230 Binding::new("c", C, Some("2")),
231 Binding::new("d", D, Some("1")),
232 Binding::new("d", D, Some("2")),
233 Binding::new("d a", DA, Some("2")),
234 ]);
235
236 let mut matcher = KeymapMatcher::new(keymap);
237
238 // Binding with pending prefix always takes precedence
239 assert_eq!(
240 matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
241 MatchResult::Pending,
242 );
243 // B alone doesn't match because a was pending, so AB is returned instead
244 assert_eq!(
245 matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
246 MatchResult::Matches(vec![(1, Box::new(AB))]),
247 );
248 assert!(!matcher.has_pending_keystrokes());
249
250 // Without an a prefix, B is dispatched like expected
251 assert_eq!(
252 matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
253 MatchResult::Matches(vec![(2, Box::new(B))]),
254 );
255 assert!(!matcher.has_pending_keystrokes());
256
257 // If a is prefixed, C will not be dispatched because there
258 // was a pending binding for it
259 assert_eq!(
260 matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
261 MatchResult::Pending,
262 );
263 assert_eq!(
264 matcher.push_keystroke(Keystroke::parse("c")?, dispatch_path.clone()),
265 MatchResult::None,
266 );
267 assert!(!matcher.has_pending_keystrokes());
268
269 // If a single keystroke matches multiple bindings in the tree
270 // all of them are returned so that we can fallback if the action
271 // handler decides to propagate the action
272 assert_eq!(
273 matcher.push_keystroke(Keystroke::parse("d")?, dispatch_path.clone()),
274 MatchResult::Matches(vec![(2, Box::new(D)), (1, Box::new(D))]),
275 );
276 // If none of the d action handlers consume the binding, a pending
277 // binding may then be used
278 assert_eq!(
279 matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
280 MatchResult::Matches(vec![(2, Box::new(DA))]),
281 );
282 assert!(!matcher.has_pending_keystrokes());
283
284 Ok(())
285 }
286
287 #[test]
288 fn test_keystroke_parsing() -> Result<()> {
289 assert_eq!(
290 Keystroke::parse("ctrl-p")?,
291 Keystroke {
292 key: "p".into(),
293 ctrl: true,
294 alt: false,
295 shift: false,
296 cmd: false,
297 function: false,
298 }
299 );
300
301 assert_eq!(
302 Keystroke::parse("alt-shift-down")?,
303 Keystroke {
304 key: "down".into(),
305 ctrl: false,
306 alt: true,
307 shift: true,
308 cmd: false,
309 function: false,
310 }
311 );
312
313 assert_eq!(
314 Keystroke::parse("shift-cmd--")?,
315 Keystroke {
316 key: "-".into(),
317 ctrl: false,
318 alt: false,
319 shift: true,
320 cmd: true,
321 function: false,
322 }
323 );
324
325 Ok(())
326 }
327
328 #[test]
329 fn test_context_predicate_parsing() -> Result<()> {
330 use KeymapContextPredicate::*;
331
332 assert_eq!(
333 KeymapContextPredicate::parse("a && (b == c || d != e)")?,
334 And(
335 Box::new(Identifier("a".into())),
336 Box::new(Or(
337 Box::new(Equal("b".into(), "c".into())),
338 Box::new(NotEqual("d".into(), "e".into())),
339 ))
340 )
341 );
342
343 assert_eq!(
344 KeymapContextPredicate::parse("!a")?,
345 Not(Box::new(Identifier("a".into())),)
346 );
347
348 Ok(())
349 }
350
351 #[test]
352 fn test_context_predicate_eval() -> Result<()> {
353 let predicate = KeymapContextPredicate::parse("a && b || c == d")?;
354
355 let mut context = KeymapContext::default();
356 context.set.insert("a".into());
357 assert!(!predicate.eval(&context));
358
359 context.set.insert("b".into());
360 assert!(predicate.eval(&context));
361
362 context.set.remove("b");
363 context.map.insert("c".into(), "x".into());
364 assert!(!predicate.eval(&context));
365
366 context.map.insert("c".into(), "d".into());
367 assert!(predicate.eval(&context));
368
369 let predicate = KeymapContextPredicate::parse("!a")?;
370 assert!(predicate.eval(&KeymapContext::default()));
371
372 Ok(())
373 }
374
375 #[test]
376 fn test_matcher() -> Result<()> {
377 #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
378 pub struct A(pub String);
379 impl_actions!(test, [A]);
380 actions!(test, [B, Ab]);
381
382 #[derive(Clone, Debug, Eq, PartialEq)]
383 struct ActionArg {
384 a: &'static str,
385 }
386
387 let keymap = Keymap::new(vec![
388 Binding::new("a", A("x".to_string()), Some("a")),
389 Binding::new("b", B, Some("a")),
390 Binding::new("a b", Ab, Some("a || b")),
391 ]);
392
393 let mut context_a = KeymapContext::default();
394 context_a.set.insert("a".into());
395
396 let mut context_b = KeymapContext::default();
397 context_b.set.insert("b".into());
398
399 let mut matcher = KeymapMatcher::new(keymap);
400
401 // Basic match
402 assert_eq!(
403 matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
404 MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
405 );
406 matcher.clear_pending();
407
408 // Multi-keystroke match
409 assert_eq!(
410 matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
411 MatchResult::Pending
412 );
413 assert_eq!(
414 matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
415 MatchResult::Matches(vec![(1, Box::new(Ab))])
416 );
417 matcher.clear_pending();
418
419 // Failed matches don't interfere with matching subsequent keys
420 assert_eq!(
421 matcher.push_keystroke(Keystroke::parse("x")?, vec![(1, context_a.clone())]),
422 MatchResult::None
423 );
424 assert_eq!(
425 matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
426 MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
427 );
428 matcher.clear_pending();
429
430 // Pending keystrokes are cleared when the context changes
431 assert_eq!(
432 matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
433 MatchResult::Pending
434 );
435 assert_eq!(
436 matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_a.clone())]),
437 MatchResult::None
438 );
439 matcher.clear_pending();
440
441 let mut context_c = KeymapContext::default();
442 context_c.set.insert("c".into());
443
444 // Pending keystrokes are maintained per-view
445 assert_eq!(
446 matcher.push_keystroke(
447 Keystroke::parse("a")?,
448 vec![(1, context_b.clone()), (2, context_c.clone())]
449 ),
450 MatchResult::Pending
451 );
452 assert_eq!(
453 matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
454 MatchResult::Matches(vec![(1, Box::new(Ab))])
455 );
456
457 Ok(())
458 }
459}