duat_core/mode/
patterns.rs

1//! Macros for very easy [`Mode`] pattern matching
2//!
3//! This module adds the [`event!`], [`alt!`], [`ctrl!`] and
4//! [`shift!`] macros, which can be used to create very succinct
5//! patterns for matching, greatly improving on the signal to noise
6//! ratio, especially inside the [`Mode::send_key`] function.
7//!
8//! [`Mode`]: super::Mode
9//! [`Mode::send_key`]: super::Mode::send_key
10use crate::mode::KeyMod;
11#[doc(inline)]
12pub use crate::{__alt__ as alt, __ctrl__ as ctrl, __event__ as event, __shift__ as shift};
13
14/// Macro for shortening [`KeyEvent`]s in pattern matching
15///
16/// This macro just turns this:
17///
18/// ```rust
19/// # use duat_core::mode::{KeyCode, KeyEvent, event};
20/// # let key_event: KeyEvent = KeyCode::Char('c').into();
21/// match key_event {
22///     event!('c') => {
23///         //...
24///     }
25///     event!(KeyCode::Backspace | KeyCode::Tab) => {
26///         //...
27///     }
28///     _ => {
29///         //...
30///     }
31/// }
32/// ```
33///
34/// Into this:
35///
36/// ```rust
37/// # use duat_core::mode::{KeyCode, KeyEvent, KeyEventKind, KeyMod};
38/// # let key_event: KeyEvent = KeyCode::Char('c').into();
39/// match key_event {
40///     KeyEvent {
41///         code: KeyCode::Char('c'),
42///         modifiers: KeyMod::NONE,
43///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
44///         ..
45///     } => {
46///         //...
47///     }
48///     KeyEvent {
49///         code: KeyCode::Backspace | KeyCode::Tab,
50///         modifiers: KeyMod::NONE,
51///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
52///         ..
53///     } => {
54///         //...
55///     }
56///     _ => {
57///         //...
58///     }
59/// }
60/// ```
61///
62/// This is very useful for pattern matching inside of [`Mode`]s,
63/// allowing you to significantly cut down on the amount of
64/// boilerplate being written.
65///
66/// You should also check out [`alt!`], [`ctrl!`] and [`shift!`],
67/// which are like this macro, but also add their respective
68/// [`KeyMod`]s, and can be nested.
69///
70/// [`KeyEvent`]: super::KeyEvent
71/// [`Mode`]: super::Mode
72#[macro_export]
73#[doc(hidden)]
74macro_rules! __event__ {
75    (@code $($tokens:tt)*) => {
76        $crate::__modified__!(@code $($tokens)*)
77    };
78    (@modif [$($mods:tt),*] $($tokens:tt)*) => {
79        $crate::__modified__!(@modif [$($mods),*] $($tokens)*)
80    };
81    (@bindings [$($mods:tt),*] $($tokens:tt)*) => {
82        $crate::__modified__!(@bindings [$($mods),*] $($tokens)*)
83    };
84    ($($tokens:tt)*) => {
85        $crate::__modified__!([] $($tokens)*)
86    };
87}
88
89/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::ALT`]
90///
91/// This macro essentially turns this:
92///
93/// ```rust
94/// # use duat_core::mode::{KeyCode, KeyEvent, alt};
95/// # let key_event: KeyEvent = KeyCode::Char('c').into();
96/// match key_event {
97///     alt!('c') => {
98///         //...
99///     }
100///     _ => {
101///         //...
102///     }
103/// }
104/// ```
105///
106/// Into this:
107///
108/// ```rust
109/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
110/// # let key_event: KeyEvent = KeyCode::Char('c').into();
111/// match key_event {
112///     KeyEvent {
113///         code: KeyCode::Char('c'),
114///         modifiers: KeyMod::ALT,
115///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
116///         ..
117///     } => {
118///         //...
119///     }
120///     _ => {
121///         //...
122///     }
123/// }
124/// ```
125///
126/// This is very useful for [`Mode`]s, which require matching on a
127/// large number of different `KeyEvent` patterns in order to decide
128/// what to do.
129///
130/// You can also use this with more complex patterns:
131///
132/// ```rust
133/// # use duat_core::mode::{KeyCode, KeyEvent, alt, ctrl, shift};
134/// # let key_event: KeyEvent = KeyCode::Char('c').into();
135/// match key_event {
136///     ctrl!(alt!('a' | 'b' | 'c')) | shift!(alt!(KeyCode::F(3 | 5))) => {
137///         //...
138///     }
139///     _ => {
140///         //...
141///     }
142/// }
143/// ```
144///
145/// For the other two modifiers with this convenience, see [`ctrl!`]
146/// and [`shift!`]. There is also [`event!`], which has no modifiers,
147/// and it _cannot_ be nested with the other ones, for obvious
148/// reasons.
149///
150/// [`KeyEvent`]: super::KeyEvent
151/// [`Mode`]: super::Mode
152#[macro_export]
153#[doc(hidden)]
154macro_rules! __alt__ {
155    (@code $($tokens:tt)*) => {
156        $crate::__modified__!(@code $($tokens)*)
157    };
158    (@modif [$($mods:tt),*] $($tokens:tt)*) => {
159        $crate::__modified__!(@modif [ALT $(, $mods)*] $($tokens)*)
160    };
161    (@bindings [$($mods:tt),*] $($tokens:tt)*) => {
162        $crate::__modified__!(@bindings [ALT $(, $mods)*] $($tokens)*)
163    };
164    ($($tokens:tt)*) => {
165        $crate::__modified__!([ALT] $($tokens)*)
166    };
167}
168
169/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::CONTROL`]
170///
171/// This macro essentially turns this:
172///
173/// ```rust
174/// # use duat_core::mode::{KeyCode, KeyEvent, ctrl};
175/// # let key_event: KeyEvent = KeyCode::Char('c').into();
176/// match key_event {
177///     ctrl!(KeyCode::Backspace) => {
178///         //...
179///     }
180///     _ => {
181///         //...
182///     }
183/// }
184/// ```
185///
186/// Into this:
187///
188/// ```rust
189/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
190/// # let key_event: KeyEvent = KeyCode::Char('c').into();
191/// match key_event {
192///     KeyEvent {
193///         code: KeyCode::Backspace,
194///         modifiers: KeyMod::CONTROL,
195///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
196///         ..
197///     } => {
198///         //...
199///     }
200///     _ => {
201///         //...
202///     }
203/// }
204/// ```
205///
206/// This is very useful for [`Mode`]s, which require matching on a
207/// large number of different `KeyEvent` patterns in order to decide
208/// what to do.
209///
210/// You can also use this with more complex patterns:
211///
212/// ```rust
213/// # use duat_core::mode::{KeyCode, KeyEvent, alt, ctrl};
214/// # let key_event: KeyEvent = KeyCode::Char('c').into();
215/// match key_event {
216///     ctrl!(alt!(KeyCode::Char('a' | 'b' | 'c') | KeyCode::BackTab)) => {
217///         //...
218///     }
219///     _ => {
220///         //...
221///     }
222/// }
223/// ```
224///
225/// For the other two modifiers with this convenience, see [`alt!`]
226/// and [`shift!`]. There is also [`event!`], which has no modifiers,
227/// and it _cannot_ be nested with the other ones, for obvious
228/// reasons.
229///
230/// [`KeyEvent`]: super::KeyEvent
231/// [`Mode`]: super::Mode
232#[macro_export]
233#[doc(hidden)]
234macro_rules! __ctrl__ {
235    (@code $($tokens:tt)*) => {
236        $crate::__modified__!(@code $($tokens)*)
237    };
238    (@modif [$($mods:tt),*] $($tokens:tt)*) => {
239        $crate::__modified__!(@modif [CONTROL $(,$mods)*] $($tokens)*)
240    };
241    (@bindings [$($mods:tt),*] $($tokens:tt)*) => {
242        $crate::__modified__!(@bindings [CONTROL $(,$mods)*] $($tokens)*)
243    };
244    ($($tokens:tt)*) => {
245        $crate::__modified__!([CONTROL] $($tokens)*)
246    };
247}
248
249/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::SHIFT`]
250///
251/// This macro essentially turns this:
252///
253/// ```rust
254/// # use duat_core::mode::{KeyCode, KeyEvent, shift};
255/// # let key_event: KeyEvent = KeyCode::Char('c').into();
256/// match key_event {
257///     shift!(KeyCode::Enter) => {
258///         //...
259///     }
260///     _ => {
261///         //...
262///     }
263/// }
264/// ```
265///
266/// Into this:
267///
268/// ```rust
269/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
270/// # let key_event: KeyEvent = KeyCode::Char('c').into();
271/// match key_event {
272///     KeyEvent {
273///         code: KeyCode::Enter,
274///         modifiers: KeyMod::SHIFT,
275///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
276///         ..
277///     } => {
278///         //...
279///     }
280///     _ => {
281///         //...
282///     }
283/// }
284/// ```
285///
286/// This is very useful for [`Mode`]s, which require matching on a
287/// large number of different `KeyEvent` patterns in order to decide
288/// what to do.
289///
290/// You can also use this with more complex patterns:
291///
292/// ```rust
293/// # use duat_core::mode::{KeyCode, KeyEvent, ctrl, shift};
294/// # let key_event: KeyEvent = KeyCode::Char('c').into();
295/// match key_event {
296///     ctrl!(shift!(KeyCode::PageDown | KeyCode::PageUp)) => {
297///         //...
298///     }
299///     _ => {
300///         //...
301///     }
302/// }
303/// ```
304///
305/// For the other two modifiers with this convenience, see [`alt!`]
306/// and [`shift!`]. There is also [`event!`], which has no modifiers,
307/// and it _cannot_ be nested with the other ones, for obvious
308/// reasons.
309///
310/// # A note on [`shift!`] specifically
311///
312/// Unlike [`ctrl!`] and [`alt!`], the [`KeyMod::SHIFT`] modifier is
313/// not always emitted, since some keys, like `'B'` can automatically
314/// be inferred to be shifted, so the shift modifier is not included
315/// with the [`KeyEvent`]. Given that, for now, you should avoid using
316/// this macro with [`KeyCode::Char`], since it will probably never
317/// match.
318///
319/// In the future, I might try to rectify this, by detecting when a
320/// key is "supposed" to be shifted, and then manually sending a
321/// modified [`KeyEvent`], which includes the shift modifier, so
322/// pattern matching can be more reliable.
323///
324/// [`KeyEvent`]: super::KeyEvent
325/// [`KeyCode::Char`]: super::KeyCode::Char
326/// [`Mode`]: super::Mode
327#[macro_export]
328#[doc(hidden)]
329macro_rules! __shift__ {
330    (@code $($tokens:tt)*) => {
331        $crate::__modified__!(@code $($tokens)*)
332    };
333    (@modif [$($mods:tt),*] $($tokens:tt)*) => {
334        $crate::__modified__!(@modif [SHIFT $(, $mods)*] $($tokens)*)
335    };
336    (@bindings [$($mods:tt),*] $($tokens:tt)*) => {
337        $crate::__modified__!(@bindings [SHIFT $(, $mods)*] $($tokens)*)
338    };
339    ($($tokens:tt)*) => {
340        $crate::__modified__!([SHIFT] $($tokens)*)
341    };
342}
343
344/// Macro that backs [`alt!`], [`ctrl!`] and [`shift!`]
345#[macro_export]
346#[doc(hidden)]
347macro_rules! __modified__ {
348    ([$($mods:ident),*] $($chars:literal)|*) => {
349        $crate::mode::KeyEvent {
350            code: $crate::mode::KeyCode::Char($($chars)|*),
351            modifiers: $crate::__join_modifiers__![$($mods),*],
352            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
353        }
354    };
355    // Straight up useless tbh.
356    ([$($mods:ident),*] $char:ident @ $chars:literal) => {
357        $crate::mode::KeyEvent {
358            code: $crate::mode::KeyCode::Char($char @ $chars),
359            modifiers: $crate::__join_modifiers__![$($mods),*],
360            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
361        }
362    };
363    ([$($mods:ident),*] $char:ident @ ($($chars:literal)|*)) => {
364        $crate::mode::KeyEvent {
365            code: $crate::mode::KeyCode::Char($char @ ($($chars)|*)),
366            modifiers: $crate::__join_modifiers__![$($mods),*],
367            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
368        }
369    };
370    // I use $excl here in order to make it look like part of the macro,
371    // so rust analyzer properly highlights it.
372    ([$($mods:ident),*] $modif:ident$excl:tt($($tokens:tt)*)) => {
373        $crate::mode::KeyEvent {
374            code: $modif$excl(@code $($tokens)*),
375            modifiers: $modif$excl(@modif [$($mods),*] $($tokens)*),
376            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
377        }
378    };
379    ([$($mods:ident),*] $code:pat) => {
380        $crate::mode::KeyEvent {
381            code: $code,
382            modifiers: $crate::__join_modifiers__![$($mods),*],
383            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
384        }
385    };
386
387    (@code $modif:ident$excl:tt($($tokens:tt)*)) => {
388        $modif$excl(@code $($tokens)*)
389    };
390    (@code $($chars:literal)|*) => {
391        $crate::mode::KeyCode::Char($($chars)|*)
392    };
393    (@code $char:ident @ $chars:literal) => {
394        $crate::mode::KeyCode::Char($char @ $chars)
395    };
396    (@code $char:ident @ ($($chars:literal)|*)) => {
397        $crate::mode::KeyCode::Char($char @ ($($chars)|*))
398    };
399    (@code $code:pat) => {
400        $code
401    };
402
403    (@modif [$($mods:ident),*] $modif:ident$excl:tt($($tokens:tt)*)) => {
404        $modif$excl(@modif [$($mods),*] $($tokens)*)
405    };
406    (@modif [$($mods:ident),*] $($other:tt)*) => {
407        $crate::__join_modifiers__![$($mods),*]
408    };
409
410    (@bindings [$($mods:ident),*] $list:ident, $modif:ident$excl:tt($($tokens:tt)*)) => {
411        $modif$excl(@bindings [$($mods),*] $list, $($tokens)*)
412    };
413    (@bindings [$($mods:ident),*] $list:ident, $char:literal) => {{
414        $list.push($crate::mode::Binding::new(
415            $crate::mode::KeyCode::Char($char),
416            $crate::__join_modifiers__![$($mods),*],
417        ))
418    }};
419    (@bindings [$($mods:ident),*] $list:ident, $($chars:literal)|*) => {{
420        let modif = $crate::__join_modifiers__![$($mods),*];
421        $list.extend([$(
422            $crate::mode::Binding::new($crate::mode::KeyCode::Char($chars), modif)
423        ),*])
424    }};
425    (@bindings [$($mods:ident),*] $list:ident, $($tokens:tt)*) => {{
426        let modif = $crate::__join_modifiers__![$($mods),*];
427        $crate::__modified__!(@fill_bindings $list, modif, $($tokens)*);
428    }};
429
430    (@fill_bindings $list:ident, $modif:expr,) => {};
431    (@fill_bindings $list:ident, $modif:expr, $($variant:ident)::+ $(| $($rest:tt)*)?) => {
432        $list.push($crate::mode::Binding::new($($variant)::+, $modif));
433        $crate::__modified__!(@fill_bindings $list, $modif, $($($rest)*)?);
434    };
435    (@fill_bindings
436        $list:ident,
437        $modif:expr,
438        $($variant:ident)::+(..)
439        $(| $($rest:tt)*)?
440    ) => {
441        // For better diagnostics and pattern validation.
442        let _unused = |code| matches!(code, $($variant)::+(..));
443        $crate::__modified__!(@two_dots_entry $list, $modif, $($variant)::+);
444        $crate::__modified__!(@fill_bindings $list, $modif, $($($rest)*)?);
445    };
446    (@fill_bindings
447        $list:ident,
448        $modif:expr,
449        $($variant:ident)::+($($patterns:tt)*)
450        $(| $($rest:tt)*)?
451    ) => {
452        // For better diagnostics and pattern validation.
453        let _unused = |code| matches!(code, $($variant)::*($($patterns)*));
454        $crate::__modified__!(@binding_entry $list, $modif, $($patterns)*);
455        $crate::__modified__!(@fill_bindings $list, $modif, $($($rest)*)?);
456    };
457    (@fill_bindings $list:ident, $modif:expr, _) => {
458        let mut binding = $crate::mode::Binding::anything();
459        binding.modif = Some($modif);
460        $list.push(binding);
461    };
462    (@fill_bindings $list:ident, $modif:expr, $($nonsense:tt)*) => {
463        // For better diagnostics and pattern validation.
464        let _unused = |code: $crate::mode::KeyCode| matches!(code, $($nonsense)*);
465        compile_error!("Pattern may be valid, but can't create known list of bindings");
466    };
467
468    (@binding_entry $list:ident, $modif:expr,) => {};
469    (@binding_entry $list:ident, $modif:expr, $s:literal..$e:literal $(| $($rest:tt)*)?) => {
470        $list.push($crate::mode::Binding::from(($s..$e, $modif)));
471        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
472    };
473    (@binding_entry $list:ident, $modif:expr, $s:literal..=$e:literal $(| $($rest:tt)*)?) => {
474        $list.push($crate::mode::Binding::from(($s..=$e, $modif)));
475        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
476    };
477    (@binding_entry $list:ident, $modif:expr, ..$e:literal $(| $($rest:tt)*)?) => {
478        $list.push($crate::mode::Binding::from((..$e, $modif)));
479        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
480    };
481    (@binding_entry $list:ident, $modif:expr, ..=$e:literal $(| $($rest:tt)*)?) => {
482        $list.push($crate::mode::Binding::from((..=$e, $modif)));
483        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
484    };
485    (@binding_entry $list:ident, $modif:expr, $s:literal.. $(| $($rest:tt)*)?) => {
486        $list.push($crate::mode::Binding::from(($s.., $modif)));
487        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
488    };
489    (@binding_entry $list:ident, $modif:expr, $elem:literal $(| $($rest:tt)*)?) => {
490        $list.push($crate::mode::Binding::from(($elem, $modif)));
491        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
492    };
493    (@binding_entry $list:ident, $modif:expr, $elem:literal $(| $($rest:tt)*)?) => {
494        $list.push($crate::mode::Binding::from(($elem, $modif)));
495        $crate::__modified__!(@binding_entry $list, $modif, $($($rest)*)?);
496    };
497
498    (@two_dots_entry $list:ident, $modif:expr, $path:ident $(::$rest:ident)+) => {{
499        $crate::__modified__!(@two_dots_entry $list, $modif, $($rest)::+);
500    }};
501    (@two_dots_entry $list:ident, $modif:expr, Char) => {{
502        $list.push($crate::mode::Binding::CharRange(
503            ::std::ops::Bound::Unbounded,
504            ::std::ops::Bound::Unbounded,
505            $modif
506        ))
507    }};
508    (@two_dots_entry $list:ident, $modif:expr, F) => {{
509        $list.push($crate::mode::Binding::FnRange(
510            ::std::ops::Bound::Unbounded,
511            ::std::ops::Bound::Unbounded,
512            $modif
513        ))
514    }};
515    (@two_dots_entry $list:ident, $modif:expr, Media) => {{
516        $list.push($crate::mode::Binding::AnyMedia($modif))
517    }};
518    (@two_dots_entry $list:ident, $modif:expr, Modifier) => {{
519        $list.push($crate::mode::Binding::AnyModifier($modif))
520    }}
521}
522
523/// A simple macro to join [`KeyMod`]s into a `const` `KeyMod` that
524/// can be used in pattern matching
525#[macro_export]
526#[doc(hidden)]
527macro_rules! __join_modifiers__ {
528    [] => { $crate::mode::KeyMod::NONE };
529    [ALT] => { $crate::mode::KeyMod::ALT };
530    [CONTROL] => { $crate::mode::KeyMod::CONTROL };
531    [SHIFT] => { $crate::mode::KeyMod::SHIFT };
532    [ALT, CONTROL] => { $crate::mode::ALT_CONTROL };
533    [CONTROL, ALT] => { $crate::mode::ALT_CONTROL };
534    [ALT, SHIFT] => { $crate::mode::ALT_SHIFT };
535    [SHIFT, ALT] => { $crate::mode::ALT_SHIFT };
536    [CONTROL, SHIFT] => { $crate::mode::CONTROL_SHIFT };
537    [SHIFT, CONTROL] => { $crate::mode::CONTROL_SHIFT };
538    [ALT, CONTROL, SHIFT] => { $crate::mode::ALT_CONTROL_SHIFT };
539    [ALT, SHIFT, CONTROL] => { $crate::mode::ALT_CONTROL_SHIFT };
540    [CONTROL, ALT, SHIFT] => { $crate::mode::ALT_CONTROL_SHIFT };
541    [CONTROL, SHIFT, ALT] => { $crate::mode::ALT_CONTROL_SHIFT };
542    [SHIFT, ALT, CONTROL] => { $crate::mode::ALT_CONTROL_SHIFT };
543    [SHIFT, CONTROL, ALT] => { $crate::mode::ALT_CONTROL_SHIFT };
544    [$($other:tt)+] => {
545        compile_error!("Don't nest identical modifier macros while forming patterns")
546    }
547}
548
549/// `const` [`KeyMod`] used in pattern matching
550#[doc(hidden)]
551pub const ALT_CONTROL: KeyMod = KeyMod::ALT.union(KeyMod::CONTROL);
552
553/// `const` [`KeyMod`] used in pattern matching
554#[doc(hidden)]
555pub const ALT_SHIFT: KeyMod = KeyMod::ALT.union(KeyMod::SHIFT);
556
557/// `const` [`KeyMod`] used in pattern matching
558#[doc(hidden)]
559pub const CONTROL_SHIFT: KeyMod = KeyMod::CONTROL.union(KeyMod::SHIFT);
560
561/// `const` [`KeyMod`] used in pattern matching
562#[doc(hidden)]
563pub const ALT_CONTROL_SHIFT: KeyMod = KeyMod::ALT.union(KeyMod::CONTROL).union(KeyMod::SHIFT);