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