duat_core/text/
ops.rs

1//! Convenience operations for the [`Text`]
2//!
3//! These include the [`Point`] struct and traits that are meant to
4//! take many kinds of inputs, like the [`TwoPoints`], which is meant
5//! to interpret up to 2 [`Point`]s as a real and ghost position in
6//! the [`Text`].
7//!
8//! [`Text`]: super::Text
9use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
10
11use serde::{Deserialize, Serialize};
12
13use super::Item;
14
15/// A position in [`Text`]
16///
17/// [`Text`]: super::Text
18#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
19pub struct Point {
20    b: u32,
21    c: u32,
22    l: u32,
23}
24
25impl Point {
26    ////////// Creation of a Point
27
28    /// Returns a new [`Point`], at the first byte
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Internal function to create [`Point`]s
34    pub(super) fn from_raw(b: usize, c: usize, l: usize) -> Self {
35        let (b, c, l) = (b as u32, c as u32, l as u32);
36        Self { b, c, l }
37    }
38
39    ////////// Querying functions
40
41    /// The len [`Point`] of a [`&str`]
42    ///
43    /// This is the equivalent of [`Text::len`], but for types
44    /// other than [`Text`]
45    ///
46    /// [`&str`]: str
47    /// [`Text::len`]: super::Text::len
48    /// [`Text`]: super::Text
49    pub fn len_of(str: impl AsRef<str>) -> Self {
50        let str = str.as_ref();
51        Self {
52            b: str.len() as u32,
53            c: str.chars().count() as u32,
54            l: str.bytes().filter(|c| *c == b'\n').count() as u32,
55        }
56    }
57
58    /// Returns the byte (relative to the beginning of the file)
59    /// of self. Indexed at 0
60    pub fn byte(&self) -> usize {
61        self.b as usize
62    }
63
64    /// Returns the char index (relative to the beginning of the
65    /// file). Indexed at 0
66    pub fn char(&self) -> usize {
67        self.c as usize
68    }
69
70    /// Returns the line. Indexed at 0
71    pub fn line(&self) -> usize {
72        self.l as usize
73    }
74
75    pub fn checked_sub(self, rhs: Point) -> Option<Point> {
76        Some(Self {
77            b: self.b.checked_sub(rhs.b)?,
78            c: self.c.checked_sub(rhs.c)?,
79            l: self.l.checked_sub(rhs.l)?,
80        })
81    }
82
83    ////////// Shifting functions
84
85    /// Moves a [`Point`] forward by one character
86    pub(crate) fn fwd(self, char: char) -> Self {
87        Self {
88            b: self.b + char.len_utf8() as u32,
89            c: self.c + 1,
90            l: self.l + (char == '\n') as u32,
91        }
92    }
93
94    /// Moves a [`Point`] in reverse by one character
95    pub(crate) fn rev(self, char: char) -> Self {
96        Self {
97            b: self.b - char.len_utf8() as u32,
98            c: self.c - 1,
99            l: self.l - (char == '\n') as u32,
100        }
101    }
102
103    /// Shifts the [`Point`] by a "signed point"
104    ///
105    /// This assumes that no overflow is going to happen
106    pub(crate) fn shift_by(self, (b, c, l): (i32, i32, i32)) -> Self {
107        Self {
108            b: (self.b as i32 + b) as u32,
109            c: (self.c as i32 + c) as u32,
110            l: (self.l as i32 + l) as u32,
111        }
112    }
113}
114
115impl std::fmt::Debug for Point {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "Point {{ b: {}, c: {}, l: {} }}", self.b, self.c, self.l)
118    }
119}
120
121impl std::fmt::Display for Point {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        write!(f, "{}, {}, {}", self.b, self.c, self.l)
124    }
125}
126
127impl std::ops::Add for Point {
128    type Output = Self;
129
130    fn add(self, rhs: Self) -> Self::Output {
131        Self {
132            b: self.b + rhs.b,
133            c: self.c + rhs.c,
134            l: self.l + rhs.l,
135        }
136    }
137}
138
139impl std::ops::AddAssign for Point {
140    fn add_assign(&mut self, rhs: Self) {
141        *self = *self + rhs;
142    }
143}
144
145impl std::ops::Sub for Point {
146    type Output = Self;
147
148    fn sub(self, rhs: Self) -> Self::Output {
149        Self {
150            b: self.b - rhs.b,
151            c: self.c - rhs.c,
152            l: self.l - rhs.l,
153        }
154    }
155}
156
157impl std::ops::SubAssign for Point {
158    fn sub_assign(&mut self, rhs: Self) {
159        *self = *self - rhs;
160    }
161}
162
163/// Given a first byte, determines how many bytes are in this
164/// UTF-8 character
165#[inline]
166pub const fn utf8_char_width(b: u8) -> u32 {
167    // https://tools.ietf.org/html/rfc3629
168    const UTF8_CHAR_WIDTH: &[u8; 256] = &[
169        // 1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
170        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
171        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
172        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
173        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
174        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
175        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
176        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
177        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
178        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
179        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
180        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
181        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
182        0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C
183        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D
184        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E
185        4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F
186    ];
187
188    UTF8_CHAR_WIDTH[b as usize] as u32
189}
190
191/// Ranges that can be used to index the [`Text`]
192///
193/// These include:
194///
195/// - A single [`usize`] (interpretation varies)
196/// - All ranges that implement [`RangeBounds<usize>`]
197/// - A single [`Point`] (interpretation varies)
198/// - A 2 [`Point`] tuple
199/// - A 2 [`Option<Point>`] tuple
200/// - And a tuple of a [`Point`] and an [`Option<Point>`]
201///
202/// The behavior of [`usize`] and [`Point`] depends on where they are
203/// sent, but is pretty straightforward:
204///
205/// - On forward iterators, they act as a [`RangeFrom`]
206/// - On reverse iterators, they act as a [`RangeTo`]
207/// - On insertion of [`Tag`]s, they act as a 1 element range
208///
209/// The purpose of this trait is to minimize the number of functions
210/// needed to perform tasks.
211///
212/// [`Text`]: super::Text
213/// [`Tag`]: super::Tag
214/// [`RangeBounds<usize>`]: std::ops::RangeBounds
215pub trait TextRange {
216    /// A "forward facing range"
217    ///
218    /// If given a single [`usize`]/[`Point`], acts like [`RangeFrom`]
219    fn to_range_fwd(self, max: usize) -> Range<usize>;
220
221    /// A "reverse facing range"
222    ///
223    /// If given a single [`usize`]/[`Point`], acts like [`RangeTo`]
224    fn to_range_rev(self, max: usize) -> Range<usize>;
225
226    /// A "single point range"
227    ///
228    /// If given a single [`usize`]/[`Point`], acts like one element
229    fn to_range_at(self, max: usize) -> Range<usize>;
230}
231
232impl TextRange for usize {
233    fn to_range_fwd(self, max: usize) -> Range<usize> {
234        self.min(max)..max
235    }
236
237    fn to_range_rev(self, max: usize) -> Range<usize> {
238        max..self.min(max)
239    }
240
241    fn to_range_at(self, max: usize) -> Range<usize> {
242        self.min(max)..(self + 1).min(max)
243    }
244}
245
246impl TextRange for Range<usize> {
247    fn to_range_fwd(self, max: usize) -> Range<usize> {
248        self.start.min(max)..self.end.min(max)
249    }
250
251    fn to_range_rev(self, max: usize) -> Range<usize> {
252        self.start.min(max)..self.end.min(max)
253    }
254
255    fn to_range_at(self, max: usize) -> Range<usize> {
256        self.start.min(max)..self.end.min(max)
257    }
258}
259
260impl TextRange for RangeInclusive<usize> {
261    fn to_range_fwd(self, max: usize) -> Range<usize> {
262        max.min(*self.start())..max.min(self.end() + 1)
263    }
264
265    fn to_range_rev(self, max: usize) -> Range<usize> {
266        max.min(*self.start())..max.min(self.end() + 1)
267    }
268
269    fn to_range_at(self, max: usize) -> Range<usize> {
270        max.min(*self.start())..max.min(self.end() + 1)
271    }
272}
273
274impl TextRange for RangeTo<usize> {
275    fn to_range_fwd(self, max: usize) -> Range<usize> {
276        0..max.min(self.end)
277    }
278
279    fn to_range_rev(self, max: usize) -> Range<usize> {
280        0..max.min(self.end)
281    }
282
283    fn to_range_at(self, max: usize) -> Range<usize> {
284        0..max.min(self.end)
285    }
286}
287
288impl TextRange for RangeToInclusive<usize> {
289    fn to_range_fwd(self, max: usize) -> Range<usize> {
290        0..max.min(self.end + 1)
291    }
292
293    fn to_range_rev(self, max: usize) -> Range<usize> {
294        0..max.min(self.end + 1)
295    }
296
297    fn to_range_at(self, max: usize) -> Range<usize> {
298        0..max.min(self.end + 1)
299    }
300}
301
302impl TextRange for RangeFrom<usize> {
303    fn to_range_fwd(self, max: usize) -> Range<usize> {
304        max.min(self.start)..max
305    }
306
307    fn to_range_rev(self, max: usize) -> Range<usize> {
308        max.min(self.start)..max
309    }
310
311    fn to_range_at(self, max: usize) -> Range<usize> {
312        max.min(self.start)..max
313    }
314}
315
316impl TextRange for RangeFull {
317    fn to_range_fwd(self, max: usize) -> Range<usize> {
318        0..max
319    }
320
321    fn to_range_rev(self, max: usize) -> Range<usize> {
322        0..max
323    }
324
325    fn to_range_at(self, max: usize) -> Range<usize> {
326        0..max
327    }
328}
329
330impl TextRange for Point {
331    fn to_range_fwd(self, max: usize) -> Range<usize> {
332        self.byte().min(max)..max
333    }
334
335    fn to_range_rev(self, max: usize) -> Range<usize> {
336        0..self.byte().min(max)
337    }
338
339    fn to_range_at(self, max: usize) -> Range<usize> {
340        self.byte().min(max)..(self.byte() + 1).min(max)
341    }
342}
343
344impl TextRange for (Point, Point) {
345    fn to_range_fwd(self, max: usize) -> Range<usize> {
346        self.0.byte().min(max)..self.1.byte().min(max)
347    }
348
349    fn to_range_rev(self, max: usize) -> Range<usize> {
350        self.0.byte().min(max)..self.1.byte().min(max)
351    }
352
353    fn to_range_at(self, max: usize) -> Range<usize> {
354        self.0.byte().min(max)..self.1.byte().min(max)
355    }
356}
357
358impl TextRange for (Option<Point>, Option<Point>) {
359    fn to_range_fwd(self, max: usize) -> Range<usize> {
360        match self {
361            (None, None) => (..).to_range_fwd(max),
362            (None, Some(end)) => (..end.byte()).to_range_fwd(max),
363            (Some(start), None) => (start.byte()..).to_range_fwd(max),
364            (Some(start), Some(end)) => (start.byte()..end.byte()).to_range_fwd(max),
365        }
366    }
367
368    fn to_range_rev(self, max: usize) -> Range<usize> {
369        match self {
370            (None, None) => (..).to_range_rev(max),
371            (None, Some(end)) => (..end.byte()).to_range_rev(max),
372            (Some(start), None) => (start.byte()..).to_range_rev(max),
373            (Some(start), Some(end)) => (start.byte()..end.byte()).to_range_rev(max),
374        }
375    }
376
377    fn to_range_at(self, max: usize) -> Range<usize> {
378        match self {
379            (None, None) => (..).to_range_rev(max),
380            (None, Some(end)) => (..end.byte()).to_range_rev(max),
381            (Some(start), None) => (start.byte()..).to_range_rev(max),
382            (Some(start), Some(end)) => (start.byte()..end.byte()).to_range_rev(max),
383        }
384    }
385}
386
387impl TextRange for (Point, Option<Point>) {
388    fn to_range_fwd(self, max: usize) -> Range<usize> {
389        match self {
390            (start, None) => (start.byte()..).to_range_fwd(max),
391            (start, Some(end)) => (start.byte()..end.byte()).to_range_fwd(max),
392        }
393    }
394
395    fn to_range_rev(self, max: usize) -> Range<usize> {
396        match self {
397            (start, None) => (start.byte()..).to_range_rev(max),
398            (start, Some(end)) => (start.byte()..end.byte()).to_range_rev(max),
399        }
400    }
401
402    fn to_range_at(self, max: usize) -> Range<usize> {
403        match self {
404            (start, None) => (start.byte()..).to_range_rev(max),
405            (start, Some(end)) => (start.byte()..end.byte()).to_range_rev(max),
406        }
407    }
408}
409
410impl TextRange for (Option<Point>, Point) {
411    fn to_range_fwd(self, max: usize) -> Range<usize> {
412        match self {
413            (None, end) => (..end.byte()).to_range_fwd(max),
414            (Some(start), end) => (start.byte()..end.byte()).to_range_fwd(max),
415        }
416    }
417
418    fn to_range_rev(self, max: usize) -> Range<usize> {
419        match self {
420            (None, end) => (..end.byte()).to_range_rev(max),
421            (Some(start), end) => (start.byte()..end.byte()).to_range_rev(max),
422        }
423    }
424
425    fn to_range_at(self, max: usize) -> Range<usize> {
426        match self {
427            (None, end) => (..end.byte()).to_range_rev(max),
428            (Some(start), end) => (start.byte()..end.byte()).to_range_rev(max),
429        }
430    }
431}
432
433/// Two positions, one for the [`Text`], and one for [ghost text]
434///
435/// This can either be a [`Point`] or `(Point, Option<Point>)` or
436/// even `(Point, Point)`. If a second [`Point`] is excluded, it
437/// is assumed to be [`Point::default()`], i.e., this
438/// [`TwoPoints`] represents the beginning of a [ghost text].
439///
440/// [`Text`]: super::Text
441/// [ghost text]: super::Tag::GhostText
442pub trait TwoPoints: Clone + Copy {
443    /// Returns two [`Point`]s, for `Text` and ghosts
444    fn to_points(self) -> (Point, Option<Point>);
445}
446
447impl TwoPoints for Point {
448    fn to_points(self) -> (Point, Option<Point>) {
449        (self, None)
450    }
451}
452
453impl TwoPoints for (Point, Point) {
454    fn to_points(self) -> (Point, Option<Point>) {
455        (self.0, Some(self.1))
456    }
457}
458
459impl TwoPoints for (Point, Option<Point>) {
460    fn to_points(self) -> (Point, Option<Point>) {
461        self
462    }
463}
464
465impl TwoPoints for Item {
466    fn to_points(self) -> (Point, Option<Point>) {
467        (self.real, self.ghost)
468    }
469}