binding.rs

  1use std::rc::Rc;
  2
  3use collections::HashMap;
  4
  5use crate::{
  6    Action, InvalidKeystrokeError, KeyBindingContextPredicate, KeybindingKeystroke, Keystroke,
  7    SharedString,
  8};
  9use smallvec::SmallVec;
 10
 11/// A keybinding and its associated metadata, from the keymap.
 12pub struct KeyBinding {
 13    pub(crate) action: Box<dyn Action>,
 14    pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>,
 15    pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
 16    pub(crate) meta: Option<KeyBindingMetaIndex>,
 17    /// The json input string used when building the keybinding, if any
 18    pub(crate) action_input: Option<SharedString>,
 19}
 20
 21impl Clone for KeyBinding {
 22    fn clone(&self) -> Self {
 23        KeyBinding {
 24            action: self.action.boxed_clone(),
 25            keystrokes: self.keystrokes.clone(),
 26            context_predicate: self.context_predicate.clone(),
 27            meta: self.meta,
 28            action_input: self.action_input.clone(),
 29        }
 30    }
 31}
 32
 33impl KeyBinding {
 34    /// Construct a new keybinding from the given data. Panics on parse error.
 35    pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
 36        let context_predicate = if let Some(context) = context {
 37            Some(KeyBindingContextPredicate::parse(context).unwrap().into())
 38        } else {
 39            None
 40        };
 41        Self::load(keystrokes, Box::new(action), context_predicate, None, None).unwrap()
 42    }
 43
 44    /// Load a keybinding from the given raw data.
 45    pub fn load(
 46        keystrokes: &str,
 47        action: Box<dyn Action>,
 48        context_predicate: Option<Rc<KeyBindingContextPredicate>>,
 49        key_equivalents: Option<&HashMap<char, char>>,
 50        action_input: Option<SharedString>,
 51    ) -> std::result::Result<Self, InvalidKeystrokeError> {
 52        let mut keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes
 53            .split_whitespace()
 54            .map(KeybindingKeystroke::parse)
 55            .collect::<std::result::Result<_, _>>()?;
 56
 57        if let Some(equivalents) = key_equivalents {
 58            for keystroke in keystrokes.iter_mut() {
 59                if keystroke.inner.key.chars().count() == 1 {
 60                    if let Some(key) = equivalents.get(&keystroke.inner.key.chars().next().unwrap())
 61                    {
 62                        keystroke.inner.key = key.to_string();
 63                    }
 64                }
 65            }
 66        }
 67
 68        Ok(Self {
 69            keystrokes,
 70            action,
 71            context_predicate,
 72            meta: None,
 73            action_input,
 74        })
 75    }
 76
 77    /// Set the metadata for this binding.
 78    pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self {
 79        self.meta = Some(meta);
 80        self
 81    }
 82
 83    /// Set the metadata for this binding.
 84    pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) {
 85        self.meta = Some(meta);
 86    }
 87
 88    /// Check if the given keystrokes match this binding.
 89    pub fn match_keystrokes(&self, typed: &[Keystroke]) -> Option<bool> {
 90        if self.keystrokes.len() < typed.len() {
 91            return None;
 92        }
 93
 94        for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
 95            if !typed.should_match(target) {
 96                return None;
 97            }
 98        }
 99
100        Some(self.keystrokes.len() > typed.len())
101    }
102
103    /// TODO:
104    pub fn match_keybinding_keystrokes(&self, typed: &[KeybindingKeystroke]) -> Option<bool> {
105        if self.keystrokes.len() < typed.len() {
106            return None;
107        }
108
109        for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
110            if !typed.inner.should_match(target) {
111                return None;
112            }
113        }
114
115        Some(self.keystrokes.len() > typed.len())
116    }
117
118    /// Get the keystrokes associated with this binding
119    pub fn keystrokes(&self) -> &[KeybindingKeystroke] {
120        self.keystrokes.as_slice()
121    }
122
123    /// Get the action associated with this binding
124    pub fn action(&self) -> &dyn Action {
125        self.action.as_ref()
126    }
127
128    /// Get the predicate used to match this binding
129    pub fn predicate(&self) -> Option<Rc<KeyBindingContextPredicate>> {
130        self.context_predicate.as_ref().map(|rc| rc.clone())
131    }
132
133    /// Get the metadata for this binding
134    pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
135        self.meta
136    }
137
138    /// Get the action input associated with the action for this binding
139    pub fn action_input(&self) -> Option<SharedString> {
140        self.action_input.clone()
141    }
142}
143
144impl std::fmt::Debug for KeyBinding {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        f.debug_struct("KeyBinding")
147            .field("keystrokes", &self.keystrokes)
148            .field("context_predicate", &self.context_predicate)
149            .field("action", &self.action.name())
150            .finish()
151    }
152}
153
154/// A unique identifier for retrieval of metadata associated with a key binding.
155/// Intended to be used as an index or key into a user-defined store of metadata
156/// associated with the binding, such as the source of the binding.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub struct KeyBindingMetaIndex(pub u32);