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