7 ops
::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign}
,
11 /// A range in text, represented as a pair of [`TextSize`][struct@TextSize].
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
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
)
28 /// Creates a new `TextRange` with the given `start` and `end` (`start..end`).
32 /// Panics if `end < start`.
37 /// # use text_size::*;
38 /// let start = TextSize::from(5);
39 /// let end = TextSize::from(10);
40 /// let range = TextRange::new(start, end);
42 /// assert_eq!(range.start(), start);
43 /// assert_eq!(range.end(), end);
44 /// assert_eq!(range.len(), end - start);
47 pub const fn new(start
: TextSize
, end
: TextSize
) -> TextRange
{
48 assert
!(start
.raw
<= end
.raw
);
49 TextRange { start, end }
52 /// Create a new `TextRange` with the given `offset` and `len` (`offset..offset + len`).
57 /// # use text_size::*;
58 /// let text = "0123456789";
60 /// let offset = TextSize::from(2);
61 /// let length = TextSize::from(5);
62 /// let range = TextRange::at(offset, length);
64 /// assert_eq!(range, TextRange::new(offset, offset + length));
65 /// assert_eq!(&text[range], "23456")
68 pub const fn at(offset
: TextSize
, len
: TextSize
) -> TextRange
{
69 TextRange
::new(offset
, TextSize
::new(offset
.raw
+ len
.raw
))
72 /// Create a zero-length range at the specified offset (`offset..offset`).
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));
85 pub const fn empty(offset
: TextSize
) -> TextRange
{
92 /// Create a range up to the given end (`..end`).
97 /// # use text_size::*;
98 /// let point: TextSize;
99 /// # point = TextSize::from(12);
100 /// let range = TextRange::up_to(point);
102 /// assert_eq!(range.len(), point);
103 /// assert_eq!(range, TextRange::new(0.into(), point));
104 /// assert_eq!(range, TextRange::at(0.into(), point));
107 pub const fn up_to(end
: TextSize
) -> TextRange
{
109 start
: TextSize
::new(0),
115 /// Identity methods.
117 /// The start point of this range.
119 pub const fn start(self) -> TextSize
{
123 /// The end point of this range.
125 pub const fn end(self) -> TextSize
{
129 /// The size of this range.
131 pub const fn len(self) -> TextSize
{
132 // HACK for const fn: math on primitives only
134 raw
: self.end().raw
- self.start().raw
,
138 /// Check if this range is empty.
140 pub const fn is_empty(self) -> bool
{
141 // HACK for const fn: math on primitives only
142 self.start().raw
== self.end().raw
146 /// Manipulation methods.
148 /// Check if this range contains an offset.
150 /// The end index is considered excluded.
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));
163 pub fn contains(self, offset
: TextSize
) -> bool
{
164 self.start() <= offset
&& offset
< self.end()
167 /// Check if this range contains an offset.
169 /// The end index is considered included.
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));
182 pub fn contains_inclusive(self, offset
: TextSize
) -> bool
{
183 self.start() <= offset
&& offset
<= self.end()
186 /// Check if this range completely contains another range.
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));
197 /// // a range always contains itself
198 /// assert!(larger.contains_range(larger));
199 /// assert!(smaller.contains_range(smaller));
202 pub fn contains_range(self, other
: TextRange
) -> bool
{
203 self.start() <= other
.start() && other
.end() <= self.end()
206 /// The range covered by both ranges, if it exists.
207 /// If the ranges touch but do not overlap, the output range is empty.
212 /// # use text_size::*;
214 /// TextRange::intersect(
215 /// TextRange::new(0.into(), 10.into()),
216 /// TextRange::new(5.into(), 15.into()),
218 /// Some(TextRange::new(5.into(), 10.into())),
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());
228 Some(TextRange
::new(start
, end
))
231 /// Extends the range to cover `other` as well.
236 /// # use text_size::*;
238 /// TextRange::cover(
239 /// TextRange::new(0.into(), 5.into()),
240 /// TextRange::new(15.into(), 20.into()),
242 /// TextRange::new(0.into(), 20.into()),
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
)
252 /// Extends the range to cover `other` offsets as well.
257 /// # use text_size::*;
259 /// TextRange::empty(0.into()).cover_offset(20.into()),
260 /// TextRange::new(0.into(), 20.into()),
264 pub fn cover_offset(self, offset
: TextSize
) -> TextRange
{
265 self.cover(TextRange
::empty(offset
))
268 /// Add an offset to this range.
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.
274 /// The unchecked version (`Add::add`) will _always_ panic on overflow,
275 /// in contrast to primitive integers, which check in debug mode only.
277 pub fn checked_add(self, offset
: TextSize
) -> Option
<TextRange
> {
279 start
: self.start
.checked_add(offset
)?
,
280 end
: self.end
.checked_add(offset
)?
,
284 /// Subtract an offset from this range.
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.
290 /// The unchecked version (`Sub::sub`) will _always_ panic on overflow,
291 /// in contrast to primitive integers, which check in debug mode only.
293 pub fn checked_sub(self, offset
: TextSize
) -> Option
<TextRange
> {
295 start
: self.start
.checked_sub(offset
)?
,
296 end
: self.end
.checked_sub(offset
)?
,
300 /// Relative order of the two ranges (overlapping ranges are considered
304 /// This is useful when, for example, binary searching an array of disjoint
310 /// # use text_size::*;
311 /// # use std::cmp::Ordering;
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);
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);
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);
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);
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);
334 pub fn ordering(self, other
: TextRange
) -> Ordering
{
335 if self.end() <= other
.start() {
337 } else if other
.end() <= self.start() {
345 impl Index
<TextRange
> for str {
348 fn index(&self, index
: TextRange
) -> &str {
349 &self[Range
::<usize>::from(index
)]
353 impl Index
<TextRange
> for String
{
356 fn index(&self, index
: TextRange
) -> &str {
357 &self[Range
::<usize>::from(index
)]
361 impl IndexMut
<TextRange
> for str {
363 fn index_mut(&mut self, index
: TextRange
) -> &mut str {
364 &mut self[Range
::<usize>::from(index
)]
368 impl IndexMut
<TextRange
> for String
{
370 fn index_mut(&mut self, index
: TextRange
) -> &mut str {
371 &mut self[Range
::<usize>::from(index
)]
375 impl RangeBounds
<TextSize
> for TextRange
{
376 fn start_bound(&self) -> Bound
<&TextSize
> {
377 Bound
::Included(&self.start
)
380 fn end_bound(&self) -> Bound
<&TextSize
> {
381 Bound
::Excluded(&self.end
)
385 impl<T
> From
<TextRange
> for Range
<T
>
390 fn from(r
: TextRange
) -> Self {
391 r
.start().into()..r
.end().into()
396 (impl $Op
:ident
for TextRange by
fn $f
:ident
= $op
:tt
) => {
397 impl $Op
<&TextSize
> for TextRange
{
398 type Output
= TextRange
;
400 fn $
f(self, other
: &TextSize
) -> TextRange
{
404 impl<T
> $Op
<T
> for &TextRange
406 TextRange
: $Op
<T
, Output
=TextRange
>,
408 type Output
= TextRange
;
410 fn $
f(self, other
: T
) -> TextRange
{
417 impl Add
<TextSize
> for TextRange
{
418 type Output
= TextRange
;
420 fn add(self, offset
: TextSize
) -> TextRange
{
421 self.checked_add(offset
)
422 .expect("TextRange +offset overflowed")
426 impl Sub
<TextSize
> for TextRange
{
427 type Output
= TextRange
;
429 fn sub(self, offset
: TextSize
) -> TextRange
{
430 self.checked_sub(offset
)
431 .expect("TextRange -offset overflowed")
435 ops
!(impl Add
for TextRange by
fn add
= +);
436 ops
!(impl Sub
for TextRange by
fn sub
= -);
438 impl<A
> AddAssign
<A
> for TextRange
440 TextRange
: Add
<A
, Output
= TextRange
>,
443 fn add_assign(&mut self, rhs
: A
) {
448 impl<S
> SubAssign
<S
> for TextRange
450 TextRange
: Sub
<S
, Output
= TextRange
>,
453 fn sub_assign(&mut self, rhs
: S
) {