Skip to main content

lutra_compiler/codespan/
span.rs

1use std::fmt::{self, Debug, Formatter};
2
3/// A span of source code
4#[derive(Clone, PartialEq, Eq, Copy)]
5pub struct Span {
6    /// Byte offset from the start of the source. 0 indexed.
7    pub start: u32,
8    /// Length of the span in bytes.
9    pub len: u16,
10
11    /// A key representing the path of the source file.
12    /// Full path is stored in [crate::SourceTree::source_ids].
13    pub source_id: u16,
14}
15
16impl Span {
17    pub fn set_end_of(&mut self, other: &Span) {
18        assert_eq!(self.source_id, other.source_id);
19        self.set_end(other.end());
20    }
21
22    pub fn set_end(&mut self, end: u32) {
23        self.len = end.saturating_sub(self.start) as u16;
24    }
25
26    pub fn end(&self) -> u32 {
27        self.start + self.len as u32
28    }
29
30    pub fn get_slice<'s>(&self, text: &'s str) -> &'s str {
31        &text[self.start as usize..self.end() as usize]
32    }
33
34    pub fn take_slice(&self, mut text: String) -> String {
35        text.truncate(self.end() as usize);
36        text.split_off(self.start as usize)
37    }
38
39    pub fn overlap(&self, other: &Self) -> bool {
40        self.start >= other.start && self.start < other.start + other.len as u32
41    }
42}
43
44impl From<Span> for std::ops::Range<usize> {
45    fn from(a: Span) -> Self {
46        a.start as usize..(a.start as usize + a.len as usize)
47    }
48}
49
50impl Debug for Span {
51    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52        write!(f, "{}:{}-{}", self.source_id, self.start, self.end())
53    }
54}
55
56impl PartialOrd for Span {
57    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
58        // We could expand this to compare source_id too, starting with minimum surprise
59        match other.source_id.partial_cmp(&self.source_id) {
60            Some(std::cmp::Ordering::Equal) => self.start.partial_cmp(&other.start),
61            _ => None,
62        }
63    }
64}
65
66impl chumsky::Span for Span {
67    type Context = u16;
68
69    type Offset = u32;
70
71    fn new(context: Self::Context, range: std::ops::Range<Self::Offset>) -> Self {
72        Self {
73            start: range.start,
74            len: (range.end - range.start) as u16,
75            source_id: context,
76        }
77    }
78
79    fn context(&self) -> Self::Context {
80        self.source_id
81    }
82
83    fn start(&self) -> Self::Offset {
84        self.start
85    }
86
87    fn end(&self) -> Self::Offset {
88        self.start + self.len as u32
89    }
90}
91
92#[cfg(test)]
93mod test {
94    use super::*;
95
96    #[test]
97    fn test_span_partial_cmp() {
98        let span1 = Span {
99            start: 10,
100            len: 20,
101            source_id: 1,
102        };
103        let span2 = Span {
104            start: 15,
105            len: 25,
106            source_id: 1,
107        };
108        let span3 = Span {
109            start: 5,
110            len: 15,
111            source_id: 2,
112        };
113
114        // span1 and span2 have the same source_id, so their start values are compared
115        assert_eq!(span1.partial_cmp(&span2), Some(std::cmp::Ordering::Less));
116        assert_eq!(span2.partial_cmp(&span1), Some(std::cmp::Ordering::Greater));
117
118        // span1 and span3 have different source_id, so their source_id values are compared
119        assert_eq!(span1.partial_cmp(&span3), None);
120        assert_eq!(span3.partial_cmp(&span1), None);
121    }
122}