keymap_matcher.rs

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