Skip to main content

probl_syntax/
span.rs

1//! Source text and byte-offset spans into it.
2
3use std::fmt;
4
5/// A half-open byte range `lo..hi` in a source file.
6#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
7pub struct Span {
8    pub lo: u32,
9    pub hi: u32,
10}
11
12impl Span {
13    pub fn new(lo: usize, hi: usize) -> Span {
14        Span {
15            lo: lo as u32,
16            hi: hi as u32,
17        }
18    }
19
20    /// The smallest span covering both `self` and `other`.
21    pub fn to(self, other: Span) -> Span {
22        Span {
23            lo: self.lo.min(other.lo),
24            hi: self.hi.max(other.hi),
25        }
26    }
27
28    pub fn range(self) -> std::ops::Range<usize> {
29        self.lo as usize..self.hi as usize
30    }
31
32    pub fn shifted(self, by: u32) -> Span {
33        Span {
34            lo: self.lo + by,
35            hi: self.hi + by,
36        }
37    }
38}
39
40impl fmt::Debug for Span {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "{}..{}", self.lo, self.hi)
43    }
44}
45
46/// A named piece of source text.
47#[derive(Clone, Debug)]
48pub struct SourceFile {
49    pub name: String,
50    pub text: String,
51}
52
53impl SourceFile {
54    pub fn new(name: impl Into<String>, text: impl Into<String>) -> SourceFile {
55        SourceFile {
56            name: name.into(),
57            text: text.into(),
58        }
59    }
60
61    /// The source text covered by `span`.
62    pub fn slice(&self, span: Span) -> &str {
63        self.text.get(span.range()).unwrap_or("")
64    }
65
66    /// 1-based line and column of a byte offset.
67    pub fn line_col(&self, offset: u32) -> (usize, usize) {
68        let offset = (offset as usize).min(self.text.len());
69        let before = &self.text[..offset];
70        let line = before.matches('\n').count() + 1;
71        let col = before.rfind('\n').map_or(offset, |nl| offset - nl - 1) + 1;
72        (line, col)
73    }
74}