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//! - [Left]/[right]/[center] alignment of output (although that is
18//!   implemented by the [Ui]).
19//! - [Spacers] for even more advanced alignment (also implemented by
20//!   the [Ui]).
21//! - The ability to [undo]/[redo] changes in the history.
22//! - In the future, button ranges that can interact with the mouse.
23//!
24//! The [`Text`] struct is created in two different ways:
25//!
26//! - By calling [`Text::new`] or one of its [`From`] implementations;
27//! - By building it with the [`txt!`] macro;
28//!
29//! The first method is recommended if you want a [`Text`] that will
30//! be modified by input. This is often the case if your [`Widget`] is
31//! some sort of text box, chief of which is the [`Buffer`], which is
32//! the central `Widget` of every text editor.
33//!
34//! The second method is what should be used most of the time, as it
35//! lets you quickly create formatted [`Widget`]s/[`StatusLine`] parts
36//! in a very modular way:
37//!
38//! ```rust
39//! # duat_core::doc_duat!(duat);
40//! use duat::prelude::*;
41//!
42//! fn number_of_horses(count: usize) -> Text {
43//!     if count == 1 {
44//!         txt!("[horses.count]1[horses] horse")
45//!     } else {
46//!         txt!("[horses.count]{}[horses] horses", count)
47//!     }
48//! }
49//!
50//! fn inlined_number_of_horses(count: usize) -> Text {
51//!     txt!(
52//!         "[horses.count]{count} [horses]{}",
53//!         if count == 1 { "horse" } else { "horses" }
54//!     )
55//! }
56//! ```
57//!
58//! You can use this whenever you need to update a widget, for
59//! example, just create a new [`Text`] to printed to the screen.
60//!
61//! However, when recreating the entire [`Text`] with a [`txt!`]
62//! macro would be too expensive, you can use [`Text`] modifying
63//! functions:
64//!
65//! ```rust
66//! # duat_core::form::set_initial(duat_core::form::get_initial());
67//! # duat_core::doc_duat!(duat);
68//! use duat::prelude::*;
69//!
70//! let mut prompted = txt!("[prompt]type a key: ");
71//! let end = prompted.len();
72//! prompted.replace_range(end..end, "a")
73//! ```
74//!
75//! A general rule of thumb for "too expensive" is this: if your
76//! [`Text`] can't scroll more than a few lines, it is not too
77//! expensive to rebuild. This way of editing the [`Text`] is mostly
78//! used on the [`Buffer`] widget and other textbox-like [`Widget`]s.
79//!
80//! [Left]: AlignLeft
81//! [right]: AlignRight
82//! [center]: AlignCenter
83//! [Spacers]: Spacer
84//! [undo]: Text::undo
85//! [redo]: Text::redo
86//! [gap buffers]: gapbuf::GapBuffer
87//! [colored]: crate::form::Form
88//! [ghost text]: Ghost
89//! [Ui]: crate::ui::traits::RawUi
90//! [`Buffer`]: crate::buffer::Buffer
91//! [`Widget`]: crate::ui::Widget
92//! [`StatusLine`]: https://docs.rs/duat/latest/duat/widgets/struct.StatusLine.html
93//! [`Mode`]: crate::mode::Mode
94use std::{rc::Rc, sync::Arc};
95
96pub(crate) use self::history::MomentFetcher;
97use self::tags::{FwdTags, InnerTags, RevTags};
98pub use self::{
99    builder::{Builder, BuilderPart, txt},
100    bytes::{Bytes, Lines, Slices, Strs},
101    history::{Change, History, Moment},
102    iter::{FwdIter, Item, Part, RevIter},
103    ops::{Point, TextIndex, TextRange, TextRangeOrIndex, TwoPoints, utf8_char_width},
104    search::{Matcheable, RegexPattern, Searcher},
105    tags::{
106        AlignCenter, AlignLeft, AlignRight, Conceal, ExtraCaret, FormTag, Ghost, GhostId,
107        MainCaret, RawTag, Spacer, SpawnId, SpawnTag, Tag, Tagger, Taggers, Tags, ToggleId,
108    },
109};
110use crate::{
111    context::Handle,
112    data::Pass,
113    form,
114    mode::{Selection, Selections},
115    opts::PrintOpts,
116    ui::{Area, Widget},
117};
118
119mod builder;
120mod bytes;
121mod history;
122mod iter;
123mod ops;
124mod records;
125mod search;
126mod shift_list;
127mod tags;
128
129/// The text of a given [`Widget`]
130///
131/// The [`Text`] is the backbone of Duat. It is the thing responsible
132/// for everything that shows up on screen.
133///
134/// You can build a [`Text`] manually, by using [`Text::new`], or with
135/// some convenience, by using the [`txt!`] macro, making use of a
136/// [`Builder`].
137///
138/// [`Widget`]: crate::ui::Widget
139pub struct Text(Box<InnerText>);
140
141#[derive(Clone)]
142struct InnerText {
143    bytes: Bytes,
144    tags: InnerTags,
145    selections: Selections,
146    // Specific to Buffers
147    history: Option<History>,
148    has_changed: bool,
149    has_unsaved_changes: bool,
150}
151
152impl Text {
153    ////////// Creation and Destruction of Text
154
155    /// Returns a new empty [`Text`]
156    pub fn new() -> Self {
157        Self::from_parts(Bytes::default(), Selections::new_empty(), false)
158    }
159
160    /// Returns a new empty [`Text`] with [`Selections`] enabled
161    pub fn with_default_main_selection() -> Self {
162        Self::from_parts(
163            Bytes::default(),
164            Selections::new(Selection::default()),
165            false,
166        )
167    }
168
169    /// Returns a new empty [`Text`] with history enabled
170    pub(crate) fn new_with_history() -> Self {
171        Self::from_parts(
172            Bytes::default(),
173            Selections::new(Selection::default()),
174            true,
175        )
176    }
177
178    /// Creates a [`Text`] from [`Bytes`]
179    pub(crate) fn from_parts(
180        mut bytes: Bytes,
181        mut selections: Selections,
182        with_history: bool,
183    ) -> Self {
184        if bytes.slices(..).next_back().is_none_or(|b| b != b'\n') {
185            let end = bytes.len();
186            bytes.apply_change(Change::str_insert("\n", end));
187        }
188        let tags = InnerTags::new(bytes.len().byte());
189
190        let selections = if selections.iter().any(|(sel, _)| {
191            [Some(sel.caret()), sel.anchor()]
192                .into_iter()
193                .flatten()
194                .any(|p| p >= bytes.len())
195        }) {
196            Selections::new(Selection::default())
197        } else {
198            selections.correct_all(&bytes);
199            selections
200        };
201
202        Self(Box::new(InnerText {
203            bytes,
204            tags,
205            selections,
206            history: with_history.then(History::default),
207            has_changed: false,
208            has_unsaved_changes: false,
209        }))
210    }
211
212    /// Returns a [`Builder`] for [`Text`]
213    ///
214    /// This builder can be used to iteratively create text, by
215    /// assuming that the user wants no* [`Tag`] overlap, and that
216    /// they want to construct the [`Text`] in [`Tag`]/content pairs.
217    ///
218    /// ```rust
219    /// # duat_core::doc_duat!(duat);
220    /// use duat::prelude::*;
221    /// let mut builder = Text::builder();
222    /// ```
223    pub fn builder() -> Builder {
224        Builder::new()
225    }
226
227    ////////// Querying functions
228
229    /// Whether the [`Bytes`] and `InnerTags` are empty
230    ///
231    /// This ignores the last `'\n'` in the [`Text`], since it is
232    /// always there no matter what.
233    ///
234    /// If you only want to check for the [`Bytes`], ignoring possible
235    /// [`Ghost`]s, see [`is_empty`].
236    ///
237    /// [`is_empty`]: Bytes::is_empty
238    pub fn is_empty_empty(&self) -> bool {
239        self.0.bytes.is_empty() && self.0.tags.is_empty()
240    }
241
242    /// The inner bytes of the [`Text`]
243    ///
244    /// Note that, since [`Text`] has an implementation of
245    /// [`std::ops::Deref<Target = Bytes>`], you mostly don't need
246    /// to call this method.
247    ///
248    /// [regex searching]: Bytes::search_fwd
249    pub fn bytes(&self) -> &Bytes {
250        &self.0.bytes
251    }
252
253    /// The parts that make up a [`Text`]
254    ///
255    /// This function is used when you want to [insert]/[remove]
256    /// [`Tag`]s (i.e., borrow the inner `InnerTags` mutably via
257    /// [`Tags`]), while still being able to read from the
258    /// [`Bytes`] and [`Selections`].
259    ///
260    /// [insert]: Tags::insert
261    /// [remove]: Tags::remove
262    /// [`&mut Bytes`]: Bytes
263    pub fn parts(&mut self) -> TextParts<'_> {
264        TextParts {
265            bytes: &self.0.bytes,
266            tags: Tags(&mut self.0.tags),
267            selections: &self.0.selections,
268        }
269    }
270
271    /// Gets the indentation level on the current line
272    pub fn indent(&self, p: Point, area: &Area, opts: PrintOpts) -> usize {
273        let range = self.line_range(p.line());
274
275        area.print_iter(self, range.start.to_two_points_after(), opts)
276            .filter_map(|(caret, item)| Some(caret).zip(item.part.as_char()))
277            .find(|(_, char)| !char.is_whitespace() || *char == '\n')
278            .map(|(caret, _)| caret.x as usize)
279            .unwrap_or(0)
280    }
281
282    ////////// Tag related query functions
283
284    /// The maximum [points] in the `at`th byte
285    ///
286    /// This point is essentially the [point] at that byte, plus the
287    /// last possible [`Point`] of any [`Ghost`]s in that
288    /// position.
289    ///
290    /// [points]: TwoPoints
291    /// [point]: Bytes::point_at_byte
292    #[inline(always)]
293    pub fn ghost_max_points_at(&self, b: usize) -> TwoPoints {
294        let point = self.point_at_byte(b);
295        if let Some(total_ghost) = self.0.tags.ghosts_total_at(point.byte()) {
296            TwoPoints::new(point, total_ghost)
297        } else {
298            TwoPoints::new_after_ghost(point)
299        }
300    }
301
302    /// The [points] at the end of the text
303    ///
304    /// This will essentially return the [last point] of the text,
305    /// alongside the last possible [`Point`] of any
306    /// [`Ghost`] at the end of the text.
307    ///
308    /// [points]: TwoPoints
309    /// [last point]: Bytes::len
310    pub fn len_points(&self) -> TwoPoints {
311        self.ghost_max_points_at(self.len().byte())
312    }
313
314    /// Points visually after the [`TwoPoints`]
315    ///
316    /// If the [`TwoPoints`] in question is concealed, treats the
317    /// next visible character as the first character, and returns
318    /// the points of the next visible character.
319    ///
320    /// This method is useful if you want to iterator reversibly
321    /// right after a certain point, thus including the character
322    /// of said point.
323    pub fn points_after(&self, tp: TwoPoints) -> Option<TwoPoints> {
324        self.iter_fwd(tp)
325            .filter_map(|item| item.part.as_char().map(|_| item.points()))
326            .chain([self.len_points()])
327            .nth(1)
328    }
329
330    /// The visual start of the line
331    ///
332    /// This point is defined not by where the line actually begins,
333    /// but by where the last '\n' was located. For example, if
334    /// [`Tag`]s create ghost text or omit text from multiple
335    /// different lines, this point may differ from where in the
336    /// [`Text`] the real line actually begins.
337    ///
338    /// The `skip` value is how many `\n` should be skipped before
339    /// returning.
340    pub fn visual_line_start(&self, mut points: TwoPoints, skip: usize) -> TwoPoints {
341        let mut iter = self.iter_rev(points).peekable();
342        let mut total_seen = 0;
343        while let Some(peek) = iter.peek() {
344            match peek.part {
345                Part::Char('\n') => {
346                    if total_seen == skip {
347                        return points;
348                    } else {
349                        total_seen += 1;
350                    }
351                }
352                Part::Char(_) => points = iter.next().unwrap().points(),
353                _ => drop(iter.next()),
354            }
355        }
356
357        points
358    }
359
360    /// Gets the [`Ghost`] of a given [`GhostId`]
361    pub fn get_ghost(&self, id: GhostId) -> Option<&Text> {
362        self.0.tags.get_ghost(id)
363    }
364
365    ////////// Modification functions
366
367    /// Replaces a [range] in the [`Text`]
368    ///
369    /// # [`TextRange`] behavior:
370    ///
371    /// If you give a single [`usize`]/[`Point`], it will be
372    /// interpreted as a range from.
373    ///
374    /// [range]: TextRange
375    pub fn replace_range(&mut self, range: impl TextRange, edit: impl ToString) {
376        let range = range.to_range(self.len().byte());
377        let (start, end) = (
378            self.point_at_byte(range.start),
379            self.point_at_byte(range.end),
380        );
381        let change = Change::new(edit, start..end, self);
382
383        self.0.has_changed = true;
384        self.apply_change_inner(0, change.as_ref());
385        self.0
386            .history
387            .as_mut()
388            .map(|h| h.apply_change(None, change));
389    }
390
391    pub(crate) fn apply_change(
392        &mut self,
393        guess_i: Option<usize>,
394        change: Change<'static, String>,
395    ) -> (Option<usize>, usize) {
396        self.0.has_changed = true;
397
398        let selections_taken = self.apply_change_inner(guess_i.unwrap_or(0), change.as_ref());
399        let history = self.0.history.as_mut();
400        let insertion_i = history.map(|h| h.apply_change(guess_i, change));
401        (insertion_i, selections_taken)
402    }
403
404    /// Merges `String`s with the body of text, given a range to
405    /// replace
406    fn apply_change_inner(&mut self, guess_i: usize, change: Change<&str>) -> usize {
407        self.0.bytes.apply_change(change);
408        self.0.tags.transform(
409            change.start().byte()..change.taken_end().byte(),
410            change.added_end().byte(),
411        );
412
413        self.0.has_unsaved_changes = true;
414        self.0.selections.apply_change(guess_i, change)
415    }
416
417    fn without_last_nl(mut self) -> Self {
418        if let Some((_, '\n')) = { self.chars_rev(..).unwrap().next() } {
419            let change = Change::remove_last_nl(self.len());
420            self.apply_change_inner(0, change);
421        }
422        self
423    }
424
425    /// Updates bounds, so that [`Tag`] ranges can visibly cross the
426    /// screen
427    ///
428    /// This is used in order to allow for very long [`Tag`] ranges
429    /// (say, a [`Form`] being applied on the range `3..999`) to show
430    /// up properly without having to lookback a bazillion [`Tag`]s
431    /// which could be in the way.
432    ///
433    /// [`Form`]: crate::form::Form
434    pub(crate) fn update_bounds(&mut self) {
435        self.0.tags.update_bounds();
436    }
437
438    /// Inserts a [`Text`] into this `Text`, in a specific [`Point`]
439    pub fn insert_text(&mut self, mut p: Point, mut text: Text) {
440        text = text.without_last_nl();
441        p = p.min(self.last_point());
442
443        let added_str = text.0.bytes.strs(..).unwrap().to_string();
444        let change = Change::str_insert(&added_str, p);
445        self.apply_change_inner(0, change);
446
447        self.0.tags.insert_tags(p, text.0.tags);
448    }
449
450    ////////// History functions
451
452    /// Undoes the last moment, if there was one
453    pub fn undo(&mut self) {
454        let mut history = self.0.history.take();
455
456        if let Some(history) = history.as_mut()
457            && let Some((changes, saved_moment)) = history.move_backwards()
458        {
459            self.apply_and_process_changes(changes);
460            self.0.has_changed = true;
461            self.0.has_unsaved_changes = !saved_moment;
462        }
463
464        self.0.history = history;
465    }
466
467    /// Redoes the last moment in the history, if there is one
468    pub fn redo(&mut self) {
469        let mut history = self.0.history.take();
470
471        if let Some(history) = history.as_mut()
472            && let Some((changes, saved_moment)) = history.move_forward()
473        {
474            self.apply_and_process_changes(changes);
475            self.0.has_changed = true;
476            self.0.has_unsaved_changes = !saved_moment;
477        }
478
479        self.0.history = history;
480    }
481
482    /// Finishes the current moment and adds a new one to the history
483    pub fn new_moment(&mut self) {
484        if let Some(h) = self.0.history.as_mut() {
485            h.new_moment()
486        }
487    }
488
489    fn apply_and_process_changes<'a>(
490        &mut self,
491        changes: impl ExactSizeIterator<Item = Change<'a, &'a str>>,
492    ) {
493        self.0.selections.clear();
494
495        let len = changes.len();
496        for (i, change) in changes.enumerate() {
497            self.apply_change_inner(0, change);
498
499            let start = change.start().min(self.last_point());
500            let added_end = match change.added_str().chars().next_back() {
501                Some(last) => change.added_end().rev(last),
502                None => start,
503            };
504
505            let selection = Selection::new(added_end, (start != added_end).then_some(start));
506            self.0.selections.insert(i, selection, i == len - 1);
507        }
508    }
509
510    ////////// Writing functions
511
512    /// Clones the inner [`Bytes`] as a [`String`]
513    ///
514    /// This function will also cut out a final '\n' from the string.
515    // NOTE: Inherent because I don't want this to implement Display
516    #[allow(clippy::inherent_to_string)]
517    pub fn to_string(&self) -> String {
518        let [s0, s1] = self.strs(..).unwrap().to_array();
519        if !s1.is_empty() {
520            s0.to_string() + s1.strip_suffix('\n').unwrap_or(s1)
521        } else {
522            s0.strip_suffix('\n').unwrap_or(s0).to_string()
523        }
524    }
525
526    /// Writes the contents of this `Text` to a [writer]
527    ///
528    /// [writer]: std::io::Write
529    pub fn save_on(&mut self, mut writer: impl std::io::Write) -> std::io::Result<usize> {
530        self.0.has_unsaved_changes = false;
531        if let Some(history) = &mut self.0.history {
532            history.declare_saved();
533        }
534
535        let [s0, s1] = self.0.bytes.slices(..).to_array();
536        Ok(writer.write(s0)? + writer.write(s1)?)
537    }
538
539    /// Wether or not the content has changed since the last [save]
540    ///
541    /// Returns `true` only if the actual bytes of the [`Text`] have
542    /// been changed, ignoring [`Tag`]s and all the other things,
543    /// since those are not written to the filesystem.
544    ///
545    /// [save]: Text::save_on
546    pub fn has_unsaved_changes(&self) -> bool {
547        self.0.has_unsaved_changes
548    }
549
550    ////////// Tag addition/deletion functions
551
552    /// Inserts a [`Tag`] at the given position
553    pub fn insert_tag<I, R>(&mut self, tagger: Tagger, r: I, tag: impl Tag<I, R>) -> Option<R>
554    where
555        R: Copy,
556    {
557        self.0.tags.insert(tagger, r, tag, false)
558    }
559
560    /// Like [`insert_tag`], but does it after other [`Tag`]s with the
561    /// same priority
562    ///
563    /// [`insert_tag`]: Self::insert_tag
564    pub fn insert_tag_after<I, R>(&mut self, tagger: Tagger, r: I, tag: impl Tag<I, R>) -> Option<R>
565    where
566        R: Copy,
567    {
568        self.0.tags.insert(tagger, r, tag, true)
569    }
570
571    /// Removes the [`Tag`]s of a [key] from a region
572    ///
573    /// # Caution
574    ///
575    /// While it is fine to do this on your own widgets, you should
576    /// refrain from using this function in a [`Buffer`]s [`Text`], as
577    /// it must iterate over all tags in the buffer, so if there are a
578    /// lot of other tags, this operation may be slow.
579    ///
580    /// # [`TextRange`] behavior
581    ///
582    /// If you give it a [`Point`] or [`usize`], it will be treated as
583    /// a one byte range.
584    ///
585    /// [key]: Taggers
586    /// [`Buffer`]: crate::buffer::Buffer
587    pub fn remove_tags(&mut self, taggers: impl Taggers, range: impl TextRangeOrIndex) {
588        let range = range.to_range(self.len().byte());
589        self.0.tags.remove_from(taggers, range)
590    }
591
592    /// Removes all [`Tag`]s
593    ///
594    /// Refrain from using this function on [`Buffer`]s, as there may
595    /// be other [`Tag`] providers, and you should avoid messing
596    /// with their tags.
597    ///
598    /// [`Buffer`]: crate::buffer::Buffer
599    pub fn clear_tags(&mut self) {
600        self.0.tags = InnerTags::new(self.0.bytes.len().byte());
601    }
602
603    /////////// Internal synchronization functions
604
605    /// Returns a [`Text`] without [`Selections`]
606    ///
607    /// You should use this if you want to send the [`Text`] across
608    /// threads.
609    pub fn no_selections(mut self) -> Selectionless {
610        self.0.selections.clear();
611        Selectionless(self)
612    }
613
614    /// Removes the tags for all the selections, used before they are
615    /// expected to move
616    pub(crate) fn add_selections(&mut self, area: &Area, opts: PrintOpts) {
617        let within = (self.0.selections.len() >= 500).then(|| {
618            let start = area.start_points(self, opts);
619            let end = area.end_points(self, opts);
620            (start.real, end.real)
621        });
622
623        let mut add_selection = |selection: &Selection, bytes: &mut Bytes, is_main: bool| {
624            let (caret, selection) = selection.tag_points(bytes);
625
626            let key = Tagger::for_selections();
627            let form = if is_main {
628                self.0.tags.insert(key, caret.byte(), MainCaret, false);
629                form::M_SEL_ID
630            } else {
631                self.0.tags.insert(key, caret.byte(), ExtraCaret, false);
632                form::E_SEL_ID
633            };
634
635            bytes.add_record([caret.byte(), caret.char(), caret.line()]);
636
637            if let Some(range) = selection {
638                self.0.tags.insert(key, range, form.to_tag(95), false);
639            }
640        };
641
642        if let Some((start, end)) = within {
643            for (_, selection, is_main) in self.0.selections.iter_within(start..end) {
644                add_selection(selection, &mut self.0.bytes, is_main);
645            }
646        } else {
647            for (selection, is_main) in self.0.selections.iter() {
648                add_selection(selection, &mut self.0.bytes, is_main);
649            }
650        }
651    }
652
653    /// Removes the [`Tag`]s for all [`Selection`]s
654    pub(crate) fn remove_selections(&mut self) {
655        self.remove_tags(Tagger::for_selections(), ..);
656    }
657
658    /// Prepares the `Text` for reloading, to be used on [`Buffer`]s
659    ///
660    /// [`Buffer`]: crate::buffer::Buffer
661    pub(crate) fn prepare_for_reloading(&mut self) {
662        self.clear_tags();
663        if let Some(history) = self.0.history.as_mut() {
664            history.prepare_for_reloading()
665        }
666    }
667
668    /// Functions to add  all [`Widget`]s that were spawned in this
669    /// `Text`
670    ///
671    /// This function should only be called right before printing,
672    /// where it is "known" that `Widget`s can no longer get rid of
673    /// the [`SpawnTag`]s
674    ///
675    /// [`Widget`]: crate::ui::Widget
676    pub(crate) fn get_widget_spawns(
677        &mut self,
678    ) -> Vec<Box<dyn FnOnce(&mut Pass, usize, Handle<dyn Widget>) + Send>> {
679        std::mem::take(&mut self.0.tags.spawn_fns)
680    }
681
682    /////////// Iterator methods
683
684    /// A forward iterator of the [chars and tags] of the [`Text`]
685    ///
686    /// [chars and tags]: Part
687    pub fn iter_fwd(&self, at: TwoPoints) -> FwdIter<'_> {
688        FwdIter::new_at(self, at)
689    }
690
691    /// A reverse iterator of the [chars and tags] of the [`Text`]
692    ///
693    /// [chars and tags]: Part
694    pub fn iter_rev(&self, at: TwoPoints) -> RevIter<'_> {
695        RevIter::new_at(self, at)
696    }
697
698    /// A forward iterator of the [`char`]s of the [`Text`]
699    ///
700    /// Each [`char`] will be accompanied by a [`Point`], which is the
701    /// position where said character starts, e.g.
702    /// [`Point::default()`] for the first character
703    pub fn chars_fwd(
704        &self,
705        range: impl TextRange,
706    ) -> Option<impl Iterator<Item = (Point, char)> + '_> {
707        self.0.bytes.chars_fwd(range)
708    }
709
710    /// A reverse iterator of the [`char`]s of the [`Text`]
711    ///
712    /// Each [`char`] will be accompanied by a [`Point`], which is the
713    /// position where said character starts, e.g.
714    /// [`Point::default()`] for the first character
715    pub fn chars_rev(
716        &self,
717        range: impl TextRange,
718    ) -> Option<impl Iterator<Item = (Point, char)> + '_> {
719        self.0.bytes.chars_rev(range)
720    }
721
722    /// A forward iterator over the [`Tag`]s of the [`Text`]
723    ///
724    /// This iterator will consider some [`Tag`]s before `b`, since
725    /// their ranges may overlap with `b`
726    ///
727    /// # Note
728    ///
729    /// Duat works fine with [`Tag`]s in the middle of a codepoint,
730    /// but external utilizers may not, so keep that in mind.
731    pub fn tags_fwd(&self, b: usize) -> FwdTags<'_> {
732        self.0.tags.fwd_at(b)
733    }
734
735    /// An reverse iterator over the [`Tag`]s of the [`Text`]
736    ///
737    /// This iterator will consider some [`Tag`]s ahead of `b`, since
738    /// their ranges may overlap with `b`
739    ///
740    /// # Note
741    ///
742    /// Duat works fine with [`Tag`]s in the middle of a codepoint,
743    /// but external utilizers may not, so keep that in mind.
744    pub fn tags_rev(&self, b: usize) -> RevTags<'_> {
745        self.0.tags.rev_at(b)
746    }
747
748    /// A forward [`Iterator`] over the [`RawTag`]s
749    ///
750    /// This [`Iterator`] does not take into account [`Tag`] ranges
751    /// that intersect with the starting point, unlike
752    /// [`Text::tags_fwd`]
753    pub fn raw_tags_fwd(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
754        self.0.tags.raw_fwd_at(b)
755    }
756
757    /// A reverse [`Iterator`] over the [`RawTag`]s
758    ///
759    /// This [`Iterator`] does not take into account [`Tag`] ranges
760    /// that intersect with the starting point, unlike
761    /// [`Text::tags_rev`]
762    pub fn raw_tags_rev(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
763        self.0.tags.raw_rev_at(b)
764    }
765
766    /// The [`Selections`] printed to this `Text`, if they exist
767    pub fn selections(&self) -> &Selections {
768        &self.0.selections
769    }
770
771    /// A mut reference to this `Text`'s [`Selections`] if they
772    /// exist
773    pub fn selections_mut(&mut self) -> &mut Selections {
774        &mut self.0.selections
775    }
776
777    /// The [`History`] of [`Moment`]s in this `Text`
778    pub fn history(&self) -> Option<&History> {
779        self.0.history.as_ref()
780    }
781
782    /// A list of all [`SpawnId`]s that belong to this `Text`
783    pub fn get_spawned_ids(&self) -> impl Iterator<Item = SpawnId> {
784        self.0.tags.get_spawned_ids()
785    }
786}
787
788impl std::ops::Deref for Text {
789    type Target = Bytes;
790
791    fn deref(&self) -> &Self::Target {
792        self.bytes()
793    }
794}
795
796impl Default for Text {
797    fn default() -> Self {
798        Self::new()
799    }
800}
801
802impl std::fmt::Debug for Text {
803    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
804        f.debug_struct("Text")
805            .field("bytes", &self.0.bytes)
806            .field("tags", &self.0.tags)
807            .finish_non_exhaustive()
808    }
809}
810
811impl Clone for Text {
812    fn clone(&self) -> Self {
813        let mut text = Self(self.0.clone());
814        if text.slices(..).next_back().is_none_or(|b| b != b'\n') {
815            let end = text.len();
816            text.apply_change(None, Change::str_insert("\n", end).to_string_change());
817        }
818
819        text
820    }
821}
822
823impl From<std::io::Error> for Text {
824    fn from(value: std::io::Error) -> Self {
825        txt!("{}", value.kind().to_string())
826    }
827}
828
829impl From<Box<dyn std::error::Error>> for Text {
830    fn from(value: Box<dyn std::error::Error>) -> Self {
831        txt!("{}", value.to_string())
832    }
833}
834
835impl From<std::path::PathBuf> for Text {
836    fn from(value: std::path::PathBuf) -> Self {
837        let value = value.to_string_lossy();
838        Self::from(value)
839    }
840}
841
842impl From<&std::path::Path> for Text {
843    fn from(value: &std::path::Path) -> Self {
844        let value = value.to_string_lossy();
845        Self::from(value)
846    }
847}
848
849impl PartialEq for Text {
850    fn eq(&self, other: &Self) -> bool {
851        self.0.bytes == other.0.bytes && self.0.tags == other.0.tags
852    }
853}
854
855impl PartialEq<&str> for Text {
856    fn eq(&self, other: &&str) -> bool {
857        self.0.bytes == *other
858    }
859}
860
861impl PartialEq<String> for Text {
862    fn eq(&self, other: &String) -> bool {
863        self.0.bytes == *other
864    }
865}
866
867impl PartialEq<Text> for &str {
868    fn eq(&self, other: &Text) -> bool {
869        other.0.bytes == *self
870    }
871}
872
873impl PartialEq<Text> for String {
874    fn eq(&self, other: &Text) -> bool {
875        other.0.bytes == *self
876    }
877}
878
879impl_from_to_string!(u8);
880impl_from_to_string!(u16);
881impl_from_to_string!(u32);
882impl_from_to_string!(u64);
883impl_from_to_string!(u128);
884impl_from_to_string!(usize);
885impl_from_to_string!(i8);
886impl_from_to_string!(i16);
887impl_from_to_string!(i32);
888impl_from_to_string!(i64);
889impl_from_to_string!(i128);
890impl_from_to_string!(isize);
891impl_from_to_string!(f32);
892impl_from_to_string!(f64);
893impl_from_to_string!(char);
894impl_from_to_string!(&str);
895impl_from_to_string!(String);
896impl_from_to_string!(Box<str>);
897impl_from_to_string!(Rc<str>);
898impl_from_to_string!(Arc<str>);
899impl_from_to_string!(std::borrow::Cow<'_, str>);
900
901/// Implements [`From<$t>`] for [`Text`]
902macro impl_from_to_string($t:ty) {
903    impl From<$t> for Text {
904        fn from(value: $t) -> Self {
905            let string = <$t as ToString>::to_string(&value);
906            let bytes = Bytes::new(&string);
907            Self::from_parts(bytes, Selections::new_empty(), false)
908        }
909    }
910}
911
912/// A [`Text`] that is guaranteed not to have [`Selections`] in it
913///
914/// Useful for sending across threads, especially when it comes to
915/// [`Logs`].
916///
917/// [`Logs`]: crate::context::Logs
918#[derive(Clone, Debug)]
919pub struct Selectionless(Text);
920
921impl Selectionless {
922    /// Gets the [`Text`] within, allowing for mutation again
923    pub fn get(&self) -> Text {
924        self.0.clone()
925    }
926}
927
928impl std::ops::Deref for Selectionless {
929    type Target = Text;
930
931    fn deref(&self) -> &Self::Target {
932        &self.0
933    }
934}
935
936impl From<Selectionless> for Text {
937    fn from(value: Selectionless) -> Self {
938        value.0
939    }
940}
941
942// SAFETY: This struct is defined by the lack of Selections, the only
943// non Send/Sync part of a Text
944unsafe impl Send for Selectionless {}
945unsafe impl Sync for Selectionless {}
946
947impl AsRef<Bytes> for Text {
948    fn as_ref(&self) -> &Bytes {
949        self.bytes()
950    }
951}
952
953/// The Parts that make up a [`Text`]
954pub struct TextParts<'a> {
955    /// The [`Bytes`] of the [`Text`]
956    pub bytes: &'a Bytes,
957    /// The [`Tags`] of the [`Text`]
958    ///
959    /// This, unlike [`Bytes`], allows mutation in the form of
960    /// [adding] and [removing] [`Tag`]s.
961    ///
962    /// [adding]: Tags::insert
963    /// [removing]: Tags::remove
964    pub tags: Tags<'a>,
965    /// The [`Selections`] of the [`Text`]
966    ///
967    /// For most [`Widget`]s, there should be no [`Selection`], since
968    /// they are just visual.
969    ///
970    /// [`Widget`]: crate::ui::Widget
971    pub selections: &'a Selections,
972}