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