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