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}