mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 12:38:02 +00:00
Add '>' child operator in keymap context predicates
This commit is contained in:
parent
f62d13de21
commit
373902d933
5 changed files with 136 additions and 56 deletions
|
@ -1349,21 +1349,24 @@ impl MutableAppContext {
|
||||||
|
|
||||||
/// Return keystrokes that would dispatch the given action closest to the focused view, if there are any.
|
/// Return keystrokes that would dispatch the given action closest to the focused view, if there are any.
|
||||||
pub(crate) fn keystrokes_for_action(
|
pub(crate) fn keystrokes_for_action(
|
||||||
&self,
|
&mut self,
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
dispatch_path: &[usize],
|
view_stack: &[usize],
|
||||||
action: &dyn Action,
|
action: &dyn Action,
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||||
for view_id in dispatch_path.iter().rev() {
|
self.keystroke_matcher.contexts.clear();
|
||||||
|
for view_id in view_stack.iter().rev() {
|
||||||
let view = self
|
let view = self
|
||||||
.cx
|
.cx
|
||||||
.views
|
.views
|
||||||
.get(&(window_id, *view_id))
|
.get(&(window_id, *view_id))
|
||||||
.expect("view in responder chain does not exist");
|
.expect("view in responder chain does not exist");
|
||||||
let keymap_context = view.keymap_context(self.as_ref());
|
self.keystroke_matcher
|
||||||
|
.contexts
|
||||||
|
.push(view.keymap_context(self.as_ref()));
|
||||||
let keystrokes = self
|
let keystrokes = self
|
||||||
.keystroke_matcher
|
.keystroke_matcher
|
||||||
.keystrokes_for_action(action, &keymap_context);
|
.keystrokes_for_action(action, &self.keystroke_matcher.contexts);
|
||||||
if keystrokes.is_some() {
|
if keystrokes.is_some() {
|
||||||
return keystrokes;
|
return keystrokes;
|
||||||
}
|
}
|
||||||
|
@ -6681,7 +6684,7 @@ mod tests {
|
||||||
view_3
|
view_3
|
||||||
});
|
});
|
||||||
|
|
||||||
// This keymap's only binding dispatches an action on view 2 because that view will have
|
// This binding only dispatches an action on view 2 because that view will have
|
||||||
// "a" and "b" in its context, but not "c".
|
// "a" and "b" in its context, but not "c".
|
||||||
cx.add_bindings(vec![Binding::new(
|
cx.add_bindings(vec![Binding::new(
|
||||||
"a",
|
"a",
|
||||||
|
@ -6691,16 +6694,31 @@ mod tests {
|
||||||
|
|
||||||
cx.add_bindings(vec![Binding::new("b", Action("b".to_string()), None)]);
|
cx.add_bindings(vec![Binding::new("b", Action("b".to_string()), None)]);
|
||||||
|
|
||||||
|
// This binding only dispatches an action on views 2 and 3, because they have
|
||||||
|
// a parent view with a in its context
|
||||||
|
cx.add_bindings(vec![Binding::new(
|
||||||
|
"c",
|
||||||
|
Action("c".to_string()),
|
||||||
|
Some("b > c"),
|
||||||
|
)]);
|
||||||
|
|
||||||
|
// This binding only dispatches an action on view 2, because they have
|
||||||
|
// a parent view with a in its context
|
||||||
|
cx.add_bindings(vec![Binding::new(
|
||||||
|
"d",
|
||||||
|
Action("d".to_string()),
|
||||||
|
Some("a && !b > b"),
|
||||||
|
)]);
|
||||||
|
|
||||||
let actions = Rc::new(RefCell::new(Vec::new()));
|
let actions = Rc::new(RefCell::new(Vec::new()));
|
||||||
cx.add_action({
|
cx.add_action({
|
||||||
let actions = actions.clone();
|
let actions = actions.clone();
|
||||||
move |view: &mut View, action: &Action, cx| {
|
move |view: &mut View, action: &Action, cx| {
|
||||||
if action.0 == "a" {
|
actions
|
||||||
actions.borrow_mut().push(format!("{} a", view.id));
|
.borrow_mut()
|
||||||
} else {
|
.push(format!("{} {}", view.id, action.0));
|
||||||
actions
|
|
||||||
.borrow_mut()
|
if action.0 == "b" {
|
||||||
.push(format!("{} {}", view.id, action.0));
|
|
||||||
cx.propagate_action();
|
cx.propagate_action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6714,14 +6732,20 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
|
||||||
|
|
||||||
assert_eq!(&*actions.borrow(), &["2 a"]);
|
assert_eq!(&*actions.borrow(), &["2 a"]);
|
||||||
|
|
||||||
actions.borrow_mut().clear();
|
actions.borrow_mut().clear();
|
||||||
|
|
||||||
cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
|
||||||
|
|
||||||
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
|
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
|
||||||
|
actions.borrow_mut().clear();
|
||||||
|
|
||||||
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("c").unwrap());
|
||||||
|
assert_eq!(&*actions.borrow(), &["3 c"]);
|
||||||
|
actions.borrow_mut().clear();
|
||||||
|
|
||||||
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("d").unwrap());
|
||||||
|
assert_eq!(&*actions.borrow(), &["2 d"]);
|
||||||
|
actions.borrow_mut().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub struct KeyPressed {
|
||||||
impl_actions!(gpui, [KeyPressed]);
|
impl_actions!(gpui, [KeyPressed]);
|
||||||
|
|
||||||
pub struct KeymapMatcher {
|
pub struct KeymapMatcher {
|
||||||
|
pub contexts: Vec<KeymapContext>,
|
||||||
pending_views: HashMap<usize, KeymapContext>,
|
pending_views: HashMap<usize, KeymapContext>,
|
||||||
pending_keystrokes: Vec<Keystroke>,
|
pending_keystrokes: Vec<Keystroke>,
|
||||||
keymap: Keymap,
|
keymap: Keymap,
|
||||||
|
@ -33,6 +34,7 @@ pub struct KeymapMatcher {
|
||||||
impl KeymapMatcher {
|
impl KeymapMatcher {
|
||||||
pub fn new(keymap: Keymap) -> Self {
|
pub fn new(keymap: Keymap) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
contexts: Vec::new(),
|
||||||
pending_views: Default::default(),
|
pending_views: Default::default(),
|
||||||
pending_keystrokes: Vec::new(),
|
pending_keystrokes: Vec::new(),
|
||||||
keymap,
|
keymap,
|
||||||
|
@ -70,7 +72,7 @@ impl KeymapMatcher {
|
||||||
pub fn push_keystroke(
|
pub fn push_keystroke(
|
||||||
&mut self,
|
&mut self,
|
||||||
keystroke: Keystroke,
|
keystroke: Keystroke,
|
||||||
dispatch_path: Vec<(usize, KeymapContext)>,
|
mut dispatch_path: Vec<(usize, KeymapContext)>,
|
||||||
) -> MatchResult {
|
) -> MatchResult {
|
||||||
let mut any_pending = false;
|
let mut any_pending = false;
|
||||||
let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Vec::new();
|
let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Vec::new();
|
||||||
|
@ -78,7 +80,11 @@ impl KeymapMatcher {
|
||||||
let first_keystroke = self.pending_keystrokes.is_empty();
|
let first_keystroke = self.pending_keystrokes.is_empty();
|
||||||
self.pending_keystrokes.push(keystroke.clone());
|
self.pending_keystrokes.push(keystroke.clone());
|
||||||
|
|
||||||
for (view_id, context) in dispatch_path {
|
self.contexts.clear();
|
||||||
|
self.contexts
|
||||||
|
.extend(dispatch_path.iter_mut().map(|e| std::mem::take(&mut e.1)));
|
||||||
|
|
||||||
|
for (i, (view_id, _)) in dispatch_path.into_iter().enumerate() {
|
||||||
// Don't require pending view entry if there are no pending keystrokes
|
// Don't require pending view entry if there are no pending keystrokes
|
||||||
if !first_keystroke && !self.pending_views.contains_key(&view_id) {
|
if !first_keystroke && !self.pending_views.contains_key(&view_id) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -87,14 +93,15 @@ impl KeymapMatcher {
|
||||||
// If there is a previous view context, invalidate that view if it
|
// If there is a previous view context, invalidate that view if it
|
||||||
// has changed
|
// has changed
|
||||||
if let Some(previous_view_context) = self.pending_views.remove(&view_id) {
|
if let Some(previous_view_context) = self.pending_views.remove(&view_id) {
|
||||||
if previous_view_context != context {
|
if previous_view_context != self.contexts[i] {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the bindings which map the pending keystrokes and current context
|
// Find the bindings which map the pending keystrokes and current context
|
||||||
for binding in self.keymap.bindings().iter().rev() {
|
for binding in self.keymap.bindings().iter().rev() {
|
||||||
match binding.match_keys_and_context(&self.pending_keystrokes, &context) {
|
match binding.match_keys_and_context(&self.pending_keystrokes, &self.contexts[i..])
|
||||||
|
{
|
||||||
BindingMatchResult::Complete(mut action) => {
|
BindingMatchResult::Complete(mut action) => {
|
||||||
// Swap in keystroke for special KeyPressed action
|
// Swap in keystroke for special KeyPressed action
|
||||||
if action.name() == "KeyPressed" && action.namespace() == "gpui" {
|
if action.name() == "KeyPressed" && action.namespace() == "gpui" {
|
||||||
|
@ -105,7 +112,7 @@ impl KeymapMatcher {
|
||||||
matched_bindings.push((view_id, action))
|
matched_bindings.push((view_id, action))
|
||||||
}
|
}
|
||||||
BindingMatchResult::Partial => {
|
BindingMatchResult::Partial => {
|
||||||
self.pending_views.insert(view_id, context.clone());
|
self.pending_views.insert(view_id, self.contexts[i].clone());
|
||||||
any_pending = true;
|
any_pending = true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -129,13 +136,13 @@ impl KeymapMatcher {
|
||||||
pub fn keystrokes_for_action(
|
pub fn keystrokes_for_action(
|
||||||
&self,
|
&self,
|
||||||
action: &dyn Action,
|
action: &dyn Action,
|
||||||
context: &KeymapContext,
|
contexts: &[KeymapContext],
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||||
self.keymap
|
self.keymap
|
||||||
.bindings()
|
.bindings()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|binding| binding.keystrokes_for_action(action, context))
|
.find_map(|binding| binding.keystrokes_for_action(action, contexts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,27 +356,70 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_context_predicate_eval() -> Result<()> {
|
fn test_context_predicate_eval() {
|
||||||
let predicate = KeymapContextPredicate::parse("a && b || c == d")?;
|
let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap();
|
||||||
|
|
||||||
let mut context = KeymapContext::default();
|
let mut context = KeymapContext::default();
|
||||||
context.set.insert("a".into());
|
context.set.insert("a".into());
|
||||||
assert!(!predicate.eval(&context));
|
assert!(!predicate.eval(&[context]));
|
||||||
|
|
||||||
|
let mut context = KeymapContext::default();
|
||||||
|
context.set.insert("a".into());
|
||||||
context.set.insert("b".into());
|
context.set.insert("b".into());
|
||||||
assert!(predicate.eval(&context));
|
assert!(predicate.eval(&[context]));
|
||||||
|
|
||||||
context.set.remove("b");
|
let mut context = KeymapContext::default();
|
||||||
|
context.set.insert("a".into());
|
||||||
context.map.insert("c".into(), "x".into());
|
context.map.insert("c".into(), "x".into());
|
||||||
assert!(!predicate.eval(&context));
|
assert!(!predicate.eval(&[context]));
|
||||||
|
|
||||||
|
let mut context = KeymapContext::default();
|
||||||
|
context.set.insert("a".into());
|
||||||
context.map.insert("c".into(), "d".into());
|
context.map.insert("c".into(), "d".into());
|
||||||
assert!(predicate.eval(&context));
|
assert!(predicate.eval(&[context]));
|
||||||
|
|
||||||
let predicate = KeymapContextPredicate::parse("!a")?;
|
let predicate = KeymapContextPredicate::parse("!a").unwrap();
|
||||||
assert!(predicate.eval(&KeymapContext::default()));
|
assert!(predicate.eval(&[KeymapContext::default()]));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
#[test]
|
||||||
|
fn test_context_child_predicate_eval() {
|
||||||
|
let predicate = KeymapContextPredicate::parse("a && b > c").unwrap();
|
||||||
|
let contexts = [
|
||||||
|
context_set(&["e", "f"]),
|
||||||
|
context_set(&["c", "d"]), // match this context
|
||||||
|
context_set(&["a", "b"]),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert!(!predicate.eval(&contexts[0..]));
|
||||||
|
assert!(predicate.eval(&contexts[1..]));
|
||||||
|
assert!(!predicate.eval(&contexts[2..]));
|
||||||
|
|
||||||
|
let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap();
|
||||||
|
let contexts = [
|
||||||
|
context_set(&["f"]),
|
||||||
|
context_set(&["e"]), // only match this context
|
||||||
|
context_set(&["c"]),
|
||||||
|
context_set(&["a", "b"]),
|
||||||
|
context_set(&["e"]),
|
||||||
|
context_set(&["c", "d"]),
|
||||||
|
context_set(&["a", "b"]),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert!(!predicate.eval(&contexts[0..]));
|
||||||
|
assert!(predicate.eval(&contexts[1..]));
|
||||||
|
assert!(!predicate.eval(&contexts[2..]));
|
||||||
|
assert!(!predicate.eval(&contexts[3..]));
|
||||||
|
assert!(!predicate.eval(&contexts[4..]));
|
||||||
|
assert!(!predicate.eval(&contexts[5..]));
|
||||||
|
assert!(!predicate.eval(&contexts[6..]));
|
||||||
|
|
||||||
|
fn context_set(names: &[&str]) -> KeymapContext {
|
||||||
|
KeymapContext {
|
||||||
|
set: names.iter().copied().map(str::to_string).collect(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -41,24 +41,24 @@ impl Binding {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_context(&self, context: &KeymapContext) -> bool {
|
fn match_context(&self, contexts: &[KeymapContext]) -> bool {
|
||||||
self.context_predicate
|
self.context_predicate
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|predicate| predicate.eval(context))
|
.map(|predicate| predicate.eval(contexts))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn match_keys_and_context(
|
pub fn match_keys_and_context(
|
||||||
&self,
|
&self,
|
||||||
pending_keystrokes: &Vec<Keystroke>,
|
pending_keystrokes: &Vec<Keystroke>,
|
||||||
context: &KeymapContext,
|
contexts: &[KeymapContext],
|
||||||
) -> BindingMatchResult {
|
) -> BindingMatchResult {
|
||||||
if self
|
if self
|
||||||
.keystrokes
|
.keystrokes
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|keystrokes| keystrokes.starts_with(&pending_keystrokes))
|
.map(|keystrokes| keystrokes.starts_with(&pending_keystrokes))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
&& self.match_context(context)
|
&& self.match_context(contexts)
|
||||||
{
|
{
|
||||||
// If the binding is completed, push it onto the matches list
|
// If the binding is completed, push it onto the matches list
|
||||||
if self
|
if self
|
||||||
|
@ -79,9 +79,9 @@ impl Binding {
|
||||||
pub fn keystrokes_for_action(
|
pub fn keystrokes_for_action(
|
||||||
&self,
|
&self,
|
||||||
action: &dyn Action,
|
action: &dyn Action,
|
||||||
context: &KeymapContext,
|
contexts: &[KeymapContext],
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||||
if self.action.eq(action) && self.match_context(context) {
|
if self.action.eq(action) && self.match_context(contexts) {
|
||||||
self.keystrokes.clone()
|
self.keystrokes.clone()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub enum KeymapContextPredicate {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Equal(String, String),
|
Equal(String, String),
|
||||||
NotEqual(String, String),
|
NotEqual(String, String),
|
||||||
|
Child(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
|
||||||
Not(Box<KeymapContextPredicate>),
|
Not(Box<KeymapContextPredicate>),
|
||||||
And(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
|
And(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
|
||||||
Or(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
|
Or(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
|
||||||
|
@ -39,7 +40,8 @@ impl KeymapContextPredicate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&self, context: &KeymapContext) -> bool {
|
pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
|
||||||
|
let Some(context) = contexts.first() else { return false };
|
||||||
match self {
|
match self {
|
||||||
Self::Identifier(name) => context.set.contains(name.as_str()),
|
Self::Identifier(name) => context.set.contains(name.as_str()),
|
||||||
Self::Equal(left, right) => context
|
Self::Equal(left, right) => context
|
||||||
|
@ -52,16 +54,14 @@ impl KeymapContextPredicate {
|
||||||
.get(left)
|
.get(left)
|
||||||
.map(|value| value != right)
|
.map(|value| value != right)
|
||||||
.unwrap_or(true),
|
.unwrap_or(true),
|
||||||
Self::Not(pred) => !pred.eval(context),
|
Self::Not(pred) => !pred.eval(contexts),
|
||||||
Self::And(left, right) => left.eval(context) && right.eval(context),
|
Self::Child(parent, child) => parent.eval(&contexts[1..]) && child.eval(contexts),
|
||||||
Self::Or(left, right) => left.eval(context) || right.eval(context),
|
Self::And(left, right) => left.eval(contexts) && right.eval(contexts),
|
||||||
|
Self::Or(left, right) => left.eval(contexts) || right.eval(contexts),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(
|
fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
|
||||||
mut source: &str,
|
|
||||||
min_precedence: u32,
|
|
||||||
) -> anyhow::Result<(KeymapContextPredicate, &str)> {
|
|
||||||
type Op =
|
type Op =
|
||||||
fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
|
fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
|
||||||
|
|
||||||
|
@ -70,10 +70,11 @@ impl KeymapContextPredicate {
|
||||||
|
|
||||||
'parse: loop {
|
'parse: loop {
|
||||||
for (operator, precedence, constructor) in [
|
for (operator, precedence, constructor) in [
|
||||||
("&&", PRECEDENCE_AND, KeymapContextPredicate::new_and as Op),
|
(">", PRECEDENCE_CHILD, Self::new_child as Op),
|
||||||
("||", PRECEDENCE_OR, KeymapContextPredicate::new_or as Op),
|
("&&", PRECEDENCE_AND, Self::new_and as Op),
|
||||||
("==", PRECEDENCE_EQ, KeymapContextPredicate::new_eq as Op),
|
("||", PRECEDENCE_OR, Self::new_or as Op),
|
||||||
("!=", PRECEDENCE_EQ, KeymapContextPredicate::new_neq as Op),
|
("==", PRECEDENCE_EQ, Self::new_eq as Op),
|
||||||
|
("!=", PRECEDENCE_EQ, Self::new_neq as Op),
|
||||||
] {
|
] {
|
||||||
if source.starts_with(operator) && precedence >= min_precedence {
|
if source.starts_with(operator) && precedence >= min_precedence {
|
||||||
source = Self::skip_whitespace(&source[operator.len()..]);
|
source = Self::skip_whitespace(&source[operator.len()..]);
|
||||||
|
@ -89,7 +90,7 @@ impl KeymapContextPredicate {
|
||||||
Ok((predicate, source))
|
Ok((predicate, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_primary(mut source: &str) -> anyhow::Result<(KeymapContextPredicate, &str)> {
|
fn parse_primary(mut source: &str) -> anyhow::Result<(Self, &str)> {
|
||||||
let next = source
|
let next = source
|
||||||
.chars()
|
.chars()
|
||||||
.next()
|
.next()
|
||||||
|
@ -140,6 +141,10 @@ impl KeymapContextPredicate {
|
||||||
Ok(Self::And(Box::new(self), Box::new(other)))
|
Ok(Self::And(Box::new(self), Box::new(other)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_child(self, other: Self) -> Result<Self> {
|
||||||
|
Ok(Self::Child(Box::new(self), Box::new(other)))
|
||||||
|
}
|
||||||
|
|
||||||
fn new_eq(self, other: Self) -> Result<Self> {
|
fn new_eq(self, other: Self) -> Result<Self> {
|
||||||
if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
|
if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
|
||||||
Ok(Self::Equal(left, right))
|
Ok(Self::Equal(left, right))
|
||||||
|
@ -157,10 +162,11 @@ impl KeymapContextPredicate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRECEDENCE_OR: u32 = 1;
|
const PRECEDENCE_CHILD: u32 = 1;
|
||||||
const PRECEDENCE_AND: u32 = 2;
|
const PRECEDENCE_OR: u32 = 2;
|
||||||
const PRECEDENCE_EQ: u32 = 3;
|
const PRECEDENCE_AND: u32 = 3;
|
||||||
const PRECEDENCE_NOT: u32 = 4;
|
const PRECEDENCE_EQ: u32 = 4;
|
||||||
|
const PRECEDENCE_NOT: u32 = 5;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -573,7 +573,7 @@ pub struct LayoutContext<'a> {
|
||||||
|
|
||||||
impl<'a> LayoutContext<'a> {
|
impl<'a> LayoutContext<'a> {
|
||||||
pub(crate) fn keystrokes_for_action(
|
pub(crate) fn keystrokes_for_action(
|
||||||
&self,
|
&mut self,
|
||||||
action: &dyn Action,
|
action: &dyn Action,
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||||
self.app
|
self.app
|
||||||
|
|
Loading…
Reference in a new issue