duat_core/text/mod.rs
1//! The primary data structure in Duat.
2//!
3//! This struct is responsible for all of the text that will be
4//! printed to the screen, as well as any modifications on it.
5//!
6//! The [`Text`] is a very versatile holder for characters, below is a
7//! list of some of its capabilities:
8//!
9//! - Be cheaply* edited at any point, due to its two [gap buffers].
10//! - Be [colored] in any way, at any point.
11//! - Have any arbitrary range concealed, that is, hidden from view,
12//! but still in there.
13//! - Arbitrary [ghost text], that is, [`Text`] that shows up, but is
14//! not actually part of the `Text`, i.e., it can be easily ignored
15//! by external modifiers (like an LSP or tree-sitter) of the
16//! buffer, without any special checks.
17//! - [Spacers] for even more advanced alignment (also implemented by
18//! the [Ui]).
19//! - In the future, button ranges that can interact with the mouse.
20//!
21//! The [`Text`] struct is created in two different ways:
22//!
23//! - By calling [`Text::new`] or one of its [`From`] implementations;
24//! - By building it with the [`txt!`] macro;
25//!
26//! The first method is recommended if you want a [`Text`] that will
27//! be modified by input. This is often the case if your [`Widget`] is
28//! some sort of text box, chief of which is the [`Buffer`], which is
29//! the central `Widget` of every text editor.
30//!
31//! The second method is what should be used most of the time, as it
32//! lets you quickly create formatted [`Widget`]s/[`StatusLine`] parts
33//! in a very modular way:
34//!
35//! ```rust
36//! # duat_core::doc_duat!(duat);
37//! use duat::prelude::*;
38//!
39//! fn number_of_horses(count: usize) -> Text {
40//! if count == 1 {
41//! txt!("[horses.count]1[horses] horse")
42//! } else {
43//! txt!("[horses.count]{}[horses] horses", count)
44//! }
45//! }
46//!
47//! fn inlined_number_of_horses(count: usize) -> Text {
48//! txt!(
49//! "[horses.count]{count} [horses]{}",
50//! if count == 1 { "horse" } else { "horses" }
51//! )
52//! }
53//! ```
54//!
55//! You can use this whenever you need to update a widget, for
56//! example, just create a new [`Text`] to printed to the screen.
57//!
58//! However, when recreating the entire [`Text`] with a [`txt!`]
59//! macro would be too expensive, you can use [`Text`] modifying
60//! functions:
61//!
62//! ```rust
63//! # duat_core::doc_duat!(duat);
64//! use duat::prelude::*;
65//!
66//! let mut prompted = txt!("[prompt]type a key: ");
67//! let end = prompted.len();
68//! prompted.replace_range(end..end, "a")
69//! ```
70//!
71//! A general rule of thumb for "too expensive" is this: if your
72//! [`Text`] can't scroll more than a few lines, it is not too
73//! expensive to rebuild. This way of editing the [`Text`] is mostly
74//! used on the [`Buffer`] widget and other textbox-like [`Widget`]s.
75//!
76//! [Spacers]: Spacer
77//! [gap buffers]: gap_buf::GapBuffer
78//! [colored]: crate::form::Form
79//! [ghost text]: Inlay
80//! [Ui]: crate::ui::traits::RawUi
81//! [`Buffer`]: crate::buffer::Buffer
82//! [`Widget`]: crate::ui::Widget
83//! [`StatusLine`]: https://docs.rs/duat/latest/duat/widgets/struct.StatusLine.html
84//! [`Mode`]: crate::mode::Mode
85use std::ops::Range;
86
87pub(crate) use crate::text::{strs::StrsBuf, tags::ToggleFn};
88use crate::{
89 Ns,
90 buffer::{Change, History},
91 context::Handle,
92 data::Pass,
93 mode::{Selection, Selections},
94 text::{
95 tags::{FwdTags, InnerTags, RevTags},
96 utils::implPartialEq,
97 },
98 ui::{SpawnId, Widget},
99};
100pub use crate::{
101 text::{
102 builder::{AsBuilderPart, Builder, BuilderPart},
103 iter::{FwdIter, RevIter, TextPart, TextPlace},
104 search::{Matches, RegexHaystack, RegexPattern},
105 strs::{Lines, Strs},
106 tags::{
107 Conceal, FormTag, InlayId, Inlay, Mask, Overlay, RawTag, Spacer, Spawn, Tag, Tags,
108 Toggle,
109 },
110 utils::{Point, TextIndex, TextRange, TextRangeOrIndex, TwoPoints, utf8_char_width},
111 },
112 txt,
113};
114
115mod builder;
116mod iter;
117mod search;
118mod shift_list;
119mod strs;
120mod tags;
121mod utils;
122
123/// The text of a given [`Widget`].
124///
125/// The [`Text`] is the backbone of Duat. It is the thing responsible
126/// for everything that shows up on screen.
127///
128/// You can build a `Text` manually, by using [`Text::new`], or with
129/// some convenience, by using the [`txt!`] macro, making use of a
130/// [`Builder`].
131///
132/// [`Widget`]: crate::ui::Widget
133pub struct Text(Box<InnerText>);
134
135#[derive(Clone)]
136struct InnerText {
137 buf: StrsBuf,
138 tags: InnerTags,
139 selections: Selections,
140 has_unsaved_changes: bool,
141}
142
143impl Text {
144 ////////// Creation and Destruction of Text
145
146 /// Returns a new empty `Text`.
147 pub fn new() -> Self {
148 Self::from_parts(StrsBuf::new(String::new()), Selections::new_empty())
149 }
150
151 /// Returns a new empty [`Text`] with [`Selections`] enabled.
152 pub fn with_default_main_selection() -> Self {
153 Self::from_parts(
154 StrsBuf::new(String::new()),
155 Selections::new(Selection::default()),
156 )
157 }
158
159 /// Creates a `Text` from a [`String`].
160 pub(crate) fn from_parts(buf: StrsBuf, mut selections: Selections) -> Self {
161 let tags = InnerTags::new(buf.len());
162
163 let selections = if selections.iter().any(|(sel, _)| {
164 [Some(sel.caret()), sel.anchor()]
165 .into_iter()
166 .flatten()
167 .any(|point| point.char() >= buf.end_point().char())
168 }) {
169 Selections::new(Selection::default())
170 } else {
171 selections.correct_all(&buf);
172 selections
173 };
174
175 Self(Box::new(InnerText {
176 buf,
177 tags,
178 selections,
179 has_unsaved_changes: false,
180 }))
181 }
182
183 /// Returns a [`Builder`] for [`Text`].
184 ///
185 /// This builder can be used to iteratively create text, by
186 /// assuming that the user wants no* [`Tag`] overlap, and that
187 /// they want to construct the [`Text`] in [`Tag`]/content pairs.
188 ///
189 /// ```rust
190 /// # duat_core::doc_duat!(duat);
191 /// use duat::prelude::*;
192 /// let mut builder = Text::builder();
193 /// ```
194 pub fn builder() -> Builder {
195 Builder::new()
196 }
197
198 ////////// Querying functions
199
200 /// Whether or not there are any characters in the [`Text`],
201 /// besides the final `b'\n'`.
202 ///
203 /// # Note
204 ///
205 /// This does not check for tags, so with a [`Inlay`],
206 /// there could actually be a "string" of characters on the
207 /// [`Text`], it just wouldn't be considered real "text". If you
208 /// want to check for the `InnerTags`'b possible emptyness as
209 /// well, see [`Text::is_empty_empty`].
210 pub fn is_empty(&self) -> bool {
211 let [s0, s1] = self.to_array();
212 (s0 == "\n" && s1.is_empty()) || (s0.is_empty() && s1 == "\n")
213 }
214
215 /// Whether the [`Strs`] and `InnerTags` are empty.
216 ///
217 /// This ignores the last `'\n'` in the [`Text`], since it is
218 /// always there no matter what.
219 ///
220 /// If you only want to check for the [`Strs`], ignoring possible
221 /// [`Inlay`]s, see [`is_empty`].
222 ///
223 /// [`is_empty`]: Strs::is_empty
224 pub fn is_empty_empty(&self) -> bool {
225 self.0.buf.is_empty() && self.0.tags.is_empty()
226 }
227
228 /// The parts that make up a [`Text`].
229 ///
230 /// This function is used when you want to [insert]/[remove]
231 /// [`Tag`]s (i.e., borrow the inner `InnerTags` mutably via
232 /// [`Tags`]), while still being able to read from the
233 /// [`Strs`] and [`Selections`].
234 ///
235 /// [insert]: Tags::insert
236 /// [remove]: Tags::remove
237 pub fn parts(&mut self) -> TextParts<'_> {
238 TextParts {
239 strs: &self.0.buf,
240 tags: self.0.tags.tags(),
241 selections: &self.0.selections,
242 }
243 }
244
245 /// Returns the [`TextMut`] for this `Text`.
246 ///
247 /// This function is used by [`Widget::text_mut`], since that
248 /// function is not supposed to allow the user to swap the
249 /// [`Text`], which could break the history of the [`Buffer`].
250 ///
251 /// For the `Buffer` specifically, it also attaches that `Buffer`
252 /// s `History` to it, which lets one undo and redo things.
253 ///
254 /// [`Buffer`]: crate::buffer::Buffer
255 pub fn as_mut(&mut self) -> TextMut<'_> {
256 TextMut { text: self, history: None }
257 }
258
259 ////////// Tag related query functions
260
261 /// The maximum [points] in the `at`th byte.
262 ///
263 /// This point is essentially the [point] at that byte, plus the
264 /// last possible [`Point`] of any [`Inlay`]s in that
265 /// position.
266 ///
267 /// [points]: TwoPoints
268 /// [point]: Strs::point_at_byte
269 #[track_caller]
270 pub fn ghost_max_points_at(&self, b: usize) -> TwoPoints {
271 let point = self.point_at_byte(b);
272 if let Some(total_ghost) = self.0.tags.ghosts_total_at(point.byte()) {
273 TwoPoints::new(point, total_ghost)
274 } else {
275 TwoPoints::new_after_ghost(point)
276 }
277 }
278
279 /// The [points] at the end of the text.
280 ///
281 /// This will essentially return the [last point] of the text,
282 /// alongside the last possible [`Point`] of any [`Inlay`] at the
283 /// end of the text.
284 ///
285 /// [points]: TwoPoints
286 /// [last point]: Strs::len
287 pub fn end_points(&self) -> TwoPoints {
288 self.ghost_max_points_at(self.len())
289 }
290
291 /// Points visually after the [`TwoPoints`].
292 ///
293 /// If the [`TwoPoints`] in question is concealed, treats the
294 /// next visible character as the first character, and returns
295 /// the points of the next visible character.
296 ///
297 /// This method is useful if you want to iterator reversibly
298 /// right after a certain point, thus including the character
299 /// of said point.
300 #[track_caller]
301 pub fn points_after(&self, tp: TwoPoints) -> Option<TwoPoints> {
302 self.iter_fwd(tp)
303 .filter_map(|item| item.part.as_char().map(|_| item.points()))
304 .chain([self.end_points()])
305 .nth(1)
306 }
307
308 /// The visual start of the line.
309 ///
310 /// This point is defined not by where the line actually begins,
311 /// but by where the last '\n' was located. For example, if
312 /// [`Tag`]s create ghost text or omit text from multiple
313 /// different lines, this point may differ from where in the
314 /// [`Text`] the real line actually begins.
315 ///
316 /// The `skip` value is how many `\n` should be skipped before
317 /// returning.
318 pub fn visual_line_start(&self, mut points: TwoPoints, skip: usize) -> TwoPoints {
319 let mut iter = self.iter_rev(points).peekable();
320 let mut total_seen = 0;
321 while let Some(peek) = iter.peek() {
322 match peek.part {
323 TextPart::Char('\n') => {
324 if total_seen == skip {
325 return points;
326 } else {
327 total_seen += 1;
328 }
329 }
330 TextPart::Char(_) => points = iter.next().unwrap().points(),
331 _ => _ = iter.next(),
332 }
333 }
334
335 points
336 }
337
338 /// Gets the [`Inlay`] of a given [`InlayId`]
339 pub fn get_ghost(&self, id: InlayId) -> Option<&Text> {
340 self.0.tags.get_ghost(id)
341 }
342
343 ////////// Modification functions
344
345 /// Replaces a [range] in the `Text`.
346 ///
347 /// # [`TextRange`] behavior:
348 ///
349 /// If you give a single [`usize`]/[`Point`], it will be
350 /// interpreted as a range from.
351 ///
352 /// [range]: TextRange
353 pub fn replace_range(&mut self, range: impl TextRange, edit: impl ToString) {
354 let range = range.to_range(self.len());
355 let (start, end) = (
356 self.point_at_byte(range.start),
357 self.point_at_byte(range.end),
358 );
359 let change = Change::new(edit, start..end, self);
360
361 self.0.buf.increment_version();
362 self.apply_change(0, change.as_ref());
363 }
364
365 /// Merges `String`s with the body of text, given a range to
366 /// replace.
367 fn apply_change(&mut self, guess_i: usize, change: Change<&str>) -> usize {
368 self.0.buf.apply_change(change);
369 self.0.tags.transform(
370 change.start().byte()..change.taken_end().byte(),
371 change.added_end().byte(),
372 );
373
374 self.0.has_unsaved_changes = true;
375 self.0.selections.apply_change(guess_i, change)
376 }
377
378 /// Inserts a `Text` into this `Text`, in a specific [`Point`].
379 pub fn insert_text(&mut self, p: impl TextIndex, text: &Text) {
380 let b = p.to_byte_index().min(self.last_point().byte());
381 let cap = text.last_point().byte();
382
383 let added_str = text.0.buf[..cap].to_string();
384 let point = self.point_at_byte(b);
385 let change = Change::str_insert(&added_str, point);
386 self.apply_change(0, change);
387
388 self.0.tags.insert_tags(point, cap, &text.0.tags);
389 }
390
391 fn apply_and_process_changes<'a>(
392 &mut self,
393 changes: impl ExactSizeIterator<Item = Change<'a, &'a str>>,
394 ) {
395 self.0.selections.clear();
396
397 let len = changes.len();
398 for (i, change) in changes.enumerate() {
399 self.apply_change(0, change);
400
401 let start = change.start().min(self.last_point());
402 let added_end = match change.added_str().chars().next_back() {
403 Some(last) => change.added_end().rev(last),
404 None => start,
405 };
406
407 let selection = Selection::new(added_end, (start != added_end).then_some(start));
408 self.0.selections.insert(i, selection, i == len - 1);
409 }
410 }
411
412 ////////// Writing functions
413
414 /// Writes the contents of this `Text` to a [writer].
415 ///
416 /// [writer]: std::io::Write
417 pub fn save_on(&mut self, mut writer: impl std::io::Write) -> std::io::Result<usize> {
418 self.0.has_unsaved_changes = false;
419
420 let [s0, s1] = self.0.buf.slices(..);
421 Ok(writer.write(s0)? + writer.write(s1)?)
422 }
423
424 /// Wether or not the content has changed since the last [save].
425 ///
426 /// Returns `true` only if the actual buf of the [`Text`] have
427 /// been changed, ignoring [`Tag`]s and all the other things,
428 /// since those are not written to the filesystem.
429 ///
430 /// [save]: Text::save_on
431 pub fn has_unsaved_changes(&self) -> bool {
432 self.0.has_unsaved_changes
433 }
434
435 ////////// Tag addition/deletion functions
436
437 /// Inserts a [`Tag`] at the given position.
438 #[track_caller]
439 pub fn insert_tag<Idx>(&mut self, ns: Ns, idx: Idx, tag: impl Tag<Idx>) {
440 self.0.tags.insert_inner(ns, idx, tag, false)
441 }
442
443 /// Like [`insert_tag`], but does it after other [`Tag`]s with the
444 /// same priority.
445 ///
446 /// [`insert_tag`]: Self::insert_tag
447 pub fn insert_tag_after<Idx>(&mut self, ns: Ns, idx: Idx, tag: impl Tag<Idx>) {
448 self.0.tags.insert_inner(ns, idx, tag, true)
449 }
450
451 /// Removes the [`Tag`]s of a [`Ns`] from a region.
452 ///
453 /// The input can either be a byte index, a [`Point`], or a
454 /// [range] of byte indices/[`Point`]s. If you are implementing a
455 /// [`Buffer`] updating hook through [`BufferUpdated`], it can be
456 /// very useful to just "undo" all of the [`Tag`] additions done
457 /// by previous updates, you can do that efficiently with this
458 /// function:
459 ///
460 /// ```rust
461 /// # duat_core::doc_duat!(duat);
462 /// use duat::prelude::*;
463 /// setup_duat!(setup);
464 ///
465 /// fn setup() {
466 /// let ns = Ns::new();
467 ///
468 /// hook::add::<BufferUpdated>(move |pa, handle| {
469 /// let buf = handle.write(pa);
470 /// // Removing on the whole Buffer
471 /// buf.text_mut().remove_tags(ns, ..);
472 /// // Logic to add Tags with ns...
473 /// });
474 /// }
475 /// ```
476 ///
477 /// [range]: std::ops::RangeBounds
478 /// [`Buffer`]: crate::buffer::Buffer
479 /// [`BufferUpdated`]: crate::hook::BufferUpdated
480 pub fn remove_tags(&mut self, ns: Ns, range: impl TextRangeOrIndex) {
481 let range = range.to_range(self.len() + 1);
482 self.0.tags.remove_from(ns, range)
483 }
484
485 /// Just like [`Text::remove_tags`] but excludes ends on the start
486 /// and starts on the end.
487 ///
488 /// In the regular [`remove_tags`] function, if you remove from a
489 /// range `x..y`, tag ranges that end in `x` or start in `y -
490 /// 1` (exclusive range) will also be removed.
491 ///
492 /// If you don't want that to happen, you can use this function
493 /// instead.
494 ///
495 /// [`remove_tags`]: Self::remove_tags
496 pub fn remove_tags_excl(&mut self, ns: Ns, range: impl TextRangeOrIndex) {
497 let range = range.to_range(self.len() + 1);
498 self.0.tags.remove_from_excl(ns, range)
499 }
500
501 /// Like [`Text::remove_tags`], but removes base on a predicate.
502 ///
503 /// If the function returns `true`, then the tag is removed. Note
504 /// that every [`RawTag`] in here is guaranteed to have the same
505 /// [`Ns`] as the one passed to the function, so you don't
506 /// need to chack for that.
507 pub fn remove_tags_if(
508 &mut self,
509 ns: Ns,
510 from: impl TextRangeOrIndex,
511 filter: impl FnMut(usize, RawTag) -> bool,
512 ) {
513 let range = from.to_range(self.len() + 1);
514 self.0.tags.remove_from_if(ns, range, filter)
515 }
516
517 /// Removes all [`Tag`]s.
518 ///
519 /// Refrain from using this function on [`Buffer`]s, as there may
520 /// be other [`Tag`] providers, and you should avoid messing
521 /// with their tags.
522 ///
523 /// [`Buffer`]: crate::buffer::Buffer
524 pub fn clear_tags(&mut self) {
525 self.0.tags = InnerTags::new(self.0.buf.len());
526 }
527
528 /////////// Iterator methods
529
530 /// A forward iterator of the [chars and tags] of the [`Text`].
531 ///
532 /// [chars and tags]: TextPart
533 #[track_caller]
534 pub fn iter_fwd(&self, at: TwoPoints) -> FwdIter<'_> {
535 FwdIter::new_at(self, at, false)
536 }
537
538 /// A reverse iterator of the [chars and tags] of the [`Text`]
539 ///
540 /// [chars and tags]: TextPart
541 pub fn iter_rev(&self, at: TwoPoints) -> RevIter<'_> {
542 RevIter::new_at(self, at)
543 }
544
545 /// A forward iterator over the [`Tag`]s of the [`Text`].
546 ///
547 /// This iterator will consider some [`Tag`]s before `b`, since
548 /// their ranges may overlap with `b`.
549 ///
550 /// The amount of tags to look for behind depeds on the internal
551 /// `min_len` factor. You can override by providing a lookaround,
552 /// which will tell Duat how many `Tag`s to look behind. If you
553 /// set it to `Some(0)`, lookaround will be disabled.
554 ///
555 /// # Note
556 ///
557 /// Duat works fine with [`Tag`]s in the middle of a codepoint,
558 /// but external utilizers may not, so keep that in mind.
559 pub fn tags_fwd(&self, b: usize, lookaround: Option<usize>) -> FwdTags<'_> {
560 self.0.tags.fwd_at(b, lookaround)
561 }
562
563 /// An reverse iterator over the [`Tag`]s of the [`Text`].
564 ///
565 /// This iterator will consider some `Tag`s ahead of `b`, since
566 /// their ranges may overlap with `b`.
567 ///
568 /// The amount of tags to look for ahead depeds on the internal
569 /// `min_len` factor. You can override by providing a lookaround,
570 /// which will tell Duat how many `Tag`s to look ahead. If you set
571 /// it to `Some(0)`, lookaround will be disabled.
572 ///
573 /// # Note
574 ///
575 /// Duat works fine with [`Tag`]s in the middle of a codepoint,
576 /// but external utilizers may not, so keep that in mind.
577 pub fn tags_rev(&self, b: usize, lookaround: Option<usize>) -> RevTags<'_> {
578 self.0.tags.rev_at(b, lookaround)
579 }
580
581 /// A forward [`Iterator`] over the [`RawTag`]s.
582 ///
583 /// This [`Iterator`] does not take into account [`Tag`] ranges
584 /// that intersect with the starting point, unlike
585 /// [`Text::tags_fwd`]
586 pub fn raw_tags_fwd(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
587 self.0.tags.raw_fwd_at(b)
588 }
589
590 /// A reverse [`Iterator`] over the [`RawTag`]s.
591 ///
592 /// This [`Iterator`] does not take into account [`Tag`] ranges
593 /// that intersect with the starting point, unlike
594 /// [`Text::tags_rev`]
595 pub fn raw_tags_rev(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
596 self.0.tags.raw_rev_at(b)
597 }
598
599 /// The [`Selections`] printed to this `Text`, if they exist.
600 pub fn selections(&self) -> &Selections {
601 &self.0.selections
602 }
603
604 /// A mut reference to this `Text`'s [`Selections`] if they
605 /// exist.
606 pub fn selections_mut(&mut self) -> &mut Selections {
607 &mut self.0.selections
608 }
609
610 /// Gets the main [`Selection`], if there is one.
611 ///
612 /// If you want a method that doesn't return an [`Option`] (for
613 /// convenience), see [`Text::main_sel`].
614 pub fn get_main_sel(&self) -> Option<&Selection> {
615 self.0.selections.get_main()
616 }
617
618 /// Gets the main [`Selection`].
619 ///
620 /// # Panics
621 ///
622 /// This method will panic if there are no `Selection`s. If you
623 /// want a non-panicking method, see [`Text::get_main_sel`].
624 #[track_caller]
625 pub fn main_sel(&self) -> &Selection {
626 self.0.selections.main()
627 }
628
629 /// A list of all [`SpawnId`]s that belong to this `Text`
630 pub fn get_spawned_ids(&self) -> impl Iterator<Item = SpawnId> {
631 self.0.tags.get_spawned_ids()
632 }
633
634 /// Returns a [`String`], but without the `\n` at the end of the
635 /// `Text`.
636 ///
637 /// Normally, when you call `Text::to_string`, (which is actually
638 /// [deref'd] into [`Strs::to_string`]), it will also include the
639 /// last `\n` character, which is always a part of the `Text`
640 /// no matter what. This function doesn't do that.
641 ///
642 /// [deref'd]: https://doc.rust-lang.org/std/ops/trait.Deref.html
643 /// [`Strs::to_string`]: ToString::to_string
644 pub fn to_string_no_last_nl(&self) -> String {
645 let mut string = self.to_string();
646 string.pop();
647 string
648 }
649
650 /// A struct representing how many changes took place since the
651 /// creation of this `Text`
652 ///
653 /// This struct tracks all [`Change`]s and [`Tag`]
654 /// additions/removals, giving you information about wether this
655 /// `Text` has changed, when comparing this to previous
656 /// [`TextVersion`]s of the same `Text`.
657 ///
658 /// This _does_ also include things like undoing and redoing. This
659 /// is done to keep track of all changes that took place, even to
660 /// previously extant states of the text.
661 pub fn version(&self) -> TextVersion {
662 let (tags, meta_tags) = self.0.tags.versions();
663
664 TextVersion {
665 strs: self.0.buf.version(),
666 tags,
667 meta_tags,
668 }
669 }
670
671 /////////// Internal functions
672
673 /// Prepares the `Text` for reloading, to be used on [`Buffer`]s.
674 ///
675 /// [`Buffer`]: crate::buffer::Buffer
676 pub(crate) fn prepare_for_reloading(&mut self) {
677 self.clear_tags();
678 }
679
680 /// Take the reloaded parts off of this `Text`.
681 pub(crate) fn take_reload_parts(&mut self) -> (StrsBuf, Selections) {
682 (
683 std::mem::take(&mut self.0.buf),
684 std::mem::replace(&mut self.0.selections, Selections::new_empty()),
685 )
686 }
687
688 /// Returns all toggle functions that surround a byte.
689 pub(crate) fn toggles_surrounding(&self, point: Point) -> Vec<(Range<Point>, ToggleFn)> {
690 self.0
691 .tags
692 .toggles_surrounding(point.byte())
693 .map(|(range, toggle_fn)| {
694 (
695 self.point_at_byte(range.start)..self.point_at_byte(range.end),
696 toggle_fn,
697 )
698 })
699 .collect()
700 }
701}
702
703impl std::ops::Deref for Text {
704 type Target = Strs;
705
706 fn deref(&self) -> &Self::Target {
707 &self.0.buf
708 }
709}
710
711/// A struct that allows for [`Text`] modifications from the
712/// [`Widget::text_mut`] function.
713///
714/// It is pretty much identical to `&mut Text`, the difference is that
715/// you can't reassign it to a new [`Text`]. This is done in order to
716/// prevent radical changes to the `Text` of the [`Buffer`] from the
717/// outside.
718///
719/// [`Buffer`]: crate::buffer::Buffer
720#[derive(Debug)]
721pub struct TextMut<'t> {
722 text: &'t mut Text,
723 history: Option<&'t mut History>,
724}
725
726impl<'t> TextMut<'t> {
727 /// Replaces a [range] in the `Text`.
728 ///
729 /// # [`TextRange`] behavior:
730 ///
731 /// If you give a single [`usize`]/[`Point`], it will be
732 /// interpreted as a range from.
733 ///
734 /// [range]: TextRange
735 pub fn replace_range(&mut self, range: impl TextRange, edit: impl ToString) {
736 let range = range.to_range(self.len());
737 let (start, end) = (
738 self.point_at_byte(range.start),
739 self.point_at_byte(range.end),
740 );
741 let change = Change::new(edit, start..end, self);
742
743 self.text.0.buf.increment_version();
744 self.text.apply_change(0, change.as_ref());
745 self.history.as_mut().map(|h| h.apply_change(None, change));
746 }
747
748 /// Applies a [`Change`] to the `Text`.
749 pub(crate) fn apply_change(
750 &mut self,
751 guess_i: Option<usize>,
752 change: Change<'static, String>,
753 ) -> (Option<usize>, usize) {
754 self.text.0.buf.increment_version();
755 let selections_taken = self
756 .text
757 .apply_change(guess_i.unwrap_or(0), change.as_ref());
758 let history = self.history.as_mut();
759 let insertion_i = history.map(|h| h.apply_change(guess_i, change));
760 (insertion_i, selections_taken)
761 }
762
763 ////////// Functions for Tag modifications
764
765 /// The parts that make up a [`Text`].
766 ///
767 /// This function is used when you want to [insert]/[remove]
768 /// [`Tag`]s (i.e., borrow the inner `InnerTags` mutably via
769 /// [`Tags`]), while still being able to read from the
770 /// [`Strs`] and [`Selections`].
771 ///
772 /// [insert]: Tags::insert
773 /// [remove]: Tags::remove
774 pub fn parts(self) -> TextParts<'t> {
775 self.text.parts()
776 }
777
778 /// Inserts a [`Tag`] at the given position.
779 pub fn insert_tag<Idx>(&mut self, ns: Ns, idx: Idx, tag: impl Tag<Idx>) {
780 self.text.insert_tag(ns, idx, tag)
781 }
782
783 /// Like [`insert_tag`], but does it after other [`Tag`]s with the
784 /// same priority.
785 ///
786 /// [`insert_tag`]: Self::insert_tag
787 pub fn insert_tag_after<Idx>(&mut self, ns: Ns, idx: Idx, tag: impl Tag<Idx>) {
788 self.text.insert_tag_after(ns, idx, tag)
789 }
790
791 /// Removes the [`Tag`]s of a [`Ns`] from a region.
792 ///
793 /// The input can either be a byte index, a [`Point`], or a
794 /// [range] of byte indices/[`Point`]s. If you are implementing a
795 /// [`Buffer`] updating hook through [`BufferUpdated`], it can be
796 /// very useful to just "undo" all of the [`Tag`] additions done
797 /// by previous updates, you can do that efficiently with this
798 /// function:
799 ///
800 /// ```rust
801 /// # duat_core::doc_duat!(duat);
802 /// use duat::prelude::*;
803 /// setup_duat!(setup);
804 ///
805 /// fn setup() {
806 /// let ns = Ns::new();
807 ///
808 /// hook::add::<BufferUpdated>(move |pa, handle| {
809 /// let buf = handle.write(pa);
810 /// // Removing on the whole Buffer
811 /// buf.text_mut().remove_tags(ns, ..);
812 /// // Logic to add Tags with ns...
813 /// });
814 /// }
815 /// ```
816 ///
817 /// [range]: std::ops::RangeBounds
818 /// [`Buffer`]: crate::buffer::Buffer
819 /// [`BufferUpdated`]: crate::hook::BufferUpdated
820 pub fn remove_tags(&mut self, ns: Ns, range: impl TextRangeOrIndex) {
821 let range = range.to_range(self.len() + 1);
822 self.text.remove_tags(ns, range)
823 }
824
825 /// Just like [`TextMut::remove_tags`] but excludes ends on the
826 /// start and starts on the end.
827 ///
828 /// In the regular [`remove_tags`] function, if you remove from a
829 /// range `x..y`, tag ranges that end in `x` or start in `y -
830 /// 1` (exclusive range) will also be removed.
831 ///
832 /// If you don't want that to happen, you can use this function
833 /// instead.
834 ///
835 /// [`remove_tags`]: Self::remove_tags
836 pub fn remove_tags_excl(&mut self, ns: Ns, range: impl TextRangeOrIndex) {
837 let range = range.to_range(self.len() + 1);
838 self.text.remove_tags_excl(ns, range)
839 }
840
841 /// Like [`TextMut::remove_tags`], but removes base on a
842 /// predicate.
843 ///
844 /// If the function returns `true`, then the tag is removed. Note
845 /// that every [`RawTag`] in here is guaranteed to have the same
846 /// [`Ns`] as the one passed to the function, so you don't
847 /// need to chack for that.
848 pub fn remove_tags_if(
849 &mut self,
850 ns: Ns,
851 from: impl TextRangeOrIndex,
852 filter: impl FnMut(usize, RawTag) -> bool,
853 ) {
854 let range = from.to_range(self.len() + 1);
855 self.text.remove_tags_if(ns, range, filter)
856 }
857
858 /// Removes all [`Tag`]s.
859 ///
860 /// Refrain from using this function on [`Buffer`]s, as there may
861 /// be other [`Tag`] providers, and you should avoid messing
862 /// with their tags.
863 ///
864 /// [`Buffer`]: crate::buffer::Buffer
865 pub fn clear_tags(&mut self) {
866 self.text.clear_tags();
867 }
868
869 ////////// Internal methods
870
871 /// Updates bounds, so that [`Tag`] ranges can visibly cross the
872 /// screen.
873 ///
874 /// This is used in order to allow for very long [`Tag`] ranges
875 /// (say, a [`Form`] being applied on the range `3..999`) to show
876 /// up properly without having to lookback a bazillion [`Tag`]s
877 /// which could be in the way.
878 ///
879 /// [`Form`]: crate::form::Form
880 pub(crate) fn update_bounds(&mut self) {
881 self.text.0.tags.update_bounds();
882 }
883
884 /// Functions to add all [`Widget`]s that were spawned in this
885 /// `Text`.
886 ///
887 /// This function should only be called right before printing,
888 /// where it is "known" that `Widget`s can no longer get rid of
889 /// the [`Spawn`]s
890 ///
891 /// [`Widget`]: crate::ui::Widget
892 pub(crate) fn get_widget_spawns(
893 &mut self,
894 ) -> Vec<(
895 SpawnId,
896 Box<dyn FnOnce(&mut Pass, usize, Handle<dyn Widget>) + Send>,
897 )> {
898 std::mem::take(&mut self.text.0.tags.spawn_fns.0)
899 }
900
901 ////////// History functions
902
903 /// Undoes the last moment, if there was one.
904 pub fn undo(&mut self) {
905 if let Some(history) = &mut self.history
906 && let Some((changes, saved_moment)) = history.move_backwards()
907 {
908 self.text.apply_and_process_changes(changes);
909 self.text.0.buf.increment_version();
910 self.text.0.has_unsaved_changes = !saved_moment;
911 }
912 }
913
914 /// Redoes the last moment in the history, if there is one.
915 pub fn redo(&mut self) {
916 if let Some(history) = &mut self.history
917 && let Some((changes, saved_moment)) = history.move_forward()
918 {
919 self.text.apply_and_process_changes(changes);
920 self.text.0.buf.increment_version();
921 self.text.0.has_unsaved_changes = !saved_moment;
922 }
923 }
924
925 /// Finishes the current moment and adds a new one to the history.
926 pub fn new_moment(&mut self) {
927 if let Some(h) = &mut self.history {
928 h.new_moment()
929 }
930 }
931
932 /// Attaches a history to this `TextMut`.
933 pub(crate) fn attach_history(&mut self, history: &'t mut History) {
934 self.history = Some(history);
935 }
936
937 ////////// Selections functions
938
939 /// A mut reference to this `Text`'s [`Selections`] if they
940 /// exist.
941 pub fn selections_mut(self) -> &'t mut Selections {
942 &mut self.text.0.selections
943 }
944}
945
946impl<'t> std::ops::Deref for TextMut<'t> {
947 type Target = Text;
948
949 fn deref(&self) -> &Self::Target {
950 self.text
951 }
952}
953
954impl AsRef<Strs> for Text {
955 fn as_ref(&self) -> &Strs {
956 &self.0.buf
957 }
958}
959
960/// The Parts that make up a [`Text`].
961pub struct TextParts<'t> {
962 /// The [`Strs`] of the whole [`Text`].
963 pub strs: &'t Strs,
964 /// The [`Tags`] of the [`Text`].
965 ///
966 /// This, unlike the previous field, allows mutation in the form
967 /// of [adding] and [removing] [`Tag`]s.
968 ///
969 /// [adding]: Tags::insert
970 /// [removing]: Tags::remove
971 pub tags: Tags<'t>,
972 /// The [`Selections`] of the [`Text`].
973 ///
974 /// For most [`Widget`]s, there should be no [`Selection`], since
975 /// they are just visual.
976 ///
977 /// [`Widget`]: crate::ui::Widget
978 pub selections: &'t Selections,
979}
980
981impl<'t> TextParts<'t> {
982 /// A struct representing how many changes took place since the
983 /// creation of this `Text`
984 ///
985 /// This struct tracks all [`Change`]s and [`Tag`]
986 /// additions/removals, giving you information about wether this
987 /// `Text` has changed, when comparing this to previous
988 /// [`TextVersion`]s of the same `Text`.
989 ///
990 /// This _does_ also include things like undoing and redoing. This
991 /// is done to keep track of all changes that took place, even to
992 /// previously extant states of the text.
993 pub fn version(&self) -> TextVersion {
994 let (tags, meta_tags) = self.tags.versions();
995
996 TextVersion {
997 strs: self.strs.version(),
998 tags,
999 meta_tags,
1000 }
1001 }
1002}
1003
1004/// A representation of how many changes took place in a [`Text`].
1005///
1006/// The purpose of this struct is merely to be compared with
1007/// previously acquired instances of itself, to just quickly check if
1008/// certain properties of the `Text` have changed.
1009///
1010/// Note that this is a [`Text`] agnostic struct, comparing the
1011/// `TextVersion`s from two different `Text`s is pointless.
1012#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1013pub struct TextVersion {
1014 /// The current version of the [`Strs`].
1015 ///
1016 /// Any change to the `Strs`, even undoing, will incur a version
1017 /// increment.
1018 pub strs: u64,
1019 /// the current version of [`Tags`].
1020 ///
1021 /// Any change to the `Tags`, be it addition or removal of
1022 /// [`Tag`]s, will incur a version increment.
1023 pub tags: u64,
1024 /// The current version of meta [`Tag`]s.
1025 ///
1026 /// Meta tags are those that can change what is even shown on the
1027 /// screen, all else being equal. Any addition or removal of meta
1028 /// `Tag`s will incur a version increment.
1029 pub meta_tags: u64,
1030}
1031
1032impl TextVersion {
1033 /// Wether there have been _any_ changes to the [`Text`] since
1034 /// this previous instance.
1035 pub fn has_changed_since(&self, other: Self) -> bool {
1036 self.strs > other.strs || self.tags > other.tags || self.meta_tags > other.meta_tags
1037 }
1038
1039 /// Wether the [`Strs`] have changed since this previous instance.
1040 pub fn strs_have_changed_since(&self, other: Self) -> bool {
1041 self.strs > other.strs
1042 }
1043
1044 /// Wether the [`Tags`] have changed since this previous instance.
1045 ///
1046 /// Note that this only tracks if [`Tag`]s have been
1047 /// added/removed. So if, for example, you [replace a range] where
1048 /// no `Tag`s existed, this would return `false`, even though the
1049 /// position of `Tag`s have changed internally.
1050 ///
1051 /// [replace a range]: Text::replace_range
1052 pub fn tags_have_changed_since(&self, other: Self) -> bool {
1053 self.tags > other.tags
1054 }
1055
1056 /// Wether this [`Text`] has "structurally changed" since this
1057 /// previous instance.
1058 ///
1059 /// A `Text` has structurally changed when printing it from the
1060 /// same point could result in a different characters being
1061 /// printed. This not only happens when the [`Strs`] change, but
1062 /// also with certain [`Tag`]s, like [`Inlay`] and [`Conceal`],
1063 /// which also add and remove characters to be printed.
1064 ///
1065 /// These `Tag`s are called "meta tags" internally, since they
1066 /// change the very structure of what `Text` has been printed.
1067 pub fn has_structurally_changed_since(&self, other: Self) -> bool {
1068 self.strs > other.strs || self.meta_tags > other.meta_tags
1069 }
1070}
1071
1072////////// Standard impls
1073
1074impl Default for Text {
1075 fn default() -> Self {
1076 Self::new()
1077 }
1078}
1079
1080impl<T: ToString> From<T> for Text {
1081 fn from(value: T) -> Self {
1082 Self::from_parts(StrsBuf::new(value.to_string()), Selections::new_empty())
1083 }
1084}
1085
1086impl std::fmt::Debug for Text {
1087 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1088 f.debug_struct("Text")
1089 .field("buf", &self.0.buf)
1090 .field("tags", &self.0.tags)
1091 .finish_non_exhaustive()
1092 }
1093}
1094
1095impl Clone for Text {
1096 fn clone(&self) -> Self {
1097 let mut text = Self(self.0.clone());
1098 if text.bytes().next_back().is_none_or(|b| b != b'\n') {
1099 let end = text.end_point();
1100 text.apply_change(0, Change::str_insert("\n", end));
1101 }
1102
1103 text
1104 }
1105}
1106
1107impl Eq for Text {}
1108implPartialEq!(text: Text, other: Text,
1109 text.0.buf == other.0.buf && text.0.tags == other.0.tags
1110);
1111implPartialEq!(text: Text, other: &str, text.0.buf == *other);
1112implPartialEq!(text: Text, other: String, text.0.buf == *other);
1113implPartialEq!(str: &str, text: Text, text.0.buf == **str);
1114implPartialEq!(str: String, text: Text, text.0.buf == **str);
1115
1116impl Eq for TextMut<'_> {}
1117implPartialEq!(text_mut: TextMut<'_>, other: TextMut<'_>, text_mut.text == other.text);
1118implPartialEq!(text_mut: TextMut<'_>, other: Text, text_mut.text == other);
1119implPartialEq!(text_mut: TextMut<'_>, other: &str, text_mut.text.0.buf == *other);
1120implPartialEq!(text_mut: TextMut<'_>, other: String, text_mut.text.0.buf == *other);
1121implPartialEq!(text: Text, other: TextMut<'_>, *text == other.text);
1122implPartialEq!(str: &str, text_mut: TextMut<'_>, text_mut.text.0.buf == **str);
1123implPartialEq!(str: String, text_mut: TextMut<'_>, text_mut.text.0.buf == **str);