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::reinsert_selections;
31#[doc(inline)]
32pub use self::{bindings::*, patterns::*};
33pub use self::{
34    cursor::{CaretOrRange, Cursor, 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#[derive(Clone, Copy, Debug)]
95pub struct User;
96
97impl Mode for User {
98    type Widget = Buffer;
99
100    fn bindings() -> Bindings {
101        bindings!(match _ {
102            event!(KeyCode::Esc) => txt!(""),
103        })
104    }
105
106    fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle<Self::Widget>) {
107        reset::<Buffer>();
108    }
109}
110
111static KEYS_WERE_SENT: AtomicUsize = AtomicUsize::new(0);
112
113/// Wether any keys were sent via [`mode::send_keys`]
114///
115/// [`mode::send_keys`]: send_keys
116pub(crate) fn keys_were_sent(_: &mut Pass) -> bool {
117    if KEYS_WERE_SENT.load(Ordering::Relaxed) > 0 {
118        KEYS_WERE_SENT.fetch_sub(1, Ordering::Relaxed);
119        true
120    } else {
121        false
122    }
123}
124
125/// Wether the currently active [`Mode`] is this one
126pub fn is_currently<M: Mode>() -> bool {
127    current_type_id() == std::any::TypeId::of::<M>()
128}
129
130/// The [`TypeId`] of the currently active [`Mode`]
131///
132/// [`TypeId`]: std::any::TypeId
133pub fn current_type_id() -> std::any::TypeId {
134    *remap::MODE_TYPE_ID.lock().unwrap()
135}
136
137/// Sends a sequence of [`KeyEvent`]s
138///
139/// Unlike with [`mode::map`] or [`mode::alias`], the sent keys are
140/// allowed to be remapped to something else. Additionally, they will
141/// also trigger the [`KeysTyped`] hook.
142///
143/// [`mode::map`]: map
144/// [`mode::alias`]: alias
145/// [`KeysTyped`]: crate::hook::KeysTyped
146pub fn type_keys(keys: impl AsRef<str>) {
147    let keys = str_to_keys(keys.as_ref());
148    if !keys.is_empty() {
149        KEYS_WERE_SENT.fetch_add(1, Ordering::Relaxed);
150        crate::context::sender()
151            .send(DuatEvent::KeyEventsSent(keys))
152            .unwrap();
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///         builder.push(AlignCenter);
264///
265///         for i in 0..5 {
266///             let text = if let Some(active) = self.active_entry
267///                 && active == i
268///             {
269///                 if self.selected_entry == i {
270///                     txt!("[menu.active]{Spacer}Entry {i}{Spacer}\n")
271///                 } else {
272///                     txt!("[menu.selected.active]{Spacer}Entry {i}{Spacer}\n")
273///                 }
274///             } else if self.selected_entry == i {
275///                 txt!("[menu.selected]{Spacer}Entry {i}{Spacer}\n")
276///             } else {
277///                 txt!("[menu]{Spacer}Entry {i}{Spacer}\n")
278///             };
279///
280///             builder.push(text);
281///         }
282///
283///         self.text = builder.build();
284///     }
285/// }
286/// ```
287///
288/// By making `shift_selection` and `toggle` `pub`, I can allow an end
289/// user to create their own [`Mode`] for this widget.
290///
291/// Now I'll implement [`Widget`] on the `Menu`, so it can show up on
292/// screen:
293///
294/// ```rust
295/// # duat_core::doc_duat!(duat);
296/// # #[derive(Default)]
297/// # struct Menu {
298/// #     text: Text,
299/// #     selected_entry: usize,
300/// #     active_entry: Option<usize>,
301/// # }
302/// # impl Menu {
303/// #     fn build_text(&mut self) { todo!(); }
304/// # }
305/// use duat::prelude::*;
306///
307/// impl Widget for Menu {
308///     fn update(_: &mut Pass, handle: &Handle<Self>) {}
309///
310///     fn needs_update(&self, _: &Pass) -> bool {
311///         false
312///     }
313///
314///     fn text(&self) -> &Text {
315///         &self.text
316///     }
317///
318///     fn text_mut(&mut self) -> &mut Text {
319///         &mut self.text
320///     }
321/// }
322/// ```
323///
324/// One thing that you'll notice is the definition of
325/// [`Widget::needs_update`]. It can always return `false` because the
326/// `Menu` only needs to update after keys are sent, and sent keys
327/// automatically trigger [`Widget::update`].
328///
329/// Now, let's take a look at some [`Widget`] methods that are used
330/// when the [`Widget`] is supposed to be handled by [`Mode`]s. Those
331/// are the [`on_focus`] and [`on_unfocus`] methods:
332///
333/// ```rust
334/// # duat_core::doc_duat!(duat);
335/// # use duat::prelude::*;
336/// # #[derive(Default)]
337/// # struct Menu {
338/// #     text: Text,
339/// #     selected_entry: usize,
340/// #     active_entry: Option<usize>,
341/// # }
342/// impl Widget for Menu {
343/// #     fn text(&self) -> &Text { todo!() }
344/// #     fn text_mut(&mut self) -> &mut Text { todo!() }
345/// #     fn update(_: &mut Pass, _: &Handle<Self>) {}
346/// #     fn needs_update(&self, _: &Pass) -> bool { todo!(); }
347///     // ...
348///     fn on_focus(_: &mut Pass, handle: &Handle<Self>) {
349///         handle.set_mask("inactive");
350///     }
351///
352///     fn on_unfocus(_: &mut Pass, handle: &Handle<Self>) {
353///         handle.set_mask("inactive");
354///     }
355/// }
356/// ```
357///
358/// These methods can do work when the wiget is focused or unfocused.
359///
360/// In this case, I chose to replace the [`Form`]s with "inactive"
361/// variants, by applying the `inactive` [mask]. This makes it so, for
362/// example, the form `"menu"` gets mapped to `"menu.inactive"`, if
363/// that form exists.
364///
365/// Do also note that [`on_focus`] and [`on_unfocus`] are optional
366/// methods, since a [`Widget`] doesn't necessarily need to change on
367/// focus/unfocus.
368///
369/// Now, all that is left to do is the `MenuMode` [`Mode`]. We just
370/// need to create an empty struct and call the methods of the `Menu`:
371///
372/// ```rust
373/// # duat_core::doc_duat!(duat);
374/// # #[derive(Default)]
375/// # struct Menu {
376/// #     text: Text,
377/// #     selected_entry: usize,
378/// #     active_entry: Option<usize>,
379/// # }
380/// # impl Menu {
381/// #     pub fn shift_selection(&mut self, shift: i32) {}
382/// #     pub fn toggle(&mut self) {}
383/// #     fn build_text(&mut self) {}
384/// # }
385/// # impl Widget for Menu {
386/// #     fn text(&self) -> &Text { todo!() }
387/// #     fn text_mut(&mut self) -> &mut Text { todo!() }
388/// #     fn update(_: &mut Pass, _: &Handle<Self>) {}
389/// #     fn needs_update(&self, _: &Pass) -> bool { todo!(); }
390/// # }
391/// use duat::prelude::*;
392///
393/// #[derive(Clone)]
394/// struct MenuMode;
395///
396/// impl Mode for MenuMode {
397///     type Widget = Menu;
398///
399///     fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>) {
400///         use KeyCode::*;
401///
402///         let menu = handle.write(pa);
403///         match key_event {
404///             event!(Down) => menu.shift_selection(1),
405///             event!(Up) => menu.shift_selection(-1),
406///             event!(Enter | Tab | Char(' ')) => menu.toggle(),
407///             event!(Esc) => mode::reset::<Buffer>(),
408///             _ => {}
409///         }
410///     }
411/// }
412/// ```
413///
414/// Notice the [`event!`] macro. This macro is useful for pattern
415/// matching [`KeyEvent`]s on [`Mode`]s. It (alongside [`alt!`],
416/// [`ctrl!`] and [`shift!`]) gets mapped to a [`KeyEvent`] that can
417/// be used for succinctly matching patterns.
418///
419/// [`Cursor`]: crate::mode::Cursor
420/// [`print`]: Widget::print
421/// [`on_focus`]: Widget::on_focus
422/// [`on_unfocus`]: Widget::on_unfocus
423/// [resizing]: crate::ui::Area::set_height
424/// [`Form`]: crate::form::Form
425/// [`duat-kak`]: https://docs.rs/duat-kak/latest/duat_kak/index.html
426/// [`form::set_weak`]: crate::form::set_weak
427/// [`form::set`]: crate::form::set
428/// [Kakoune]: https://github.com/mawww/kakoune
429/// [`Text`]: crate::text::Text
430/// [`&mut Selections`]: Selections
431/// [mask]: Handle::set_mask
432#[allow(unused_variables)]
433pub trait Mode: Sized + Clone + Send + 'static {
434    /// The [`Widget`] that this [`Mode`] controls
435    type Widget: Widget;
436
437    /// Sends a [`KeyEvent`] to this [`Mode`]
438    fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>);
439
440    /// A function to trigger after switching to this [`Mode`]
441    ///
442    /// This can be some initial setup, like adding [`Tag`]s to the
443    /// [`Text`] in order to show some important visual help for that
444    /// specific [`Mode`].
445    ///
446    /// [`Tag`]: crate::text::Tag
447    /// [`Text`]: crate::text::Text
448    fn on_switch(&mut self, pa: &mut Pass, handle: Handle<Self::Widget>) {}
449
450    /// A function to trigger before switching off this [`Mode`]
451    ///
452    /// This can be some final cleanup like removing the [`Text`]
453    /// entirely, for example.
454    ///
455    /// You might think "Wait, can't I just do these things before
456    /// calling [`mode::set`] or [`mode::reset`]?". Yeah, you could,
457    /// but these functions can fail, so you would do cleanup without
458    /// actually leaving the [`Mode`]. [`before_exit`] _only_ triggers
459    /// if the switch was actually successful.
460    ///
461    /// [`Text`]: crate::text::Text
462    /// [`mode::set`]: set
463    /// [`mode::reset`]: reset
464    /// [`before_exit`]: Mode::before_exit
465    fn before_exit(&mut self, pa: &mut Pass, handle: Handle<Self::Widget>) {}
466
467    /// A list of all available keybindings for this `Mode`
468    ///
469    /// The [`Bindings`] struct serves the purpose of documenting the
470    /// key bindings of a `Mode`. Note that if a [`KeyEvent`] does
471    /// _not_ match in the list, then that `KeyEvent` will _not_ be
472    /// sent to a `Mode`, and a message of no binding will be sent
473    /// instead.
474    ///
475    /// You should implement this function using the [`bindings!`]
476    /// macro. In it, you use a `match` statement to select keys, with
477    /// each pattern returning a description [`Text`] for the binding.
478    /// Here's an example using some Vim keybindings:
479    ///
480    /// ```rust
481    /// duat_core::doc_duat!(duat);
482    /// use duat::prelude::*;
483    ///
484    /// #[derive(Clone)]
485    /// struct VimNormal;
486    ///
487    /// impl Mode for VimNormal {
488    ///     # type Widget = Buffer;
489    ///     # fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle) {}
490    ///     // ...
491    ///     fn bindings() -> mode::Bindings {
492    ///         let word = txt!("[a]word[separator]|[a]WORD");
493    ///
494    ///         let objects = mode::bindings!(match _ {
495    ///             event!('w' | 'W') => txt!("Until next {word}"),
496    ///             event!('e' | 'E') => txt!("End of {word}"),
497    ///             event!('b' | 'B') => txt!("Until start of {word}"),
498    ///             // All unlisted keys will not be sent.
499    ///         });
500    ///
501    ///         mode::bindings!(match _ {
502    ///             event!(KeyCode::Char('0'..='9')) => txt!("Add to count"),
503    ///             event!('w' | 'W') => txt!("Move to next {word}"),
504    ///             event!('e' | 'E') => txt!("Move to end of {word}"),
505    ///             event!('b' | 'B') => txt!("Move to start of {word}"),
506    ///             event!('r') => (txt!("Replace selection with [a]char"), match _ {
507    ///                 event!(KeyCode::Char('a')) => txt!("Character to replace with"),
508    ///             }),
509    ///             event!('d') => (txt!("Delete the next object"), objects.clone()),
510    ///             event!('c') => (txt!("Change the next object"), objects.clone()),
511    ///             _ => txt!("Not properly documented, but will be sent"),
512    ///         })
513    ///     }
514    /// }
515    /// ```
516    ///
517    /// One thing to note about this function is that it will only be
518    /// called _once_ for each `Mode`, since Duat considers each mode
519    /// as a static collection of key bindings, each doing their own
520    /// thing.
521    ///
522    /// This is the reason why this function takes no arguments, as it
523    /// is not supposed to depend on the state of the application.
524    ///
525    /// [`Text`]: crate::text::Text
526    fn bindings() -> Bindings {
527        let word = txt!("[a]word[separator]|[a]WORD");
528
529        bindings!(match _ {
530            _ => txt!("No key binding declarations, implement [function]Mode::bindings"),
531        })
532    }
533
534    /// DO NOT IMPLEMENT THIS FUNCTION, IT IS MEANT FOR `&str` ONLY
535    #[doc(hidden)]
536    fn just_keys(&self) -> Option<&str> {
537        None
538    }
539}
540
541/// A mouse event, representing a click, drag, hover, etc
542#[derive(Debug, Clone, Copy)]
543pub struct MouseEvent {
544    /// The position on the [`Text`] where the mouse was.
545    ///
546    /// [`Text`]: crate::text::Text
547    pub points: Option<TwoPointsPlace>,
548    /// Thee coordinate on screen where the mouse was.
549    pub coord: Coord,
550    /// What the mouse did.
551    pub kind: MouseEventKind,
552    /// Modifiers that were pressed during this mouse event.
553    pub modifiers: KeyMod,
554}
555
556/// Return the length of a strin in chars
557#[allow(dead_code)]
558#[doc(hidden)]
559pub const fn len_chars(s: &str) -> usize {
560    let mut i = 0;
561    let b = s.as_bytes();
562    let mut nchars = 0;
563    while i < b.len() {
564        if crate::text::utf8_char_width(b[i]) > 0 {
565            nchars += 1;
566        }
567        i += 1;
568    }
569    nchars
570}
571
572/// Maps each [`char`] in an `&str` to a [`KeyEvent`]
573#[allow(dead_code)]
574#[doc(hidden)]
575pub fn key_events<const LEN: usize>(str: &str, modif: KeyMod) -> [KeyEvent; LEN] {
576    let mut events = [KeyEvent::new(KeyCode::Esc, KeyMod::NONE); LEN];
577
578    for (event, char) in events.iter_mut().zip(str.chars()) {
579        *event = KeyEvent::new(KeyCode::Char(char), modif)
580    }
581
582    events
583}
584
585// This implementation exists only to allow &strs to be passed to
586// remaps.
587impl Mode for &'static str {
588    // Doesn't matter
589    type Widget = Buffer;
590
591    fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle<Self::Widget>) {
592        unreachable!("&strs are only meant to be sent as AsGives, turning into keys");
593    }
594
595    fn just_keys(&self) -> Option<&str> {
596        Some(self)
597    }
598}