Skip to main content

brief/
span.rs

1use std::ops::Range;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub struct Span {
5    pub start: u32,
6    pub len: u32,
7}
8
9impl Span {
10    pub const DUMMY: Span = Span { start: 0, len: 0 };
11
12    pub fn new(start: usize, len: usize) -> Self {
13        Span {
14            start: start as u32,
15            len: len as u32,
16        }
17    }
18
19    pub fn end(&self) -> usize {
20        (self.start + self.len) as usize
21    }
22    pub fn range(&self) -> Range<usize> {
23        self.start as usize..self.end()
24    }
25
26    pub fn join(self, other: Span) -> Span {
27        let start = self.start.min(other.start);
28        let end = (self.start + self.len).max(other.start + other.len);
29        Span {
30            start,
31            len: end - start,
32        }
33    }
34}
35
36#[derive(Debug, Clone)]
37pub struct SourceMap {
38    pub path: String,
39    pub source: String,
40    line_starts: Vec<u32>,
41}
42
43impl SourceMap {
44    pub fn new(path: impl Into<String>, source: impl Into<String>) -> Self {
45        let source = source.into();
46        let mut line_starts = vec![0u32];
47        for (i, b) in source.bytes().enumerate() {
48            if b == b'\n' {
49                line_starts.push((i + 1) as u32);
50            }
51        }
52        SourceMap {
53            path: path.into(),
54            source,
55            line_starts,
56        }
57    }
58
59    pub fn line_col(&self, offset: u32) -> (usize, usize) {
60        let idx = match self.line_starts.binary_search(&offset) {
61            Ok(i) => i,
62            Err(i) => i.saturating_sub(1),
63        };
64        let line_start = self.line_starts[idx];
65        let prefix = &self.source.as_bytes()[line_start as usize..offset as usize];
66        let col = std::str::from_utf8(prefix)
67            .map(|s| s.chars().count())
68            .unwrap_or(0)
69            + 1;
70        (idx + 1, col)
71    }
72
73    pub fn line_text(&self, line: usize) -> &str {
74        let start = self.line_starts[line - 1] as usize;
75        let end = self
76            .line_starts
77            .get(line)
78            .map(|n| *n as usize)
79            .unwrap_or(self.source.len());
80        let s = &self.source[start..end];
81        s.trim_end_matches('\n').trim_end_matches('\r')
82    }
83}