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);