1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
5pub struct FileId(pub u32);
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub struct Span {
10 pub file: FileId,
12 pub start: u32,
14 pub end: u32,
16}
17
18impl Span {
19 pub fn new(file: FileId, start: usize, end: usize) -> Self {
21 Span {
22 file,
23 start: start as u32,
24 end: end as u32,
25 }
26 }
27
28 pub fn to(self, other: Span) -> Span {
30 Span {
31 file: self.file,
32 start: self.start.min(other.start),
33 end: self.end.max(other.end),
34 }
35 }
36
37 pub fn range(self) -> std::ops::Range<usize> {
39 self.start as usize..self.end as usize
40 }
41}
42
43#[derive(Clone, Debug)]
45pub struct SourceFile {
46 pub name: String,
48 pub text: String,
50 line_starts: Vec<usize>,
51}
52
53impl SourceFile {
54 pub fn new(name: impl Into<String>, text: impl Into<String>) -> Self {
56 let text = text.into();
57 let mut line_starts = vec![0];
58 line_starts.extend(text.match_indices('\n').map(|(i, _)| i + 1));
59 SourceFile {
60 name: name.into(),
61 text,
62 line_starts,
63 }
64 }
65
66 pub fn line_col(&self, offset: usize) -> (usize, usize) {
68 let offset = offset.min(self.text.len());
69 let line = match self.line_starts.binary_search(&offset) {
70 Ok(i) => i,
71 Err(i) => i - 1,
72 };
73 let start = self.line_starts[line];
74 let col = self
75 .text
76 .get(start..offset)
77 .map_or(offset - start, |s| s.chars().count());
78 (line + 1, col + 1)
79 }
80
81 pub fn line_text(&self, line: usize) -> &str {
83 let start = self.line_starts[line - 1];
84 let end = self
85 .line_starts
86 .get(line)
87 .copied()
88 .unwrap_or(self.text.len());
89 self.text[start..end].trim_end_matches(['\n', '\r'])
90 }
91
92 pub fn line_start(&self, line: usize) -> usize {
94 self.line_starts[line - 1]
95 }
96}
97
98#[derive(Clone, Debug, Default)]
100pub struct SourceMap {
101 files: Vec<SourceFile>,
102}
103
104impl SourceMap {
105 pub fn add(&mut self, name: impl Into<String>, text: impl Into<String>) -> FileId {
107 self.files.push(SourceFile::new(name, text));
108 FileId(self.files.len() as u32 - 1)
109 }
110
111 pub fn get(&self, id: FileId) -> &SourceFile {
113 &self.files[id.0 as usize]
114 }
115}