binding.rs

  1use anyhow::Result;
  2use smallvec::SmallVec;
  3
  4use crate::Action;
  5
  6use super::{KeymapContext, KeymapContextPredicate, Keystroke};
  7
  8pub struct Binding {
  9    action: Box<dyn Action>,
 10    keystrokes: Option<SmallVec<[Keystroke; 2]>>,
 11    context_predicate: Option<KeymapContextPredicate>,
 12}
 13
 14impl Binding {
 15    pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
 16        Self::load(keystrokes, Box::new(action), context).unwrap()
 17    }
 18
 19    pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
 20        let context = if let Some(context) = context {
 21            Some(KeymapContextPredicate::parse(context)?)
 22        } else {
 23            None
 24        };
 25
 26        let keystrokes = if keystrokes == "*" {
 27            None // Catch all context
 28        } else {
 29            Some(
 30                keystrokes
 31                    .split_whitespace()
 32                    .map(Keystroke::parse)
 33                    .collect::<Result<_>>()?,
 34            )
 35        };
 36
 37        Ok(Self {
 38            keystrokes,
 39            action,
 40            context_predicate: context,
 41        })
 42    }
 43
 44    fn match_context(&self, contexts: &[KeymapContext]) -> bool {
 45        self.context_predicate
 46            .as_ref()
 47            .map(|predicate| predicate.eval(contexts))
 48            .unwrap_or(true)
 49    }
 50
 51    pub fn match_keys_and_context(
 52        &self,
 53        pending_keystrokes: &Vec<Keystroke>,
 54        contexts: &[KeymapContext],
 55    ) -> BindingMatchResult {
 56        if self
 57            .keystrokes
 58            .as_ref()
 59            .map(|keystrokes| keystrokes.starts_with(&pending_keystrokes))
 60            .unwrap_or(true)
 61            && self.match_context(contexts)
 62        {
 63            // If the binding is completed, push it onto the matches list
 64            if self
 65                .keystrokes
 66                .as_ref()
 67                .map(|keystrokes| keystrokes.len() == pending_keystrokes.len())
 68                .unwrap_or(true)
 69            {
 70                BindingMatchResult::Complete(self.action.boxed_clone())
 71            } else {
 72                BindingMatchResult::Partial
 73            }
 74        } else {
 75            BindingMatchResult::Fail
 76        }
 77    }
 78
 79    pub fn keystrokes_for_action(
 80        &self,
 81        action: &dyn Action,
 82        contexts: &[KeymapContext],
 83    ) -> Option<SmallVec<[Keystroke; 2]>> {
 84        if self.action.eq(action) && self.match_context(contexts) {
 85            self.keystrokes.clone()
 86        } else {
 87            None
 88        }
 89    }
 90
 91    pub fn keystrokes(&self) -> Option<&[Keystroke]> {
 92        self.keystrokes.as_deref()
 93    }
 94
 95    pub fn action(&self) -> &dyn Action {
 96        self.action.as_ref()
 97    }
 98}
 99
100pub enum BindingMatchResult {
101    Complete(Box<dyn Action>),
102    Partial,
103    Fail,
104}