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, MouseEventKind, MouseButton};
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;
31pub use self::{
32    cursor::{CaretOrRange, Cursor, Cursors, Selection, Selections, VPoint},
33    patterns::*,
34    remap::*,
35    switch::*,
36};
37use crate::{
38    buffer::Buffer,
39    context::Handle,
40    data::Pass,
41    session::{DuatEvent, TwoPointsPlace},
42    ui::{Coord, Widget},
43};
44
45mod cursor;
46mod patterns;
47mod remap;
48mod switch;
49
50/// A blank [`Mode`], intended for plugin authors to use
51///
52/// This [`Mode`] just resets to the default [`Buffer`] `Mode`, no
53/// matter what key is pressed. It is instead used for mapping keys to
54/// other `Mode`s in a common place:
55///
56/// ```rust
57/// # duat_core::doc_duat!(duat);
58/// # mod plugin0 {
59/// #     use duat_core::{buffer::Buffer, context::Handle, data::Pass, mode::{Mode, KeyEvent}};
60/// #     #[derive(Clone, Copy, Debug)]
61/// #     pub struct PluginMode0;
62/// #     impl Mode for PluginMode0 {
63/// #         type Widget = Buffer;
64/// #         fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle) {}
65/// #     }
66/// # }
67/// # mod plugin1 {
68/// #     use duat_core::{buffer::Buffer, context::Handle, data::Pass, mode::{Mode, KeyEvent}};
69/// #     #[derive(Clone, Copy, Debug)]
70/// #     pub struct PluginMode1;
71/// #     impl Mode for PluginMode1 {
72/// #         type Widget = Buffer;
73/// #         fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle) {}
74/// #     }
75/// # }
76/// # mod duat_kak {
77/// #     use duat_core::{buffer::Buffer, context::Handle, data::Pass, mode::{Mode, KeyEvent}};
78/// #     #[derive(Clone, Copy, Debug)]
79/// #     pub struct Normal;
80/// #     impl Mode for Normal {
81/// #         type Widget = Buffer;
82/// #         fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle) {}
83/// #     }
84/// # }
85/// setup_duat!(setup);
86/// use duat::prelude::*;
87/// use plugin0::*;
88/// use plugin1::*;
89///
90/// fn setup() {
91///     map::<User>("0", PluginMode0);
92///     map::<User>("1", PluginMode1);
93///     map::<duat_kak::Normal>(" ", User);
94/// }
95/// ```
96///
97/// This way, there is a common "hub" for mappings, which plugins can
98/// use in order to map their own [`Mode`]s without interfering with
99/// the user's mapping.
100#[derive(Clone, Copy, Debug)]
101pub struct User;
102
103impl Mode for User {
104    type Widget = Buffer;
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/// Sends a sequence of [`KeyEvent`]s
126///
127/// Unlike with [`mode::map`] or [`mode::alias`], the sent keys are
128/// allowed to be remapped to something else.
129///
130/// [`mode::map`]: map
131/// [`mode::alias`]: alias
132pub fn send_keys(keys: impl AsRef<str>) {
133    let keys = str_to_keys(keys.as_ref());
134    if !keys.is_empty() {
135        KEYS_WERE_SENT.fetch_add(1, Ordering::Relaxed);
136        crate::context::sender()
137            .send(DuatEvent::KeyEventsSent(keys))
138            .unwrap();
139    }
140}
141
142/// Wether the reverse modifier should be [alt] as opposed to [shift]
143///
144/// [shift]: KeyMod::SHIFT
145/// [alt]: KeyMod::ALT
146static ALT_IS_REVERSE: AtomicBool = AtomicBool::new(false);
147
148/// Wether [alt] should be the reverse [modifier], instead of [shift]
149///
150/// On most editing models, the key that reverses actions (mostly
151/// searching), is the [shift] key, (like `shift + n` to go to the
152/// previouse match). In other situations though, that may not be the
153/// case, like with [`duat-kak`], where that key is [alt] (for
154/// consistency reasons).
155///
156/// Changing this key via [`set_alt_is_reverse`] does not cause any
157/// internal changes in [`duat-core`] or [`duat`]. It is only
158/// meant to serve as a general setting for plugins to follow.
159///
160/// [modifier]: KeyMod
161/// [shift]: KeyMod::SHIFT
162/// [alt]: KeyMod::ALT
163/// [`duat-kak`]: docs.rs/duat-kak/latest/duat_kak
164/// [`duat-core`]: docs.rs/duat-core/latest/duat_core
165/// [`duat`]: docs.rs/duat/latest/duat
166pub fn alt_is_reverse() -> bool {
167    ALT_IS_REVERSE.load(Ordering::Relaxed)
168}
169
170/// Sets wether [alt] should be the reverse [modifier], instead of
171/// [shift]
172///
173/// On most editing models, the key that reverses actions (mostly
174/// searching), is the [shift] key, (like `shift + n` to go to the
175/// previouse match). In other situations though, that may not be the
176/// case, like with [`duat-kak`], where that key is [alt] (for
177/// consistency reasons).
178///
179/// The value of this setting can be retrieved with
180/// [`alt_is_reverse`].
181///
182/// [modifier]: KeyMod
183/// [shift]: KeyMod::SHIFT
184/// [alt]: KeyMod::ALT
185/// [`duat-kak`]: docs.rs/duat-kak/latest/duat_kak
186/// [`duat-core`]: docs.rs/duat-core/latest/duat_core
187/// [`duat`]: docs.rs/duat/latest/duat
188pub fn set_alt_is_reverse(value: bool) -> bool {
189    ALT_IS_REVERSE.swap(value, Ordering::Relaxed)
190}
191
192/// A mode for a [`Widget`]
193///
194/// [`Mode`]s are the way that Duat decides how keys are going to
195/// modify widgets.
196///
197/// For this example, I will create a `Menu` widget. This example
198/// doesn't make use of the [`Cursor`] methods from the [`Handle`].
199/// Those are methods that modify [`Selection`]s, and can use them to
200/// modify the [`Text`] in a declarative fashion. For an example with
201/// [`Cursor`]s, see the documentation for `Handle`s.
202///
203/// First, the [`Widget`] itself:
204///
205/// ```rust
206/// # duat_core::doc_duat!(duat);
207/// use duat::prelude::*;
208///
209/// #[derive(Default)]
210/// struct Menu {
211///     text: Text,
212///     selected_entry: usize,
213///     active_etry: Option<usize>,
214/// }
215/// ```
216/// In this widget, the entries will be selectable via a [`Mode`], by
217/// pressing the up and down keys. Let's say that said menu has five
218/// entries, and one of them can be active at a time:
219///
220/// ```rust
221/// # duat_core::doc_duat!(duat);
222/// # use duat::prelude::*;
223/// # struct Menu {
224/// #     text: Text,
225/// #     selected_entry: usize,
226/// #     active_entry: Option<usize>,
227/// # }
228/// impl Menu {
229///     pub fn shift_selection(&mut self, shift: i32) {
230///         let selected = self.selected_entry as i32 + shift;
231///         self.selected_entry = if selected < 0 {
232///             4
233///         } else if selected > 4 {
234///             0
235///         } else {
236///             selected as usize
237///         };
238///     }
239///
240///     pub fn toggle(&mut self) {
241///         self.active_entry = match self.active_entry {
242///             Some(entry) if entry == self.selected_entry => None,
243///             Some(_) | None => Some(self.selected_entry),
244///         };
245///     }
246///
247///     fn build_text(&mut self) {
248///         let mut builder = Text::builder();
249///         builder.push(AlignCenter);
250///
251///         for i in 0..5 {
252///             let text = if let Some(active) = self.active_entry
253///                 && active == i
254///             {
255///                 if self.selected_entry == i {
256///                     txt!("[menu.active]{Spacer}Entry {i}{Spacer}\n")
257///                 } else {
258///                     txt!("[menu.selected.active]{Spacer}Entry {i}{Spacer}\n")
259///                 }
260///             } else if self.selected_entry == i {
261///                 txt!("[menu.selected]{Spacer}Entry {i}{Spacer}\n")
262///             } else {
263///                 txt!("[menu]{Spacer}Entry {i}{Spacer}\n")
264///             };
265///
266///             builder.push(text);
267///         }
268///
269///         self.text = builder.build();
270///     }
271/// }
272/// ```
273///
274/// By making `shift_selection` and `toggle` `pub`, I can allow an end
275/// user to create their own [`Mode`] for this widget.
276///
277/// Now I'll implement [`Widget`] on the `Menu`, so it can show up on
278/// screen:
279///
280/// ```rust
281/// # duat_core::doc_duat!(duat);
282/// # #[derive(Default)]
283/// # struct Menu {
284/// #     text: Text,
285/// #     selected_entry: usize,
286/// #     active_entry: Option<usize>,
287/// # }
288/// # impl Menu {
289/// #     fn build_text(&mut self) { todo!(); }
290/// # }
291/// use duat::prelude::*;
292///
293/// impl Widget for Menu {
294///     fn update(_: &mut Pass, handle: &Handle<Self>) {}
295///
296///     fn needs_update(&self, _: &Pass) -> bool {
297///         false
298///     }
299///
300///     fn text(&self) -> &Text {
301///         &self.text
302///     }
303///
304///     fn text_mut(&mut self) -> &mut Text {
305///         &mut self.text
306///     }
307/// }
308/// ```
309///
310/// One thing that you'll notice is the definition of
311/// [`Widget::needs_update`]. It can always return `false` because the
312/// `Menu` only needs to update after keys are sent, and sent keys
313/// automatically trigger [`Widget::update`].
314///
315/// Now, let's take a look at some [`Widget`] methods that are used
316/// when the [`Widget`] is supposed to be handled by [`Mode`]s. Those
317/// are the [`on_focus`] and [`on_unfocus`] methods:
318///
319/// ```rust
320/// # duat_core::doc_duat!(duat);
321/// # use duat::prelude::*;
322/// # #[derive(Default)]
323/// # struct Menu {
324/// #     text: Text,
325/// #     selected_entry: usize,
326/// #     active_entry: Option<usize>,
327/// # }
328/// impl Widget for Menu {
329/// #     fn text(&self) -> &Text { todo!() }
330/// #     fn text_mut(&mut self) -> &mut Text { todo!() }
331/// #     fn update(_: &mut Pass, _: &Handle<Self>) {}
332/// #     fn needs_update(&self, _: &Pass) -> bool { todo!(); }
333///     // ...
334///     fn on_focus(_: &mut Pass, handle: &Handle<Self>) {
335///         handle.set_mask("inactive");
336///     }
337///
338///     fn on_unfocus(_: &mut Pass, handle: &Handle<Self>) {
339///         handle.set_mask("inactive");
340///     }
341/// }
342/// ```
343///
344/// These methods can do work when the wiget is focused or unfocused.
345///
346/// In this case, I chose to replace the [`Form`]s with "inactive"
347/// variants, by applying the `inactive` [mask]. This makes it so, for
348/// example, the form `"menu"` gets mapped to `"menu.inactive"`, if
349/// that form exists.
350///
351/// Do also note that [`on_focus`] and [`on_unfocus`] are optional
352/// methods, since a [`Widget`] doesn't necessarily need to change on
353/// focus/unfocus.
354///
355/// Now, all that is left to do is the `MenuMode` [`Mode`]. We just
356/// need to create an empty struct and call the methods of the `Menu`:
357///
358/// ```rust
359/// # duat_core::doc_duat!(duat);
360/// # #[derive(Default)]
361/// # struct Menu {
362/// #     text: Text,
363/// #     selected_entry: usize,
364/// #     active_entry: Option<usize>,
365/// # }
366/// # impl Menu {
367/// #     pub fn shift_selection(&mut self, shift: i32) {}
368/// #     pub fn toggle(&mut self) {}
369/// #     fn build_text(&mut self) {}
370/// # }
371/// # impl Widget for Menu {
372/// #     fn text(&self) -> &Text { todo!() }
373/// #     fn text_mut(&mut self) -> &mut Text { todo!() }
374/// #     fn update(_: &mut Pass, _: &Handle<Self>) {}
375/// #     fn needs_update(&self, _: &Pass) -> bool { todo!(); }
376/// # }
377/// use duat::prelude::*;
378///
379/// #[derive(Clone)]
380/// struct MenuMode;
381///
382/// impl Mode for MenuMode {
383///     type Widget = Menu;
384///
385///     fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>) {
386///         use KeyCode::*;
387///
388///         let menu = handle.write(pa);
389///         match key_event {
390///             event!(Down) => menu.shift_selection(1),
391///             event!(Up) => menu.shift_selection(-1),
392///             event!(Enter | Tab | Char(' ')) => menu.toggle(),
393///             event!(Esc) => mode::reset::<Buffer>(),
394///             _ => {}
395///         }
396///     }
397/// }
398/// ```
399///
400/// Notice the [`event!`] macro. This macro is useful for pattern
401/// matching [`KeyEvent`]s on [`Mode`]s. It (alongside [`alt!`],
402/// [`ctrl!`] and [`shift!`]) gets mapped to a [`KeyEvent`] that can
403/// be used for succinctly matching patterns.
404///
405/// [`Cursor`]: crate::mode::Cursor
406/// [`print`]: Widget::print
407/// [`on_focus`]: Widget::on_focus
408/// [`on_unfocus`]: Widget::on_unfocus
409/// [resizing]: crate::ui::Area::set_height
410/// [`Form`]: crate::form::Form
411/// [`duat-kak`]: https://docs.rs/duat-kak/latest/duat_kak/index.html
412/// [`form::set_weak`]: crate::form::set_weak
413/// [`form::set`]: crate::form::set
414/// [Kakoune]: https://github.com/mawww/kakoune
415/// [`Text`]: crate::text::Text
416/// [`&mut Selections`]: Selections
417/// [mask]: Handle::set_mask
418#[allow(unused_variables)]
419pub trait Mode: Sized + Clone + Send + 'static {
420    /// The [`Widget`] that this [`Mode`] controls
421    type Widget: Widget;
422
423    /// Sends a [`KeyEvent`] to this [`Mode`]
424    fn send_key(&mut self, pa: &mut Pass, key_event: KeyEvent, handle: Handle<Self::Widget>);
425
426    /// A function to trigger after switching to this [`Mode`]
427    ///
428    /// This can be some initial setup, like adding [`Tag`]s to the
429    /// [`Text`] in order to show some important visual help for that
430    /// specific [`Mode`].
431    ///
432    /// [`Tag`]: crate::text::Tag
433    /// [`Text`]: crate::text::Text
434    fn on_switch(&mut self, pa: &mut Pass, handle: Handle<Self::Widget>) {}
435
436    /// A function to trigger before switching off this [`Mode`]
437    ///
438    /// This can be some final cleanup like removing the [`Text`]
439    /// entirely, for example.
440    ///
441    /// You might think "Wait, can't I just do these things before
442    /// calling [`mode::set`] or [`mode::reset`]?". Yeah, you could,
443    /// but these functions can fail, so you would do cleanup without
444    /// actually leaving the [`Mode`]. [`before_exit`] _only_ triggers
445    /// if the switch was actually successful.
446    ///
447    /// [`Text`]: crate::text::Text
448    /// [`mode::set`]: set
449    /// [`mode::reset`]: reset
450    /// [`before_exit`]: Mode::before_exit
451    fn before_exit(&mut self, pa: &mut Pass, handle: Handle<Self::Widget>) {}
452
453    /// DO NOT IMPLEMENT THIS FUNCTION, IT IS MEANT FOR `&str` ONLY
454    #[doc(hidden)]
455    fn just_keys(&self) -> Option<&str> {
456        None
457    }
458}
459
460// This implementation exists only to allow &strs to be passed to
461// remaps.
462impl Mode for &'static str {
463    // Doesn't matter
464    type Widget = Buffer;
465
466    fn send_key(&mut self, _: &mut Pass, _: KeyEvent, _: Handle<Self::Widget>) {
467        unreachable!("&strs are only meant to be sent as AsGives, turning into keys");
468    }
469
470    fn just_keys(&self) -> Option<&str> {
471        Some(self)
472    }
473}
474
475/// A mouse event, representing a click, drag, hover, etc
476#[derive(Debug, Clone, Copy)]
477pub struct MouseEvent {
478    /// The position on the [`Text`] where the mouse was.
479    ///
480    /// [`Text`]: crate::text::Text
481    pub points: Option<TwoPointsPlace>,
482    /// Thee coordinate on screen where the mouse was.
483    pub coord: Coord,
484    /// What the mouse did.
485    pub kind: MouseEventKind,
486    /// Modifiers that were pressed during this mouse event.
487    pub modifiers: KeyMod,
488}
489
490/// Return the length of a strin in chars
491#[allow(dead_code)]
492#[doc(hidden)]
493pub const fn len_chars(s: &str) -> usize {
494    let mut i = 0;
495    let b = s.as_bytes();
496    let mut nchars = 0;
497    while i < b.len() {
498        if crate::text::utf8_char_width(b[i]) > 0 {
499            nchars += 1;
500        }
501        i += 1;
502    }
503    nchars
504}
505
506/// Maps each [`char`] in an `&str` to a [`KeyEvent`]
507#[allow(dead_code)]
508#[doc(hidden)]
509pub fn key_events<const LEN: usize>(str: &str, modif: KeyMod) -> [KeyEvent; LEN] {
510    let mut events = [KeyEvent::new(KeyCode::Esc, KeyMod::NONE); LEN];
511
512    for (event, char) in events.iter_mut().zip(str.chars()) {
513        *event = KeyEvent::new(KeyCode::Char(char), modif)
514    }
515
516    events
517}