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