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