duat_core/mode/mod.rs
1//! [`Mode`]s that handle user input
2//!
3//! Each `Mode` controls a specifig type of [`Widget`], and
4//! switching `Mode`s is how one sets the current `Widget`. For
5//! example, the [`Standard`] (like most `Mode`s), controls the
6//! [`Buffer`] `Widget`. So when you switch to that `Mode`, you
7//! return to the active `Buffer` if you were focused on another
8//! `Widget`.
9//!
10//! Other than the [`Buffer`] the main [`Widget`] that is controled by
11//! [`Mode`]s is the [`PromptLine`]. It is an example of a `Widget`
12//! that has many `Mode`s implemented for it. Chief of which is
13//! [`RunCommands`], but there is also [`IncSearch`] and
14//! [`PipeSelections`], and the creation of more `Mode`s for the
15//! [`PromptLine`] is very much encouraged.
16//!
17//! [`Standard`]: docs.rs/duat/latest/duat/modes/struct.Standard.html
18//! [`PromptLine`]: docs.rs/duat/latest/duat/widgets/struct.PromptLine.html
19//! [`RunCommands`]: docs.rs/duat/latest/duat/modes/struct.RunCommands.html
20//! [`IncSearch`]: docs.rs/duat/latest/duat/modes/struct.IncSearch.html
21//! [`PipeSelections`]: docs.rs/duat/latest/duat/modes/struct.PipeSelections.html
22use core::str;
23use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
24
25pub use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, MouseButton, MouseEventKind};
26
27/// Key modifiers, like Shift, Alt, Super, Shift + Alt, etc
28pub type KeyMod = crossterm::event::KeyModifiers;
29
30pub(crate) use self::cursor::{ModSelection, on_each_cursor, reinsert_selections};
31#[doc(inline)]
32pub use self::{bindings::*, patterns::*};
33pub use self::{
34 cursor::{CaretOrRange, Cursor, CursorMatches, Selection, Selections, VPoint},
35 remap::*,
36 switch::*,
37};
38use crate::{
39 buffer::Buffer,
40 context::Handle,
41 data::Pass,
42 session::DuatEvent,
43 text::{TwoPoints, txt},
44 ui::{Coord, Widget},
45};
46
47mod bindings;
48mod cursor;
49mod patterns;
50mod remap;
51mod switch;
52
53/// A blank [`Mode`], intended for plugin authors to use
54///
55/// The only key this `Mode` actually accepts is [`KeyCode::Esc`], in
56/// which case it promptly resets to the default [`Buffer`] `Mode`,
57/// which is `duatmode`'s "`Normal`" by default.
58///
59/// This means that you can compose more complex expressions and
60/// accrue them in the `User` mode. For example, something that I find
61/// particularly useful in prose filetypes is the following:
62///
63/// ```
64/// # duat_core::doc_duat!(duat);
65/// use duat::prelude::*;
66/// setup_duat!(setup);
67///
68/// fn setup() {
69/// map::<User>("f", "<Esc><A-j>|fold -s -w 80<Enter>")
70/// .doc("Fold selected lines by whitespace");
71/// }
72/// ```
73///
74/// This is also where [`Plugin`] remappings should be done:
75///
76/// ```
77/// # duat_core::doc_duat!(duat);
78/// use duat::prelude::*;
79///
80/// map::<User>("fb", |pa: &mut Pass| {
81/// mode::set(pa, mode::RunCommands::new_with("frobnificate "));
82/// });
83///
84/// cmd::add("frobnificate", |pa: &mut Pass, buf: Handle| {
85/// // Do stuff
86/// Ok(None)
87/// });
88/// ```
89///
90/// [`Plugin`]: crate::Plugin
91#[derive(Clone, Copy, Debug)]
92pub struct User;
93
94impl Mode for User {
95 type Widget = Buffer;
96
97 fn bindings() -> Bindings {
98 bindings!(match _ {
99 event!(KeyCode::Esc) => txt!(""),
100 })
101 }
102
103 fn send_key(&mut self, pa: &mut Pass, _: KeyEvent, _: Handle<Self::Widget>) {
104 reset::<Buffer>(pa);
105 }
106}
107
108static KEYS_WERE_SENT: AtomicUsize = AtomicUsize::new(0);
109
110/// Wether any keys were sent via [`mode::send_keys`]
111///
112/// [`mode::send_keys`]: send_keys
113pub(crate) fn keys_were_sent(_: &mut Pass) -> bool {
114 if KEYS_WERE_SENT.load(Ordering::Relaxed) > 0 {
115 KEYS_WERE_SENT.fetch_sub(1, Ordering::Relaxed);
116 true
117 } else {
118 false
119 }
120}
121
122/// Wether the currently active [`Mode`] is this one
123pub fn is_currently<M: Mode>() -> bool {
124 current_type_id() == std::any::TypeId::of::<M>()
125}
126
127/// The [`TypeId`] of the currently active [`Mode`]
128///
129/// [`TypeId`]: std::any::TypeId
130pub fn current_type_id() -> std::any::TypeId {
131 *remap::MODE_TYPE_ID.lock().unwrap()
132}
133
134/// Sends a sequence of [`KeyEvent`]s
135///
136/// Unlike with [`mode::map`] or [`mode::alias`], the sent keys are
137/// allowed to be remapped to something else. Additionally, they will
138/// also trigger the [`KeyTyped`] hook.
139///
140/// [`mode::map`]: map
141/// [`mode::alias`]: alias
142/// [`KeyTyped`]: crate::hook::KeyTyped
143pub fn type_keys(keys: impl IntoIterator<Item = KeyEvent>) {
144 let keys: Vec<_> = keys.into_iter().collect();
145 if !keys.is_empty() {
146 KEYS_WERE_SENT.fetch_add(1, Ordering::Relaxed);
147 crate::context::sender().send(DuatEvent::KeyEventsSent(keys));
148 }
149}
150
151/// Wether the reverse modifier should be [alt] as opposed to [shift]
152///
153/// [shift]: KeyMod::SHIFT
154/// [alt]: KeyMod::ALT
155static ALT_IS_REVERSE: AtomicBool = AtomicBool::new(false);
156
157/// Wether [alt] should be the reverse [modifier], instead of [shift]
158///
159/// On most editing models, the key that reverses actions (mostly
160/// searching), is the [shift] key, (like `shift + n` to go to the
161/// previouse match). In other situations though, that may not be the
162/// case, like with [`duat-kak`], where that key is [alt] (for
163/// consistency reasons).
164///
165/// Changing this key via [`set_alt_is_reverse`] does not cause any
166/// internal changes in [`duat-core`] or [`duat`]. It is only
167/// meant to serve as a general setting for plugins to follow.
168///
169/// [modifier]: KeyMod
170/// [shift]: KeyMod::SHIFT
171/// [alt]: KeyMod::ALT
172/// [`duat-kak`]: docs.rs/duat-kak/latest/duat_kak
173/// [`duat-core`]: docs.rs/duat-core/latest/duat_core
174/// [`duat`]: docs.rs/duat/latest/duat
175pub fn alt_is_reverse() -> bool {
176 ALT_IS_REVERSE.load(Ordering::Relaxed)
177}
178
179/// Sets wether [alt] should be the reverse [modifier], instead of
180/// [shift]
181///
182/// On most editing models, the key that reverses actions (mostly
183/// searching), is the [shift] key, (like `shift + n` to go to the
184/// previouse match). In other situations though, that may not be the
185/// case, like with [`duat-kak`], where that key is [alt] (for
186/// consistency reasons).
187///
188/// The value of this setting can be retrieved with
189/// [`alt_is_reverse`].
190///
191/// [modifier]: KeyMod
192/// [shift]: KeyMod::SHIFT
193/// [alt]: KeyMod::ALT
194/// [`duat-kak`]: docs.rs/duat-kak/latest/duat_kak
195/// [`duat-core`]: docs.rs/duat-core/latest/duat_core
196/// [`duat`]: docs.rs/duat/latest/duat
197pub fn set_alt_is_reverse(value: bool) -> bool {
198 ALT_IS_REVERSE.swap(value, Ordering::Relaxed)
199}
200
201/// A mode for a [`Widget`]
202///
203/// [`Mode`]s are the way that Duat decides how keys are going to
204/// modify widgets.
205///
206/// For this example, I will create a `Menu` widget. This example
207/// doesn't make use of the [`Cursor`] methods from the [`Handle`].
208/// Those are methods that modify [`Selection`]s, and can use them to
209/// modify the [`Text`] in a declarative fashion. For an example with
210/// [`Cursor`]s, see the documentation for `Handle`s.
211///
212/// First, the [`Widget`] itself:
213///
214/// ```rust
215/// # duat_core::doc_duat!(duat);
216/// use duat::prelude::*;
217///
218/// #[derive(Default)]
219/// struct Menu {
220/// text: Text,
221/// selected_entry: usize,
222/// active_etry: Option<usize>,
223/// }
224/// ```
225/// In this widget, the entries will be selectable via a [`Mode`], by
226/// pressing the up and down keys. Let's say that said menu has five
227/// entries, and one of them can be active at a time:
228///
229/// ```rust
230/// # duat_core::doc_duat!(duat);
231/// # use duat::prelude::*;
232/// # struct Menu {
233/// # text: Text,
234/// # selected_entry: usize,
235/// # active_entry: Option<usize>,
236/// # }
237/// impl Menu {
238/// pub fn shift_selection(&mut self, shift: i32) {
239/// let selected = self.selected_entry as i32 + shift;
240/// self.selected_entry = if selected < 0 {
241/// 4
242/// } else if selected > 4 {
243/// 0
244/// } else {
245/// selected as usize
246/// };
247/// }
248///
249/// pub fn toggle(&mut self) {
250/// self.active_entry = match self.active_entry {
251/// Some(entry) if entry == self.selected_entry => None,
252/// Some(_) | None => Some(self.selected_entry),
253/// };
254/// }
255///
256/// fn build_text(&mut self) {
257/// let mut builder = Text::builder();
258///
259/// for i in 0..5 {
260/// let text = if let Some(active) = self.active_entry
261/// && active == i
262/// {
263/// if self.selected_entry == i {
264/// txt!("[menu.active]{Spacer}Entry {i}{Spacer}\n")
265/// } else {
266/// txt!("[menu.selected.active]{Spacer}Entry {i}{Spacer}\n")
267/// }
268/// } else if self.selected_entry == i {
269/// txt!("[menu.selected]{Spacer}Entry {i}{Spacer}\n")
270/// } else {
271/// txt!("[menu]{Spacer}Entry {i}{Spacer}\n")
272/// };
273///
274/// builder.push(text);
275/// }
276///
277/// self.text = builder.build();
278/// }
279/// }
280/// ```
281///
282/// By making `shift_selection` and `toggle` `pub`, I can allow an end
283/// user to create their own [`Mode`] for this widget.
284///
285/// Now I'll implement [`Widget`] on the `Menu`, so it can show up on
286/// screen:
287///
288/// ```rust
289/// # duat_core::doc_duat!(duat);
290/// # #[derive(Default)]
291/// # struct Menu {
292/// # text: Text,
293/// # selected_entry: usize,
294/// # active_entry: Option<usize>,
295/// # }
296/// # impl Menu {
297/// # fn build_text(&mut self) { todo!(); }
298/// # }
299/// use duat::prelude::*;
300///
301/// impl Widget for Menu {
302/// fn text(&self) -> &Text {
303/// &self.text
304/// }
305///
306/// fn text_mut(&mut self) -> TextMut<'_> {
307/// self.text.as_mut()
308/// }
309/// }
310/// ```
311///
312/// Now, let's take a look at some [`Widget`] methods that are used
313/// when the [`Widget`] is supposed to be handled by [`Mode`]s.
314///
315/// ```rust
316/// # duat_core::doc_duat!(duat);
317/// # use duat::prelude::*;
318/// # #[derive(Default)]
319/// # struct Menu {
320/// # text: Text,
321/// # selected_entry: usize,
322/// # active_entry: Option<usize>,
323/// # }
324/// fn add_menu_hooks() {
325/// let mask_ns = Ns::new();
326///
327/// hook::add::<FocusedOn<Menu>>(move |pa, (_, menu)| {
328/// menu.text_parts(pa).tags.remove(mask_ns, ..);
329/// menu.text_parts(pa).tags.insert(mask_ns, .., Mask("active"));
330/// });
331///
332/// hook::add::<UnfocusedFrom<Menu>>(move |pa, (menu, _)| {
333/// menu.text_parts(pa).tags.remove(mask_ns, ..);
334/// menu.text_parts(pa)
335/// .tags
336/// .insert(mask_ns, .., Mask("inactive"));
337/// });
338/// }
339/// # impl Widget for Menu {
340/// # fn text(&self) -> &Text { todo!() }
341/// # fn text_mut(&mut self) -> TextMut<'_> { todo!() }
342/// # }
343/// ```
344///
345/// These methods can do work when the wiget is focused or unfocused.
346///
347/// In this case, I chose to replace the [`Form`]s with "inactive"
348/// variants, by applying the `inactive` [mask]. This makes it so, for
349/// example, the form `"menu"` gets mapped to `"menu.inactive"`, if
350/// that form exists.
351///
352/// Now, all that is left to do is the `MenuMode` [`Mode`]. We just
353/// need to create an empty struct and call the methods of the `Menu`:
354///
355/// ```rust
356/// # duat_core::doc_duat!(duat);
357/// # #[derive(Default)]
358/// # struct Menu {
359/// # text: Text,
360/// # selected_entry: usize,
361/// # active_entry: Option<usize>,
362/// # }
363/// # impl Menu {
364/// # pub fn shift_selection(&mut self, shift: i32) {}
365/// # pub fn toggle(&mut self) {}
366/// # fn build_text(&mut self) {}
367/// # }
368/// # impl Widget for Menu {
369/// # fn text(&self) -> &Text { todo!() }
370/// # fn text_mut(&mut self) -> TextMut<'_> { todo!() }
371/// # }
372/// use duat::prelude::*;
373///
374/// #[derive(Clone)]
375/// struct MenuMode;
376///
377/// impl Mode for MenuMode {
378/// type Widget = Menu;
379///
380/// fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>) {
381/// use KeyCode::*;
382///
383/// let menu = handle.write(pa);
384/// match key_event {
385/// event!(Down) => menu.shift_selection(1),
386/// event!(Up) => menu.shift_selection(-1),
387/// event!(Enter | Tab | Char(' ')) => menu.toggle(),
388/// event!(Esc) => mode::reset::<Buffer>(pa),
389/// _ => {}
390/// }
391/// }
392/// }
393/// ```
394///
395/// Notice the [`event!`] macro. This macro is useful for pattern
396/// matching [`KeyEvent`]s on [`Mode`]s. It (alongside [`alt!`],
397/// [`ctrl!`] and [`shift!`]) gets mapped to a [`KeyEvent`] that can
398/// be used for succinctly matching patterns.
399///
400/// [`Cursor`]: crate::mode::Cursor
401/// [resizing]: crate::ui::Area::set_height
402/// [`Form`]: crate::form::Form
403/// [`duat-kak`]: https://docs.rs/duat-kak/latest/duat_kak/index.html
404/// [`form::set_weak`]: crate::form::set_weak
405/// [`form::set`]: crate::form::set
406/// [Kakoune]: https://github.com/mawww/kakoune
407/// [`Text`]: crate::text::Text
408/// [`&mut Selections`]: Selections
409/// [mask]: crate::text::Mask
410#[allow(unused_variables)]
411pub trait Mode: Sized + Send + 'static {
412 /// The [`Widget`] that this [`Mode`] controls
413 type Widget: Widget;
414
415 /// Sends a [`KeyEvent`] to this [`Mode`]
416 fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>);
417
418 /// A list of all available keybindings for this `Mode`
419 ///
420 /// The [`Bindings`] struct serves the purpose of documenting the
421 /// key bindings of a `Mode`. Note that if a [`KeyEvent`] does
422 /// _not_ match in the list, then that `KeyEvent` will _not_ be
423 /// sent to a `Mode`, and a message of no binding will be sent
424 /// instead.
425 ///
426 /// You should implement this function using the [`bindings!`]
427 /// macro. In it, you use a `match` statement to select keys, with
428 /// each pattern returning a description [`Text`] for the binding.
429 /// Here's an example using some Vim keybindings:
430 ///
431 /// ```rust
432 /// duat_core::doc_duat!(duat);
433 /// use duat::prelude::*;
434 ///
435 /// #[derive(Clone)]
436 /// struct VimNormal;
437 ///
438 /// impl Mode for VimNormal {
439 /// # type Widget = Buffer;
440 /// # fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle) {}
441 /// // ...
442 /// fn bindings() -> mode::Bindings {
443 /// let word = txt!("[a]word[separator]|[a]WORD");
444 ///
445 /// let objects = mode::bindings!(match _ {
446 /// event!('w' | 'W') => txt!("Until next {word}"),
447 /// event!('e' | 'E') => txt!("End of {word}"),
448 /// event!('b' | 'B') => txt!("Until start of {word}"),
449 /// // All unlisted keys will not be sent.
450 /// });
451 ///
452 /// mode::bindings!(match _ {
453 /// event!(KeyCode::Char('0'..='9')) => txt!("Add to count"),
454 /// event!('w' | 'W') => txt!("Move to next {word}"),
455 /// event!('e' | 'E') => txt!("Move to end of {word}"),
456 /// event!('b' | 'B') => txt!("Move to start of {word}"),
457 /// event!('r') => (txt!("Replace selection with [a]char"), match _ {
458 /// event!(KeyCode::Char('a')) => txt!("Character to replace with"),
459 /// }),
460 /// event!('d') => (txt!("Delete the next object"), objects.clone()),
461 /// event!('c') => (txt!("Change the next object"), objects.clone()),
462 /// _ => txt!("Not properly documented, but will be sent"),
463 /// })
464 /// }
465 /// }
466 /// ```
467 ///
468 /// One thing to note about this function is that it will only be
469 /// called _once_ for each `Mode`, since Duat considers each mode
470 /// as a static collection of key bindings, each doing their own
471 /// thing.
472 ///
473 /// This is the reason why this function takes no arguments, as it
474 /// is not supposed to depend on the state of the application.
475 ///
476 /// [`Text`]: crate::text::Text
477 fn bindings() -> Bindings {
478 bindings!(match _ {
479 _ => txt!("No key binding declarations, implement [function]Mode::bindings"),
480 })
481 }
482}
483
484/// A mouse event, representing a click, drag, hover, etc.
485#[derive(Debug, Clone, Copy, PartialEq)]
486pub struct MouseEvent<'h, W: ?Sized = dyn Widget> {
487 /// The [`Handle`] where the event took place.
488 pub handle: &'h Handle<W>,
489 /// The position on the [`Text`] where the mouse was.
490 ///
491 /// [`Text`]: crate::text::Text
492 pub points: Option<TwoPointsPlace>,
493 /// Thee coordinate on screen where the mouse was.
494 pub coord: Coord,
495 /// What the mouse did.
496 pub kind: MouseEventKind,
497 /// Modifiers that were pressed during this mouse event.
498 pub modifiers: KeyMod,
499}
500
501/// An event representing a [`MouseEvent`] over a [`Toggle`] region.
502///
503/// [`Toggle`]: crate::text::Toggle
504#[derive(Debug, Clone, Copy, PartialEq)]
505pub struct ToggleEvent<'h> {
506 /// The [`Handle`] where the event took place.
507 pub handle: &'h Handle<dyn Widget>,
508 /// The position on the [`Text`] where the mouse was.
509 ///
510 /// [`Text`]: crate::text::Text
511 pub points: TwoPoints,
512 /// Thee coordinate on screen where the mouse was.
513 pub coord: Coord,
514 /// What the mouse did.
515 pub kind: MouseEventKind,
516 /// Modifiers that were pressed during this mouse event.
517 pub modifiers: KeyMod,
518}
519
520/// Where exactly did the [`TwoPoints`] for a given [`Coord`] match.
521///
522/// It can be either an exact match, that is, the mouse was in the
523/// position of the `TwoPoints`, or it can be on the same line as the
524/// `TwoPoints` at the end of the line.
525#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
526pub enum TwoPointsPlace {
527 /// The mouse was on top of the character that matched.
528 Within(TwoPoints),
529 /// The mouse was on the same line as the character.
530 AheadOf(TwoPoints),
531}
532
533impl TwoPointsPlace {
534 /// The [`TwoPoints`] that were interacted with.
535 pub fn points(&self) -> TwoPoints {
536 match self {
537 TwoPointsPlace::Within(points) | TwoPointsPlace::AheadOf(points) => *points,
538 }
539 }
540
541 /// Returns [`Some`] if the [`TwoPoints`] were within the
542 /// [`Text`].
543 ///
544 /// [`Text`]: crate::text::Text
545 #[must_use]
546 pub fn as_within(&self) -> Option<TwoPoints> {
547 if let Self::Within(v) = self {
548 Some(*v)
549 } else {
550 None
551 }
552 }
553
554 /// Returns [`Some`] if the [`TwoPoints`] were ahead of the
555 /// [`Text`].
556 ///
557 /// [`Text`]: crate::text::Text
558 #[must_use]
559 pub fn as_ahead(&self) -> Option<TwoPoints> {
560 if let Self::Within(v) = self {
561 Some(*v)
562 } else {
563 None
564 }
565 }
566}
567
568/// Return the length of a strin in chars
569#[allow(dead_code)]
570#[doc(hidden)]
571pub const fn len_chars(s: &str) -> usize {
572 let mut i = 0;
573 let b = s.as_bytes();
574 let mut nchars = 0;
575 while i < b.len() {
576 if crate::text::utf8_char_width(b[i]) > 0 {
577 nchars += 1;
578 }
579 i += 1;
580 }
581 nchars
582}
583
584/// Maps each [`char`] in an `&str` to a [`KeyEvent`]
585#[allow(dead_code)]
586#[doc(hidden)]
587pub fn key_events<const LEN: usize>(str: &str, modif: KeyMod) -> [KeyEvent; LEN] {
588 let mut events = [KeyEvent::new(KeyCode::Esc, KeyMod::NONE); LEN];
589
590 for (event, char) in events.iter_mut().zip(str.chars()) {
591 *event = KeyEvent::new(KeyCode::Char(char), modif)
592 }
593
594 events
595}
596
597// This implementation exists only to allow &strs to be passed to
598// remaps.
599// impl Mode for &'static str {
600// // Doesn't matter
601// type Widget = Buffer;
602//
603// fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _:
604// Handle<Self::Widget>) { unreachable!("&strs are only meant
605// to be sent as AsGives, turning into keys"); }
606//
607// fn just_keys(&self) -> Option<&str> {
608// Some(self)
609// }
610// }