line_column/span.rs
1//! Out of the box [`Span`] for storing source code and text range.
2
3use core::{fmt, ops};
4use std::string::String;
5
6#[cfg(feature = "sync")]
7use std::sync::Arc;
8#[cfg(not(feature = "sync"))]
9use std::rc::Rc as Arc;
10
11pub use text_size::{TextRange, TextSize};
12
13pub mod wrapper;
14
15/// [`text_size::TextRange`] wrapper
16///
17/// Stored source code pointers, allowing for easy retrieval of lines, columns, and source code text
18///
19/// If `len() == 0`, it is used to indicate offset
20///
21/// **NOTE**: Default [`Span`] not implement [`Sync`] and [`Send`], about `sync` feature
22///
23/// # Examples
24///
25/// ```
26/// use line_column::span::*;
27///
28/// let source = Span::new_full("foo,bar,baz");
29/// let comma = source.create(TextRange::at(3.into(), TextSize::of(',')));
30/// let bar = comma.after().take(TextSize::of("bar"));
31///
32/// assert_eq!(comma.text(), ",");
33/// assert_eq!(bar.text(), "bar");
34/// assert_eq!(bar.source(), "foo,bar,baz");
35/// assert_eq!(bar.line_column(), (1, 5));
36/// ```
37#[derive(Clone, Default)]
38pub struct Span {
39 source: Arc<String>,
40 range: TextRange,
41}
42
43impl fmt::Debug for Span {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let text = self.text();
46 write!(f, "Span({text:?}@{:?})", self.range())
47 }
48}
49
50impl Span {
51 /// New a source and span range.
52 ///
53 /// **NOTE**: It is not recommended to call repeatedly,
54 /// otherwise the `source` will be allocated repeatedly. Consider using [`Span::create`]
55 ///
56 /// # Panics
57 ///
58 /// - Panics if `range` out of source.
59 /// - Panics if `source.len()` out of [`TextSize`].
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use line_column::span::*;
65 ///
66 /// let source = "abcdef";
67 /// let span = Span::new(source, TextRange::new(2.into(), 4.into()));
68 /// assert_eq!(span.text(), "cd");
69 /// ```
70 #[inline]
71 #[track_caller]
72 pub fn new(source: impl Into<String>, range: TextRange) -> Self {
73 Self::checked_new(source.into().into(), range)
74 }
75
76 /// New a full span of source.
77 ///
78 /// **NOTE**: It is not recommended to call repeatedly,
79 /// otherwise the `source` will be allocated repeatedly. Consider using [`Span::create`]
80 ///
81 /// # Panics
82 ///
83 /// - Panics if `source.len()` out of [`TextSize`].
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// use line_column::span::*;
89 ///
90 /// let source = "abcdef";
91 /// let full = Span::new_full(source);
92 /// assert_eq!(full.text(), "abcdef");
93 /// ```
94 #[inline]
95 pub fn new_full(source: impl Into<String>) -> Self {
96 let source = source.into();
97 let range = TextRange::up_to(len_size(source.len()));
98 Self::checked_new(source.into(), range)
99 }
100
101 /// New a span **source** range from exist span.
102 ///
103 /// # Panics
104 ///
105 /// - Panics if `range` out of source.
106 ///
107 /// # Examples
108 ///
109 /// ```
110 /// use line_column::span::*;
111 ///
112 /// let source = "abcdef";
113 /// let full = Span::new_full(source);
114 /// assert_eq!(full.text(), "abcdef");
115 ///
116 /// let span = full.create(TextRange::at(1.into(), 3.into()));
117 /// assert_eq!(span.text(), "bcd");
118 /// let span2 = span.create(TextRange::at(3.into(), 3.into()));
119 /// assert_eq!(span2.text(), "def");
120 /// ```
121 #[inline]
122 #[track_caller]
123 pub fn create(&self, range: TextRange) -> Self {
124 Self::checked_new(self.source.clone(), range)
125 }
126
127 /// New a span **relative** range from exist span.
128 ///
129 /// # Panics
130 ///
131 /// - Panics if `range+start` out of source.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use line_column::span::*;
137 ///
138 /// let source = "abcdef";
139 /// let full = Span::new_full(source);
140 /// assert_eq!(full.text(), "abcdef");
141 ///
142 /// let span = full.slice(TextRange::at(1.into(), 3.into()));
143 /// assert_eq!(span.text(), "bcd");
144 /// let span2 = span.slice(TextRange::at(1.into(), 3.into()));
145 /// assert_eq!(span2.text(), "cde");
146 /// ```
147 #[inline]
148 #[track_caller]
149 pub fn slice(&self, range: TextRange) -> Self {
150 let start = self.range.start();
151 self.create(range+start)
152 }
153
154 /// New splitted pair of spans from this span, using **relative** index (`len`).
155 ///
156 /// # Panics
157 ///
158 /// - Panics if `len+start()` out of source.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use line_column::span::*;
164 ///
165 /// let source = "abcdef";
166 /// let full = Span::new_full(source);
167 /// assert_eq!(full.text(), "abcdef");
168 ///
169 /// let (a, span) = full.split(TextSize::of("a"));
170 /// assert_eq!(a.text(), "a");
171 /// assert_eq!(span.text(), "bcdef");
172 ///
173 /// let (bcd, span2) = span.split(TextSize::of("bcd"));
174 /// assert_eq!(bcd.text(), "bcd");
175 /// assert_eq!(span2.text(), "ef");
176 /// ```
177 #[inline]
178 #[track_caller]
179 pub fn split(&self, len: TextSize) -> (Self, Self) {
180 self.split_at(self.range.start()+len)
181 }
182
183 /// New splitted pair of spans from this span, using **source** `index`.
184 ///
185 /// # Panics
186 ///
187 /// - Panics if `index` out of source.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// use line_column::span::*;
193 ///
194 /// let source = "abcdef";
195 /// let full = Span::new_full(source);
196 /// assert_eq!(full.text(), "abcdef");
197 ///
198 /// let (a, span) = full.split_at(TextSize::of("a"));
199 /// assert_eq!(a.text(), "a");
200 /// assert_eq!(span.text(), "bcdef");
201 ///
202 /// let (bcd, span2) = span.split_at(TextSize::of("abcd"));
203 /// assert_eq!(bcd.text(), "bcd");
204 /// assert_eq!(span2.text(), "ef");
205 /// ```
206 #[inline]
207 #[track_caller]
208 pub fn split_at(&self, index: TextSize) -> (Self, Self) {
209 self.split_at_range(TextRange::empty(index))
210 }
211
212 /// New splitted pair of spans from this span, using **source** `range`.
213 ///
214 /// # Panics
215 ///
216 /// - Panics if `range.end()` out of source.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use line_column::span::*;
222 ///
223 /// let source = "abcdef";
224 /// let full = Span::new_full(source);
225 /// let sub = full.create(TextRange::at(TextSize::of("ab"), TextSize::of("cd")));
226 /// assert_eq!(full.text(), "abcdef");
227 /// assert_eq!(sub.text(), "cd");
228 ///
229 /// let (a, b) = full.split_at_range(sub.range());
230 /// assert_eq!(a.text(), "ab");
231 /// assert_eq!(b.text(), "ef");
232 /// ```
233 #[inline]
234 #[track_caller]
235 pub fn split_at_range(&self, range: TextRange) -> (Self, Self) {
236 let start = self.range.start();
237 let end = self.range.end();
238 (
239 self.create(TextRange::new(start, range.start())),
240 self.create(TextRange::new(range.end(), end)),
241 )
242 }
243
244 #[inline]
245 #[track_caller]
246 fn checked_new(source: Arc<String>, range: TextRange) -> Self {
247 let source_length = len_size(source.len());
248
249 assert!(range.end() <= source_length, "range end > source length ({:?} > {source_length:?})", range.end());
250
251 Self { source, range }
252 }
253
254 /// Returns the is empty of this [`Span`] range.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use line_column::span::*;
260 ///
261 /// let span = Span::new_full("foo");
262 /// let empty = span.create(TextRange::empty(1.into()));
263 /// assert_eq!(span.is_empty(), false);
264 /// assert_eq!(empty.is_empty(), true);
265 /// assert_eq!(empty.range(), TextRange::new(1.into(), 1.into()));
266 /// ```
267 #[inline]
268 pub fn is_empty(&self) -> bool {
269 self.range().is_empty()
270 }
271
272 /// Returns the length of this [`Span`] range.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use line_column::span::*;
278 ///
279 /// let span = Span::new_full("foo");
280 /// let empty = span.create(TextRange::empty(1.into()));
281 /// assert_eq!(span.len(), TextSize::new(3));
282 /// assert_eq!(empty.len(), TextSize::new(0));
283 /// ```
284 #[inline]
285 pub fn len(&self) -> TextSize {
286 self.range().len()
287 }
288
289 /// Returns the source before of this [`Span`].
290 ///
291 /// # Examples
292 ///
293 /// ```
294 /// use line_column::span::*;
295 ///
296 /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 6.into()));
297 /// assert_eq!(span.text(), "bar");
298 /// assert_eq!(span.before().text(), "foo");
299 /// ```
300 pub fn before(&self) -> Self {
301 let range = TextRange::up_to(self.range().start());
302 self.create(range)
303 }
304
305 /// Returns the source after of this [`Span`].
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use line_column::span::*;
311 ///
312 /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 6.into()));
313 /// assert_eq!(span.text(), "bar");
314 /// assert_eq!(span.after().text(), "baz");
315 /// ```
316 pub fn after(&self) -> Self {
317 let end = TextSize::of(self.source());
318 let range = TextRange::new(self.range().end(), end);
319 self.create(range)
320 }
321
322 /// Returns truncated sub-span.
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// use line_column::span::*;
328 ///
329 /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 7.into()));
330 /// assert_eq!(span.text(), "barb");
331 /// assert_eq!(span.take(3.into()).text(), "bar");
332 /// ```
333 pub fn take(&self, len: TextSize) -> Self {
334 let range = self.range;
335 let new_len = range.len().min(len);
336 let new_range = TextRange::at(self.range.start(), new_len);
337 self.create(new_range)
338 }
339
340 /// Returns the start of this [`Span`].
341 ///
342 /// # Examples
343 ///
344 /// ```
345 /// use line_column::span::*;
346 ///
347 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
348 /// assert_eq!(span.start().range(), TextRange::new(1.into(), 1.into()));
349 /// ```
350 pub fn start(&self) -> Self {
351 self.create(TextRange::empty(self.range.start()))
352 }
353
354 /// Returns the end of this [`Span`].
355 ///
356 /// # Examples
357 ///
358 /// ```
359 /// use line_column::span::*;
360 ///
361 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
362 /// assert_eq!(span.end().range(), TextRange::new(4.into(), 4.into()));
363 /// ```
364 pub fn end(&self) -> Self {
365 self.create(TextRange::empty(self.range.end()))
366 }
367
368 /// Returns the start index of this [`Span`] range.
369 ///
370 /// # Examples
371 ///
372 /// ```
373 /// use line_column::span::*;
374 ///
375 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
376 /// assert_eq!(span.index(), TextSize::new(1));
377 /// ```
378 #[inline]
379 pub fn index(&self) -> TextSize {
380 self.range().start()
381 }
382
383 /// Returns the source text of the range reference.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// use line_column::span::*;
389 ///
390 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
391 /// assert_eq!(span.text(), "bcd");
392 /// ```
393 #[doc(alias = "as_str")]
394 pub fn text(&self) -> &str {
395 &self.source()[self.range()]
396 }
397
398 /// Returns the source text of the range reference.
399 ///
400 /// # Examples
401 ///
402 /// ```
403 /// use line_column::span::*;
404 ///
405 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
406 /// assert_eq!(span.range(), TextRange::new(1.into(), 4.into()));
407 /// ```
408 pub fn range(&self) -> TextRange {
409 self.range
410 }
411
412 /// Returns the source text.
413 ///
414 /// # Examples
415 ///
416 /// ```
417 /// use line_column::span::*;
418 ///
419 /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
420 /// assert_eq!(span.source(), "abcdef");
421 /// assert_eq!(span.text(), "bcd");
422 /// ```
423 pub fn source(&self) -> &str {
424 &self.source
425 }
426}
427
428impl Span {
429 /// Use [`line_column`](crate::line_column) calculate line and column
430 ///
431 /// # Examples
432 ///
433 /// ```
434 /// use line_column::span::*;
435 ///
436 /// let span = Span::new("ab\ncdef", TextRange::empty(TextSize::of("ab\ncd")));
437 /// assert_eq!(span.before().text(), "ab\ncd");
438 /// assert_eq!(span.line_column(), (2, 3));
439 /// ```
440 pub fn line_column(&self) -> (u32, u32) {
441 crate::line_column(self.source(), self.index().into())
442 }
443
444 /// Get line from [`Span::line_column`]
445 pub fn line(&self) -> u32 {
446 self.line_column().0
447 }
448
449 /// Get column from [`Span::line_column`]
450 pub fn column(&self) -> u32 {
451 self.line_column().1
452 }
453
454 /// Returns the current line of this [`Span`].
455 ///
456 /// Maybe include end of line char, like `'\n'`.
457 ///
458 /// # Examples
459 ///
460 /// ```
461 /// use line_column::span::*;
462 ///
463 /// let span = Span::new_full("foo\nbar\nbaz");
464 /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
465 /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
466 /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
467 ///
468 /// assert_eq!(next.text(), "bar\nb");
469 /// assert_eq!(tail.text(), "baz");
470 /// assert_eq!(endl.text(), "\nba");
471 ///
472 /// assert_eq!(span.current_line().text(), "foo\n");
473 /// assert_eq!(next.current_line().text(), "bar\n");
474 /// assert_eq!(tail.current_line().text(), "baz");
475 /// assert_eq!(endl.current_line().text(), "foo\n");
476 /// ```
477 pub fn current_line(&self) -> Self {
478 let before = &self.source[..self.range.start().into()];
479 let line_start = before.rfind('\n').map_or(0, |it| it+1);
480 let rest = &self.source[line_start..];
481
482 let line_len = match rest.split_once('\n') {
483 Some((line, _)) => TextSize::of(line) + TextSize::of('\n'),
484 None => TextSize::of(rest),
485 };
486 let range = TextRange::at(len_size(line_start), line_len);
487 self.create(range)
488 }
489
490 /// Returns the previous line of this [`Span`].
491 ///
492 /// # Examples
493 ///
494 /// ```
495 /// use line_column::span::*;
496 ///
497 /// let span = Span::new_full("foo\nbar\nbaz");
498 /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
499 /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
500 /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
501 ///
502 /// assert_eq!(next.text(), "bar\nb");
503 /// assert_eq!(tail.text(), "baz");
504 /// assert_eq!(endl.text(), "\nba");
505 ///
506 /// assert_eq!(span.prev_line().text(), "");
507 /// assert_eq!(next.prev_line().text(), "foo\n");
508 /// assert_eq!(tail.prev_line().text(), "bar\n");
509 /// assert_eq!(endl.prev_line().text(), "");
510 /// ```
511 pub fn prev_line(&self) -> Self {
512 let index = self.current_line().index();
513 if let Some(prev_line_offset) = index.checked_sub(TextSize::of('\n')) {
514 self.create(TextRange::empty(prev_line_offset)).current_line()
515 } else {
516 self.create(TextRange::empty(TextSize::new(0)))
517 }
518 }
519
520 /// Returns the next line of this [`Span`].
521 ///
522 /// # Examples
523 ///
524 /// ```
525 /// use line_column::span::*;
526 ///
527 /// let span = Span::new_full("foo\nbar\nbaz");
528 /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
529 /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
530 /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
531 ///
532 /// assert_eq!(next.text(), "bar\nb");
533 /// assert_eq!(tail.text(), "baz");
534 /// assert_eq!(endl.text(), "\nba");
535 ///
536 /// assert_eq!(span.next_line().text(), "bar\n");
537 /// assert_eq!(next.next_line().text(), "baz");
538 /// assert_eq!(tail.next_line().text(), "");
539 /// assert_eq!(endl.next_line().text(), "bar\n");
540 /// ```
541 pub fn next_line(&self) -> Self {
542 let cur_line_end = self.current_line().range().end();
543 if self.source().len() == cur_line_end.into() {
544 self.create(TextRange::empty(cur_line_end))
545 } else {
546 let range = TextRange::empty(cur_line_end);
547 self.create(range).current_line()
548 }
549 }
550}
551
552impl Span {
553 /// Returns the trim end of this [`Span`] range.
554 ///
555 /// # Examples
556 ///
557 /// ```
558 /// use line_column::span::*;
559 ///
560 /// let span = Span::new("foo bar baz", TextRange::new(4.into(), 9.into()));
561 /// assert_eq!(span.text(), " bar ");
562 /// assert_eq!(span.trim_end().text(), " bar");
563 /// ```
564 pub fn trim_end(&self) -> Self {
565 let text = self.text();
566 let trimmed = text.trim_end();
567 let len = TextSize::of(trimmed);
568 self.create(TextRange::at(self.range.start(), len))
569 }
570
571 /// Returns the trim start of this [`Span`] range.
572 ///
573 /// # Examples
574 ///
575 /// ```
576 /// use line_column::span::*;
577 ///
578 /// let span = Span::new("foo bar baz", TextRange::new(4.into(), 9.into()));
579 /// assert_eq!(span.text(), " bar ");
580 /// assert_eq!(span.trim_start().text(), "bar ");
581 /// ```
582 pub fn trim_start(&self) -> Self {
583 let text = self.text();
584 let trimmed = text.trim_start();
585 let len = TextSize::of(trimmed);
586
587 let offset = TextSize::of(text) - len;
588 let start = self.range.start() + offset;
589 self.create(TextRange::at(start, len))
590 }
591}
592
593#[inline]
594#[track_caller]
595fn len_size(len: usize) -> TextSize {
596 match TextSize::try_from(len) {
597 Ok(source_length) => source_length,
598 _ => panic!("source length {len} overflow TextSize"),
599 }
600}
601
602#[cfg(test)]
603mod tests {
604 use core::iter::successors;
605 use std::{format, vec::Vec};
606
607 use super::*;
608
609 #[track_caller]
610 fn check_texts(spans: impl IntoIterator<Item = Span>, expect: &[&str]) {
611 let spans = Vec::from_iter(spans);
612 let texts = spans.iter().map(|it| it.text()).collect::<Vec<_>>();
613 assert_eq!(texts, expect);
614 }
615
616 #[cfg(feature = "sync")]
617 fn _test_sync(span: Span) {
618 fn check_sync(_: impl Sync) {}
619 check_sync(span);
620 }
621
622 #[test]
623 #[should_panic = "range end > source length"]
624 fn new_panic_out_of_source() {
625 let _span = Span::new("x", TextRange::up_to(TextSize::of("xy")));
626 }
627
628 #[test]
629 fn next_lines_without_end_eol() {
630 let source = "foo\nbar\n\nbaz";
631 let span = Span::new_full(source);
632 let lines =
633 successors(span.current_line().into(), |it| Some(it.next_line()))
634 .take_while(|it| !it.is_empty())
635 .collect::<Vec<_>>();
636 check_texts(lines, &[
637 "foo\n",
638 "bar\n",
639 "\n",
640 "baz",
641 ]);
642 }
643
644 #[test]
645 fn next_lines_multi_bytes_char() {
646 let source = "测试\n实现\n\n多字节";
647 let span = Span::new_full(source);
648 let lines =
649 successors(span.current_line().into(), |it| Some(it.next_line()))
650 .take_while(|it| !it.is_empty())
651 .collect::<Vec<_>>();
652 check_texts(lines, &[
653 "测试\n",
654 "实现\n",
655 "\n",
656 "多字节",
657 ]);
658 }
659
660 #[test]
661 fn next_lines_with_end_eol() {
662 let source = "foo\nbar\n\nbaz\n";
663 let span = Span::new_full(source);
664 let lines =
665 successors(span.current_line().into(), |it| Some(it.next_line()))
666 .take_while(|it| !it.is_empty())
667 .collect::<Vec<_>>();
668 check_texts(lines, &[
669 "foo\n",
670 "bar\n",
671 "\n",
672 "baz\n",
673 ]);
674 }
675
676 #[test]
677 fn next_lines_first_empty_line() {
678 let source = "\nfoo\nbar\n\nbaz";
679 let span = Span::new_full(source);
680 let lines =
681 successors(span.current_line().into(), |it| Some(it.next_line()))
682 .take_while(|it| !it.is_empty())
683 .collect::<Vec<_>>();
684 check_texts(lines, &[
685 "\n",
686 "foo\n",
687 "bar\n",
688 "\n",
689 "baz",
690 ]);
691 }
692
693 #[test]
694 fn prev_lines_with_end_eol() {
695 let source = "foo\nbar\n\nbaz\n";
696 let span = Span::new(source, TextRange::empty(TextSize::of(source)));
697 let lines =
698 successors(span.current_line().into(), |it| Some(it.prev_line()))
699 .skip(1)
700 .take_while(|it| !it.is_empty())
701 .collect::<Vec<_>>();
702 check_texts(lines, &[
703 "baz\n",
704 "\n",
705 "bar\n",
706 "foo\n",
707 ]);
708 }
709
710 #[test]
711 fn prev_lines_without_end_eol() {
712 let source = "foo\nbar\n\nbaz";
713 let span = Span::new(source, TextRange::empty(TextSize::of(source)));
714 let lines =
715 successors(span.current_line().into(), |it| Some(it.prev_line()))
716 .take_while(|it| !it.is_empty())
717 .collect::<Vec<_>>();
718 check_texts(lines, &[
719 "baz",
720 "\n",
721 "bar\n",
722 "foo\n",
723 ]);
724 }
725
726 #[test]
727 fn prev_lines_multi_bytes_char() {
728 let source = "测试\n实现\n\n多字节";
729 let span = Span::new(source, TextRange::empty(TextSize::of(source)));
730 let lines =
731 successors(span.current_line().into(), |it| Some(it.prev_line()))
732 .take_while(|it| !it.is_empty())
733 .collect::<Vec<_>>();
734 check_texts(lines, &[
735 "多字节",
736 "\n",
737 "实现\n",
738 "测试\n",
739 ]);
740 }
741
742 #[test]
743 fn test_trim_start() {
744 let datas = [
745 "",
746 "f",
747 "foo",
748 " ",
749 " f",
750 " foo",
751 " ",
752 " f",
753 " foo",
754 " f",
755 " foo",
756 ];
757 for prefix in ["", "x"] {
758 for suffix in ["", "x", " ", " "] {
759 for data in datas {
760 let source = format!("{prefix}{data}{suffix}");
761 let range = TextRange::new(
762 TextSize::of(prefix),
763 TextSize::of(&source),
764 );
765 let span = Span::new(&source, range);
766 assert_eq!(span.trim_start().text(), source[range].trim_start());
767 }
768 }
769 }
770 }
771
772 #[test]
773 fn test_trim_end() {
774 let datas = [
775 "",
776 "f",
777 "foo",
778 " ",
779 " f",
780 "foo ",
781 " ",
782 "f ",
783 "foo ",
784 "f ",
785 "foo ",
786 ];
787 for prefix in ["", "x", " ", " "] {
788 for suffix in ["", "x"] {
789 for data in datas {
790 let source = format!("{prefix}{data}{suffix}");
791 let range = TextRange::new(
792 TextSize::new(0),
793 TextSize::of(&source) - TextSize::of(suffix),
794 );
795 let span = Span::new(&source, range);
796 assert_eq!(span.trim_end().text(), source[range].trim_end());
797 }
798 }
799 }
800 }
801}