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