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