Skip to main content

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);