1use std::ops::Range;
2use std::path::{Path, PathBuf};
3use std::sync::atomic::{AtomicU32, Ordering};
4use std::sync::Arc;
5
6use rustc_hash::FxHasher;
7
8use super::*;
9
10type HashMap<K, V> = flurry::HashMap<K, V, core::hash::BuildHasherDefault<FxHasher>>;
11
12#[derive(Debug)]
30pub struct CodeMap {
31 files: HashMap<SourceId, Arc<SourceFile>>,
32 names: HashMap<FileName, SourceId>,
33 seen: HashMap<PathBuf, SourceId>,
34 next_file_id: AtomicU32,
35}
36impl CodeMap {
37 pub fn new() -> Self {
39 Self {
40 files: HashMap::default(),
41 names: HashMap::default(),
42 seen: HashMap::default(),
43 next_file_id: AtomicU32::new(1),
44 }
45 }
46
47 pub fn add(&self, name: impl Into<FileName>, source: String) -> SourceId {
55 let name = name.into();
64 if let FileName::Real(ref path) = name {
65 let guard = self.seen.guard();
66 match self.seen.get(path, &guard) {
67 Some(id) => *id,
68 None => {
69 let path = path.clone();
70 let source_id = self.insert_file(name, source, None);
71 match self.seen.try_insert(path, source_id, &guard) {
72 Ok(id) => *id,
73 Err(err) => *err.current,
74 }
75 }
76 }
77 } else {
78 self.insert_file(name, source, None)
79 }
80 }
81
82 pub fn add_file<P: AsRef<Path>>(&self, path: P) -> std::io::Result<SourceId> {
87 let path = path.as_ref();
88 let name = path.into();
89 let guard = self.seen.guard();
90 match self.seen.get(path, &guard) {
91 Some(id) => Ok(*id),
92 None => {
93 let source = std::fs::read_to_string(path)?;
94 let source_id = self.insert_file(name, source, None);
95 match self.seen.try_insert(path.to_path_buf(), source_id, &guard) {
96 Ok(id) => Ok(*id),
97 Err(err) => Ok(*err.current),
98 }
99 }
100 }
101 }
102
103 pub fn add_child(
114 &self,
115 name: impl Into<FileName>,
116 source: String,
117 parent: SourceSpan,
118 ) -> SourceId {
119 self.insert_file(name.into(), source, Some(parent))
120 }
121
122 fn insert_file(&self, name: FileName, source: String, parent: Option<SourceSpan>) -> SourceId {
123 let file_id = self.next_file_id();
124 let filename = name.clone();
125 let name_guard = self.names.guard();
126 self.names.insert(filename, file_id, &name_guard);
127 let file_guard = self.files.guard();
128 self.files.insert(
129 file_id,
130 Arc::new(SourceFile::new(file_id, name, source, parent)),
131 &file_guard,
132 );
133 file_id
134 }
135
136 pub fn get(&self, file_id: SourceId) -> Result<Arc<SourceFile>, Error> {
138 if file_id == SourceId::UNKNOWN {
139 Err(Error::FileMissing)
140 } else {
141 let guard = self.files.guard();
142 self.files
143 .get(&file_id, &guard)
144 .cloned()
145 .ok_or(Error::FileMissing)
146 }
147 }
148
149 pub fn get_with_span(&self, span: SourceSpan) -> Result<Arc<SourceFile>, Error> {
153 self.get(span.source_id)
154 }
155
156 pub fn parent(&self, file_id: SourceId) -> Option<SourceSpan> {
160 self.get(file_id).ok().and_then(|f| f.parent())
161 }
162
163 pub fn get_file_id(&self, filename: &FileName) -> Option<SourceId> {
165 let guard = self.names.guard();
166 self.names.get(filename, &guard).copied()
167 }
168
169 pub fn get_by_name(&self, filename: &FileName) -> Option<Arc<SourceFile>> {
171 self.get_file_id(filename).and_then(|id| self.get(id).ok())
172 }
173
174 pub fn name(&self, file_id: SourceId) -> Result<FileName, Error> {
178 let file = self.get(file_id)?;
179 Ok(file.name().clone())
180 }
181
182 pub fn name_for_spanned<S: Spanned>(&self, spanned: &S) -> Result<FileName, Error> {
186 self.name(spanned.span().source_id)
187 }
188
189 pub fn line_column_to_span(
195 &self,
196 file_id: SourceId,
197 line: impl Into<LineIndex>,
198 column: impl Into<ColumnIndex>,
199 ) -> Result<SourceSpan, Error> {
200 let f = self.get(file_id)?;
201 let span = f.line_column_to_span(line.into(), column.into())?;
202 let start = SourceIndex::new(file_id, span.start());
203 let end = SourceIndex::new(file_id, span.end());
204 Ok(SourceSpan::new(start, end))
205 }
206
207 fn line_span(
208 &self,
209 file_id: SourceId,
210 line_index: impl Into<LineIndex>,
211 ) -> Result<codespan::Span, Error> {
212 let f = self.get(file_id)?;
213 f.line_span(line_index.into())
214 }
215
216 fn line_index(
217 &self,
218 file_id: SourceId,
219 byte_index: impl Into<ByteIndex>,
220 ) -> Result<LineIndex, Error> {
221 Ok(self.get(file_id)?.line_index(byte_index.into()))
222 }
223
224 pub fn location<S: Spanned>(&self, spanned: &S) -> Result<Location, Error> {
228 let span = spanned.span();
229 self.location_at_index(span.source_id, span.start)
230 }
231
232 pub fn location_at_index(
234 &self,
235 file_id: SourceId,
236 byte_index: impl Into<ByteIndex>,
237 ) -> Result<Location, Error> {
238 self.get(file_id)?.location(byte_index)
239 }
240
241 pub fn source_span(&self, file_id: SourceId) -> Result<SourceSpan, Error> {
243 Ok(self.get(file_id)?.source_span())
244 }
245
246 pub fn source_slice<'a, S: Spanned>(&'a self, spanned: &S) -> Result<&'a str, Error> {
248 let span = spanned.span();
249 let f = self.get(span.source_id)?;
250 let slice = f.source_slice(span)?;
251 unsafe { Ok(std::mem::transmute::<&str, &'a str>(slice)) }
252 }
253
254 #[inline(always)]
255 fn next_file_id(&self) -> SourceId {
256 let id = self.next_file_id.fetch_add(1, Ordering::Relaxed);
257 SourceId::new(id)
258 }
259}
260impl Default for CodeMap {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265impl<'a> Files<'a> for CodeMap {
266 type FileId = SourceId;
267 type Name = String;
268 type Source = &'a str;
269
270 fn name(&self, file_id: Self::FileId) -> Result<Self::Name, Error> {
271 Ok(format!("{}", self.get(file_id)?.name()))
272 }
273
274 fn source(&self, file_id: Self::FileId) -> Result<&'a str, Error> {
275 use std::mem;
276
277 let f = self.get(file_id)?;
278 Ok(unsafe { mem::transmute::<&str, &'a str>(f.source()) })
279 }
280
281 fn line_index(&self, file_id: Self::FileId, byte_index: usize) -> Result<usize, Error> {
282 Ok(self.line_index(file_id, byte_index as u32)?.to_usize())
283 }
284
285 fn line_range(&self, file_id: Self::FileId, line_index: usize) -> Result<Range<usize>, Error> {
286 let span = self.line_span(file_id, line_index as u32)?;
287
288 Ok(span.start().to_usize()..span.end().to_usize())
289 }
290}