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. This macro _cannot_ be nested with
69/// them however, for obvious reasons.
70///
71/// [`KeyEvent`]: super::KeyEvent
72/// [`Mode`]: super::Mode
73#[macro_export]
74#[doc(hidden)]
75macro_rules! __event__ {
76    ($($chars:literal)|+) => {
77        $crate::mode::KeyEvent {
78            code: $crate::mode::KeyCode::Char($($chars)|+),
79            modifiers: $crate::mode::KeyMod::NONE,
80            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
81        }
82    };
83    // Straight up useless tbh.
84    ($char:ident @ $chars:literal) => {
85        $crate::mode::KeyEvent {
86            code: $crate::mode::KeyCode::Char($char @ $chars),
87            modifiers: $crate::mode::KeyMod::NONE,
88            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
89        }
90    };
91    ($char:ident @ ($($chars:literal)|+)) => {
92        $crate::mode::KeyEvent {
93            code: $crate::mode::KeyCode::Char($char @ ($($chars)|+)),
94            modifiers: $crate::mode::KeyMod::NONE,
95            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
96        }
97    };
98    ($code:pat) => {
99        $crate::mode::KeyEvent {
100            code: $code,
101            modifiers: $crate::mode::KeyMod::NONE,
102            ..
103        }
104    }
105}
106
107/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::ALT`]
108///
109/// This macro essentially turns this:
110///
111/// ```rust
112/// # use duat_core::mode::{KeyCode, KeyEvent, alt};
113/// # let key_event: KeyEvent = KeyCode::Char('c').into();
114/// match key_event {
115///     alt!('c') => {
116///         //...
117///     }
118///     _ => {
119///         //...
120///     }
121/// }
122/// ```
123///
124/// Into this:
125///
126/// ```rust
127/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
128/// # let key_event: KeyEvent = KeyCode::Char('c').into();
129/// match key_event {
130///     KeyEvent {
131///         code: KeyCode::Char('c'),
132///         modifiers: KeyMod::ALT,
133///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
134///         ..
135///     } => {
136///         //...
137///     }
138///     _ => {
139///         //...
140///     }
141/// }
142/// ```
143///
144/// This is very useful for [`Mode`]s, which require matching on a
145/// large number of different `KeyEvent` patterns in order to decide
146/// what to do.
147///
148/// You can also use this with more complex patterns:
149///
150/// ```rust
151/// # use duat_core::mode::{KeyCode, KeyEvent, alt, ctrl, shift};
152/// # let key_event: KeyEvent = KeyCode::Char('c').into();
153/// match key_event {
154///     ctrl!(alt!('a' | 'b' | 'c')) | shift!(alt!(KeyCode::F(3 | 5))) => {
155///         //...
156///     }
157///     _ => {
158///         //...
159///     }
160/// }
161/// ```
162///
163/// For the other two modifiers with this convenience, see [`ctrl!`]
164/// and [`shift!`]. There is also [`event!`], which has no modifiers,
165/// and it _cannot_ be nested with the other ones, for obvious
166/// reasons.
167///
168/// [`KeyEvent`]: super::KeyEvent
169/// [`Mode`]: super::Mode
170#[macro_export]
171#[doc(hidden)]
172macro_rules! __alt__ {
173    ($($chars:literal)|+) => {
174        $crate::mode::KeyEvent {
175            code: $crate::mode::KeyCode::Char($($chars)|+),
176            modifiers: $crate::mode::KeyMod::ALT,
177            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
178        }
179    };
180    // Straight up useless tbh.
181    ($char:ident @ $chars:literal) => {
182        $crate::mode::KeyEvent {
183            code: $crate::mode::KeyCode::Char($char @ $chars),
184            modifiers: $crate::mode::KeyMod::ALT,
185            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
186        }
187    };
188    ($char:ident @ ($($chars:literal)|+)) => {
189        $crate::mode::KeyEvent {
190            code: $crate::mode::KeyCode::Char($char @ ($($chars)|+)),
191            modifiers: $crate::mode::KeyMod::ALT,
192            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
193        }
194    };
195    // I use $excl here in order to make it look like part of the macro,
196    // so rust analyzer properly highlights it.
197    ($modif:ident$excl:tt($($tokens:tt)+)) => {
198        $crate::mode::KeyEvent {
199            code: $modif$excl(@code $($tokens)+),
200            modifiers: $modif$excl(@modif [ALT] $($tokens)+),
201            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
202        }
203    };
204    ($code:pat) => {
205        $crate::mode::KeyEvent {
206            code: $code,
207            modifiers: $crate::mode::KeyMod::ALT,
208            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
209        }
210    };
211
212    (@code $modif:ident$excl:tt($($tokens:tt)+)) => {
213        $modif$excl(@code $($tokens)+)
214    };
215    (@code $($chars:literal)|+) => {
216        $crate::mode::KeyCode::Char($($chars)|+)
217    };
218    (@code $char:ident @ $chars:literal) => {
219        $crate::mode::KeyCode::Char($char @ $chars)
220    };
221    (@code $char:ident @ ($($chars:literal)|+)) => {
222        $crate::mode::KeyCode::Char($char @ ($($chars)|+))
223    };
224    (@code $code:pat) => {
225        $code
226    };
227
228    (@modif [$($list:ident),+] $modif:ident$excl:tt($($tokens:tt)+)) => {
229        $modif$excl(@modif [ALT, $($list),+] $($tokens)+)
230    };
231    (@modif [$($list:ident),+] $($other:tt)+) => {
232        $crate::__join_modifiers__![ALT, $($list),+]
233    };
234}
235
236/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::CONTROL`]
237///
238/// This macro essentially turns this:
239///
240/// ```rust
241/// # use duat_core::mode::{KeyCode, KeyEvent, ctrl};
242/// # let key_event: KeyEvent = KeyCode::Char('c').into();
243/// match key_event {
244///     ctrl!(KeyCode::Backspace) => {
245///         //...
246///     }
247///     _ => {
248///         //...
249///     }
250/// }
251/// ```
252///
253/// Into this:
254///
255/// ```rust
256/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
257/// # let key_event: KeyEvent = KeyCode::Char('c').into();
258/// match key_event {
259///     KeyEvent {
260///         code: KeyCode::Backspace,
261///         modifiers: KeyMod::CONTROL,
262///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
263///         ..
264///     } => {
265///         //...
266///     }
267///     _ => {
268///         //...
269///     }
270/// }
271/// ```
272///
273/// This is very useful for [`Mode`]s, which require matching on a
274/// large number of different `KeyEvent` patterns in order to decide
275/// what to do.
276///
277/// You can also use this with more complex patterns:
278///
279/// ```rust
280/// # use duat_core::mode::{KeyCode, KeyEvent, alt, ctrl};
281/// # let key_event: KeyEvent = KeyCode::Char('c').into();
282/// match key_event {
283///     ctrl!(alt!(KeyCode::Char('a' | 'b' | 'c') | KeyCode::BackTab)) => {
284///         //...
285///     }
286///     _ => {
287///         //...
288///     }
289/// }
290/// ```
291///
292/// For the other two modifiers with this convenience, see [`alt!`]
293/// and [`shift!`]. There is also [`event!`], which has no modifiers,
294/// and it _cannot_ be nested with the other ones, for obvious
295/// reasons.
296///
297/// [`KeyEvent`]: super::KeyEvent
298/// [`Mode`]: super::Mode
299#[macro_export]
300#[doc(hidden)]
301macro_rules! __ctrl__ {
302    ($($chars:literal)|+) => {
303        $crate::mode::KeyEvent {
304            code: $crate::mode::KeyCode::Char($($chars)|+),
305            modifiers: $crate::mode::KeyMod::CONTROL,
306            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
307        }
308    };
309    // Straight up useless tbh.
310    ($char:ident @ $chars:literal) => {
311        $crate::mode::KeyEvent {
312            code: $crate::mode::KeyCode::Char($char @ $chars),
313            modifiers: $crate::mode::KeyMod::CONTROL,
314            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
315        }
316    };
317    ($char:ident @ ($($chars:literal)|+)) => {
318        $crate::mode::KeyEvent {
319            code: $crate::mode::KeyCode::Char($char @ ($($chars)|+)),
320            modifiers: $crate::mode::KeyMod::CONTROL,
321            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
322        }
323    };
324    // I use $excl here in order to make it look like part of the macro,
325    // so rust analyzer properly highlights it.
326    ($modif:ident$excl:tt($($tokens:tt)+)) => {
327        $crate::mode::KeyEvent {
328            code: $modif$excl(@code $($tokens)+),
329            modifiers: $modif$excl(@modif [CONTROL] $($tokens)+),
330            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
331        }
332    };
333    ($code:pat) => {
334        $crate::mode::KeyEvent {
335            code: $code,
336            modifiers: $crate::mode::KeyMod::CONTROL,
337            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
338        }
339    };
340
341    (@code $modif:ident$excl:tt($($tokens:tt)+)) => {
342        $modif$excl(@code $($tokens)+)
343    };
344    (@code $($chars:literal)|+) => {
345        $crate::mode::KeyCode::Char($($chars)|+)
346    };
347    (@code $char:ident @ $chars:literal) => {
348        $crate::mode::KeyCode::Char($char @ $chars)
349    };
350    (@code $char:ident @ ($($chars:literal)|+)) => {
351        $crate::mode::KeyCode::Char($char @ ($($chars)|+))
352    };
353    (@code $code:pat) => {
354        $code
355    };
356
357    (@modif [$($list:ident),+] $modif:ident$excl:tt($($tokens:tt)+)) => {
358        $modif$excl(@modif [CONTROL, $($list),+] $($tokens)+)
359    };
360    (@modif [$($list:ident),+] $($other:tt)+) => {
361        $crate::__join_modifiers__![CONTROL, $($list),+]
362    }
363}
364
365/// Macro for pattern matching [`KeyEvent`]s with [`KeyMod::SHIFT`]
366///
367/// This macro essentially turns this:
368///
369/// ```rust
370/// # use duat_core::mode::{KeyCode, KeyEvent, shift};
371/// # let key_event: KeyEvent = KeyCode::Char('c').into();
372/// match key_event {
373///     shift!(KeyCode::Enter) => {
374///         //...
375///     }
376///     _ => {
377///         //...
378///     }
379/// }
380/// ```
381///
382/// Into this:
383///
384/// ```rust
385/// # use duat_core::mode::{KeyCode, KeyEvent, KeyMod, KeyEventKind};
386/// # let key_event: KeyEvent = KeyCode::Char('c').into();
387/// match key_event {
388///     KeyEvent {
389///         code: KeyCode::Enter,
390///         modifiers: KeyMod::SHIFT,
391///         kind: KeyEventKind::Press | KeyEventKind::Repeat,
392///         ..
393///     } => {
394///         //...
395///     }
396///     _ => {
397///         //...
398///     }
399/// }
400/// ```
401///
402/// This is very useful for [`Mode`]s, which require matching on a
403/// large number of different `KeyEvent` patterns in order to decide
404/// what to do.
405///
406/// You can also use this with more complex patterns:
407///
408/// ```rust
409/// # use duat_core::mode::{KeyCode, KeyEvent, ctrl, shift};
410/// # let key_event: KeyEvent = KeyCode::Char('c').into();
411/// match key_event {
412///     ctrl!(shift!(KeyCode::PageDown | KeyCode::PageUp)) => {
413///         //...
414///     }
415///     _ => {
416///         //...
417///     }
418/// }
419/// ```
420///
421/// For the other two modifiers with this convenience, see [`alt!`]
422/// and [`shift!`]. There is also [`event!`], which has no modifiers,
423/// and it _cannot_ be nested with the other ones, for obvious
424/// reasons.
425///
426/// # A note on [`shift!`] specifically
427///
428/// Unlike [`ctrl!`] and [`alt!`], the [`KeyMod::SHIFT`] modifier is
429/// not always emitted, since some keys, like `'B'` can automatically
430/// be inferred to be shifted, so the shift modifier is not included
431/// with the [`KeyEvent`]. Given that, for now, you should avoid using
432/// this macro with [`KeyCode::Char`], since it will probably never
433/// match.
434///
435/// In the future, I might try to rectify this, by detecting when a
436/// key is "supposed" to be shifted, and then manually sending a
437/// modified [`KeyEvent`], which includes the shift modifier, so
438/// pattern matching can be more reliable.
439///
440/// [`KeyEvent`]: super::KeyEvent
441/// [`KeyCode::Char`]: super::KeyCode::Char
442/// [`Mode`]: super::Mode
443#[macro_export]
444#[doc(hidden)]
445macro_rules! __shift__ {
446    ($($chars:literal)|+) => {
447        $crate::mode::KeyEvent {
448            code: $crate::mode::KeyCode::Char($($chars)|+),
449            modifiers: $crate::mode::KeyMod::SHIFT,
450            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
451        }
452    };
453    // Straight up useless tbh.
454    ($char:ident @ $chars:literal) => {
455        $crate::mode::KeyEvent {
456            code: $crate::mode::KeyCode::Char($char @ $chars),
457            modifiers: $crate::mode::KeyMod::SHIFT,
458            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
459        }
460    };
461    ($char:ident @ ($($chars:literal)|+)) => {
462        $crate::mode::KeyEvent {
463            code: $crate::mode::KeyCode::Char($char @ ($($chars)|+)),
464            modifiers: $crate::mode::KeyMod::SHIFT,
465            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
466        }
467    };
468    // I use $excl here in order to make it look like part of the macro,
469    // so rust analyzer properly highlights it.
470    ($modif:ident$excl:tt($($tokens:tt)+)) => {
471        $crate::mode::KeyEvent {
472            code: $modif$excl(@code $($tokens)+),
473            modifiers: $modif$excl(@modif [SHIFT] $($tokens)+),
474            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
475        }
476    };
477    ($code:pat) => {
478        $crate::mode::KeyEvent {
479            code: $code,
480            modifiers: $crate::mode::KeyMod::SHIFT,
481            kind: $crate::mode::KeyEventKind::Press | $crate::mode::KeyEventKind::Repeat, ..
482        }
483    };
484
485    (@code $modif:ident$excl:tt($($tokens:tt)+)) => {
486        $modif$excl(@code $($tokens)+)
487    };
488    (@code $($chars:literal)|+) => {
489        $crate::mode::KeyCode::Char($($chars)|+)
490    };
491    (@code $char:ident @ $chars:literal) => {
492        $crate::mode::KeyCode::Char($char @ $chars)
493    };
494    (@code $char:ident @ ($($chars:literal)|+)) => {
495        $crate::mode::KeyCode::Char($char @ ($($chars)|+))
496    };
497    (@code $code:pat) => {
498        $code
499    };
500
501    (@modif [$($list:ident),+] $modif:ident$excl:tt($($tokens:tt)+)) => {
502        $modif$excl(@modif [SHIFT, $($list),+] $($tokens)+)
503    };
504    (@modif [$($list:ident),+] $($other:tt)+) => {
505        $crate::__join_modifiers__![SHIFT, $($list),+]
506    }
507}
508
509/// A simple macro to join [`KeyMod`]s into a `const` `KeyMod` that
510/// can be used in pattern matching
511#[macro_export]
512#[doc(hidden)]
513macro_rules! __join_modifiers__ {
514    [ALT, CONTROL] => { $crate::mode::ALT_CONTROL };
515    [CONTROL, ALT] => { $crate::mode::ALT_CONTROL };
516    [ALT, SHIFT] => { $crate::mode::ALT_SHIFT };
517    [SHIFT, ALT] => { $crate::mode::ALT_SHIFT };
518    [CONTROL, SHIFT] => { $crate::mode::CONTROL_SHIFT };
519    [SHIFT, CONTROL] => { $crate::mode::CONTROL_SHIFT };
520    [ALT, CONTROL, SHIFTl] => { $crate::mode::ALT_CONTROL_SHIFT };
521    [ALT, SHIFT, CONTROLl] => { $crate::mode::ALT_CONTROL_SHIFT };
522    [CONTROL, ALT, SHIFTl] => { $crate::mode::ALT_CONTROL_SHIFT };
523    [CONTROL, SHIFT, ALTl] => { $crate::mode::ALT_CONTROL_SHIFT };
524    [SHIFT, ALT, CONTROLl] => { $crate::mode::ALT_CONTROL_SHIFT };
525    [SHIFT, CONTROL, ALTl] => { $crate::mode::ALT_CONTROL_SHIFT };
526    [$($other:tt)+] => {
527        compile_error!("Don't nest identical modifier macros while forming patterns")
528    }
529}
530
531/// `const` [`KeyMod`] used in pattern matching
532#[doc(hidden)]
533pub const ALT_CONTROL: KeyMod = KeyMod::ALT.union(KeyMod::CONTROL);
534
535/// `const` [`KeyMod`] used in pattern matching
536#[doc(hidden)]
537pub const ALT_SHIFT: KeyMod = KeyMod::ALT.union(KeyMod::SHIFT);
538
539/// `const` [`KeyMod`] used in pattern matching
540#[doc(hidden)]
541pub const CONTROL_SHIFT: KeyMod = KeyMod::CONTROL.union(KeyMod::SHIFT);
542
543/// `const` [`KeyMod`] used in pattern matching
544#[doc(hidden)]
545pub const ALT_CONTROL_SHIFT: KeyMod = KeyMod::ALT.union(KeyMod::CONTROL).union(KeyMod::SHIFT);