1use std::fmt;
4use std::ops::Range;
5
6#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub struct FileId(pub u32);
9
10impl fmt::Debug for FileId {
11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12 write!(f, "f{}", self.0)
13 }
14}
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash)]
19pub struct Span {
20 pub file: FileId,
21 pub start: u32,
22 pub end: u32,
23}
24
25impl Span {
26 pub fn new(file: FileId, start: usize, end: usize) -> Self {
27 Span { file, start: start as u32, end: end as u32 }
28 }
29
30 pub fn to(self, other: Span) -> Span {
32 debug_assert_eq!(self.file, other.file);
33 Span { file: self.file, start: self.start.min(other.start), end: self.end.max(other.end) }
34 }
35
36 pub fn range(self) -> Range<usize> {
37 self.start as usize..self.end as usize
38 }
39
40 pub fn len(self) -> usize {
41 (self.end - self.start) as usize
42 }
43
44 pub fn is_empty(self) -> bool {
45 self.start == self.end
46 }
47}
48
49impl fmt::Debug for Span {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "{}..{}", self.start, self.end)
52 }
53}
54
55pub struct SourceFile {
56 pub name: String,
57 pub text: String,
58 line_starts: Vec<u32>,
59 pub(crate) ariadne: ariadne::Source<String>,
60}
61
62impl SourceFile {
63 fn new(name: String, text: String) -> Self {
64 let mut line_starts = vec![0];
65 for (i, b) in text.bytes().enumerate() {
66 if b == b'\n' {
67 line_starts.push(i as u32 + 1);
68 }
69 }
70 let ariadne = ariadne::Source::from(text.clone());
71 SourceFile { name, text, line_starts, ariadne }
72 }
73
74 pub fn line_col(&self, offset: u32) -> (usize, usize) {
76 let line = self.line_starts.partition_point(|&s| s <= offset).max(1) - 1;
77 let start = self.line_starts[line] as usize;
78 let col = self.text[start..offset as usize].chars().count();
79 (line + 1, col + 1)
80 }
81
82 pub fn slice(&self, span: Span) -> &str {
83 &self.text[span.range()]
84 }
85}
86
87#[derive(Default)]
89pub struct SourceMap {
90 files: Vec<SourceFile>,
91}
92
93impl SourceMap {
94 pub fn new() -> Self {
95 Self::default()
96 }
97
98 pub fn add(&mut self, name: impl Into<String>, text: impl Into<String>) -> FileId {
99 let id = FileId(self.files.len() as u32);
100 self.files.push(SourceFile::new(name.into(), text.into()));
101 id
102 }
103
104 pub fn load(&mut self, path: &std::path::Path) -> std::io::Result<FileId> {
106 let text = std::fs::read_to_string(path)?;
107 Ok(self.add(path.display().to_string(), text))
108 }
109
110 pub fn file(&self, id: FileId) -> &SourceFile {
111 &self.files[id.0 as usize]
112 }
113
114 pub fn text(&self, id: FileId) -> &str {
115 &self.file(id).text
116 }
117
118 pub fn slice(&self, span: Span) -> &str {
119 self.file(span.file).slice(span)
120 }
121
122 pub fn location(&self, span: Span) -> String {
124 let f = self.file(span.file);
125 let (l, c) = f.line_col(span.start);
126 format!("{}:{}:{}", f.name, l, c)
127 }
128
129 pub fn files(&self) -> impl Iterator<Item = (FileId, &SourceFile)> {
130 self.files.iter().enumerate().map(|(i, f)| (FileId(i as u32), f))
131 }
132}