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(msg:"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(msg:"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}
457