1use crate::SharedString;
2use anyhow::{Context as _, Result};
3use std::fmt;
4
5#[derive(Clone, Default, Eq, PartialEq, Hash)]
22pub struct KeyContext(Vec<ContextEntry>);
23
24#[derive(Clone, Debug, Eq, PartialEq, Hash)]
25pub struct ContextEntry {
27 pub key: SharedString,
29 pub value: Option<SharedString>,
31}
32
33impl<'a> TryFrom<&'a str> for KeyContext {
34 type Error = anyhow::Error;
35
36 fn try_from(value: &'a str) -> Result<Self> {
37 Self::parse(value)
38 }
39}
40
41impl KeyContext {
42 pub fn new_with_defaults() -> Self {
47 let mut context = Self::default();
48 #[cfg(target_os = "macos")]
49 context.set("os", "macos");
50 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
51 context.set("os", "linux");
52 #[cfg(target_os = "windows")]
53 context.set("os", "windows");
54 #[cfg(not(any(
55 target_os = "macos",
56 target_os = "linux",
57 target_os = "freebsd",
58 target_os = "windows"
59 )))]
60 context.set("os", "unknown");
61 context
62 }
63
64 pub fn primary(&self) -> Option<&ContextEntry> {
67 self.0.iter().find(|p| p.value.is_none())
68 }
69
70 pub fn secondary(&self) -> impl Iterator<Item = &ContextEntry> {
72 let primary = self.primary();
73 self.0.iter().filter(move |&p| Some(p) != primary)
74 }
75
76 pub fn parse(source: &str) -> Result<Self> {
96 let mut context = Self::default();
97 let source = skip_whitespace(source);
98 Self::parse_expr(source, &mut context)?;
99 Ok(context)
100 }
101
102 fn parse_expr(mut source: &str, context: &mut Self) -> Result<()> {
103 if source.is_empty() {
104 return Ok(());
105 }
106
107 let key = source
108 .chars()
109 .take_while(|c| is_identifier_char(*c))
110 .collect::<String>();
111 anyhow::ensure!(
113 !key.is_empty(),
114 "expected a context identifier at {source:?}"
115 );
116 source = skip_whitespace(&source[key.len()..]);
117 if let Some(suffix) = source.strip_prefix('=') {
118 source = skip_whitespace(suffix);
119 let value = source
120 .chars()
121 .take_while(|c| is_identifier_char(*c))
122 .collect::<String>();
123 source = skip_whitespace(&source[value.len()..]);
124 context.set(key, value);
125 } else {
126 context.add(key);
127 }
128
129 Self::parse_expr(source, context)
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.0.is_empty()
135 }
136
137 pub fn clear(&mut self) {
139 self.0.clear();
140 }
141
142 pub fn extend(&mut self, other: &Self) {
144 for entry in &other.0 {
145 if !self.contains(&entry.key) {
146 self.0.push(entry.clone());
147 }
148 }
149 }
150
151 pub fn add<I: Into<SharedString>>(&mut self, identifier: I) {
153 let key = identifier.into();
154
155 if !self.contains(&key) {
156 self.0.push(ContextEntry { key, value: None })
157 }
158 }
159
160 pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
162 let key = key.into();
163 if !self.contains(&key) {
164 self.0.push(ContextEntry {
165 key,
166 value: Some(value.into()),
167 })
168 }
169 }
170
171 pub fn contains(&self, key: &str) -> bool {
173 self.0.iter().any(|entry| entry.key.as_ref() == key)
174 }
175
176 pub fn get(&self, key: &str) -> Option<&SharedString> {
179 self.0
180 .iter()
181 .find(|entry| entry.key.as_ref() == key)?
182 .value
183 .as_ref()
184 }
185}
186
187impl fmt::Debug for KeyContext {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 let mut entries = self.0.iter().peekable();
190 while let Some(entry) = entries.next() {
191 if let Some(ref value) = entry.value {
192 write!(f, "{}={}", entry.key, value)?;
193 } else {
194 write!(f, "{}", entry.key)?;
195 }
196 if entries.peek().is_some() {
197 write!(f, " ")?;
198 }
199 }
200 Ok(())
201 }
202}
203
204#[derive(Clone, Debug, Eq, PartialEq, Hash)]
229pub enum KeyBindingContextPredicate {
230 Identifier(SharedString),
232 Equal(SharedString, SharedString),
234 NotEqual(SharedString, SharedString),
237 Descendant(
240 Box<KeyBindingContextPredicate>,
241 Box<KeyBindingContextPredicate>,
242 ),
243 Not(Box<KeyBindingContextPredicate>),
245 And(
247 Box<KeyBindingContextPredicate>,
248 Box<KeyBindingContextPredicate>,
249 ),
250 Or(
252 Box<KeyBindingContextPredicate>,
253 Box<KeyBindingContextPredicate>,
254 ),
255}
256
257impl fmt::Display for KeyBindingContextPredicate {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 match self {
260 Self::Identifier(name) => write!(f, "{name}"),
261 Self::Equal(left, right) => write!(f, "{left} == {right}"),
262 Self::NotEqual(left, right) => write!(f, "{left} != {right}"),
263 Self::Descendant(parent, child) => write!(f, "{parent} > {child}"),
264 Self::Not(pred) => match pred.as_ref() {
265 Self::Identifier(name) => write!(f, "!{name}"),
266 _ => write!(f, "!({pred})"),
267 },
268 Self::And(..) => self.fmt_joined(f, " && ", LogicalOperator::And, |node| {
269 matches!(node, Self::Or(..))
270 }),
271 Self::Or(..) => self.fmt_joined(f, " || ", LogicalOperator::Or, |node| {
272 matches!(node, Self::And(..))
273 }),
274 }
275 }
276}
277
278impl KeyBindingContextPredicate {
279 pub fn parse(source: &str) -> Result<Self> {
316 let source = skip_whitespace(source);
317 let (predicate, rest) = Self::parse_expr(source, 0)?;
318 if let Some(next) = rest.chars().next() {
319 anyhow::bail!("unexpected character '{next:?}'");
320 } else {
321 Ok(predicate)
322 }
323 }
324
325 pub fn depth_of(&self, contexts: &[KeyContext]) -> Option<usize> {
346 for depth in (0..=contexts.len()).rev() {
347 let context_slice = &contexts[0..depth];
348 if self.eval_inner(context_slice, contexts) {
349 return Some(depth);
350 }
351 }
352 None
353 }
354
355 #[allow(unused)]
358 pub fn eval(&self, contexts: &[KeyContext]) -> bool {
359 self.eval_inner(contexts, contexts)
360 }
361
362 pub fn eval_inner(&self, contexts: &[KeyContext], all_contexts: &[KeyContext]) -> bool {
368 let Some(context) = contexts.last() else {
369 return false;
370 };
371 match self {
372 Self::Identifier(name) => context.contains(name),
373 Self::Equal(left, right) => context
374 .get(left)
375 .map(|value| value == right)
376 .unwrap_or(false),
377 Self::NotEqual(left, right) => context
378 .get(left)
379 .map(|value| value != right)
380 .unwrap_or(true),
381 Self::Not(pred) => {
382 for i in 0..all_contexts.len() {
383 if pred.eval_inner(&all_contexts[..=i], all_contexts) {
384 return false;
385 }
386 }
387 true
388 }
389 Self::Descendant(parent, child) => {
396 for i in 0..contexts.len() - 1 {
397 if parent.eval_inner(&contexts[..=i], all_contexts) {
399 if !child.eval_inner(&contexts[i + 1..], &contexts[i + 1..]) {
400 return false;
401 }
402 return true;
403 }
404 }
405 false
406 }
407 Self::And(left, right) => {
408 left.eval_inner(contexts, all_contexts) && right.eval_inner(contexts, all_contexts)
409 }
410 Self::Or(left, right) => {
411 left.eval_inner(contexts, all_contexts) || right.eval_inner(contexts, all_contexts)
412 }
413 }
414 }
415
416 pub fn is_superset(&self, other: &Self) -> bool {
432 if self == other {
433 return true;
434 }
435
436 if let KeyBindingContextPredicate::Or(left, right) = self {
437 return left.is_superset(other) || right.is_superset(other);
438 }
439
440 match other {
441 KeyBindingContextPredicate::Descendant(_, child) => self.is_superset(child),
442 KeyBindingContextPredicate::And(left, right) => {
443 self.is_superset(left) || self.is_superset(right)
444 }
445 KeyBindingContextPredicate::Identifier(_) => false,
446 KeyBindingContextPredicate::Equal(_, _) => false,
447 KeyBindingContextPredicate::NotEqual(_, _) => false,
448 KeyBindingContextPredicate::Not(_) => false,
449 KeyBindingContextPredicate::Or(_, _) => false,
450 }
451 }
452
453 fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
454 type Op = fn(
455 KeyBindingContextPredicate,
456 KeyBindingContextPredicate,
457 ) -> Result<KeyBindingContextPredicate>;
458
459 let (mut predicate, rest) = Self::parse_primary(source)?;
460 source = rest;
461
462 'parse: loop {
463 for (operator, precedence, constructor) in [
464 (">", PRECEDENCE_CHILD, Self::new_child as Op),
465 ("&&", PRECEDENCE_AND, Self::new_and as Op),
466 ("||", PRECEDENCE_OR, Self::new_or as Op),
467 ("==", PRECEDENCE_EQ, Self::new_eq as Op),
468 ("!=", PRECEDENCE_EQ, Self::new_neq as Op),
469 ] {
470 if source.starts_with(operator) && precedence >= min_precedence {
471 source = skip_whitespace(&source[operator.len()..]);
472 let (right, rest) = Self::parse_expr(source, precedence + 1)?;
473 predicate = constructor(predicate, right)?;
474 source = rest;
475 continue 'parse;
476 }
477 }
478 break;
479 }
480
481 Ok((predicate, source))
482 }
483
484 fn parse_primary(mut source: &str) -> anyhow::Result<(Self, &str)> {
485 let next = source.chars().next().context("unexpected end")?;
486 match next {
487 '(' => {
488 source = skip_whitespace(&source[1..]);
489 let (predicate, rest) = Self::parse_expr(source, 0)?;
490 let stripped = rest.strip_prefix(')').context("expected a ')'")?;
491 source = skip_whitespace(stripped);
492 Ok((predicate, source))
493 }
494 '!' => {
495 let source = skip_whitespace(&source[1..]);
496 let (predicate, source) = Self::parse_expr(source, PRECEDENCE_NOT)?;
497 Ok((KeyBindingContextPredicate::Not(Box::new(predicate)), source))
498 }
499 _ if is_identifier_char(next) => {
500 let len = source
501 .find(|c: char| !is_identifier_char(c) && !is_vim_operator_char(c))
502 .unwrap_or(source.len());
503 let (identifier, rest) = source.split_at(len);
504 source = skip_whitespace(rest);
505 Ok((
506 KeyBindingContextPredicate::Identifier(identifier.to_string().into()),
507 source,
508 ))
509 }
510 _ if is_vim_operator_char(next) => {
511 let (operator, rest) = source.split_at(1);
512 source = skip_whitespace(rest);
513 Ok((
514 KeyBindingContextPredicate::Identifier(operator.to_string().into()),
515 source,
516 ))
517 }
518 _ => anyhow::bail!("unexpected character '{next:?}'"),
519 }
520 }
521
522 fn new_or(self, other: Self) -> Result<Self> {
523 Ok(Self::Or(Box::new(self), Box::new(other)))
524 }
525
526 fn new_and(self, other: Self) -> Result<Self> {
527 Ok(Self::And(Box::new(self), Box::new(other)))
528 }
529
530 fn new_child(self, other: Self) -> Result<Self> {
531 Ok(Self::Descendant(Box::new(self), Box::new(other)))
532 }
533
534 fn new_eq(self, other: Self) -> Result<Self> {
535 if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
536 Ok(Self::Equal(left, right))
537 } else {
538 anyhow::bail!("operands of == must be identifiers");
539 }
540 }
541
542 fn new_neq(self, other: Self) -> Result<Self> {
543 if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
544 Ok(Self::NotEqual(left, right))
545 } else {
546 anyhow::bail!("operands of != must be identifiers");
547 }
548 }
549
550 fn fmt_joined(
551 &self,
552 f: &mut fmt::Formatter<'_>,
553 separator: &str,
554 operator: LogicalOperator,
555 needs_parens: impl Fn(&Self) -> bool + Copy,
556 ) -> fmt::Result {
557 let mut first = true;
558 self.fmt_joined_inner(f, separator, operator, needs_parens, &mut first)
559 }
560
561 fn fmt_joined_inner(
562 &self,
563 f: &mut fmt::Formatter<'_>,
564 separator: &str,
565 operator: LogicalOperator,
566 needs_parens: impl Fn(&Self) -> bool + Copy,
567 first: &mut bool,
568 ) -> fmt::Result {
569 match (operator, self) {
570 (LogicalOperator::And, Self::And(left, right))
571 | (LogicalOperator::Or, Self::Or(left, right)) => {
572 left.fmt_joined_inner(f, separator, operator, needs_parens, first)?;
573 right.fmt_joined_inner(f, separator, operator, needs_parens, first)
574 }
575 (_, node) => {
576 if !*first {
577 f.write_str(separator)?;
578 }
579 *first = false;
580
581 if needs_parens(node) {
582 write!(f, "({node})")
583 } else {
584 write!(f, "{node}")
585 }
586 }
587 }
588 }
589}
590
591#[derive(Clone, Copy)]
592enum LogicalOperator {
593 And,
594 Or,
595}
596
597const PRECEDENCE_CHILD: u32 = 1;
598const PRECEDENCE_OR: u32 = 2;
599const PRECEDENCE_AND: u32 = 3;
600const PRECEDENCE_EQ: u32 = 4;
601const PRECEDENCE_NOT: u32 = 5;
602
603fn is_identifier_char(c: char) -> bool {
604 c.is_alphanumeric() || c == '_' || c == '-'
605}
606
607fn is_vim_operator_char(c: char) -> bool {
608 c == '>' || c == '<' || c == '~' || c == '"' || c == '?'
609}
610
611fn skip_whitespace(source: &str) -> &str {
612 let len = source
613 .find(|c: char| !c.is_whitespace())
614 .unwrap_or(source.len());
615 &source[len..]
616}
617
618#[cfg(test)]
619mod tests {
620 use core::slice;
621
622 use super::*;
623 use crate as gpui;
624 use KeyBindingContextPredicate::*;
625
626 #[test]
627 fn test_actions_definition() {
628 {
629 actions!(test_only, [A, B, C, D, E, F, G]);
630 }
631
632 {
633 actions!(
634 test_only,
635 [
636 H, I, J, K, L, M, N, ]
638 );
639 }
640 }
641
642 #[test]
643 fn test_parse_context() {
644 let mut expected = KeyContext::default();
645 expected.add("baz");
646 expected.set("foo", "bar");
647 assert_eq!(KeyContext::parse("baz foo=bar").unwrap(), expected);
648 assert_eq!(KeyContext::parse("baz foo = bar").unwrap(), expected);
649 assert_eq!(
650 KeyContext::parse(" baz foo = bar baz").unwrap(),
651 expected
652 );
653 assert_eq!(KeyContext::parse(" baz foo = bar").unwrap(), expected);
654 }
655
656 #[test]
657 fn test_parse_malformed_context() {
658 for source in [
659 ".",
660 "Editor && enabled",
661 "=insert",
662 "mode==insert",
663 "mode=\"insert\"",
664 ] {
665 assert!(KeyContext::parse(source).is_err(), "accepted {source:?}");
666 }
667 assert!(KeyContext::parse(" \n ").unwrap().is_empty());
668 }
669
670 #[test]
671 fn test_parse_identifiers() {
672 assert_eq!(
674 KeyBindingContextPredicate::parse("abc12").unwrap(),
675 Identifier("abc12".into())
676 );
677 assert_eq!(
678 KeyBindingContextPredicate::parse("_1a").unwrap(),
679 Identifier("_1a".into())
680 );
681 }
682
683 #[test]
684 fn test_parse_negations() {
685 assert_eq!(
686 KeyBindingContextPredicate::parse("!abc").unwrap(),
687 Not(Box::new(Identifier("abc".into())))
688 );
689 assert_eq!(
690 KeyBindingContextPredicate::parse(" ! ! abc").unwrap(),
691 Not(Box::new(Not(Box::new(Identifier("abc".into())))))
692 );
693 }
694
695 #[test]
696 fn test_parse_equality_operators() {
697 assert_eq!(
698 KeyBindingContextPredicate::parse("a == b").unwrap(),
699 Equal("a".into(), "b".into())
700 );
701 assert_eq!(
702 KeyBindingContextPredicate::parse("c!=d").unwrap(),
703 NotEqual("c".into(), "d".into())
704 );
705 assert_eq!(
706 KeyBindingContextPredicate::parse("c == !d")
707 .unwrap_err()
708 .to_string(),
709 "operands of == must be identifiers"
710 );
711 }
712
713 #[test]
714 fn test_parse_boolean_operators() {
715 assert_eq!(
716 KeyBindingContextPredicate::parse("a || b").unwrap(),
717 Or(
718 Box::new(Identifier("a".into())),
719 Box::new(Identifier("b".into()))
720 )
721 );
722 assert_eq!(
723 KeyBindingContextPredicate::parse("a || !b && c").unwrap(),
724 Or(
725 Box::new(Identifier("a".into())),
726 Box::new(And(
727 Box::new(Not(Box::new(Identifier("b".into())))),
728 Box::new(Identifier("c".into()))
729 ))
730 )
731 );
732 assert_eq!(
733 KeyBindingContextPredicate::parse("a && b || c&&d").unwrap(),
734 Or(
735 Box::new(And(
736 Box::new(Identifier("a".into())),
737 Box::new(Identifier("b".into()))
738 )),
739 Box::new(And(
740 Box::new(Identifier("c".into())),
741 Box::new(Identifier("d".into()))
742 ))
743 )
744 );
745 assert_eq!(
746 KeyBindingContextPredicate::parse("a == b && c || d == e && f").unwrap(),
747 Or(
748 Box::new(And(
749 Box::new(Equal("a".into(), "b".into())),
750 Box::new(Identifier("c".into()))
751 )),
752 Box::new(And(
753 Box::new(Equal("d".into(), "e".into())),
754 Box::new(Identifier("f".into()))
755 ))
756 )
757 );
758 assert_eq!(
759 KeyBindingContextPredicate::parse("a && b && c && d").unwrap(),
760 And(
761 Box::new(And(
762 Box::new(And(
763 Box::new(Identifier("a".into())),
764 Box::new(Identifier("b".into()))
765 )),
766 Box::new(Identifier("c".into())),
767 )),
768 Box::new(Identifier("d".into()))
769 ),
770 );
771 }
772
773 #[test]
774 fn test_parse_parenthesized_expressions() {
775 assert_eq!(
776 KeyBindingContextPredicate::parse("a && (b == c || d != e)").unwrap(),
777 And(
778 Box::new(Identifier("a".into())),
779 Box::new(Or(
780 Box::new(Equal("b".into(), "c".into())),
781 Box::new(NotEqual("d".into(), "e".into())),
782 )),
783 ),
784 );
785 assert_eq!(
786 KeyBindingContextPredicate::parse(" ( a || b ) ").unwrap(),
787 Or(
788 Box::new(Identifier("a".into())),
789 Box::new(Identifier("b".into())),
790 )
791 );
792 }
793
794 #[test]
795 fn test_is_superset() {
796 assert_is_superset("editor", "editor", true);
797 assert_is_superset("editor", "workspace", false);
798
799 assert_is_superset("editor", "editor && vim_mode", true);
800 assert_is_superset("editor", "mode == full && editor", true);
801 assert_is_superset("editor && mode == full", "editor", false);
802
803 assert_is_superset("editor", "something > editor", true);
804 assert_is_superset("editor", "editor > menu", false);
805
806 assert_is_superset("foo || bar || baz", "bar", true);
807 assert_is_superset("foo || bar || baz", "quux", false);
808
809 #[track_caller]
810 fn assert_is_superset(a: &str, b: &str, result: bool) {
811 let a = KeyBindingContextPredicate::parse(a).unwrap();
812 let b = KeyBindingContextPredicate::parse(b).unwrap();
813 assert_eq!(a.is_superset(&b), result, "({a:?}).is_superset({b:?})");
814 }
815 }
816
817 #[test]
818 fn test_child_operator() {
819 let predicate = KeyBindingContextPredicate::parse("parent > child").unwrap();
820
821 let parent_context = KeyContext::try_from("parent").unwrap();
822 let child_context = KeyContext::try_from("child").unwrap();
823
824 let contexts = vec![parent_context.clone(), child_context.clone()];
825 assert!(predicate.eval(&contexts));
826
827 let grandparent_context = KeyContext::try_from("grandparent").unwrap();
828
829 let contexts = vec![
830 grandparent_context,
831 parent_context.clone(),
832 child_context.clone(),
833 ];
834 assert!(predicate.eval(&contexts));
835
836 let other_context = KeyContext::try_from("other").unwrap();
837
838 let contexts = vec![other_context.clone(), child_context.clone()];
839 assert!(!predicate.eval(&contexts));
840
841 let contexts = vec![parent_context.clone(), other_context, child_context.clone()];
842 assert!(predicate.eval(&contexts));
843
844 assert!(!predicate.eval(&[]));
845 assert!(!predicate.eval(slice::from_ref(&child_context)));
846 assert!(!predicate.eval(&[parent_context]));
847
848 let zany_predicate = KeyBindingContextPredicate::parse("child > child").unwrap();
849 assert!(!zany_predicate.eval(slice::from_ref(&child_context)));
850 assert!(zany_predicate.eval(&[child_context.clone(), child_context]));
851 }
852
853 #[test]
854 fn test_not_operator() {
855 let not_predicate = KeyBindingContextPredicate::parse("!editor").unwrap();
856 let editor_context = KeyContext::try_from("editor").unwrap();
857 let workspace_context = KeyContext::try_from("workspace").unwrap();
858 let parent_context = KeyContext::try_from("parent").unwrap();
859 let child_context = KeyContext::try_from("child").unwrap();
860
861 assert!(not_predicate.eval(slice::from_ref(&workspace_context)));
862 assert!(!not_predicate.eval(slice::from_ref(&editor_context)));
863 assert!(!not_predicate.eval(&[editor_context.clone(), workspace_context.clone()]));
864 assert!(!not_predicate.eval(&[workspace_context.clone(), editor_context.clone()]));
865
866 let complex_not = KeyBindingContextPredicate::parse("!editor && workspace").unwrap();
867 assert!(complex_not.eval(slice::from_ref(&workspace_context)));
868 assert!(!complex_not.eval(&[editor_context.clone(), workspace_context.clone()]));
869
870 let not_mode_predicate = KeyBindingContextPredicate::parse("!(mode == full)").unwrap();
871 let mut mode_context = KeyContext::default();
872 mode_context.set("mode", "full");
873 assert!(!not_mode_predicate.eval(&[mode_context.clone()]));
874
875 let mut other_mode_context = KeyContext::default();
876 other_mode_context.set("mode", "partial");
877 assert!(not_mode_predicate.eval(&[other_mode_context]));
878
879 let not_descendant = KeyBindingContextPredicate::parse("!(parent > child)").unwrap();
880 assert!(not_descendant.eval(slice::from_ref(&parent_context)));
881 assert!(not_descendant.eval(slice::from_ref(&child_context)));
882 assert!(!not_descendant.eval(&[parent_context.clone(), child_context.clone()]));
883
884 let not_descendant = KeyBindingContextPredicate::parse("parent > !child").unwrap();
885 assert!(!not_descendant.eval(slice::from_ref(&parent_context)));
886 assert!(!not_descendant.eval(slice::from_ref(&child_context)));
887 assert!(!not_descendant.eval(&[parent_context, child_context]));
888
889 let double_not = KeyBindingContextPredicate::parse("!!editor").unwrap();
890 assert!(double_not.eval(slice::from_ref(&editor_context)));
891 assert!(!double_not.eval(slice::from_ref(&workspace_context)));
892
893 let workspace_context = KeyContext::try_from("Workspace").unwrap();
895 let pane_context = KeyContext::try_from("Pane").unwrap();
896 let editor_context = KeyContext::try_from("Editor").unwrap();
897
898 let workspace_pane_editor = vec![
900 workspace_context.clone(),
901 pane_context.clone(),
902 editor_context.clone(),
903 ];
904
905 let pane_pane_editor = KeyBindingContextPredicate::parse("Pane > (Pane > Editor)").unwrap();
907 assert!(!pane_pane_editor.eval(&workspace_pane_editor));
908
909 let workspace_pane_editor_predicate =
910 KeyBindingContextPredicate::parse("Workspace > Pane > Editor").unwrap();
911 assert!(workspace_pane_editor_predicate.eval(&workspace_pane_editor));
912
913 let pane_pane_then_editor =
915 KeyBindingContextPredicate::parse("(Pane > Pane) > Editor").unwrap();
916 assert!(!pane_pane_then_editor.eval(&workspace_pane_editor));
917
918 let pane_not_workspace = KeyBindingContextPredicate::parse("Pane > !Workspace").unwrap();
920 assert!(pane_not_workspace.eval(&[pane_context.clone(), editor_context.clone()]));
921 assert!(!pane_not_workspace.eval(&[pane_context.clone(), workspace_context.clone()]));
922
923 let not_workspace = KeyBindingContextPredicate::parse("!Workspace").unwrap();
925 assert!(!not_workspace.eval(slice::from_ref(&workspace_context)));
926 assert!(not_workspace.eval(slice::from_ref(&pane_context)));
927 assert!(not_workspace.eval(slice::from_ref(&editor_context)));
928 assert!(!not_workspace.eval(&workspace_pane_editor));
929 }
930
931 #[test]
934 fn test_context_display() {
935 fn ident(s: &str) -> Box<KeyBindingContextPredicate> {
936 Box::new(Identifier(SharedString::new(s)))
937 }
938 fn eq(a: &str, b: &str) -> Box<KeyBindingContextPredicate> {
939 Box::new(Equal(SharedString::new(a), SharedString::new(b)))
940 }
941 fn not_eq(a: &str, b: &str) -> Box<KeyBindingContextPredicate> {
942 Box::new(NotEqual(SharedString::new(a), SharedString::new(b)))
943 }
944 fn and(
945 a: Box<KeyBindingContextPredicate>,
946 b: Box<KeyBindingContextPredicate>,
947 ) -> Box<KeyBindingContextPredicate> {
948 Box::new(And(a, b))
949 }
950 fn or(
951 a: Box<KeyBindingContextPredicate>,
952 b: Box<KeyBindingContextPredicate>,
953 ) -> Box<KeyBindingContextPredicate> {
954 Box::new(Or(a, b))
955 }
956 fn descendant(
957 a: Box<KeyBindingContextPredicate>,
958 b: Box<KeyBindingContextPredicate>,
959 ) -> Box<KeyBindingContextPredicate> {
960 Box::new(Descendant(a, b))
961 }
962 fn not(a: Box<KeyBindingContextPredicate>) -> Box<KeyBindingContextPredicate> {
963 Box::new(Not(a))
964 }
965
966 let test_cases = [
967 (ident("a"), "a"),
968 (eq("a", "b"), "a == b"),
969 (not_eq("a", "b"), "a != b"),
970 (descendant(ident("a"), ident("b")), "a > b"),
971 (not(ident("a")), "!a"),
972 (not_eq("a", "b"), "a != b"),
973 (descendant(ident("a"), ident("b")), "a > b"),
974 (not(and(ident("a"), ident("b"))), "!(a && b)"),
975 (not(or(ident("a"), ident("b"))), "!(a || b)"),
976 (and(ident("a"), ident("b")), "a && b"),
977 (and(and(ident("a"), ident("b")), ident("c")), "a && b && c"),
978 (or(ident("a"), ident("b")), "a || b"),
979 (or(or(ident("a"), ident("b")), ident("c")), "a || b || c"),
980 (or(ident("a"), and(ident("b"), ident("c"))), "a || (b && c)"),
981 (
982 and(
983 and(
984 and(ident("a"), eq("b", "c")),
985 not(descendant(ident("d"), ident("e"))),
986 ),
987 eq("f", "g"),
988 ),
989 "a && b == c && !(d > e) && f == g",
990 ),
991 (
992 and(and(ident("a"), or(ident("b"), ident("c"))), ident("d")),
993 "a && (b || c) && d",
994 ),
995 (
996 or(or(ident("a"), and(ident("b"), ident("c"))), ident("d")),
997 "a || (b && c) || d",
998 ),
999 ];
1000
1001 for (predicate, expected) in test_cases {
1002 let actual = predicate.to_string();
1003 assert_eq!(actual, expected);
1004 let parsed = KeyBindingContextPredicate::parse(&actual).unwrap();
1005 assert_eq!(parsed, *predicate);
1006 }
1007 }
1008}