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