Skip to main content

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 splited span pair relative index from exist span.
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 splited span pair index from exist span.
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        let start = self.range.start();
210        let end = self.range.end();
211        (
212            self.create(TextRange::new(start, index)),
213            self.create(TextRange::new(index, end)),
214        )
215    }
216
217    #[inline]
218    #[track_caller]
219    fn checked_new(source: Arc<String>, range: TextRange) -> Self {
220        let source_length = len_size(source.len());
221
222        assert!(range.end() <= source_length, "range end > source length ({:?} > {source_length:?})", range.end());
223
224        Self { source, range }
225    }
226
227    /// Returns the is empty of this [`Span`] range.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use line_column::span::*;
233    ///
234    /// let span = Span::new_full("foo");
235    /// let empty = span.create(TextRange::empty(1.into()));
236    /// assert_eq!(span.is_empty(),  false);
237    /// assert_eq!(empty.is_empty(), true);
238    /// assert_eq!(empty.range(),    TextRange::new(1.into(), 1.into()));
239    /// ```
240    #[inline]
241    pub fn is_empty(&self) -> bool {
242        self.range().is_empty()
243    }
244
245    /// Returns the length of this [`Span`] range.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use line_column::span::*;
251    ///
252    /// let span = Span::new_full("foo");
253    /// let empty = span.create(TextRange::empty(1.into()));
254    /// assert_eq!(span.len(),  TextSize::new(3));
255    /// assert_eq!(empty.len(), TextSize::new(0));
256    /// ```
257    #[inline]
258    pub fn len(&self) -> TextSize {
259        self.range().len()
260    }
261
262    /// Returns the source before of this [`Span`].
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use line_column::span::*;
268    ///
269    /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 6.into()));
270    /// assert_eq!(span.text(),          "bar");
271    /// assert_eq!(span.before().text(), "foo");
272    /// ```
273    pub fn before(&self) -> Self {
274        let range = TextRange::up_to(self.range().start());
275        self.create(range)
276    }
277
278    /// Returns the source after of this [`Span`].
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use line_column::span::*;
284    ///
285    /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 6.into()));
286    /// assert_eq!(span.text(),          "bar");
287    /// assert_eq!(span.after().text(),  "baz");
288    /// ```
289    pub fn after(&self) -> Self {
290        let end = TextSize::of(self.source());
291        let range = TextRange::new(self.range().end(), end);
292        self.create(range)
293    }
294
295    /// Returns truncated sub-span.
296    ///
297    /// # Examples
298    ///
299    /// ```
300    /// use line_column::span::*;
301    ///
302    /// let span = Span::new("foobarbaz", TextRange::new(3.into(), 7.into()));
303    /// assert_eq!(span.text(), "barb");
304    /// assert_eq!(span.take(3.into()).text(), "bar");
305    /// ```
306    pub fn take(&self, len: TextSize) -> Self {
307        let range = self.range;
308        let new_len = range.len().min(len);
309        let new_range = TextRange::at(self.range.start(), new_len);
310        self.create(new_range)
311    }
312
313    /// Returns the start of this [`Span`].
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use line_column::span::*;
319    ///
320    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
321    /// assert_eq!(span.start().range(), TextRange::new(1.into(), 1.into()));
322    /// ```
323    pub fn start(&self) -> Self {
324        self.create(TextRange::empty(self.range.start()))
325    }
326
327    /// Returns the end of this [`Span`].
328    ///
329    /// # Examples
330    ///
331    /// ```
332    /// use line_column::span::*;
333    ///
334    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
335    /// assert_eq!(span.end().range(), TextRange::new(4.into(), 4.into()));
336    /// ```
337    pub fn end(&self) -> Self {
338        self.create(TextRange::empty(self.range.end()))
339    }
340
341    /// Returns the start index of this [`Span`] range.
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// use line_column::span::*;
347    ///
348    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
349    /// assert_eq!(span.index(), TextSize::new(1));
350    /// ```
351    #[inline]
352    pub fn index(&self) -> TextSize {
353        self.range().start()
354    }
355
356    /// Returns the source text of the range reference.
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// use line_column::span::*;
362    ///
363    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
364    /// assert_eq!(span.text(), "bcd");
365    /// ```
366    #[doc(alias = "as_str")]
367    pub fn text(&self) -> &str {
368        &self.source()[self.range()]
369    }
370
371    /// Returns the source text of the range reference.
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use line_column::span::*;
377    ///
378    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
379    /// assert_eq!(span.range(),       TextRange::new(1.into(), 4.into()));
380    /// ```
381    pub fn range(&self) -> TextRange {
382        self.range
383    }
384
385    /// Returns the source text.
386    ///
387    /// # Examples
388    ///
389    /// ```
390    /// use line_column::span::*;
391    ///
392    /// let span = Span::new("abcdef", TextRange::new(1.into(), 4.into()));
393    /// assert_eq!(span.source(), "abcdef");
394    /// assert_eq!(span.text(),   "bcd");
395    /// ```
396    pub fn source(&self) -> &str {
397        &self.source
398    }
399}
400
401impl Span {
402    /// Use [`line_column`](crate::line_column) calculate line and column
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use line_column::span::*;
408    ///
409    /// let span = Span::new("ab\ncdef", TextRange::empty(TextSize::of("ab\ncd")));
410    /// assert_eq!(span.before().text(), "ab\ncd");
411    /// assert_eq!(span.line_column(), (2, 3));
412    /// ```
413    pub fn line_column(&self) -> (u32, u32) {
414        crate::line_column(self.source(), self.index().into())
415    }
416
417    /// Get line from [`Span::line_column`]
418    pub fn line(&self) -> u32 {
419        self.line_column().0
420    }
421
422    /// Get column from [`Span::line_column`]
423    pub fn column(&self) -> u32 {
424        self.line_column().1
425    }
426
427    /// Returns the current line of this [`Span`].
428    ///
429    /// Maybe include end of line char, like `'\n'`.
430    ///
431    /// # Examples
432    ///
433    /// ```
434    /// use line_column::span::*;
435    ///
436    /// let span = Span::new_full("foo\nbar\nbaz");
437    /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
438    /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
439    /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
440    ///
441    /// assert_eq!(next.text(), "bar\nb");
442    /// assert_eq!(tail.text(), "baz");
443    /// assert_eq!(endl.text(), "\nba");
444    ///
445    /// assert_eq!(span.current_line().text(), "foo\n");
446    /// assert_eq!(next.current_line().text(), "bar\n");
447    /// assert_eq!(tail.current_line().text(), "baz");
448    /// assert_eq!(endl.current_line().text(), "foo\n");
449    /// ```
450    pub fn current_line(&self) -> Self {
451        let before = &self.source[..self.range.start().into()];
452        let line_start = before.rfind('\n').map_or(0, |it| it+1);
453        let rest = &self.source[line_start..];
454
455        let line_len = match rest.split_once('\n') {
456            Some((line, _)) => TextSize::of(line) + TextSize::of('\n'),
457            None => TextSize::of(rest),
458        };
459        let range = TextRange::at(len_size(line_start), line_len);
460        self.create(range)
461    }
462
463    /// Returns the previous line of this [`Span`].
464    ///
465    /// # Examples
466    ///
467    /// ```
468    /// use line_column::span::*;
469    ///
470    /// let span = Span::new_full("foo\nbar\nbaz");
471    /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
472    /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
473    /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
474    ///
475    /// assert_eq!(next.text(), "bar\nb");
476    /// assert_eq!(tail.text(), "baz");
477    /// assert_eq!(endl.text(), "\nba");
478    ///
479    /// assert_eq!(span.prev_line().text(), "");
480    /// assert_eq!(next.prev_line().text(), "foo\n");
481    /// assert_eq!(tail.prev_line().text(), "bar\n");
482    /// assert_eq!(endl.prev_line().text(), "");
483    /// ```
484    pub fn prev_line(&self) -> Self {
485        let index = self.current_line().index();
486        if let Some(prev_line_offset) = index.checked_sub(TextSize::of('\n')) {
487            self.create(TextRange::empty(prev_line_offset)).current_line()
488        } else {
489            self.create(TextRange::empty(TextSize::new(0)))
490        }
491    }
492
493    /// Returns the next line of this [`Span`].
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// use line_column::span::*;
499    ///
500    /// let span = Span::new_full("foo\nbar\nbaz");
501    /// let next = span.create(TextRange::at(TextSize::of("foo\n"), 5.into()));
502    /// let tail = span.create(TextRange::at(TextSize::of("foo\nbar\n"), 3.into()));
503    /// let endl = span.create(TextRange::at(TextSize::of("foo"), 3.into()));
504    ///
505    /// assert_eq!(next.text(), "bar\nb");
506    /// assert_eq!(tail.text(), "baz");
507    /// assert_eq!(endl.text(), "\nba");
508    ///
509    /// assert_eq!(span.next_line().text(), "bar\n");
510    /// assert_eq!(next.next_line().text(), "baz");
511    /// assert_eq!(tail.next_line().text(), "");
512    /// assert_eq!(endl.next_line().text(), "bar\n");
513    /// ```
514    pub fn next_line(&self) -> Self {
515        let cur_line_end = self.current_line().range().end();
516        if self.source().len() == cur_line_end.into() {
517            self.create(TextRange::empty(cur_line_end))
518        } else {
519            let range = TextRange::empty(cur_line_end);
520            self.create(range).current_line()
521        }
522    }
523}
524
525impl Span {
526    /// Returns the trim end of this [`Span`] range.
527    ///
528    /// # Examples
529    ///
530    /// ```
531    /// use line_column::span::*;
532    ///
533    /// let span = Span::new("foo  bar  baz", TextRange::new(4.into(), 9.into()));
534    /// assert_eq!(span.text(), " bar ");
535    /// assert_eq!(span.trim_end().text(), " bar");
536    /// ```
537    pub fn trim_end(&self) -> Self {
538        let text = self.text();
539        let trimmed = text.trim_end();
540        let len = TextSize::of(trimmed);
541        self.create(TextRange::at(self.range.start(), len))
542    }
543
544    /// Returns the trim start of this [`Span`] range.
545    ///
546    /// # Examples
547    ///
548    /// ```
549    /// use line_column::span::*;
550    ///
551    /// let span = Span::new("foo  bar  baz", TextRange::new(4.into(), 9.into()));
552    /// assert_eq!(span.text(), " bar ");
553    /// assert_eq!(span.trim_start().text(), "bar ");
554    /// ```
555    pub fn trim_start(&self) -> Self {
556        let text = self.text();
557        let trimmed = text.trim_start();
558        let len = TextSize::of(trimmed);
559
560        let offset = TextSize::of(text) - len;
561        let start = self.range.start() + offset;
562        self.create(TextRange::at(start, len))
563    }
564}
565
566#[inline]
567#[track_caller]
568fn len_size(len: usize) -> TextSize {
569    match TextSize::try_from(len) {
570        Ok(source_length) => source_length,
571        _ => panic!("source length {len} overflow TextSize"),
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use core::iter::successors;
578    use std::{format, vec::Vec};
579
580    use super::*;
581
582    #[track_caller]
583    fn check_texts(spans: impl IntoIterator<Item = Span>, expect: &[&str]) {
584        let spans = Vec::from_iter(spans);
585        let texts = spans.iter().map(|it| it.text()).collect::<Vec<_>>();
586        assert_eq!(texts, expect);
587    }
588
589    #[cfg(feature = "sync")]
590    fn _test_sync(span: Span) {
591        fn check_sync(_: impl Sync) {}
592        check_sync(span);
593    }
594
595    #[test]
596    #[should_panic = "range end > source length"]
597    fn new_panic_out_of_source() {
598        let _span = Span::new("x", TextRange::up_to(TextSize::of("xy")));
599    }
600
601    #[test]
602    fn next_lines_without_end_eol() {
603        let source = "foo\nbar\n\nbaz";
604        let span = Span::new_full(source);
605        let lines =
606            successors(span.current_line().into(), |it| Some(it.next_line()))
607                .take_while(|it| !it.is_empty())
608                .collect::<Vec<_>>();
609        check_texts(lines, &[
610            "foo\n",
611            "bar\n",
612            "\n",
613            "baz",
614        ]);
615    }
616
617    #[test]
618    fn next_lines_multi_bytes_char() {
619        let source = "测试\n实现\n\n多字节";
620        let span = Span::new_full(source);
621        let lines =
622            successors(span.current_line().into(), |it| Some(it.next_line()))
623                .take_while(|it| !it.is_empty())
624                .collect::<Vec<_>>();
625        check_texts(lines, &[
626            "测试\n",
627            "实现\n",
628            "\n",
629            "多字节",
630        ]);
631    }
632
633    #[test]
634    fn next_lines_with_end_eol() {
635        let source = "foo\nbar\n\nbaz\n";
636        let span = Span::new_full(source);
637        let lines =
638            successors(span.current_line().into(), |it| Some(it.next_line()))
639                .take_while(|it| !it.is_empty())
640                .collect::<Vec<_>>();
641        check_texts(lines, &[
642            "foo\n",
643            "bar\n",
644            "\n",
645            "baz\n",
646        ]);
647    }
648
649    #[test]
650    fn next_lines_first_empty_line() {
651        let source = "\nfoo\nbar\n\nbaz";
652        let span = Span::new_full(source);
653        let lines =
654            successors(span.current_line().into(), |it| Some(it.next_line()))
655                .take_while(|it| !it.is_empty())
656                .collect::<Vec<_>>();
657        check_texts(lines, &[
658            "\n",
659            "foo\n",
660            "bar\n",
661            "\n",
662            "baz",
663        ]);
664    }
665
666    #[test]
667    fn prev_lines_with_end_eol() {
668        let source = "foo\nbar\n\nbaz\n";
669        let span = Span::new(source, TextRange::empty(TextSize::of(source)));
670        let lines =
671            successors(span.current_line().into(), |it| Some(it.prev_line()))
672                .skip(1)
673                .take_while(|it| !it.is_empty())
674                .collect::<Vec<_>>();
675        check_texts(lines, &[
676            "baz\n",
677            "\n",
678            "bar\n",
679            "foo\n",
680        ]);
681    }
682
683    #[test]
684    fn prev_lines_without_end_eol() {
685        let source = "foo\nbar\n\nbaz";
686        let span = Span::new(source, TextRange::empty(TextSize::of(source)));
687        let lines =
688            successors(span.current_line().into(), |it| Some(it.prev_line()))
689                .take_while(|it| !it.is_empty())
690                .collect::<Vec<_>>();
691        check_texts(lines, &[
692            "baz",
693            "\n",
694            "bar\n",
695            "foo\n",
696        ]);
697    }
698
699    #[test]
700    fn prev_lines_multi_bytes_char() {
701        let source = "测试\n实现\n\n多字节";
702        let span = Span::new(source, TextRange::empty(TextSize::of(source)));
703        let lines =
704            successors(span.current_line().into(), |it| Some(it.prev_line()))
705                .take_while(|it| !it.is_empty())
706                .collect::<Vec<_>>();
707        check_texts(lines, &[
708            "多字节",
709            "\n",
710            "实现\n",
711            "测试\n",
712        ]);
713    }
714
715    #[test]
716    fn test_trim_start() {
717        let datas = [
718            "",
719            "f",
720            "foo",
721            " ",
722            " f",
723            " foo",
724            "  ",
725            "  f",
726            "  foo",
727            "  f",
728            "  foo",
729        ];
730        for prefix in ["", "x"] {
731            for suffix in ["", "x", " ", "  "] {
732                for data in datas {
733                    let source = format!("{prefix}{data}{suffix}");
734                    let range = TextRange::new(
735                        TextSize::of(prefix),
736                        TextSize::of(&source),
737                    );
738                    let span = Span::new(&source, range);
739                    assert_eq!(span.trim_start().text(), source[range].trim_start());
740                }
741            }
742        }
743    }
744
745    #[test]
746    fn test_trim_end() {
747        let datas = [
748            "",
749            "f",
750            "foo",
751            " ",
752            " f",
753            "foo ",
754            "  ",
755            "f  ",
756            "foo  ",
757            "f  ",
758            "foo  ",
759        ];
760        for prefix in ["", "x", " ", "  "] {
761            for suffix in ["", "x"] {
762                for data in datas {
763                    let source = format!("{prefix}{data}{suffix}");
764                    let range = TextRange::new(
765                        TextSize::new(0),
766                        TextSize::of(&source) - TextSize::of(suffix),
767                    );
768                    let span = Span::new(&source, range);
769                    assert_eq!(span.trim_end().text(), source[range].trim_end());
770                }
771            }
772        }
773    }
774}