Add '>' child operator in keymap context predicates

This commit is contained in:
Max Brunsfeld 2023-01-16 16:00:46 -08:00
parent f62d13de21
commit 373902d933
5 changed files with 136 additions and 56 deletions

View file

@ -41,24 +41,24 @@ impl Binding {
})
}
fn match_context(&self, context: &KeymapContext) -> bool {
fn match_context(&self, contexts: &[KeymapContext]) -> bool {
self.context_predicate
.as_ref()
.map(|predicate| predicate.eval(context))
.map(|predicate| predicate.eval(contexts))
.unwrap_or(true)
}
pub fn match_keys_and_context(
&self,
pending_keystrokes: &Vec<Keystroke>,
context: &KeymapContext,
contexts: &[KeymapContext],
) -> BindingMatchResult {
if self
.keystrokes
.as_ref()
.map(|keystrokes| keystrokes.starts_with(&pending_keystrokes))
.unwrap_or(true)
&& self.match_context(context)
&& self.match_context(contexts)
{
// If the binding is completed, push it onto the matches list
if self
@ -79,9 +79,9 @@ impl Binding {
pub fn keystrokes_for_action(
&self,
action: &dyn Action,
context: &KeymapContext,
contexts: &[KeymapContext],
) -> 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()
} else {
None

View file

@ -23,6 +23,7 @@ pub enum KeymapContextPredicate {
Identifier(String),
Equal(String, String),
NotEqual(String, String),
Child(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
Not(Box<KeymapContextPredicate>),
And(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 {
Self::Identifier(name) => context.set.contains(name.as_str()),
Self::Equal(left, right) => context
@ -52,16 +54,14 @@ impl KeymapContextPredicate {
.get(left)
.map(|value| value != right)
.unwrap_or(true),
Self::Not(pred) => !pred.eval(context),
Self::And(left, right) => left.eval(context) && right.eval(context),
Self::Or(left, right) => left.eval(context) || right.eval(context),
Self::Not(pred) => !pred.eval(contexts),
Self::Child(parent, child) => parent.eval(&contexts[1..]) && child.eval(contexts),
Self::And(left, right) => left.eval(contexts) && right.eval(contexts),
Self::Or(left, right) => left.eval(contexts) || right.eval(contexts),
}
}
fn parse_expr(
mut source: &str,
min_precedence: u32,
) -> anyhow::Result<(KeymapContextPredicate, &str)> {
fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
type Op =
fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
@ -70,10 +70,11 @@ impl KeymapContextPredicate {
'parse: loop {
for (operator, precedence, constructor) in [
("&&", PRECEDENCE_AND, KeymapContextPredicate::new_and as Op),
("||", PRECEDENCE_OR, KeymapContextPredicate::new_or as Op),
("==", PRECEDENCE_EQ, KeymapContextPredicate::new_eq as Op),
("!=", PRECEDENCE_EQ, KeymapContextPredicate::new_neq as Op),
(">", PRECEDENCE_CHILD, Self::new_child as Op),
("&&", PRECEDENCE_AND, Self::new_and as Op),
("||", PRECEDENCE_OR, Self::new_or as Op),
("==", PRECEDENCE_EQ, Self::new_eq as Op),
("!=", PRECEDENCE_EQ, Self::new_neq as Op),
] {
if source.starts_with(operator) && precedence >= min_precedence {
source = Self::skip_whitespace(&source[operator.len()..]);
@ -89,7 +90,7 @@ impl KeymapContextPredicate {
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
.chars()
.next()
@ -140,6 +141,10 @@ impl KeymapContextPredicate {
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> {
if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
Ok(Self::Equal(left, right))
@ -157,10 +162,11 @@ impl KeymapContextPredicate {
}
}
const PRECEDENCE_OR: u32 = 1;
const PRECEDENCE_AND: u32 = 2;
const PRECEDENCE_EQ: u32 = 3;
const PRECEDENCE_NOT: u32 = 4;
const PRECEDENCE_CHILD: u32 = 1;
const PRECEDENCE_OR: u32 = 2;
const PRECEDENCE_AND: u32 = 3;
const PRECEDENCE_EQ: u32 = 4;
const PRECEDENCE_NOT: u32 = 5;
#[cfg(test)]
mod tests {