duat_base/modes/
mod.rs

1//! Additional [`Mode`]s for Duat
2//!
3//! This module adds some very important `Mode`s, not only for base
4//! Duat configuration, but also in extending it with a lot of
5//! functionality.
6//!
7//! Chief among the extensible modes in here is the [`Prompt`] and
8//! [`IncSearch`] `Mode`s, which can leverage traits to do a wide
9//! variety of things:
10//!
11//! ```rust
12//! # duat_core::doc_duat!(duat);
13//! # use duat_base::modes::IncSearcher;
14//! use duat::prelude::*;
15//!
16//! #[derive(Clone, Copy)]
17//! struct KeepMatching;
18//!
19//! impl IncSearcher for KeepMatching {
20//!     fn search(&mut self, pa: &mut Pass, pat: &str, handle: Handle<Buffer>) {
21//!         handle.edit_all(pa, |mut c| {
22//!             c.set_caret_on_start();
23//!             let range = c.range();
24//!             if c.search(pat).range(range).next().is_none() {
25//!                 c.destroy();
26//!             }
27//!         });
28//!     }
29//!
30//!     fn prompt(&self) -> Text {
31//!         txt!("[Prompt]keep matching")
32//!     }
33//! }
34//! ```
35//!
36//! The above [`IncSearcher`] will do the incremental search, and keep
37//! only the [`Cursor`]s that matched at some point inside of their
38//! selections.
39//!
40//! [`Mode`]: duat_core::mode::Mode
41//! [`Cursor`]: duat_core::mode::Cursor
42use std::sync::Mutex;
43
44use duat_core::{buffer::Buffer, context::Handle, data::Pass};
45
46pub use self::{
47    inc_search::{ExtendFwd, ExtendRev, IncSearch, IncSearcher, SearchFwd, SearchRev},
48    pager::{Pager, PagerSearch},
49    prompt::{PipeSelections, Prompt, PromptMode, RunCommands},
50};
51
52mod inc_search;
53mod pager;
54mod prompt;
55
56static CLIPBOARD: Mutex<Vec<String>> = Mutex::new(Vec::new());
57
58/// Copy the text of the [`Selection`]s in the [`Handle`]
59///
60/// This function does _not_ make use of the same clipboard as
61/// [`duat_core::clipboard`]. That one acts in the same way as the
62/// system's clipboard.
63///
64/// This clipboard is able to store any number of selections, which
65/// can later be copied via [`paste_strings`]. If only one non empty
66/// selection is copied, it will also be copied to the system's
67/// clipboard.
68///
69/// [`Selection`]: duat_core::mode::Selection
70pub fn copy_selections(pa: &mut Pass, handle: &Handle<Buffer>) {
71    let mut copies: Vec<String> = Vec::new();
72    handle.edit_all(pa, |c| copies.push(c.selection().to_string()));
73    if copies.len() == 1 && !copies.first().unwrap().is_empty() {
74        duat_core::clipboard::set_text(copies.first().unwrap());
75    }
76    *CLIPBOARD.lock().unwrap() = copies
77}
78
79/// Pastes the strings copied with [`copy_selections`]
80///
81/// If the system clipboard was updated since after the last call to
82/// [`copy_selections`], this function will return a one element
83/// [`Vec<String>`] with the new contents of the system clipboard,
84/// instead of the selections taken via [`copy_selections`].
85pub fn paste_strings() -> Vec<String> {
86    static SYSTEM_CLIPB: Mutex<Option<String>> = Mutex::new(None);
87
88    let paste = duat_core::clipboard::get_text();
89
90    let mut sys_clipb = SYSTEM_CLIPB.lock().unwrap();
91
92    // If there was no previous clipboard, or it has changed, copy the new
93    // pasted text
94    if let Some(paste) = paste
95        && sys_clipb.as_ref().is_none_or(|sc| *sc != paste)
96    {
97        *CLIPBOARD.lock().unwrap() = vec![paste.clone()];
98        *sys_clipb = Some(paste.clone());
99        vec![paste]
100    } else {
101        CLIPBOARD.lock().unwrap().clone()
102    }
103}