1use std::ops::{Bound, RangeBounds};
2
3use crossterm::event::{
4 KeyCode, KeyEvent, KeyEventKind, KeyEventState, MediaKeyCode, ModifierKeyCode,
5};
6
7pub use crate::__bindings__ as bindings;
8use crate::{
9 mode::KeyMod,
10 text::{Text, txt},
11};
12
13#[derive(Clone)]
28pub struct Bindings {
29 pub title: Option<Text>,
34 pub list: Vec<(Vec<Binding>, Text, Option<Bindings>)>,
42}
43
44impl Bindings {
45 pub fn matches_sequence(&self, seq: &[KeyEvent]) -> bool {
48 let mut list = &self.list;
49 seq.iter().all(|key_event| {
50 if let Some((.., bindings)) = list.iter().find(matches_event(*key_event)) {
51 list = &bindings.as_ref().unwrap_or(self).list;
52 true
53 } else {
54 false
55 }
56 })
57 }
58
59 pub fn sequence_has_followup(&self, seq: &[KeyEvent]) -> bool {
62 let mut list = &self.list;
63 seq.iter().all(|key_event| {
64 if let Some((.., bindings)) = list.iter().find(matches_event(*key_event))
65 && let Some(bindings) = bindings
66 {
67 list = &bindings.list;
68 true
69 } else {
70 false
71 }
72 })
73 }
74
75 pub fn bindings_for(&self, seq: &[KeyEvent]) -> Option<&Bindings> {
77 let mut bindings = self;
78 if seq.is_empty() {
79 Some(self)
80 } else {
81 seq.iter()
82 .map_while(|key_event| {
83 let (.., nested) = bindings.list.iter().find(matches_event(*key_event))?;
84 bindings = nested.as_ref()?;
85 Some(bindings)
86 })
87 .nth(seq.len() - 1)
88 }
89 }
90
91 pub fn description_for<'a>(&'a self, seq: &[KeyEvent]) -> Option<&'a Text> {
95 let mut bindings = Some(self);
96 seq.iter()
97 .map_while(|key_event| {
98 let (_, text, nested) = bindings?.list.iter().find(matches_event(*key_event))?;
99 bindings = nested.as_ref();
100 Some(text)
101 })
102 .last()
103 }
104
105 pub fn description_for_mut<'a>(&'a mut self, seq: &[KeyEvent]) -> Option<&'a mut Text> {
109 let mut bindings = Some(self);
110 seq.iter()
111 .map_while(move |key_event| {
112 let (_, text, nested) =
113 bindings.take()?.list.iter_mut().find(|(list, ..)| {
114 list.iter().any(|binding| binding.matches(*key_event))
115 })?;
116 bindings = nested.as_mut();
117 Some(text)
118 })
119 .last()
120 }
121}
122
123fn matches_event(
124 key_event: KeyEvent,
125) -> impl FnMut(&&(Vec<Binding>, Text, Option<Bindings>)) -> bool {
126 move |(list, ..)| list.iter().any(|binding| binding.matches(key_event))
127}
128
129impl std::fmt::Debug for Bindings {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.debug_struct("Bindings")
132 .field("list", &self.list)
133 .finish()
134 }
135}
136
137#[derive(Debug, Clone, Copy)]
144pub enum Binding {
145 CharRange(Bound<char>, Bound<char>, KeyMod),
148 FnRange(Bound<u8>, Bound<u8>, KeyMod),
151 AnyModifier(KeyMod),
155 AnyMedia(KeyMod),
157 Any(Option<KeyMod>),
160 Event(KeyCode, KeyMod),
162}
163
164impl Binding {
165 pub fn new(code: KeyCode, modif: KeyMod) -> Self {
169 Self::Event(code, modif)
170 }
171
172 pub fn anything() -> Self {
174 Self::Any(None)
175 }
176
177 pub fn as_text(&self) -> Text {
184 let mut builder = Text::builder();
185
186 if !matches!(self, Binding::Event(..))
187 && self.modifier().is_some_and(|modif| modif != KeyMod::NONE)
188 {
189 builder.push(txt!("[key.angle]<"));
190 builder.push(super::modifier_text(
191 self.modifier().unwrap_or(KeyMod::NONE),
192 ))
193 }
194
195 builder.push(match self {
196 Binding::CharRange(b0, b1, _) => match (b0, b1) {
197 (Bound::Included(lhs), Bound::Included(rhs)) => {
198 txt!("[key.char]'{lhs}'[key.range]..=[key.char]'{rhs}'")
199 }
200 (Bound::Included(lhs), Bound::Excluded(rhs)) => {
201 txt!("[key.char]'{lhs}'[key.range]..[key.char]'{rhs}'")
202 }
203 (Bound::Included(char), Bound::Unbounded) => {
204 txt!("[key.char]'{char}'[key.range]..")
205 }
206 (Bound::Excluded(lhs), Bound::Included(rhs)) => {
207 txt!("[key.char]'{lhs}'[key.range]>..=[key.char]'{rhs}'")
208 }
209 (Bound::Excluded(lhs), Bound::Excluded(rhs)) => {
210 txt!("[key.char]'{lhs}'[key.range]>..[key.char]'{rhs}'")
211 }
212 (Bound::Excluded(char), Bound::Unbounded) => {
213 txt!("[key.char]'{char}'[key.range]>..")
214 }
215 (Bound::Unbounded, Bound::Included(char)) => {
216 txt!("[key.range]..=[key.char]'{char}'")
217 }
218 (Bound::Unbounded, Bound::Excluded(char)) => {
219 txt!("[key.range]..[key.char]'{char}'")
220 }
221 (Bound::Unbounded, Bound::Unbounded) => txt!("[key.any.char]{{char}}"),
222 },
223 Binding::FnRange(b0, b1, _) => match (b0, b1) {
224 (Bound::Included(lhs), Bound::Included(rhs)) => {
225 txt!("[key.special]F{lhs}[key.range]..=[key.special]F{rhs}")
226 }
227 (Bound::Included(lhs), Bound::Excluded(rhs)) => {
228 txt!("[key.special]F{lhs}[key.range]..[key.special]F{rhs}")
229 }
230 (Bound::Included(num), Bound::Unbounded) => {
231 txt!("[key.special]F{num}[key.range]=..")
232 }
233 (Bound::Excluded(lhs), Bound::Included(rhs)) => {
234 txt!("[key.special]F{lhs}[key.range]>..=[key.special]F{rhs}")
235 }
236 (Bound::Excluded(lhs), Bound::Excluded(rhs)) => {
237 txt!("[key.special]F{lhs}[key.range]>..[key.special]F{rhs}")
238 }
239 (Bound::Excluded(num), Bound::Unbounded) => {
240 txt!("[key.special]F{num}[key.range]>..")
241 }
242 (Bound::Unbounded, Bound::Included(num)) => {
243 txt!("[key.range]..=[key.special]F{num}")
244 }
245 (Bound::Unbounded, Bound::Excluded(num)) => {
246 txt!("[key.range]..[key.special]F{num}")
247 }
248 (Bound::Unbounded, Bound::Unbounded) => txt!("[key.any]{{f key}}"),
249 },
250 Binding::AnyModifier(_) => txt!("[key.any]{{modifier}}"),
251 Binding::AnyMedia(_) => txt!("[key.any]{{media key}}"),
252 Binding::Any(_) => txt!("[key.any]{{any}}"),
253 Binding::Event(code, modif) => super::keys_to_text(&[KeyEvent::new(*code, *modif)]),
254 });
255
256 if !matches!(self, Binding::Event(..))
257 && self.modifier().is_some_and(|modif| modif != KeyMod::NONE)
258 {
259 builder.push(txt!("[key.angle]>"));
260 }
261
262 builder.build()
263 }
264
265 pub fn modifier(&self) -> Option<KeyMod> {
270 match self {
271 Binding::CharRange(.., modif)
272 | Binding::FnRange(.., modif)
273 | Binding::AnyModifier(modif)
274 | Binding::AnyMedia(modif)
275 | Binding::Event(_, modif) => Some(*modif),
276 Binding::Any(modif) => *modif,
277 }
278 }
279
280 pub fn as_key_event(&self) -> Option<KeyEvent> {
285 let &Binding::Event(code, modifiers) = self else {
286 return None;
287 };
288
289 Some(KeyEvent {
290 code,
291 modifiers,
292 kind: KeyEventKind::Press,
293 state: KeyEventState::NONE,
294 })
295 }
296
297 pub fn matches(&self, key_event: KeyEvent) -> bool {
299 fn contains<T: Ord>(b0: Bound<T>, b1: Bound<T>, subject: T) -> bool {
300 match b0 {
301 Bound::Included(b0) if subject < b0 => return false,
302 Bound::Excluded(b0) if subject <= b0 => return false,
303 _ => {}
304 }
305 match b1 {
306 Bound::Included(b1) if subject <= b1 => true,
307 Bound::Excluded(b1) if subject < b1 => true,
308 Bound::Unbounded => true,
309 _ => false,
310 }
311 }
312
313 if key_event.is_release() {
314 return false;
315 }
316
317 match *self {
318 Binding::CharRange(b0, b1, modifiers) => {
319 if let KeyCode::Char(char) = key_event.code {
320 key_event.modifiers == modifiers && contains(b0, b1, char)
321 } else {
322 false
323 }
324 }
325 Binding::FnRange(b0, b1, modifiers) => {
326 if let KeyCode::F(num) = key_event.code {
327 key_event.modifiers == modifiers && contains(b0, b1, num)
328 } else {
329 false
330 }
331 }
332 Binding::AnyModifier(modifiers) => {
333 matches!(key_event.code, KeyCode::Modifier(_)) && key_event.modifiers == modifiers
334 }
335 Binding::AnyMedia(modifiers) => {
336 matches!(key_event.code, KeyCode::Media(_)) && key_event.modifiers == modifiers
337 }
338 Binding::Any(modif) => modif.is_none_or(|modif| modif == key_event.modifiers),
339 Binding::Event(code, modif) => code == key_event.code && modif == key_event.modifiers,
340 }
341 }
342}
343
344macro_rules! implFromRange {
345 ($($range:ident)::+) => {
346 impl From<($($range)::+<char>, KeyMod)> for Binding {
347 fn from((chars, modif): ($($range)::+<char>, KeyMod)) -> Self {
348 Binding::CharRange(
349 chars.start_bound().cloned(),
350 chars.end_bound().cloned(),
351 modif
352 )
353 }
354 }
355
356 impl From<($($range)::+<u8>, KeyMod)> for Binding {
357 fn from((fns, modif): ($($range)::+<u8>, KeyMod)) -> Self {
358 Binding::FnRange(fns.start_bound().cloned(), fns.end_bound().cloned(), modif)
359 }
360 }
361 };
362}
363
364implFromRange!(std::ops::Range);
365implFromRange!(std::ops::RangeFrom);
366implFromRange!(std::ops::RangeInclusive);
367implFromRange!(std::ops::RangeTo);
368implFromRange!(std::ops::RangeToInclusive);
369
370impl From<(char, KeyMod)> for Binding {
371 fn from((char, modif): (char, KeyMod)) -> Self {
372 Binding::Event(KeyCode::Char(char), modif)
373 }
374}
375
376impl From<(u8, KeyMod)> for Binding {
377 fn from((num, modif): (u8, KeyMod)) -> Self {
378 Binding::Event(KeyCode::F(num), modif)
379 }
380}
381
382impl From<(MediaKeyCode, KeyMod)> for Binding {
383 fn from((media, modif): (MediaKeyCode, KeyMod)) -> Self {
384 Binding::Event(KeyCode::Media(media), modif)
385 }
386}
387
388impl From<(ModifierKeyCode, KeyMod)> for Binding {
389 fn from((modifier, modif): (ModifierKeyCode, KeyMod)) -> Self {
390 Binding::Event(KeyCode::Modifier(modifier), modif)
391 }
392}
393
394#[macro_export]
395#[doc(hidden)]
396macro_rules! __bindings__ {
397 (match _ $match:tt) => {{
398 #[allow(clippy::vec_init_then_push)]
399 let bindings: Vec<_> = $crate::mode::bindings!(@bindings $match);
400
401 #[allow(clippy::vec_init_then_push)]
402 let descriptions = $crate::mode::bindings!(@descriptions $match);
403
404 #[allow(clippy::vec_init_then_push)]
405 let followups = $crate::mode::bindings!(@followups $match);
406
407 $crate::mode::Bindings {
408 title: None,
409 list: bindings
410 .into_iter()
411 .zip(descriptions)
412 .zip(followups)
413 .map(|((b, d), f)| (b, d, f))
414 .collect()
415 }
416 }};
417
418 (@bindings { $($patterns:tt)* }) => {{
419 let mut list = vec![Vec::new()];
420 $crate::mode::bindings!(@binding_entry list: $($patterns)*);
421 list
422 }};
423
424 (@binding_entry $list:ident:) => {};
425 (@binding_entry
426 $list:ident: $modif:ident$excl:tt($($tokens:tt)*) | $($rest:tt)*
427 ) => {
428 let last = $list.last_mut().unwrap();
429 $modif$excl(@bindings [] last, $($tokens)*);
430 $crate::mode::bindings!(@binding_entry $list: $($rest)*);
431 };
432 (@binding_entry
433 $list:ident: $modif:ident$excl:tt($($tokens:tt)*) => $result:expr $(, $($rest:tt)*)?
434 ) => {
435 let last = $list.last_mut().unwrap();
436 $modif$excl(@bindings [] last, $($tokens)*);
437 $list.push(Vec::new());
438 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
439 };
440 (@binding_entry
441 $list:ident: $modif:ident$excl:tt($($tokens:tt)*) => $result:tt $(,)? $($rest:tt)*
442 ) => {
443 let last = $list.last_mut().unwrap();
444 $modif$excl(@bindings last, [] $($tokens)*);
445 $list.push(Vec::new());
446 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
447 };
448 (@binding_entry $list:ident: _ | $($rest:tt)*) => {
449 $list.last_mut().unwrap().push($crate::mode::Binding::anything());
450 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
451 };
452 (@binding_entry $list:ident: _ => $result:expr, $($rest:tt)*) => {
453 $list.last_mut().unwrap().push($crate::mode::Binding::anything());
454 $list.push(Vec::new());
455 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
456 };
457 (@binding_entry $list:ident: _ => $result:tt $(,)? $($rest:tt)*) => {
458 $list.last_mut().unwrap().push($crate::mode::Binding::anything());
459 $list.push(Vec::new());
460 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
461 };
462 (@binding_entry $list:ident: $pattern:expr => $result:expr, $($rest:tt)*) => {
463 $list.last_mut().push($crate::mode::Binding::anything());
464 $list.push(Vec::new());
465 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
466 };
467 (@binding_entry $list:ident: $binding_pat:expr => $matcher:tt $(,)? $($rest:tt)*) => {
468 $list.last_mut().unwrap().push($binding_pat);
469 $list.push(Vec::new());
470 $crate::mode::bindings!(@binding_entry $list: $($($rest)*)?);
471 };
472
473 (@descriptions { $($patterns:tt)* }) => {{
474 let mut list = Vec::new();
475 $crate::mode::bindings!(@description_entry list: $($patterns)*);
476 list
477 }};
478
479 (@description_entry $list:ident:) => {};
480 (@description_entry
481 $list:ident:
482 $pattern:pat => ($text:expr, $($matcher:tt)+)
483 $(,$($rest:tt)*)?
484 ) => {
485 $list.push($text);
486 $crate::mode::bindings!(@description_entry $list: $($($rest)*)?);
487 };
488 (@description_entry $list:ident: $pattern:pat => $text:expr $(,$($rest:tt)*)?) => {
489 $list.push($text);
490 $crate::mode::bindings!(@description_entry $list: $($($rest)*)?);
491 };
492
493 (@followups { $($patterns:tt)+ }) => {{
494 let mut list = Vec::new();
495 $crate::mode::bindings!(@followup_entry list: $($patterns)+);
496 list
497 }};
498
499 (@followup_entry $list:ident:) => {};
500 (@followup_entry
501 $list:ident:
502 $pattern:pat => ($texts:expr, match _ $match:tt)
503 $(,$($rest:tt)*)?
504 ) => {
505 $list.push(Some($crate::mode::bindings! { match _ $match }));
506 $crate::mode::bindings!(@followup_entry $list: $($($rest)*)?);
507 };
508 (@followup_entry
509 $list:ident:
510 $pattern:pat => ($text:expr, $bindings:expr)
511 $(,$($rest:tt)*)?
512 ) => {
513 $list.push(Some($bindings));
514 $crate::mode::bindings!(@followup_entry $list: $($($rest)*)?);
515 };
516 (@followup_entry $list:ident: $pattern:pat => $text:expr $(,$($rest:tt)*)?) => {
517 $list.push(None);
518 $crate::mode::bindings!(@followup_entry $list: $($($rest)*)?);
519 };
520}