1mod binding;
2mod context;
3
4pub use binding::*;
5pub use context::*;
6
7use crate::{Action, AsKeystroke, Keystroke, Unbind, is_no_action, is_unbind};
8use open_gpui_collections::{HashSet, TypeIdHashMap};
9use smallvec::SmallVec;
10
11#[derive(Copy, Clone, Eq, PartialEq, Default)]
14pub struct KeymapVersion(usize);
15
16#[derive(Default)]
18pub struct Keymap {
19 bindings: Vec<KeyBinding>,
20 binding_indices_by_action_id: TypeIdHashMap<SmallVec<[usize; 3]>>,
21 disabled_binding_indices: Vec<usize>,
22 version: KeymapVersion,
23}
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
27pub struct BindingIndex(usize);
28
29fn disabled_binding_matches_context(disabled_binding: &KeyBinding, binding: &KeyBinding) -> bool {
30 match (
31 &disabled_binding.context_predicate,
32 &binding.context_predicate,
33 ) {
34 (None, _) => true,
35 (Some(_), None) => false,
36 (Some(disabled_predicate), Some(predicate)) => disabled_predicate.is_superset(predicate),
37 }
38}
39
40fn binding_is_unbound(disabled_binding: &KeyBinding, binding: &KeyBinding) -> bool {
41 disabled_binding.keystrokes == binding.keystrokes
42 && disabled_binding
43 .action()
44 .as_any()
45 .downcast_ref::<Unbind>()
46 .is_some_and(|unbind| unbind.0.as_ref() == binding.action.name())
47}
48
49impl Keymap {
50 pub fn new(bindings: Vec<KeyBinding>) -> Self {
52 let mut this = Self::default();
53 this.add_bindings(bindings);
54 this
55 }
56
57 pub fn version(&self) -> KeymapVersion {
59 self.version
60 }
61
62 pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
64 for binding in bindings {
65 let action_id = binding.action().as_any().type_id();
66 if is_no_action(&*binding.action) || is_unbind(&*binding.action) {
67 self.disabled_binding_indices.push(self.bindings.len());
68 } else {
69 self.binding_indices_by_action_id
70 .entry(action_id)
71 .or_default()
72 .push(self.bindings.len());
73 }
74 self.bindings.push(binding);
75 }
76
77 self.version.0 += 1;
78 }
79
80 pub fn clear(&mut self) {
82 self.bindings.clear();
83 self.binding_indices_by_action_id.clear();
84 self.disabled_binding_indices.clear();
85 self.version.0 += 1;
86 }
87
88 pub fn bindings(&self) -> impl DoubleEndedIterator<Item = &KeyBinding> + ExactSizeIterator {
90 self.bindings.iter()
91 }
92
93 pub fn bindings_for_action<'a>(
96 &'a self,
97 action: &'a dyn Action,
98 ) -> impl 'a + DoubleEndedIterator<Item = &'a KeyBinding> {
99 let action_id = action.type_id();
100 let binding_indices = self
101 .binding_indices_by_action_id
102 .get(&action_id)
103 .map_or(&[] as _, SmallVec::as_slice)
104 .iter();
105
106 binding_indices.filter_map(|ix| {
107 let binding = &self.bindings[*ix];
108 if !binding.action().partial_eq(action) {
109 return None;
110 }
111
112 for disabled_ix in &self.disabled_binding_indices {
113 if disabled_ix > ix {
114 let disabled_binding = &self.bindings[*disabled_ix];
115 if disabled_binding.keystrokes != binding.keystrokes {
116 continue;
117 }
118
119 if is_no_action(&*disabled_binding.action) {
120 if disabled_binding_matches_context(disabled_binding, binding) {
121 return None;
122 }
123 } else if is_unbind(&*disabled_binding.action)
124 && disabled_binding_matches_context(disabled_binding, binding)
125 && binding_is_unbound(disabled_binding, binding)
126 {
127 return None;
128 }
129 }
130 }
131
132 Some(binding)
133 })
134 }
135
136 pub fn all_bindings_for_input(&self, input: &[Keystroke]) -> Vec<KeyBinding> {
139 self.bindings()
140 .rev()
141 .filter(|binding| {
142 binding
143 .match_keystrokes(input)
144 .is_some_and(|pending| !pending)
145 })
146 .cloned()
147 .collect()
148 }
149
150 pub fn bindings_for_input(
165 &self,
166 input: &[impl AsKeystroke],
167 context_stack: &[KeyContext],
168 ) -> (SmallVec<[KeyBinding; 1]>, bool) {
169 let mut matched_bindings = SmallVec::<[(usize, BindingIndex, &KeyBinding); 1]>::new();
170 let mut pending_bindings = SmallVec::<[(BindingIndex, &KeyBinding); 1]>::new();
171
172 for (ix, binding) in self.bindings().enumerate().rev() {
173 let Some(depth) = self.binding_enabled(binding, context_stack) else {
174 continue;
175 };
176 let Some(pending) = binding.match_keystrokes(input) else {
177 continue;
178 };
179
180 if !pending {
181 matched_bindings.push((depth, BindingIndex(ix), binding));
182 } else {
183 pending_bindings.push((BindingIndex(ix), binding));
184 }
185 }
186
187 matched_bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| {
188 depth_b.cmp(depth_a).then(ix_b.cmp(ix_a))
189 });
190
191 let mut bindings: SmallVec<[_; 1]> = SmallVec::new();
192 let mut first_binding_index = None;
193 let mut unbound_bindings: Vec<&KeyBinding> = Vec::new();
194
195 for (_, ix, binding) in matched_bindings {
196 if is_no_action(&*binding.action) {
197 if let Some(meta) = binding.meta {
200 if meta.0 == 0 {
201 break;
202 }
203 } else {
204 break;
206 }
207 continue;
209 }
210
211 if is_unbind(&*binding.action) {
212 unbound_bindings.push(binding);
213 continue;
214 }
215
216 if unbound_bindings
217 .iter()
218 .any(|disabled_binding| binding_is_unbound(disabled_binding, binding))
219 {
220 continue;
221 }
222
223 bindings.push(binding.clone());
224 first_binding_index.get_or_insert(ix);
225 }
226
227 let mut pending = HashSet::default();
228 for (ix, binding) in pending_bindings.into_iter().rev() {
229 if let Some(binding_ix) = first_binding_index
230 && binding_ix > ix
231 {
232 continue;
233 }
234 if is_no_action(&*binding.action) || is_unbind(&*binding.action) {
235 pending.remove(&&binding.keystrokes);
236 continue;
237 }
238 pending.insert(&binding.keystrokes);
239 }
240
241 (bindings, !pending.is_empty())
242 }
243 fn binding_enabled(&self, binding: &KeyBinding, contexts: &[KeyContext]) -> Option<usize> {
246 if let Some(predicate) = &binding.context_predicate {
247 predicate.depth_of(contexts)
248 } else {
249 Some(contexts.len())
250 }
251 }
252
253 pub fn possible_next_bindings_for_input(
255 &self,
256 input: &[Keystroke],
257 context_stack: &[KeyContext],
258 ) -> Vec<KeyBinding> {
259 let mut bindings = self
260 .bindings()
261 .enumerate()
262 .rev()
263 .filter_map(|(ix, binding)| {
264 let depth = self.binding_enabled(binding, context_stack)?;
265 let pending = binding.match_keystrokes(input);
266 match pending {
267 None => None,
268 Some(is_pending) => {
269 if !is_pending
270 || is_no_action(&*binding.action)
271 || is_unbind(&*binding.action)
272 {
273 return None;
274 }
275 Some((depth, BindingIndex(ix), binding))
276 }
277 }
278 })
279 .collect::<Vec<_>>();
280
281 bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| {
282 depth_b.cmp(depth_a).then(ix_b.cmp(ix_a))
283 });
284
285 bindings
286 .into_iter()
287 .map(|(_, _, binding)| binding.clone())
288 .collect::<Vec<_>>()
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use open_gpui::{NoAction, Unbind};
296
297 actions!(
298 test_only,
299 [ActionAlpha, ActionBeta, ActionGamma, ActionDelta,]
300 );
301
302 #[test]
303 fn test_keymap() {
304 let bindings = [
305 KeyBinding::new("ctrl-a", ActionAlpha {}, None),
306 KeyBinding::new("ctrl-a", ActionBeta {}, Some("pane")),
307 KeyBinding::new("ctrl-a", ActionGamma {}, Some("editor && mode==full")),
308 ];
309
310 let mut keymap = Keymap::default();
311 keymap.add_bindings(bindings.clone());
312
313 assert_eq!(keymap.binding_enabled(&bindings[0], &[]), Some(0));
315 assert_eq!(
316 keymap.binding_enabled(&bindings[0], &[KeyContext::parse("terminal").unwrap()]),
317 Some(1)
318 );
319
320 assert_eq!(
322 keymap.binding_enabled(&bindings[1], &[KeyContext::parse("barf x=y").unwrap()]),
323 None
324 );
325 assert_eq!(
326 keymap.binding_enabled(&bindings[1], &[KeyContext::parse("pane x=y").unwrap()]),
327 Some(1)
328 );
329
330 assert_eq!(
331 keymap.binding_enabled(&bindings[2], &[KeyContext::parse("editor").unwrap()]),
332 None
333 );
334 assert_eq!(
335 keymap.binding_enabled(
336 &bindings[2],
337 &[KeyContext::parse("editor mode=full").unwrap()]
338 ),
339 Some(1)
340 );
341 }
342
343 #[test]
344 fn test_depth_precedence() {
345 let bindings = [
346 KeyBinding::new("ctrl-a", ActionBeta {}, Some("pane")),
347 KeyBinding::new("ctrl-a", ActionGamma {}, Some("editor")),
348 ];
349
350 let mut keymap = Keymap::default();
351 keymap.add_bindings(bindings);
352
353 let (result, pending) = keymap.bindings_for_input(
354 &[Keystroke::parse("ctrl-a").unwrap()],
355 &[
356 KeyContext::parse("pane").unwrap(),
357 KeyContext::parse("editor").unwrap(),
358 ],
359 );
360
361 assert!(!pending);
362 assert_eq!(result.len(), 2);
363 assert!(result[0].action.partial_eq(&ActionGamma {}));
364 assert!(result[1].action.partial_eq(&ActionBeta {}));
365 }
366
367 #[test]
368 fn test_keymap_disabled() {
369 let bindings = [
370 KeyBinding::new("ctrl-a", ActionAlpha {}, Some("editor")),
371 KeyBinding::new("ctrl-b", ActionAlpha {}, Some("editor")),
372 KeyBinding::new("ctrl-a", NoAction {}, Some("editor && mode==full")),
373 KeyBinding::new("ctrl-b", NoAction {}, None),
374 ];
375
376 let mut keymap = Keymap::default();
377 keymap.add_bindings(bindings);
378
379 assert!(
381 keymap
382 .bindings_for_input(
383 &[Keystroke::parse("ctrl-a").unwrap()],
384 &[KeyContext::parse("barf").unwrap()],
385 )
386 .0
387 .is_empty()
388 );
389 assert!(
390 !keymap
391 .bindings_for_input(
392 &[Keystroke::parse("ctrl-a").unwrap()],
393 &[KeyContext::parse("editor").unwrap()],
394 )
395 .0
396 .is_empty()
397 );
398
399 assert!(
401 keymap
402 .bindings_for_input(
403 &[Keystroke::parse("ctrl-a").unwrap()],
404 &[KeyContext::parse("editor mode=full").unwrap()],
405 )
406 .0
407 .is_empty()
408 );
409
410 assert!(
412 keymap
413 .bindings_for_input(
414 &[Keystroke::parse("ctrl-b").unwrap()],
415 &[KeyContext::parse("barf").unwrap()],
416 )
417 .0
418 .is_empty()
419 );
420 }
421
422 #[test]
423 fn test_multiple_keystroke_binding_disabled() {
425 let bindings = [
426 KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
427 KeyBinding::new("space w w", NoAction {}, Some("editor")),
428 ];
429
430 let mut keymap = Keymap::default();
431 keymap.add_bindings(bindings);
432
433 let space = || Keystroke::parse("space").unwrap();
434 let w = || Keystroke::parse("w").unwrap();
435
436 let space_w = [space(), w()];
437 let space_w_w = [space(), w(), w()];
438
439 let workspace_context = || [KeyContext::parse("workspace").unwrap()];
440
441 let editor_workspace_context = || {
442 [
443 KeyContext::parse("workspace").unwrap(),
444 KeyContext::parse("editor").unwrap(),
445 ]
446 };
447
448 let space_workspace = keymap.bindings_for_input(&[space()], &workspace_context());
450 assert!(space_workspace.0.is_empty());
451 assert!(space_workspace.1);
452
453 let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context());
454 assert!(space_editor.0.is_empty());
455 assert!(!space_editor.1);
456
457 let space_w_workspace = keymap.bindings_for_input(&space_w, &workspace_context());
459 assert!(space_w_workspace.0.is_empty());
460 assert!(space_w_workspace.1);
461
462 let space_w_editor = keymap.bindings_for_input(&space_w, &editor_workspace_context());
463 assert!(space_w_editor.0.is_empty());
464 assert!(!space_w_editor.1);
465
466 let space_w_w_workspace = keymap.bindings_for_input(&space_w_w, &workspace_context());
468 assert!(!space_w_w_workspace.0.is_empty());
469 assert!(!space_w_w_workspace.1);
470
471 let space_w_w_editor = keymap.bindings_for_input(&space_w_w, &editor_workspace_context());
472 assert!(space_w_w_editor.0.is_empty());
473 assert!(!space_w_w_editor.1);
474
475 let bindings = [
478 KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
479 KeyBinding::new("space w w", NoAction {}, Some("editor")),
480 KeyBinding::new("space w x", ActionAlpha {}, Some("editor")),
481 ];
482 let mut keymap = Keymap::default();
483 keymap.add_bindings(bindings);
484
485 let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context());
486 assert!(space_editor.0.is_empty());
487 assert!(space_editor.1);
488
489 let bindings = [
492 KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
493 KeyBinding::new("space w x", ActionAlpha {}, Some("editor")),
494 KeyBinding::new("space w w", NoAction {}, Some("editor")),
495 ];
496 let mut keymap = Keymap::default();
497 keymap.add_bindings(bindings);
498
499 let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context());
500 assert!(space_editor.0.is_empty());
501 assert!(space_editor.1);
502
503 let bindings = [
506 KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
507 KeyBinding::new("space w x", ActionAlpha {}, Some("workspace")),
508 KeyBinding::new("space w w", NoAction {}, Some("editor")),
509 ];
510 let mut keymap = Keymap::default();
511 keymap.add_bindings(bindings);
512
513 let space_editor = keymap.bindings_for_input(&[space()], &editor_workspace_context());
514 assert!(space_editor.0.is_empty());
515 assert!(space_editor.1);
516 }
517
518 #[test]
519 fn test_override_multikey() {
520 let bindings = [
521 KeyBinding::new("ctrl-w left", ActionAlpha {}, Some("editor")),
522 KeyBinding::new("ctrl-w", NoAction {}, Some("editor")),
523 ];
524
525 let mut keymap = Keymap::default();
526 keymap.add_bindings(bindings);
527
528 let (result, pending) = keymap.bindings_for_input(
530 &[Keystroke::parse("ctrl-w").unwrap()],
531 &[KeyContext::parse("editor").unwrap()],
532 );
533 assert!(result.is_empty());
534 assert!(pending);
535
536 let bindings = [
537 KeyBinding::new("ctrl-w left", ActionAlpha {}, Some("editor")),
538 KeyBinding::new("ctrl-w", ActionBeta {}, Some("editor")),
539 ];
540
541 let mut keymap = Keymap::default();
542 keymap.add_bindings(bindings);
543
544 let (result, pending) = keymap.bindings_for_input(
546 &[Keystroke::parse("ctrl-w").unwrap()],
547 &[KeyContext::parse("editor").unwrap()],
548 );
549 assert_eq!(result.len(), 1);
550 assert!(!pending);
551 }
552
553 #[test]
554 fn test_simple_disable() {
555 let bindings = [
556 KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")),
557 KeyBinding::new("ctrl-x", NoAction {}, Some("editor")),
558 ];
559
560 let mut keymap = Keymap::default();
561 keymap.add_bindings(bindings);
562
563 let (result, pending) = keymap.bindings_for_input(
565 &[Keystroke::parse("ctrl-x").unwrap()],
566 &[KeyContext::parse("editor").unwrap()],
567 );
568 assert!(result.is_empty());
569 assert!(!pending);
570 }
571
572 #[test]
573 fn test_fail_to_disable() {
574 let bindings = [
576 KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")),
577 KeyBinding::new("ctrl-x", NoAction {}, Some("workspace")),
578 ];
579
580 let mut keymap = Keymap::default();
581 keymap.add_bindings(bindings);
582
583 let (result, pending) = keymap.bindings_for_input(
585 &[Keystroke::parse("ctrl-x").unwrap()],
586 &[
587 KeyContext::parse("workspace").unwrap(),
588 KeyContext::parse("editor").unwrap(),
589 ],
590 );
591 assert_eq!(result.len(), 1);
592 assert!(!pending);
593 }
594
595 #[test]
596 fn test_disable_deeper() {
597 let bindings = [
598 KeyBinding::new("ctrl-x", ActionAlpha {}, Some("workspace")),
599 KeyBinding::new("ctrl-x", NoAction {}, Some("editor")),
600 ];
601
602 let mut keymap = Keymap::default();
603 keymap.add_bindings(bindings);
604
605 let (result, pending) = keymap.bindings_for_input(
607 &[Keystroke::parse("ctrl-x").unwrap()],
608 &[
609 KeyContext::parse("workspace").unwrap(),
610 KeyContext::parse("editor").unwrap(),
611 ],
612 );
613 assert_eq!(result.len(), 0);
614 assert!(!pending);
615 }
616
617 #[test]
618 fn test_pending_match_enabled() {
619 let bindings = [
620 KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")),
621 KeyBinding::new("ctrl-x 0", ActionAlpha, Some("Workspace")),
622 ];
623 let mut keymap = Keymap::default();
624 keymap.add_bindings(bindings);
625
626 let matched = keymap.bindings_for_input(
627 &[Keystroke::parse("ctrl-x")].map(Result::unwrap),
628 &[
629 KeyContext::parse("Workspace"),
630 KeyContext::parse("Pane"),
631 KeyContext::parse("Editor vim_mode=normal"),
632 ]
633 .map(Result::unwrap),
634 );
635 assert_eq!(matched.0.len(), 1);
636 assert!(matched.0[0].action.partial_eq(&ActionBeta));
637 assert!(matched.1);
638 }
639
640 #[test]
641 fn test_pending_match_enabled_extended() {
642 let bindings = [
643 KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")),
644 KeyBinding::new("ctrl-x 0", NoAction, Some("Workspace")),
645 ];
646 let mut keymap = Keymap::default();
647 keymap.add_bindings(bindings);
648
649 let matched = keymap.bindings_for_input(
650 &[Keystroke::parse("ctrl-x")].map(Result::unwrap),
651 &[
652 KeyContext::parse("Workspace"),
653 KeyContext::parse("Pane"),
654 KeyContext::parse("Editor vim_mode=normal"),
655 ]
656 .map(Result::unwrap),
657 );
658 assert_eq!(matched.0.len(), 1);
659 assert!(matched.0[0].action.partial_eq(&ActionBeta));
660 assert!(!matched.1);
661 let bindings = [
662 KeyBinding::new("ctrl-x", ActionBeta, Some("Workspace")),
663 KeyBinding::new("ctrl-x 0", NoAction, Some("vim_mode == normal")),
664 ];
665 let mut keymap = Keymap::default();
666 keymap.add_bindings(bindings);
667
668 let matched = keymap.bindings_for_input(
669 &[Keystroke::parse("ctrl-x")].map(Result::unwrap),
670 &[
671 KeyContext::parse("Workspace"),
672 KeyContext::parse("Pane"),
673 KeyContext::parse("Editor vim_mode=normal"),
674 ]
675 .map(Result::unwrap),
676 );
677 assert_eq!(matched.0.len(), 1);
678 assert!(matched.0[0].action.partial_eq(&ActionBeta));
679 assert!(!matched.1);
680 }
681
682 #[test]
683 fn test_overriding_prefix() {
684 let bindings = [
685 KeyBinding::new("ctrl-x 0", ActionAlpha, Some("Workspace")),
686 KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")),
687 ];
688 let mut keymap = Keymap::default();
689 keymap.add_bindings(bindings);
690
691 let matched = keymap.bindings_for_input(
692 &[Keystroke::parse("ctrl-x")].map(Result::unwrap),
693 &[
694 KeyContext::parse("Workspace"),
695 KeyContext::parse("Pane"),
696 KeyContext::parse("Editor vim_mode=normal"),
697 ]
698 .map(Result::unwrap),
699 );
700 assert_eq!(matched.0.len(), 1);
701 assert!(matched.0[0].action.partial_eq(&ActionBeta));
702 assert!(!matched.1);
703 }
704
705 #[test]
706 fn test_context_precedence_with_same_source() {
707 let bindings = [
710 KeyBinding::new("cmd-r", ActionAlpha {}, Some("Workspace")),
711 KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor")),
712 ];
713
714 let mut keymap = Keymap::default();
715 keymap.add_bindings(bindings);
716
717 let (result, _) = keymap.bindings_for_input(
719 &[Keystroke::parse("cmd-r").unwrap()],
720 &[
721 KeyContext::parse("Workspace").unwrap(),
722 KeyContext::parse("Editor").unwrap(),
723 ],
724 );
725
726 assert_eq!(result.len(), 2);
728 assert!(result[0].action.partial_eq(&ActionBeta {})); assert!(result[1].action.partial_eq(&ActionAlpha {})); }
731
732 #[test]
733 fn test_bindings_for_action() {
734 let bindings = [
735 KeyBinding::new("ctrl-a", ActionAlpha {}, Some("pane")),
736 KeyBinding::new("ctrl-b", ActionBeta {}, Some("editor && mode == full")),
737 KeyBinding::new("ctrl-c", ActionGamma {}, Some("workspace")),
738 KeyBinding::new("ctrl-a", NoAction {}, Some("pane && active")),
739 KeyBinding::new("ctrl-b", NoAction {}, Some("editor")),
740 ];
741
742 let mut keymap = Keymap::default();
743 keymap.add_bindings(bindings);
744
745 assert_bindings(&keymap, &ActionAlpha {}, &["ctrl-a"]);
746 assert_bindings(&keymap, &ActionBeta {}, &[]);
747 assert_bindings(&keymap, &ActionGamma {}, &["ctrl-c"]);
748
749 #[track_caller]
750 fn assert_bindings(keymap: &Keymap, action: &dyn Action, expected: &[&str]) {
751 let actual = keymap
752 .bindings_for_action(action)
753 .map(|binding| binding.keystrokes[0].inner().unparse())
754 .collect::<Vec<_>>();
755 assert_eq!(actual, expected, "{:?}", action);
756 }
757 }
758
759 #[test]
760 fn test_targeted_unbind_ignores_target_context() {
761 let bindings = [
762 KeyBinding::new("tab", ActionAlpha {}, Some("Editor")),
763 KeyBinding::new("tab", ActionBeta {}, Some("Editor && showing_completions")),
764 KeyBinding::new(
765 "tab",
766 Unbind("test_only::ActionAlpha".into()),
767 Some("Editor && edit_prediction"),
768 ),
769 ];
770
771 let mut keymap = Keymap::default();
772 keymap.add_bindings(bindings);
773
774 let (result, pending) = keymap.bindings_for_input(
775 &[Keystroke::parse("tab").unwrap()],
776 &[KeyContext::parse("Editor showing_completions edit_prediction").unwrap()],
777 );
778
779 assert!(!pending);
780 assert_eq!(result.len(), 1);
781 assert!(result[0].action.partial_eq(&ActionBeta {}));
782 }
783
784 #[test]
785 fn test_bindings_for_action_keeps_binding_for_narrower_targeted_unbind() {
786 let bindings = [
787 KeyBinding::new("tab", ActionAlpha {}, Some("Editor")),
788 KeyBinding::new(
789 "tab",
790 Unbind("test_only::ActionAlpha".into()),
791 Some("Editor && edit_prediction"),
792 ),
793 KeyBinding::new("tab", ActionBeta {}, Some("Editor && showing_completions")),
794 ];
795
796 let mut keymap = Keymap::default();
797 keymap.add_bindings(bindings);
798
799 assert_bindings(&keymap, &ActionAlpha {}, &["tab"]);
800 assert_bindings(&keymap, &ActionBeta {}, &["tab"]);
801
802 #[track_caller]
803 fn assert_bindings(keymap: &Keymap, action: &dyn Action, expected: &[&str]) {
804 let actual = keymap
805 .bindings_for_action(action)
806 .map(|binding| binding.keystrokes[0].inner().unparse())
807 .collect::<Vec<_>>();
808 assert_eq!(actual, expected, "{:?}", action);
809 }
810 }
811
812 #[test]
813 fn test_bindings_for_action_removes_binding_for_broader_targeted_unbind() {
814 let bindings = [
815 KeyBinding::new("tab", ActionAlpha {}, Some("Editor && edit_prediction")),
816 KeyBinding::new(
817 "tab",
818 Unbind("test_only::ActionAlpha".into()),
819 Some("Editor"),
820 ),
821 ];
822
823 let mut keymap = Keymap::default();
824 keymap.add_bindings(bindings);
825
826 assert!(keymap.bindings_for_action(&ActionAlpha {}).next().is_none());
827 }
828
829 #[test]
830 fn test_source_precedence_sorting() {
831 let mut keymap = Keymap::default();
834
835 let mut default_binding = KeyBinding::new("cmd-r", ActionAlpha {}, Some("Editor"));
837 default_binding.set_meta(KeyBindingMetaIndex(3)); keymap.add_bindings([default_binding]);
839
840 let mut user_binding = KeyBinding::new("cmd-r", ActionBeta {}, Some("Editor"));
842 user_binding.set_meta(KeyBindingMetaIndex(0)); keymap.add_bindings([user_binding]);
844
845 let (result, _) = keymap.bindings_for_input(
847 &[Keystroke::parse("cmd-r").unwrap()],
848 &[KeyContext::parse("Editor").unwrap()],
849 );
850
851 assert_eq!(result.len(), 2);
853 assert!(result[0].action.partial_eq(&ActionBeta {}));
854 assert!(result[1].action.partial_eq(&ActionAlpha {}));
855 }
856}