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::{AsBuilderPart, 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, 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, SpawnId, 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, p: impl TextIndex, text: &Text) {
442 let b = p.to_byte_index().min(self.last_point().byte());
443 let cap = text.last_point().byte();
444
445 let added_str = text.0.bytes.strs(..cap).unwrap().to_string();
446 let point = self.point_at_byte(b);
447 let change = Change::str_insert(&added_str, point);
448 self.apply_change_inner(0, change);
449
450 self.0.tags.insert_tags(point, cap, &text.0.tags);
451 }
452
453 ////////// History functions
454
455 /// Undoes the last moment, if there was one
456 pub fn undo(&mut self) {
457 let mut history = self.0.history.take();
458
459 if let Some(history) = history.as_mut()
460 && let Some((changes, saved_moment)) = history.move_backwards()
461 {
462 self.apply_and_process_changes(changes);
463 self.0.has_changed = true;
464 self.0.has_unsaved_changes = !saved_moment;
465 }
466
467 self.0.history = history;
468 }
469
470 /// Redoes the last moment in the history, if there is one
471 pub fn redo(&mut self) {
472 let mut history = self.0.history.take();
473
474 if let Some(history) = history.as_mut()
475 && let Some((changes, saved_moment)) = history.move_forward()
476 {
477 self.apply_and_process_changes(changes);
478 self.0.has_changed = true;
479 self.0.has_unsaved_changes = !saved_moment;
480 }
481
482 self.0.history = history;
483 }
484
485 /// Finishes the current moment and adds a new one to the history
486 pub fn new_moment(&mut self) {
487 if let Some(h) = self.0.history.as_mut() {
488 h.new_moment()
489 }
490 }
491
492 fn apply_and_process_changes<'a>(
493 &mut self,
494 changes: impl ExactSizeIterator<Item = Change<'a, &'a str>>,
495 ) {
496 self.0.selections.clear();
497
498 let len = changes.len();
499 for (i, change) in changes.enumerate() {
500 self.apply_change_inner(0, change);
501
502 let start = change.start().min(self.last_point());
503 let added_end = match change.added_str().chars().next_back() {
504 Some(last) => change.added_end().rev(last),
505 None => start,
506 };
507
508 let selection = Selection::new(added_end, (start != added_end).then_some(start));
509 self.0.selections.insert(i, selection, i == len - 1);
510 }
511 }
512
513 ////////// Writing functions
514
515 /// Clones the inner [`Bytes`] as a [`String`]
516 ///
517 /// This function will also cut out a final '\n' from the string.
518 // NOTE: Inherent because I don't want this to implement Display
519 #[allow(clippy::inherent_to_string)]
520 pub fn to_string(&self) -> String {
521 let [s0, s1] = self.strs(..).unwrap().to_array();
522 if !s1.is_empty() {
523 s0.to_string() + s1.strip_suffix('\n').unwrap_or(s1)
524 } else {
525 s0.strip_suffix('\n').unwrap_or(s0).to_string()
526 }
527 }
528
529 /// Writes the contents of this `Text` to a [writer]
530 ///
531 /// [writer]: std::io::Write
532 pub fn save_on(&mut self, mut writer: impl std::io::Write) -> std::io::Result<usize> {
533 self.0.has_unsaved_changes = false;
534 if let Some(history) = &mut self.0.history {
535 history.declare_saved();
536 }
537
538 let [s0, s1] = self.0.bytes.slices(..).to_array();
539 Ok(writer.write(s0)? + writer.write(s1)?)
540 }
541
542 /// Wether or not the content has changed since the last [save]
543 ///
544 /// Returns `true` only if the actual bytes of the [`Text`] have
545 /// been changed, ignoring [`Tag`]s and all the other things,
546 /// since those are not written to the filesystem.
547 ///
548 /// [save]: Text::save_on
549 pub fn has_unsaved_changes(&self) -> bool {
550 self.0.has_unsaved_changes
551 }
552
553 ////////// Tag addition/deletion functions
554
555 /// Inserts a [`Tag`] at the given position
556 pub fn insert_tag<I, R>(&mut self, tagger: Tagger, r: I, tag: impl Tag<I, R>) -> Option<R>
557 where
558 R: Copy,
559 {
560 self.0.tags.insert(tagger, r, tag, false)
561 }
562
563 /// Like [`insert_tag`], but does it after other [`Tag`]s with the
564 /// same priority
565 ///
566 /// [`insert_tag`]: Self::insert_tag
567 pub fn insert_tag_after<I, R>(&mut self, tagger: Tagger, r: I, tag: impl Tag<I, R>) -> Option<R>
568 where
569 R: Copy,
570 {
571 self.0.tags.insert(tagger, r, tag, true)
572 }
573
574 /// Removes the [`Tag`]s of a [key] from a region
575 ///
576 /// # Caution
577 ///
578 /// While it is fine to do this on your own widgets, you should
579 /// refrain from using this function in a [`Buffer`]s [`Text`], as
580 /// it must iterate over all tags in the buffer, so if there are a
581 /// lot of other tags, this operation may be slow.
582 ///
583 /// # [`TextRange`] behavior
584 ///
585 /// If you give it a [`Point`] or [`usize`], it will be treated as
586 /// a one byte range.
587 ///
588 /// [key]: Taggers
589 /// [`Buffer`]: crate::buffer::Buffer
590 pub fn remove_tags(&mut self, taggers: impl Taggers, range: impl TextRangeOrIndex) {
591 let range = range.to_range(self.len().byte());
592 self.0.tags.remove_from(taggers, range)
593 }
594
595 /// Removes all [`Tag`]s
596 ///
597 /// Refrain from using this function on [`Buffer`]s, as there may
598 /// be other [`Tag`] providers, and you should avoid messing
599 /// with their tags.
600 ///
601 /// [`Buffer`]: crate::buffer::Buffer
602 pub fn clear_tags(&mut self) {
603 self.0.tags = InnerTags::new(self.0.bytes.len().byte());
604 }
605
606 /////////// Internal synchronization functions
607
608 /// Returns a [`Text`] without [`Selections`]
609 ///
610 /// You should use this if you want to send the [`Text`] across
611 /// threads.
612 pub fn no_selections(mut self) -> Selectionless {
613 self.0.selections.clear();
614 Selectionless(self)
615 }
616
617 /// Removes the tags for all the selections, used before they are
618 /// expected to move
619 pub(crate) fn add_selections(&mut self, area: &Area, opts: PrintOpts) {
620 let within = (self.0.selections.len() >= 500).then(|| {
621 let start = area.start_points(self, opts);
622 let end = area.end_points(self, opts);
623 (start.real, end.real)
624 });
625
626 let mut add_selection = |selection: &Selection, bytes: &mut Bytes, is_main: bool| {
627 let (caret, selection) = selection.tag_points(bytes);
628
629 let key = Tagger::for_selections();
630 let form = if is_main {
631 self.0.tags.insert(key, caret.byte(), MainCaret, false);
632 form::M_SEL_ID
633 } else {
634 self.0.tags.insert(key, caret.byte(), ExtraCaret, false);
635 form::E_SEL_ID
636 };
637
638 bytes.add_record([caret.byte(), caret.char(), caret.line()]);
639
640 if let Some(range) = selection {
641 self.0.tags.insert(key, range, form.to_tag(95), false);
642 }
643 };
644
645 if let Some((start, end)) = within {
646 for (_, selection, is_main) in self.0.selections.iter_within(start..end) {
647 add_selection(selection, &mut self.0.bytes, is_main);
648 }
649 } else {
650 for (selection, is_main) in self.0.selections.iter() {
651 add_selection(selection, &mut self.0.bytes, is_main);
652 }
653 }
654 }
655
656 /// Removes the [`Tag`]s for all [`Selection`]s
657 pub(crate) fn remove_selections(&mut self) {
658 self.remove_tags(Tagger::for_selections(), ..);
659 }
660
661 /// Prepares the `Text` for reloading, to be used on [`Buffer`]s
662 ///
663 /// [`Buffer`]: crate::buffer::Buffer
664 pub(crate) fn prepare_for_reloading(&mut self) {
665 self.clear_tags();
666 if let Some(history) = self.0.history.as_mut() {
667 history.prepare_for_reloading()
668 }
669 }
670
671 /// Functions to add all [`Widget`]s that were spawned in this
672 /// `Text`
673 ///
674 /// This function should only be called right before printing,
675 /// where it is "known" that `Widget`s can no longer get rid of
676 /// the [`SpawnTag`]s
677 ///
678 /// [`Widget`]: crate::ui::Widget
679 pub(crate) fn get_widget_spawns(
680 &mut self,
681 ) -> Vec<Box<dyn FnOnce(&mut Pass, usize, Handle<dyn Widget>) + Send>> {
682 std::mem::take(&mut self.0.tags.spawn_fns)
683 }
684
685 /////////// Iterator methods
686
687 /// A forward iterator of the [chars and tags] of the [`Text`]
688 ///
689 /// [chars and tags]: Part
690 pub fn iter_fwd(&self, at: TwoPoints) -> FwdIter<'_> {
691 FwdIter::new_at(self, at)
692 }
693
694 /// A reverse iterator of the [chars and tags] of the [`Text`]
695 ///
696 /// [chars and tags]: Part
697 pub fn iter_rev(&self, at: TwoPoints) -> RevIter<'_> {
698 RevIter::new_at(self, at)
699 }
700
701 /// A forward iterator of the [`char`]s of the [`Text`]
702 ///
703 /// Each [`char`] will be accompanied by a [`Point`], which is the
704 /// position where said character starts, e.g.
705 /// [`Point::default()`] for the first character
706 pub fn chars_fwd(
707 &self,
708 range: impl TextRange,
709 ) -> Option<impl Iterator<Item = (Point, char)> + '_> {
710 self.0.bytes.chars_fwd(range)
711 }
712
713 /// A reverse iterator of the [`char`]s of the [`Text`]
714 ///
715 /// Each [`char`] will be accompanied by a [`Point`], which is the
716 /// position where said character starts, e.g.
717 /// [`Point::default()`] for the first character
718 pub fn chars_rev(
719 &self,
720 range: impl TextRange,
721 ) -> Option<impl Iterator<Item = (Point, char)> + '_> {
722 self.0.bytes.chars_rev(range)
723 }
724
725 /// A forward iterator over the [`Tag`]s of the [`Text`]
726 ///
727 /// This iterator will consider some [`Tag`]s before `b`, since
728 /// their ranges may overlap with `b`
729 ///
730 /// # Note
731 ///
732 /// Duat works fine with [`Tag`]s in the middle of a codepoint,
733 /// but external utilizers may not, so keep that in mind.
734 pub fn tags_fwd(&self, b: usize) -> FwdTags<'_> {
735 self.0.tags.fwd_at(b)
736 }
737
738 /// An reverse iterator over the [`Tag`]s of the [`Text`]
739 ///
740 /// This iterator will consider some [`Tag`]s ahead of `b`, since
741 /// their ranges may overlap with `b`
742 ///
743 /// # Note
744 ///
745 /// Duat works fine with [`Tag`]s in the middle of a codepoint,
746 /// but external utilizers may not, so keep that in mind.
747 pub fn tags_rev(&self, b: usize) -> RevTags<'_> {
748 self.0.tags.rev_at(b)
749 }
750
751 /// A forward [`Iterator`] over the [`RawTag`]s
752 ///
753 /// This [`Iterator`] does not take into account [`Tag`] ranges
754 /// that intersect with the starting point, unlike
755 /// [`Text::tags_fwd`]
756 pub fn raw_tags_fwd(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
757 self.0.tags.raw_fwd_at(b)
758 }
759
760 /// A reverse [`Iterator`] over the [`RawTag`]s
761 ///
762 /// This [`Iterator`] does not take into account [`Tag`] ranges
763 /// that intersect with the starting point, unlike
764 /// [`Text::tags_rev`]
765 pub fn raw_tags_rev(&self, b: usize) -> impl Iterator<Item = (usize, RawTag)> {
766 self.0.tags.raw_rev_at(b)
767 }
768
769 /// The [`Selections`] printed to this `Text`, if they exist
770 pub fn selections(&self) -> &Selections {
771 &self.0.selections
772 }
773
774 /// A mut reference to this `Text`'s [`Selections`] if they
775 /// exist
776 pub fn selections_mut(&mut self) -> &mut Selections {
777 &mut self.0.selections
778 }
779
780 /// The [`History`] of [`Moment`]s in this `Text`
781 pub fn history(&self) -> Option<&History> {
782 self.0.history.as_ref()
783 }
784
785 /// A list of all [`SpawnId`]s that belong to this `Text`
786 pub fn get_spawned_ids(&self) -> impl Iterator<Item = SpawnId> {
787 self.0.tags.get_spawned_ids()
788 }
789}
790
791impl std::ops::Deref for Text {
792 type Target = Bytes;
793
794 fn deref(&self) -> &Self::Target {
795 self.bytes()
796 }
797}
798
799impl Default for Text {
800 fn default() -> Self {
801 Self::new()
802 }
803}
804
805impl std::fmt::Debug for Text {
806 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
807 f.debug_struct("Text")
808 .field("bytes", &self.0.bytes)
809 .field("tags", &self.0.tags)
810 .finish_non_exhaustive()
811 }
812}
813
814impl Clone for Text {
815 fn clone(&self) -> Self {
816 let mut text = Self(self.0.clone());
817 if text.slices(..).next_back().is_none_or(|b| b != b'\n') {
818 let end = text.len();
819 text.apply_change(None, Change::str_insert("\n", end).to_string_change());
820 }
821
822 text
823 }
824}
825
826impl From<std::io::Error> for Text {
827 fn from(value: std::io::Error) -> Self {
828 txt!("{}", value.kind().to_string())
829 }
830}
831
832impl From<Box<dyn std::error::Error>> for Text {
833 fn from(value: Box<dyn std::error::Error>) -> Self {
834 txt!("{}", value.to_string())
835 }
836}
837
838impl From<std::path::PathBuf> for Text {
839 fn from(value: std::path::PathBuf) -> Self {
840 let value = value.to_string_lossy();
841 Self::from(value)
842 }
843}
844
845impl From<&std::path::Path> for Text {
846 fn from(value: &std::path::Path) -> Self {
847 let value = value.to_string_lossy();
848 Self::from(value)
849 }
850}
851
852impl PartialEq for Text {
853 fn eq(&self, other: &Self) -> bool {
854 self.0.bytes == other.0.bytes && self.0.tags == other.0.tags
855 }
856}
857
858impl PartialEq<&str> for Text {
859 fn eq(&self, other: &&str) -> bool {
860 self.0.bytes == *other
861 }
862}
863
864impl PartialEq<String> for Text {
865 fn eq(&self, other: &String) -> bool {
866 self.0.bytes == *other
867 }
868}
869
870impl PartialEq<Text> for &str {
871 fn eq(&self, other: &Text) -> bool {
872 other.0.bytes == *self
873 }
874}
875
876impl PartialEq<Text> for String {
877 fn eq(&self, other: &Text) -> bool {
878 other.0.bytes == *self
879 }
880}
881
882/// Implements [`From<$t>`] for [`Text`]
883macro_rules! impl_from_to_string {
884 ($t:ty) => {
885 impl From<$t> for Text {
886 fn from(value: $t) -> Self {
887 let string = <$t as ToString>::to_string(&value);
888 let bytes = Bytes::new(&string);
889 Self::from_parts(bytes, Selections::new_empty(), false)
890 }
891 }
892 };
893}
894
895impl_from_to_string!(u8);
896impl_from_to_string!(u16);
897impl_from_to_string!(u32);
898impl_from_to_string!(u64);
899impl_from_to_string!(u128);
900impl_from_to_string!(usize);
901impl_from_to_string!(i8);
902impl_from_to_string!(i16);
903impl_from_to_string!(i32);
904impl_from_to_string!(i64);
905impl_from_to_string!(i128);
906impl_from_to_string!(isize);
907impl_from_to_string!(f32);
908impl_from_to_string!(f64);
909impl_from_to_string!(char);
910impl_from_to_string!(&str);
911impl_from_to_string!(String);
912impl_from_to_string!(Box<str>);
913impl_from_to_string!(Rc<str>);
914impl_from_to_string!(Arc<str>);
915impl_from_to_string!(std::borrow::Cow<'_, str>);
916
917/// A [`Text`] that is guaranteed not to have [`Selections`] in it
918///
919/// Useful for sending across threads, especially when it comes to
920/// [`Logs`], since [`Text`] doesn't implement [`Send`] because of the
921/// inner [`Selections`].
922///
923/// [`Logs`]: crate::context::Logs
924#[derive(Clone, Debug)]
925pub struct Selectionless(Text);
926
927impl Selectionless {
928 /// Consumes `self`, taking the inner [`Text`]
929 pub fn take(self) -> Text {
930 self.0
931 }
932
933 /// Gets the [`Text`] within, allowing for mutation again
934 pub fn get(&self) -> Text {
935 self.0.clone()
936 }
937
938 /// A reference to the [`Text`] within
939 pub fn text(&self) -> &Text {
940 &self.0
941 }
942}
943
944impl std::ops::Deref for Selectionless {
945 type Target = Text;
946
947 fn deref(&self) -> &Self::Target {
948 self.text()
949 }
950}
951
952impl From<Selectionless> for Text {
953 fn from(value: Selectionless) -> Self {
954 value.take()
955 }
956}
957
958// SAFETY: This struct is defined by the lack of Selections, the only
959// non Send/Sync part of a Text
960unsafe impl Send for Selectionless {}
961unsafe impl Sync for Selectionless {}
962
963impl AsRef<Bytes> for Text {
964 fn as_ref(&self) -> &Bytes {
965 self.bytes()
966 }
967}
968
969/// The Parts that make up a [`Text`]
970pub struct TextParts<'a> {
971 /// The [`Bytes`] of the [`Text`]
972 pub bytes: &'a Bytes,
973 /// The [`Tags`] of the [`Text`]
974 ///
975 /// This, unlike [`Bytes`], allows mutation in the form of
976 /// [adding] and [removing] [`Tag`]s.
977 ///
978 /// [adding]: Tags::insert
979 /// [removing]: Tags::remove
980 pub tags: Tags<'a>,
981 /// The [`Selections`] of the [`Text`]
982 ///
983 /// For most [`Widget`]s, there should be no [`Selection`], since
984 /// they are just visual.
985 ///
986 /// [`Widget`]: crate::ui::Widget
987 pub selections: &'a Selections,
988}