Skip to main content

java_lang/
span.rs

1use std::{cmp::Ordering, fmt, hash::Hash, ops::Range};
2
3/// A span of source code, represented as a range of byte offsets.
4///
5/// Spans are cheap to copy as they only contain byte offsets.
6#[derive(Clone, Copy, PartialEq, Eq, Hash)]
7pub struct Span {
8    start: usize,
9    end: usize,
10}
11
12impl Span {
13    /// Create a new span from byte offsets.
14    pub fn new(start: usize, end: usize) -> Self {
15        Span { start, end }
16    }
17
18    /// Create a span that covers two spans.
19    pub fn join(self, other: impl std::borrow::Borrow<Span>) -> Self {
20        let other = other.borrow();
21        Self {
22            start: self.start.min(other.start),
23            end: self.end.max(other.end),
24        }
25    }
26
27    /// Get the start byte offset.
28    pub fn start(&self) -> usize {
29        self.start
30    }
31
32    /// Get the end byte offset.
33    pub fn end(&self) -> usize {
34        self.end
35    }
36
37    /// Get the byte range.
38    pub fn range(&self) -> Range<usize> {
39        self.start..self.end
40    }
41
42    /// Get the source text covered by this span.
43    pub fn source_text(self, full_source: &str) -> &str {
44        let start = self.start.min(full_source.len());
45        let end = self.end.min(full_source.len());
46        &full_source[start..end]
47    }
48
49    /// Get the (line, column) of the start position (1-indexed).
50    pub fn start_line_col(self, source: &str) -> (usize, usize) {
51        let offset = self.start.min(source.len());
52        let mut line = 1;
53        let mut col = 1;
54        for (i, ch) in source.char_indices() {
55            if i >= offset {
56                break;
57            }
58            if ch == '\n' {
59                line += 1;
60                col = 1;
61            } else {
62                col += 1;
63            }
64        }
65        (line, col)
66    }
67
68    /// Create a call site span (no source tracking).
69    pub fn call_site() -> Self {
70        Span { start: 0, end: 0 }
71    }
72}
73
74impl PartialOrd for Span {
75    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76        Some(self.cmp(other))
77    }
78}
79
80impl Ord for Span {
81    fn cmp(&self, other: &Self) -> Ordering {
82        match self.start.cmp(&other.start) {
83            Ordering::Equal => self.end.cmp(&other.end),
84            ord => ord,
85        }
86    }
87}
88
89impl fmt::Debug for Span {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "Span({}..{})", self.start, self.end)
92    }
93}
94
95impl Default for Span {
96    fn default() -> Self {
97        Self::call_site()
98    }
99}