| 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 | |