text_size/
range.rs

1use cmp::Ordering;
2
3use {
4    crate::TextSize,
5    std::{
6        cmp, fmt,
7        ops::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign},
8    },
9};
10
11/// A range in text, represented as a pair of [`TextSize`][struct@TextSize].
12///
13/// It is a logic error for `start` to be greater than `end`.
14#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
15pub struct TextRange {
16    // Invariant: start <= end
17    start: TextSize,
18    end: TextSize,
19}
20
21impl fmt::Debug for TextRange {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(f, "{}..{}", self.start().raw, self.end().raw)
24    }
25}
26
27impl TextRange {
28    /// Creates a new `TextRange` with the given `start` and `end` (`start..end`).
29    ///
30    /// # Panics
31    ///
32    /// Panics if `end < start`.
33    ///
34    /// # Examples
35    ///
36    /// ```rust
37    /// # use text_size::*;
38    /// let start = TextSize::from(5);
39    /// let end = TextSize::from(10);
40    /// let range = TextRange::new(start, end);
41    ///
42    /// assert_eq!(range.start(), start);
43    /// assert_eq!(range.end(), end);
44    /// assert_eq!(range.len(), end - start);
45    /// ```
46    #[inline]
47    pub const fn new(start: TextSize, end: TextSize) -> TextRange {
48        assert!(start.raw <= end.raw);
49        TextRange { start, end }
50    }
51
52    /// Create a new `TextRange` with the given `offset` and `len` (`offset..offset + len`).
53    ///
54    /// # Examples
55    ///
56    /// ```rust
57    /// # use text_size::*;
58    /// let text = "0123456789";
59    ///
60    /// let offset = TextSize::from(2);
61    /// let length = TextSize::from(5);
62    /// let range = TextRange::at(offset, length);
63    ///
64    /// assert_eq!(range, TextRange::new(offset, offset + length));
65    /// assert_eq!(&text[range], "23456")
66    /// ```
67    #[inline]
68    pub const fn at(offset: TextSize, len: TextSize) -> TextRange {
69        TextRange::new(offset, TextSize::new(offset.raw + len.raw))
70    }
71
72    /// Create a zero-length range at the specified offset (`offset..offset`).
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    /// # use text_size::*;
78    /// let point: TextSize;
79    /// # point = TextSize::from(3);
80    /// let range = TextRange::empty(point);
81    /// assert!(range.is_empty());
82    /// assert_eq!(range, TextRange::new(point, point));
83    /// ```
84    #[inline]
85    pub const fn empty(offset: TextSize) -> TextRange {
86        TextRange {
87            start: offset,
88            end: offset,
89        }
90    }
91
92    /// Create a range up to the given end (`..end`).
93    ///
94    /// # Examples
95    ///
96    /// ```rust
97    /// # use text_size::*;
98    /// let point: TextSize;
99    /// # point = TextSize::from(12);
100    /// let range = TextRange::up_to(point);
101    ///
102    /// assert_eq!(range.len(), point);
103    /// assert_eq!(range, TextRange::new(0.into(), point));
104    /// assert_eq!(range, TextRange::at(0.into(), point));
105    /// ```
106    #[inline]
107    pub const fn up_to(end: TextSize) -> TextRange {
108        TextRange {
109            start: TextSize::new(0),
110            end,
111        }
112    }
113}
114
115/// Identity methods.
116impl TextRange {
117    /// The start point of this range.
118    #[inline]
119    pub const fn start(self) -> TextSize {
120        self.start
121    }
122
123    /// The end point of this range.
124    #[inline]
125    pub const fn end(self) -> TextSize {
126        self.end
127    }
128
129    /// The size of this range.
130    #[inline]
131    pub const fn len(self) -> TextSize {
132        // HACK for const fn: math on primitives only
133        TextSize {
134            raw: self.end().raw - self.start().raw,
135        }
136    }
137
138    /// Check if this range is empty.
139    #[inline]
140    pub const fn is_empty(self) -> bool {
141        // HACK for const fn: math on primitives only
142        self.start().raw == self.end().raw
143    }
144}
145
146/// Manipulation methods.
147impl TextRange {
148    /// Check if this range contains an offset.
149    ///
150    /// The end index is considered excluded.
151    ///
152    /// # Examples
153    ///
154    /// ```rust
155    /// # use text_size::*;
156    /// let (start, end): (TextSize, TextSize);
157    /// # start = 10.into(); end = 20.into();
158    /// let range = TextRange::new(start, end);
159    /// assert!(range.contains(start));
160    /// assert!(!range.contains(end));
161    /// ```
162    #[inline]
163    pub fn contains(self, offset: TextSize) -> bool {
164        self.start() <= offset && offset < self.end()
165    }
166
167    /// Check if this range contains an offset.
168    ///
169    /// The end index is considered included.
170    ///
171    /// # Examples
172    ///
173    /// ```rust
174    /// # use text_size::*;
175    /// let (start, end): (TextSize, TextSize);
176    /// # start = 10.into(); end = 20.into();
177    /// let range = TextRange::new(start, end);
178    /// assert!(range.contains_inclusive(start));
179    /// assert!(range.contains_inclusive(end));
180    /// ```
181    #[inline]
182    pub fn contains_inclusive(self, offset: TextSize) -> bool {
183        self.start() <= offset && offset <= self.end()
184    }
185
186    /// Check if this range completely contains another range.
187    ///
188    /// # Examples
189    ///
190    /// ```rust
191    /// # use text_size::*;
192    /// let larger = TextRange::new(0.into(), 20.into());
193    /// let smaller = TextRange::new(5.into(), 15.into());
194    /// assert!(larger.contains_range(smaller));
195    /// assert!(!smaller.contains_range(larger));
196    ///
197    /// // a range always contains itself
198    /// assert!(larger.contains_range(larger));
199    /// assert!(smaller.contains_range(smaller));
200    /// ```
201    #[inline]
202    pub fn contains_range(self, other: TextRange) -> bool {
203        self.start() <= other.start() && other.end() <= self.end()
204    }
205
206    /// The range covered by both ranges, if it exists.
207    /// If the ranges touch but do not overlap, the output range is empty.
208    ///
209    /// # Examples
210    ///
211    /// ```rust
212    /// # use text_size::*;
213    /// assert_eq!(
214    ///     TextRange::intersect(
215    ///         TextRange::new(0.into(), 10.into()),
216    ///         TextRange::new(5.into(), 15.into()),
217    ///     ),
218    ///     Some(TextRange::new(5.into(), 10.into())),
219    /// );
220    /// ```
221    #[inline]
222    pub fn intersect(self, other: TextRange) -> Option<TextRange> {
223        let start = cmp::max(self.start(), other.start());
224        let end = cmp::min(self.end(), other.end());
225        if end < start {
226            return None;
227        }
228        Some(TextRange::new(start, end))
229    }
230
231    /// Extends the range to cover `other` as well.
232    ///
233    /// # Examples
234    ///
235    /// ```rust
236    /// # use text_size::*;
237    /// assert_eq!(
238    ///     TextRange::cover(
239    ///         TextRange::new(0.into(), 5.into()),
240    ///         TextRange::new(15.into(), 20.into()),
241    ///     ),
242    ///     TextRange::new(0.into(), 20.into()),
243    /// );
244    /// ```
245    #[inline]
246    pub fn cover(self, other: TextRange) -> TextRange {
247        let start = cmp::min(self.start(), other.start());
248        let end = cmp::max(self.end(), other.end());
249        TextRange::new(start, end)
250    }
251
252    /// Extends the range to cover `other` offsets as well.
253    ///
254    /// # Examples
255    ///
256    /// ```rust
257    /// # use text_size::*;
258    /// assert_eq!(
259    ///     TextRange::empty(0.into()).cover_offset(20.into()),
260    ///     TextRange::new(0.into(), 20.into()),
261    /// )
262    /// ```
263    #[inline]
264    pub fn cover_offset(self, offset: TextSize) -> TextRange {
265        self.cover(TextRange::empty(offset))
266    }
267
268    /// Add an offset to this range.
269    ///
270    /// Note that this is not appropriate for changing where a `TextRange` is
271    /// within some string; rather, it is for changing the reference anchor
272    /// that the `TextRange` is measured against.
273    ///
274    /// The unchecked version (`Add::add`) will _always_ panic on overflow,
275    /// in contrast to primitive integers, which check in debug mode only.
276    #[inline]
277    pub fn checked_add(self, offset: TextSize) -> Option<TextRange> {
278        Some(TextRange {
279            start: self.start.checked_add(offset)?,
280            end: self.end.checked_add(offset)?,
281        })
282    }
283
284    /// Subtract an offset from this range.
285    ///
286    /// Note that this is not appropriate for changing where a `TextRange` is
287    /// within some string; rather, it is for changing the reference anchor
288    /// that the `TextRange` is measured against.
289    ///
290    /// The unchecked version (`Sub::sub`) will _always_ panic on overflow,
291    /// in contrast to primitive integers, which check in debug mode only.
292    #[inline]
293    pub fn checked_sub(self, offset: TextSize) -> Option<TextRange> {
294        Some(TextRange {
295            start: self.start.checked_sub(offset)?,
296            end: self.end.checked_sub(offset)?,
297        })
298    }
299
300    /// Relative order of the two ranges (overlapping ranges are considered
301    /// equal).
302    ///
303    ///
304    /// This is useful when, for example, binary searching an array of disjoint
305    /// ranges.
306    ///
307    /// # Examples
308    ///
309    /// ```
310    /// # use text_size::*;
311    /// # use std::cmp::Ordering;
312    ///
313    /// let a = TextRange::new(0.into(), 3.into());
314    /// let b = TextRange::new(4.into(), 5.into());
315    /// assert_eq!(a.ordering(b), Ordering::Less);
316    ///
317    /// let a = TextRange::new(0.into(), 3.into());
318    /// let b = TextRange::new(3.into(), 5.into());
319    /// assert_eq!(a.ordering(b), Ordering::Less);
320    ///
321    /// let a = TextRange::new(0.into(), 3.into());
322    /// let b = TextRange::new(2.into(), 5.into());
323    /// assert_eq!(a.ordering(b), Ordering::Equal);
324    ///
325    /// let a = TextRange::new(0.into(), 3.into());
326    /// let b = TextRange::new(2.into(), 2.into());
327    /// assert_eq!(a.ordering(b), Ordering::Equal);
328    ///
329    /// let a = TextRange::new(2.into(), 3.into());
330    /// let b = TextRange::new(2.into(), 2.into());
331    /// assert_eq!(a.ordering(b), Ordering::Greater);
332    /// ```
333    #[inline]
334    pub fn ordering(self, other: TextRange) -> Ordering {
335        if self.end() <= other.start() {
336            Ordering::Less
337        } else if other.end() <= self.start() {
338            Ordering::Greater
339        } else {
340            Ordering::Equal
341        }
342    }
343}
344
345impl Index<TextRange> for str {
346    type Output = str;
347    #[inline]
348    fn index(&self, index: TextRange) -> &str {
349        &self[Range::<usize>::from(index)]
350    }
351}
352
353impl Index<TextRange> for String {
354    type Output = str;
355    #[inline]
356    fn index(&self, index: TextRange) -> &str {
357        &self[Range::<usize>::from(index)]
358    }
359}
360
361impl IndexMut<TextRange> for str {
362    #[inline]
363    fn index_mut(&mut self, index: TextRange) -> &mut str {
364        &mut self[Range::<usize>::from(index)]
365    }
366}
367
368impl IndexMut<TextRange> for String {
369    #[inline]
370    fn index_mut(&mut self, index: TextRange) -> &mut str {
371        &mut self[Range::<usize>::from(index)]
372    }
373}
374
375impl RangeBounds<TextSize> for TextRange {
376    fn start_bound(&self) -> Bound<&TextSize> {
377        Bound::Included(&self.start)
378    }
379
380    fn end_bound(&self) -> Bound<&TextSize> {
381        Bound::Excluded(&self.end)
382    }
383}
384
385impl<T> From<TextRange> for Range<T>
386where
387    T: From<TextSize>,
388{
389    #[inline]
390    fn from(r: TextRange) -> Self {
391        r.start().into()..r.end().into()
392    }
393}
394
395macro_rules! ops {
396    (impl $Op:ident for TextRange by fn $f:ident = $op:tt) => {
397        impl $Op<&TextSize> for TextRange {
398            type Output = TextRange;
399            #[inline]
400            fn $f(self, other: &TextSize) -> TextRange {
401                self $op *other
402            }
403        }
404        impl<T> $Op<T> for &TextRange
405        where
406            TextRange: $Op<T, Output=TextRange>,
407        {
408            type Output = TextRange;
409            #[inline]
410            fn $f(self, other: T) -> TextRange {
411                *self $op other
412            }
413        }
414    };
415}
416
417impl Add<TextSize> for TextRange {
418    type Output = TextRange;
419    #[inline]
420    fn add(self, offset: TextSize) -> TextRange {
421        self.checked_add(offset)
422            .expect("TextRange +offset overflowed")
423    }
424}
425
426impl Sub<TextSize> for TextRange {
427    type Output = TextRange;
428    #[inline]
429    fn sub(self, offset: TextSize) -> TextRange {
430        self.checked_sub(offset)
431            .expect("TextRange -offset overflowed")
432    }
433}
434
435ops!(impl Add for TextRange by fn add = +);
436ops!(impl Sub for TextRange by fn sub = -);
437
438impl<A> AddAssign<A> for TextRange
439where
440    TextRange: Add<A, Output = TextRange>,
441{
442    #[inline]
443    fn add_assign(&mut self, rhs: A) {
444        *self = *self + rhs
445    }
446}
447
448impl<S> SubAssign<S> for TextRange
449where
450    TextRange: Sub<S, Output = TextRange>,
451{
452    #[inline]
453    fn sub_assign(&mut self, rhs: S) {
454        *self = *self - rhs
455    }
456}