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