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