matcher.rs

  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// }