matcher.rs

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