binding.rs

  1use std::rc::Rc;
  2
  3use collections::HashMap;
  4
  5use crate::{Action, InvalidKeystrokeError, KeyBindingContextPredicate, Keystroke};
  6use smallvec::SmallVec;
  7
  8/// A keybinding and its associated metadata, from the keymap.
  9pub struct KeyBinding {
 10    pub(crate) action: Box<dyn Action>,
 11    pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
 12    pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
 13}
 14
 15impl Clone for KeyBinding {
 16    fn clone(&self) -> Self {
 17        KeyBinding {
 18            action: self.action.boxed_clone(),
 19            keystrokes: self.keystrokes.clone(),
 20            context_predicate: self.context_predicate.clone(),
 21        }
 22    }
 23}
 24
 25impl KeyBinding {
 26    /// Construct a new keybinding from the given data. Panics on parse error.
 27    pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
 28        let context_predicate = if let Some(context) = context {
 29            Some(KeyBindingContextPredicate::parse(context).unwrap().into())
 30        } else {
 31            None
 32        };
 33        Self::load(keystrokes, Box::new(action), context_predicate, None).unwrap()
 34    }
 35
 36    /// Load a keybinding from the given raw data.
 37    pub fn load(
 38        keystrokes: &str,
 39        action: Box<dyn Action>,
 40        context_predicate: Option<Rc<KeyBindingContextPredicate>>,
 41        key_equivalents: Option<&HashMap<char, char>>,
 42    ) -> std::result::Result<Self, InvalidKeystrokeError> {
 43        let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
 44            .split_whitespace()
 45            .map(Keystroke::parse)
 46            .collect::<std::result::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,
 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<Rc<KeyBindingContextPredicate>> {
 92        self.context_predicate.as_ref().map(|rc| rc.clone())
 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}