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