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