1 | use cmp::Ordering;
|
2 |
|
3 | use {
|
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)]
|
15 | pub struct TextRange {
|
16 | // Invariant: start <= end
|
17 | start: TextSize,
|
18 | end: TextSize,
|
19 | }
|
20 |
|
21 | impl 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 |
|
27 | impl 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.
|
116 | impl 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.
|
147 | impl 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 |
|
345 | impl 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 |
|
353 | impl 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 |
|
361 | impl 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 |
|
368 | impl 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 |
|
375 | impl 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 |
|
385 | impl<T> From<TextRange> for Range<T>
|
386 | where
|
387 | T: From<TextSize>,
|
388 | {
|
389 | #[inline ]
|
390 | fn from(r: TextRange) -> Self {
|
391 | r.start().into()..r.end().into()
|
392 | }
|
393 | }
|
394 |
|
395 | macro_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 |
|
417 | impl 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 |
|
426 | impl 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 |
|
435 | ops!(impl Add for TextRange by fn add = +);
|
436 | ops!(impl Sub for TextRange by fn sub = -);
|
437 |
|
438 | impl<A> AddAssign<A> for TextRange
|
439 | where
|
440 | TextRange: Add<A, Output = TextRange>,
|
441 | {
|
442 | #[inline ]
|
443 | fn add_assign(&mut self, rhs: A) {
|
444 | *self = *self + rhs
|
445 | }
|
446 | }
|
447 |
|
448 | impl<S> SubAssign<S> for TextRange
|
449 | where
|
450 | TextRange: Sub<S, Output = TextRange>,
|
451 | {
|
452 | #[inline ]
|
453 | fn sub_assign(&mut self, rhs: S) {
|
454 | *self = *self - rhs
|
455 | }
|
456 | }
|
457 | |