Skip to main content

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// }