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}