binding.rs

  1use collections::HashMap;
  2
  3use crate::{Action, KeyBindingContextPredicate, Keystroke};
  4use anyhow::Result;
  5use smallvec::SmallVec;
  6
  7/// A keybinding and its associated metadata, from the keymap.
  8pub struct KeyBinding {
  9    pub(crate) action: Box<dyn Action>,
 10    pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
 11    pub(crate) context_predicate: Option<KeyBindingContextPredicate>,
 12}
 13
 14impl Clone for KeyBinding {
 15    fn clone(&self) -> Self {
 16        KeyBinding {
 17            action: self.action.boxed_clone(),
 18            keystrokes: self.keystrokes.clone(),
 19            context_predicate: self.context_predicate.clone(),
 20        }
 21    }
 22}
 23
 24impl KeyBinding {
 25    /// Construct a new keybinding from the given data.
 26    pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
 27        Self::load(keystrokes, Box::new(action), context_predicate, None).unwrap()
 28    }
 29
 30    /// Load a keybinding from the given raw data.
 31    pub fn load(
 32        keystrokes: &str,
 33        action: Box<dyn Action>,
 34        context: Option<&str>,
 35        key_equivalents: Option<&HashMap<char, char>>,
 36    ) -> Result<Self> {
 37        let context = if let Some(context) = context {
 38            Some(KeyBindingContextPredicate::parse(context)?)
 39        } else {
 40            None
 41        };
 42
 43        let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
 44            .split_whitespace()
 45            .map(Keystroke::parse)
 46            .collect::<Result<_>>()?;
 47
 48        if let Some(equivalents) = key_equivalents {
 49            for keystroke in keystrokes.iter_mut() {
 50                if keystroke.key.chars().count() == 1 {
 51                    if let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap()) {
 52                        keystroke.key = key.to_string();
 53                    }
 54                }
 55            }
 56        }
 57
 58        Ok(Self {
 59            keystrokes,
 60            action,
 61            context_predicate: context,
 62        })
 63    }
 64
 65    /// Check if the given keystrokes match this binding.
 66    pub fn match_keystrokes(&self, typed: &[Keystroke]) -> Option<bool> {
 67        if self.keystrokes.len() < typed.len() {
 68            return None;
 69        }
 70
 71        for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
 72            if !typed.should_match(target) {
 73                return None;
 74            }
 75        }
 76
 77        Some(self.keystrokes.len() > typed.len())
 78    }
 79
 80    /// Get the keystrokes associated with this binding
 81    pub fn keystrokes(&self) -> &[Keystroke] {
 82        self.keystrokes.as_slice()
 83    }
 84
 85    /// Get the action associated with this binding
 86    pub fn action(&self) -> &dyn Action {
 87        self.action.as_ref()
 88    }
 89
 90    /// Get the predicate used to match this binding
 91    pub fn predicate(&self) -> Option<&KeyBindingContextPredicate> {
 92        self.context_predicate.as_ref()
 93    }
 94}
 95
 96impl std::fmt::Debug for KeyBinding {
 97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 98        f.debug_struct("KeyBinding")
 99            .field("keystrokes", &self.keystrokes)
100            .field("context_predicate", &self.context_predicate)
101            .field("action", &self.action.name())
102            .finish()
103    }
104}