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