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}