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