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