matcher.rs

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