keymap_matcher.rs

  1mod binding;
  2mod keymap;
  3mod keymap_context;
  4mod keystroke;
  5
  6use std::{any::TypeId, fmt::Debug};
  7
  8use collections::HashMap;
  9use smallvec::SmallVec;
 10
 11use crate::Action;
 12
 13pub use binding::{Binding, BindingMatchResult};
 14pub use keymap::Keymap;
 15pub use keymap_context::{KeymapContext, KeymapContextPredicate};
 16pub use keystroke::Keystroke;
 17
 18pub struct KeymapMatcher {
 19    pub contexts: Vec<KeymapContext>,
 20    pending_views: HashMap<usize, KeymapContext>,
 21    pending_keystrokes: Vec<Keystroke>,
 22    keymap: Keymap,
 23}
 24
 25impl KeymapMatcher {
 26    pub fn new(keymap: Keymap) -> Self {
 27        Self {
 28            contexts: Vec::new(),
 29            pending_views: Default::default(),
 30            pending_keystrokes: Vec::new(),
 31            keymap,
 32        }
 33    }
 34
 35    pub fn set_keymap(&mut self, keymap: Keymap) {
 36        self.clear_pending();
 37        self.keymap = keymap;
 38    }
 39
 40    pub fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
 41        self.clear_pending();
 42        self.keymap.add_bindings(bindings);
 43    }
 44
 45    pub fn clear_bindings(&mut self) {
 46        self.clear_pending();
 47        self.keymap.clear();
 48    }
 49
 50    pub fn bindings_for_action_type(&self, action_type: TypeId) -> impl Iterator<Item = &Binding> {
 51        self.keymap.bindings_for_action_type(action_type)
 52    }
 53
 54    pub fn clear_pending(&mut self) {
 55        self.pending_keystrokes.clear();
 56        self.pending_views.clear();
 57    }
 58
 59    pub fn has_pending_keystrokes(&self) -> bool {
 60        !self.pending_keystrokes.is_empty()
 61    }
 62
 63    pub fn push_keystroke(
 64        &mut self,
 65        keystroke: Keystroke,
 66        mut dispatch_path: Vec<(usize, KeymapContext)>,
 67    ) -> MatchResult {
 68        let mut any_pending = false;
 69        let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Vec::new();
 70
 71        let first_keystroke = self.pending_keystrokes.is_empty();
 72        self.pending_keystrokes.push(keystroke.clone());
 73
 74        self.contexts.clear();
 75        self.contexts
 76            .extend(dispatch_path.iter_mut().map(|e| std::mem::take(&mut e.1)));
 77
 78        for (i, (view_id, _)) in dispatch_path.into_iter().enumerate() {
 79            // Don't require pending view entry if there are no pending keystrokes
 80            if !first_keystroke && !self.pending_views.contains_key(&view_id) {
 81                continue;
 82            }
 83
 84            // If there is a previous view context, invalidate that view if it
 85            // has changed
 86            if let Some(previous_view_context) = self.pending_views.remove(&view_id) {
 87                if previous_view_context != self.contexts[i] {
 88                    continue;
 89                }
 90            }
 91
 92            // Find the bindings which map the pending keystrokes and current context
 93            for binding in self.keymap.bindings().iter().rev() {
 94                match binding.match_keys_and_context(&self.pending_keystrokes, &self.contexts[i..])
 95                {
 96                    BindingMatchResult::Complete(action) => {
 97                        matched_bindings.push((view_id, action))
 98                    }
 99                    BindingMatchResult::Partial => {
100                        self.pending_views.insert(view_id, self.contexts[i].clone());
101                        any_pending = true;
102                    }
103                    _ => {}
104                }
105            }
106        }
107
108        if !any_pending {
109            self.clear_pending();
110        }
111
112        if !matched_bindings.is_empty() {
113            MatchResult::Matches(matched_bindings)
114        } else if any_pending {
115            MatchResult::Pending
116        } else {
117            MatchResult::None
118        }
119    }
120
121    pub fn keystrokes_for_action(
122        &self,
123        action: &dyn Action,
124        contexts: &[KeymapContext],
125    ) -> Option<SmallVec<[Keystroke; 2]>> {
126        self.keymap
127            .bindings()
128            .iter()
129            .rev()
130            .find_map(|binding| binding.keystrokes_for_action(action, contexts))
131    }
132}
133
134impl Default for KeymapMatcher {
135    fn default() -> Self {
136        Self::new(Keymap::default())
137    }
138}
139
140pub enum MatchResult {
141    None,
142    Pending,
143    Matches(Vec<(usize, Box<dyn Action>)>),
144}
145
146impl Debug for MatchResult {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        match self {
149            MatchResult::None => f.debug_struct("MatchResult::None").finish(),
150            MatchResult::Pending => f.debug_struct("MatchResult::Pending").finish(),
151            MatchResult::Matches(matches) => f
152                .debug_list()
153                .entries(
154                    matches
155                        .iter()
156                        .map(|(view_id, action)| format!("{view_id}, {}", action.name())),
157                )
158                .finish(),
159        }
160    }
161}
162
163impl PartialEq for MatchResult {
164    fn eq(&self, other: &Self) -> bool {
165        match (self, other) {
166            (MatchResult::None, MatchResult::None) => true,
167            (MatchResult::Pending, MatchResult::Pending) => true,
168            (MatchResult::Matches(matches), MatchResult::Matches(other_matches)) => {
169                matches.len() == other_matches.len()
170                    && matches.iter().zip(other_matches.iter()).all(
171                        |((view_id, action), (other_view_id, other_action))| {
172                            view_id == other_view_id && action.eq(other_action.as_ref())
173                        },
174                    )
175            }
176            _ => false,
177        }
178    }
179}
180
181impl Eq for MatchResult {}
182
183impl Clone for MatchResult {
184    fn clone(&self) -> Self {
185        match self {
186            MatchResult::None => MatchResult::None,
187            MatchResult::Pending => MatchResult::Pending,
188            MatchResult::Matches(matches) => MatchResult::Matches(
189                matches
190                    .iter()
191                    .map(|(view_id, action)| (*view_id, Action::boxed_clone(action.as_ref())))
192                    .collect(),
193            ),
194        }
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use anyhow::Result;
201    use serde::Deserialize;
202
203    use crate::{actions, impl_actions, keymap_matcher::KeymapContext};
204
205    use super::*;
206
207    #[test]
208    fn test_push_keystroke() -> Result<()> {
209        actions!(test, [B, AB, C, D, DA]);
210
211        let mut context1 = KeymapContext::default();
212        context1.set.insert("1".into());
213
214        let mut context2 = KeymapContext::default();
215        context2.set.insert("2".into());
216
217        let dispatch_path = vec![(2, context2), (1, context1)];
218
219        let keymap = Keymap::new(vec![
220            Binding::new("a b", AB, Some("1")),
221            Binding::new("b", B, Some("2")),
222            Binding::new("c", C, Some("2")),
223            Binding::new("d", D, Some("1")),
224            Binding::new("d", D, Some("2")),
225            Binding::new("d a", DA, Some("2")),
226        ]);
227
228        let mut matcher = KeymapMatcher::new(keymap);
229
230        // Binding with pending prefix always takes precedence
231        assert_eq!(
232            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
233            MatchResult::Pending,
234        );
235        // B alone doesn't match because a was pending, so AB is returned instead
236        assert_eq!(
237            matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
238            MatchResult::Matches(vec![(1, Box::new(AB))]),
239        );
240        assert!(!matcher.has_pending_keystrokes());
241
242        // Without an a prefix, B is dispatched like expected
243        assert_eq!(
244            matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
245            MatchResult::Matches(vec![(2, Box::new(B))]),
246        );
247        assert!(!matcher.has_pending_keystrokes());
248
249        // If a is prefixed, C will not be dispatched because there
250        // was a pending binding for it
251        assert_eq!(
252            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
253            MatchResult::Pending,
254        );
255        assert_eq!(
256            matcher.push_keystroke(Keystroke::parse("c")?, dispatch_path.clone()),
257            MatchResult::None,
258        );
259        assert!(!matcher.has_pending_keystrokes());
260
261        // If a single keystroke matches multiple bindings in the tree
262        // all of them are returned so that we can fallback if the action
263        // handler decides to propagate the action
264        assert_eq!(
265            matcher.push_keystroke(Keystroke::parse("d")?, dispatch_path.clone()),
266            MatchResult::Matches(vec![(2, Box::new(D)), (1, Box::new(D))]),
267        );
268        // If none of the d action handlers consume the binding, a pending
269        // binding may then be used
270        assert_eq!(
271            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
272            MatchResult::Matches(vec![(2, Box::new(DA))]),
273        );
274        assert!(!matcher.has_pending_keystrokes());
275
276        Ok(())
277    }
278
279    #[test]
280    fn test_keystroke_parsing() -> Result<()> {
281        assert_eq!(
282            Keystroke::parse("ctrl-p")?,
283            Keystroke {
284                key: "p".into(),
285                ctrl: true,
286                alt: false,
287                shift: false,
288                cmd: false,
289                function: false,
290            }
291        );
292
293        assert_eq!(
294            Keystroke::parse("alt-shift-down")?,
295            Keystroke {
296                key: "down".into(),
297                ctrl: false,
298                alt: true,
299                shift: true,
300                cmd: false,
301                function: false,
302            }
303        );
304
305        assert_eq!(
306            Keystroke::parse("shift-cmd--")?,
307            Keystroke {
308                key: "-".into(),
309                ctrl: false,
310                alt: false,
311                shift: true,
312                cmd: true,
313                function: false,
314            }
315        );
316
317        Ok(())
318    }
319
320    #[test]
321    fn test_context_predicate_parsing() -> Result<()> {
322        use KeymapContextPredicate::*;
323
324        assert_eq!(
325            KeymapContextPredicate::parse("a && (b == c || d != e)")?,
326            And(
327                Box::new(Identifier("a".into())),
328                Box::new(Or(
329                    Box::new(Equal("b".into(), "c".into())),
330                    Box::new(NotEqual("d".into(), "e".into())),
331                ))
332            )
333        );
334
335        assert_eq!(
336            KeymapContextPredicate::parse("!a")?,
337            Not(Box::new(Identifier("a".into())),)
338        );
339
340        Ok(())
341    }
342
343    #[test]
344    fn test_context_predicate_eval() {
345        let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap();
346
347        let mut context = KeymapContext::default();
348        context.set.insert("a".into());
349        assert!(!predicate.eval(&[context]));
350
351        let mut context = KeymapContext::default();
352        context.set.insert("a".into());
353        context.set.insert("b".into());
354        assert!(predicate.eval(&[context]));
355
356        let mut context = KeymapContext::default();
357        context.set.insert("a".into());
358        context.map.insert("c".into(), "x".into());
359        assert!(!predicate.eval(&[context]));
360
361        let mut context = KeymapContext::default();
362        context.set.insert("a".into());
363        context.map.insert("c".into(), "d".into());
364        assert!(predicate.eval(&[context]));
365
366        let predicate = KeymapContextPredicate::parse("!a").unwrap();
367        assert!(predicate.eval(&[KeymapContext::default()]));
368    }
369
370    #[test]
371    fn test_context_child_predicate_eval() {
372        let predicate = KeymapContextPredicate::parse("a && b > c").unwrap();
373        let contexts = [
374            context_set(&["e", "f"]),
375            context_set(&["c", "d"]), // match this context
376            context_set(&["a", "b"]),
377        ];
378
379        assert!(!predicate.eval(&contexts[0..]));
380        assert!(predicate.eval(&contexts[1..]));
381        assert!(!predicate.eval(&contexts[2..]));
382
383        let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap();
384        let contexts = [
385            context_set(&["f"]),
386            context_set(&["e"]), // only match this context
387            context_set(&["c"]),
388            context_set(&["a", "b"]),
389            context_set(&["e"]),
390            context_set(&["c", "d"]),
391            context_set(&["a", "b"]),
392        ];
393
394        assert!(!predicate.eval(&contexts[0..]));
395        assert!(predicate.eval(&contexts[1..]));
396        assert!(!predicate.eval(&contexts[2..]));
397        assert!(!predicate.eval(&contexts[3..]));
398        assert!(!predicate.eval(&contexts[4..]));
399        assert!(!predicate.eval(&contexts[5..]));
400        assert!(!predicate.eval(&contexts[6..]));
401
402        fn context_set(names: &[&str]) -> KeymapContext {
403            KeymapContext {
404                set: names.iter().copied().map(str::to_string).collect(),
405                ..Default::default()
406            }
407        }
408    }
409
410    #[test]
411    fn test_matcher() -> Result<()> {
412        #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
413        pub struct A(pub String);
414        impl_actions!(test, [A]);
415        actions!(test, [B, Ab]);
416
417        #[derive(Clone, Debug, Eq, PartialEq)]
418        struct ActionArg {
419            a: &'static str,
420        }
421
422        let keymap = Keymap::new(vec![
423            Binding::new("a", A("x".to_string()), Some("a")),
424            Binding::new("b", B, Some("a")),
425            Binding::new("a b", Ab, Some("a || b")),
426        ]);
427
428        let mut context_a = KeymapContext::default();
429        context_a.set.insert("a".into());
430
431        let mut context_b = KeymapContext::default();
432        context_b.set.insert("b".into());
433
434        let mut matcher = KeymapMatcher::new(keymap);
435
436        // Basic match
437        assert_eq!(
438            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
439            MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
440        );
441        matcher.clear_pending();
442
443        // Multi-keystroke match
444        assert_eq!(
445            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
446            MatchResult::Pending
447        );
448        assert_eq!(
449            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
450            MatchResult::Matches(vec![(1, Box::new(Ab))])
451        );
452        matcher.clear_pending();
453
454        // Failed matches don't interfere with matching subsequent keys
455        assert_eq!(
456            matcher.push_keystroke(Keystroke::parse("x")?, vec![(1, context_a.clone())]),
457            MatchResult::None
458        );
459        assert_eq!(
460            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
461            MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
462        );
463        matcher.clear_pending();
464
465        // Pending keystrokes are cleared when the context changes
466        assert_eq!(
467            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
468            MatchResult::Pending
469        );
470        assert_eq!(
471            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_a.clone())]),
472            MatchResult::None
473        );
474        matcher.clear_pending();
475
476        let mut context_c = KeymapContext::default();
477        context_c.set.insert("c".into());
478
479        // Pending keystrokes are maintained per-view
480        assert_eq!(
481            matcher.push_keystroke(
482                Keystroke::parse("a")?,
483                vec![(1, context_b.clone()), (2, context_c.clone())]
484            ),
485            MatchResult::Pending
486        );
487        assert_eq!(
488            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
489            MatchResult::Matches(vec![(1, Box::new(Ab))])
490        );
491
492        Ok(())
493    }
494}