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}