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