1use crate::{Action, DispatchContext, Keymap, KeymapVersion, Keystroke};
2use parking_lot::Mutex;
3use smallvec::SmallVec;
4use std::sync::Arc;
5
6pub struct KeyMatcher {
7 pending_keystrokes: Vec<Keystroke>,
8 keymap: Arc<Mutex<Keymap>>,
9 keymap_version: KeymapVersion,
10}
11
12impl KeyMatcher {
13 pub fn new(keymap: Arc<Mutex<Keymap>>) -> Self {
14 let keymap_version = keymap.lock().version();
15 Self {
16 pending_keystrokes: Vec::new(),
17 keymap_version,
18 keymap,
19 }
20 }
21
22 // todo!("replace with a function that calls an FnMut for every binding matching the action")
23 // pub fn bindings_for_action(&self, action_id: TypeId) -> impl Iterator<Item = &Binding> {
24 // self.keymap.lock().bindings_for_action(action_id)
25 // }
26
27 pub fn clear_pending(&mut self) {
28 self.pending_keystrokes.clear();
29 }
30
31 pub fn has_pending_keystrokes(&self) -> bool {
32 !self.pending_keystrokes.is_empty()
33 }
34
35 /// Pushes a keystroke onto the matcher.
36 /// The result of the new keystroke is returned:
37 /// KeyMatch::None =>
38 /// No match is valid for this key given any pending keystrokes.
39 /// KeyMatch::Pending =>
40 /// There exist bindings which are still waiting for more keys.
41 /// KeyMatch::Complete(matches) =>
42 /// One or more bindings have received the necessary key presses.
43 /// Bindings added later will take precedence over earlier bindings.
44 pub fn match_keystroke(
45 &mut self,
46 keystroke: &Keystroke,
47 context_stack: &[&DispatchContext],
48 ) -> KeyMatch {
49 let keymap = self.keymap.lock();
50 // Clear pending keystrokes if the keymap has changed since the last matched keystroke.
51 if keymap.version() != self.keymap_version {
52 self.keymap_version = keymap.version();
53 self.pending_keystrokes.clear();
54 }
55
56 let mut pending_key = None;
57
58 for binding in keymap.bindings().iter().rev() {
59 for candidate in keystroke.match_candidates() {
60 self.pending_keystrokes.push(candidate.clone());
61 match binding.match_keystrokes(&self.pending_keystrokes, context_stack) {
62 KeyMatch::Some(action) => {
63 self.pending_keystrokes.clear();
64 return KeyMatch::Some(action);
65 }
66 KeyMatch::Pending => {
67 pending_key.get_or_insert(candidate);
68 }
69 KeyMatch::None => {}
70 }
71 self.pending_keystrokes.pop();
72 }
73 }
74
75 if let Some(pending_key) = pending_key {
76 self.pending_keystrokes.push(pending_key);
77 }
78
79 if self.pending_keystrokes.is_empty() {
80 KeyMatch::None
81 } else {
82 KeyMatch::Pending
83 }
84 }
85
86 pub fn keystrokes_for_action(
87 &self,
88 action: &dyn Action,
89 contexts: &[&DispatchContext],
90 ) -> Option<SmallVec<[Keystroke; 2]>> {
91 self.keymap
92 .lock()
93 .bindings()
94 .iter()
95 .rev()
96 .find_map(|binding| binding.keystrokes_for_action(action, contexts))
97 }
98}
99
100pub enum KeyMatch {
101 None,
102 Pending,
103 Some(Box<dyn Action>),
104}
105
106impl KeyMatch {
107 pub fn is_some(&self) -> bool {
108 matches!(self, KeyMatch::Some(_))
109 }
110}
111
112// #[cfg(test)]
113// mod tests {
114// use anyhow::Result;
115// use serde::Deserialize;
116
117// use crate::{actions, impl_actions, keymap_matcher::ActionContext};
118
119// use super::*;
120
121// #[test]
122// fn test_keymap_and_view_ordering() -> Result<()> {
123// actions!(test, [EditorAction, ProjectPanelAction]);
124
125// let mut editor = ActionContext::default();
126// editor.add_identifier("Editor");
127
128// let mut project_panel = ActionContext::default();
129// project_panel.add_identifier("ProjectPanel");
130
131// // Editor 'deeper' in than project panel
132// let dispatch_path = vec![(2, editor), (1, project_panel)];
133
134// // But editor actions 'higher' up in keymap
135// let keymap = Keymap::new(vec![
136// Binding::new("left", EditorAction, Some("Editor")),
137// Binding::new("left", ProjectPanelAction, Some("ProjectPanel")),
138// ]);
139
140// let mut matcher = KeymapMatcher::new(keymap);
141
142// assert_eq!(
143// matcher.match_keystroke(Keystroke::parse("left")?, dispatch_path.clone()),
144// KeyMatch::Matches(vec![
145// (2, Box::new(EditorAction)),
146// (1, Box::new(ProjectPanelAction)),
147// ]),
148// );
149
150// Ok(())
151// }
152
153// #[test]
154// fn test_push_keystroke() -> Result<()> {
155// actions!(test, [B, AB, C, D, DA, E, EF]);
156
157// let mut context1 = ActionContext::default();
158// context1.add_identifier("1");
159
160// let mut context2 = ActionContext::default();
161// context2.add_identifier("2");
162
163// let dispatch_path = vec![(2, context2), (1, context1)];
164
165// let keymap = Keymap::new(vec![
166// Binding::new("a b", AB, Some("1")),
167// Binding::new("b", B, Some("2")),
168// Binding::new("c", C, Some("2")),
169// Binding::new("d", D, Some("1")),
170// Binding::new("d", D, Some("2")),
171// Binding::new("d a", DA, Some("2")),
172// ]);
173
174// let mut matcher = KeymapMatcher::new(keymap);
175
176// // Binding with pending prefix always takes precedence
177// assert_eq!(
178// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
179// KeyMatch::Pending,
180// );
181// // B alone doesn't match because a was pending, so AB is returned instead
182// assert_eq!(
183// matcher.match_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
184// KeyMatch::Matches(vec![(1, Box::new(AB))]),
185// );
186// assert!(!matcher.has_pending_keystrokes());
187
188// // Without an a prefix, B is dispatched like expected
189// assert_eq!(
190// matcher.match_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
191// KeyMatch::Matches(vec![(2, Box::new(B))]),
192// );
193// assert!(!matcher.has_pending_keystrokes());
194
195// // If a is prefixed, C will not be dispatched because there
196// // was a pending binding for it
197// assert_eq!(
198// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
199// KeyMatch::Pending,
200// );
201// assert_eq!(
202// matcher.match_keystroke(Keystroke::parse("c")?, dispatch_path.clone()),
203// KeyMatch::None,
204// );
205// assert!(!matcher.has_pending_keystrokes());
206
207// // If a single keystroke matches multiple bindings in the tree
208// // all of them are returned so that we can fallback if the action
209// // handler decides to propagate the action
210// assert_eq!(
211// matcher.match_keystroke(Keystroke::parse("d")?, dispatch_path.clone()),
212// KeyMatch::Matches(vec![(2, Box::new(D)), (1, Box::new(D))]),
213// );
214
215// // If none of the d action handlers consume the binding, a pending
216// // binding may then be used
217// assert_eq!(
218// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
219// KeyMatch::Matches(vec![(2, Box::new(DA))]),
220// );
221// assert!(!matcher.has_pending_keystrokes());
222
223// Ok(())
224// }
225
226// #[test]
227// fn test_keystroke_parsing() -> Result<()> {
228// assert_eq!(
229// Keystroke::parse("ctrl-p")?,
230// Keystroke {
231// key: "p".into(),
232// ctrl: true,
233// alt: false,
234// shift: false,
235// cmd: false,
236// function: false,
237// ime_key: None,
238// }
239// );
240
241// assert_eq!(
242// Keystroke::parse("alt-shift-down")?,
243// Keystroke {
244// key: "down".into(),
245// ctrl: false,
246// alt: true,
247// shift: true,
248// cmd: false,
249// function: false,
250// ime_key: None,
251// }
252// );
253
254// assert_eq!(
255// Keystroke::parse("shift-cmd--")?,
256// Keystroke {
257// key: "-".into(),
258// ctrl: false,
259// alt: false,
260// shift: true,
261// cmd: true,
262// function: false,
263// ime_key: None,
264// }
265// );
266
267// Ok(())
268// }
269
270// #[test]
271// fn test_context_predicate_parsing() -> Result<()> {
272// use KeymapContextPredicate::*;
273
274// assert_eq!(
275// KeymapContextPredicate::parse("a && (b == c || d != e)")?,
276// And(
277// Box::new(Identifier("a".into())),
278// Box::new(Or(
279// Box::new(Equal("b".into(), "c".into())),
280// Box::new(NotEqual("d".into(), "e".into())),
281// ))
282// )
283// );
284
285// assert_eq!(
286// KeymapContextPredicate::parse("!a")?,
287// Not(Box::new(Identifier("a".into())),)
288// );
289
290// Ok(())
291// }
292
293// #[test]
294// fn test_context_predicate_eval() {
295// let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap();
296
297// let mut context = ActionContext::default();
298// context.add_identifier("a");
299// assert!(!predicate.eval(&[context]));
300
301// let mut context = ActionContext::default();
302// context.add_identifier("a");
303// context.add_identifier("b");
304// assert!(predicate.eval(&[context]));
305
306// let mut context = ActionContext::default();
307// context.add_identifier("a");
308// context.add_key("c", "x");
309// assert!(!predicate.eval(&[context]));
310
311// let mut context = ActionContext::default();
312// context.add_identifier("a");
313// context.add_key("c", "d");
314// assert!(predicate.eval(&[context]));
315
316// let predicate = KeymapContextPredicate::parse("!a").unwrap();
317// assert!(predicate.eval(&[ActionContext::default()]));
318// }
319
320// #[test]
321// fn test_context_child_predicate_eval() {
322// let predicate = KeymapContextPredicate::parse("a && b > c").unwrap();
323// let contexts = [
324// context_set(&["e", "f"]),
325// context_set(&["c", "d"]), // match this context
326// context_set(&["a", "b"]),
327// ];
328
329// assert!(!predicate.eval(&contexts[0..]));
330// assert!(predicate.eval(&contexts[1..]));
331// assert!(!predicate.eval(&contexts[2..]));
332
333// let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap();
334// let contexts = [
335// context_set(&["f"]),
336// context_set(&["e"]), // only match this context
337// context_set(&["c"]),
338// context_set(&["a", "b"]),
339// context_set(&["e"]),
340// context_set(&["c", "d"]),
341// context_set(&["a", "b"]),
342// ];
343
344// assert!(!predicate.eval(&contexts[0..]));
345// assert!(predicate.eval(&contexts[1..]));
346// assert!(!predicate.eval(&contexts[2..]));
347// assert!(!predicate.eval(&contexts[3..]));
348// assert!(!predicate.eval(&contexts[4..]));
349// assert!(!predicate.eval(&contexts[5..]));
350// assert!(!predicate.eval(&contexts[6..]));
351
352// fn context_set(names: &[&str]) -> ActionContext {
353// let mut keymap = ActionContext::new();
354// names
355// .iter()
356// .for_each(|name| keymap.add_identifier(name.to_string()));
357// keymap
358// }
359// }
360
361// #[test]
362// fn test_matcher() -> Result<()> {
363// #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
364// pub struct A(pub String);
365// impl_actions!(test, [A]);
366// actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]);
367
368// #[derive(Clone, Debug, Eq, PartialEq)]
369// struct ActionArg {
370// a: &'static str,
371// }
372
373// let keymap = Keymap::new(vec![
374// Binding::new("a", A("x".to_string()), Some("a")),
375// Binding::new("b", B, Some("a")),
376// Binding::new("a b", Ab, Some("a || b")),
377// Binding::new("$", Dollar, Some("a")),
378// Binding::new("\"", Quote, Some("a")),
379// Binding::new("alt-s", Ess, Some("a")),
380// Binding::new("ctrl-`", Backtick, Some("a")),
381// ]);
382
383// let mut context_a = ActionContext::default();
384// context_a.add_identifier("a");
385
386// let mut context_b = ActionContext::default();
387// context_b.add_identifier("b");
388
389// let mut matcher = KeymapMatcher::new(keymap);
390
391// // Basic match
392// assert_eq!(
393// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
394// KeyMatch::Matches(vec![(1, Box::new(A("x".to_string())))])
395// );
396// matcher.clear_pending();
397
398// // Multi-keystroke match
399// assert_eq!(
400// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
401// KeyMatch::Pending
402// );
403// assert_eq!(
404// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
405// KeyMatch::Matches(vec![(1, Box::new(Ab))])
406// );
407// matcher.clear_pending();
408
409// // Failed matches don't interfere with matching subsequent keys
410// assert_eq!(
411// matcher.match_keystroke(Keystroke::parse("x")?, vec![(1, context_a.clone())]),
412// KeyMatch::None
413// );
414// assert_eq!(
415// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
416// KeyMatch::Matches(vec![(1, Box::new(A("x".to_string())))])
417// );
418// matcher.clear_pending();
419
420// // Pending keystrokes are cleared when the context changes
421// assert_eq!(
422// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
423// KeyMatch::Pending
424// );
425// assert_eq!(
426// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_a.clone())]),
427// KeyMatch::None
428// );
429// matcher.clear_pending();
430
431// let mut context_c = ActionContext::default();
432// context_c.add_identifier("c");
433
434// // Pending keystrokes are maintained per-view
435// assert_eq!(
436// matcher.match_keystroke(
437// Keystroke::parse("a")?,
438// vec![(1, context_b.clone()), (2, context_c.clone())]
439// ),
440// KeyMatch::Pending
441// );
442// assert_eq!(
443// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
444// KeyMatch::Matches(vec![(1, Box::new(Ab))])
445// );
446
447// // handle Czech $ (option + 4 key)
448// assert_eq!(
449// matcher.match_keystroke(Keystroke::parse("alt-รง->$")?, vec![(1, context_a.clone())]),
450// KeyMatch::Matches(vec![(1, Box::new(Dollar))])
451// );
452
453// // handle Brazillian quote (quote key then space key)
454// assert_eq!(
455// matcher.match_keystroke(Keystroke::parse("space->\"")?, vec![(1, context_a.clone())]),
456// KeyMatch::Matches(vec![(1, Box::new(Quote))])
457// );
458
459// // handle ctrl+` on a brazillian keyboard
460// assert_eq!(
461// matcher.match_keystroke(Keystroke::parse("ctrl-->`")?, vec![(1, context_a.clone())]),
462// KeyMatch::Matches(vec![(1, Box::new(Backtick))])
463// );
464
465// // handle alt-s on a US keyboard
466// assert_eq!(
467// matcher.match_keystroke(Keystroke::parse("alt-s->ร")?, vec![(1, context_a.clone())]),
468// KeyMatch::Matches(vec![(1, Box::new(Ess))])
469// );
470
471// Ok(())
472// }
473// }