1use crate::SourceId;
2use lazy_static::lazy_static;
3use serde::{Deserialize, Serialize};
4use std::{
5 cmp,
6 fmt::{self, Display},
7 hash::Hash,
8 sync::Arc,
9};
10
11lazy_static! {
12 static ref DUMMY_SPAN: Span = Span::new(
13 Source {
14 text: Arc::from(""),
15 line_starts: Arc::new(vec![])
16 },
17 0,
18 0,
19 None
20 )
21 .unwrap();
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(transparent, remote = "Self")]
28pub struct Source {
29 pub text: Arc<str>,
30 #[serde(skip)]
31 pub line_starts: Arc<Vec<usize>>,
32}
33
34impl serde::Serialize for Source {
35 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36 where
37 S: serde::Serializer,
38 {
39 Self::serialize(self, serializer)
40 }
41}
42
43impl<'de> serde::Deserialize<'de> for Source {
44 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
45 let mut src = Self::deserialize(deserializer)?;
46 src.line_starts = Self::calc_line_starts(&src.text);
47 Ok(src)
48 }
49}
50
51impl Source {
52 fn calc_line_starts(text: &str) -> Arc<Vec<usize>> {
53 let mut lines_starts = Vec::with_capacity(text.len() / 80);
54 lines_starts.push(0);
55 for (idx, c) in text.char_indices() {
56 if c == '\n' {
57 lines_starts.push(idx + c.len_utf8())
58 }
59 }
60 Arc::new(lines_starts)
61 }
62
63 pub fn new(text: &str) -> Self {
64 Self {
65 text: Arc::from(text),
66 line_starts: Self::calc_line_starts(text),
67 }
68 }
69
70 pub fn line_col_zero_index(&self, position: usize) -> LineCol {
72 if position > self.text.len() || self.text.is_empty() {
73 LineCol { line: 0, col: 0 }
74 } else {
75 let (line, line_start) = match self.line_starts.binary_search(&position) {
76 Ok(line) => (line, self.line_starts.get(line)),
77 Err(0) => (0, None),
78 Err(line) => (line - 1, self.line_starts.get(line - 1)),
79 };
80 line_start.map_or(LineCol { line: 0, col: 0 }, |line_start| LineCol {
81 line,
82 col: position - line_start,
83 })
84 }
85 }
86
87 pub fn line_col_one_index(&self, position: usize) -> LineCol {
89 let LineCol { line, col } = self.line_col_zero_index(position);
90 LineCol {
91 line: line + 1,
92 col: col + 1,
93 }
94 }
95}
96
97impl From<&str> for Source {
98 fn from(value: &str) -> Self {
99 Self::new(value)
100 }
101}
102
103#[derive(Clone, Serialize, Deserialize)]
105pub struct Span {
106 src: Source,
108 start: usize,
110 end: usize,
112 source_id: Option<SourceId>,
114}
115
116impl PartialOrd for Span {
117 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
118 if !Arc::ptr_eq(&self.src.text, &other.src.text) {
119 None
120 } else {
121 match self.start.partial_cmp(&other.start) {
122 Some(core::cmp::Ordering::Equal) => self.end.partial_cmp(&other.end),
123 ord => ord,
124 }
125 }
126 }
127}
128
129impl Hash for Span {
130 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
131 self.start.hash(state);
132 self.end.hash(state);
133 self.source_id.hash(state);
134 }
135}
136
137impl PartialEq for Span {
138 fn eq(&self, other: &Self) -> bool {
139 self.start == other.start && self.end == other.end && self.source_id == other.source_id
140 }
141}
142
143impl Eq for Span {}
144
145impl From<Span> for std::ops::Range<usize> {
146 fn from(value: Span) -> Self {
147 Self {
148 start: value.start,
149 end: value.end,
150 }
151 }
152}
153
154impl Span {
155 pub fn dummy() -> Span {
156 DUMMY_SPAN.clone()
157 }
158
159 pub fn new(src: Source, start: usize, end: usize, source: Option<SourceId>) -> Option<Span> {
160 let _ = src.text.get(start..end)?;
161 Some(Span {
162 src,
163 start,
164 end,
165 source_id: source,
166 })
167 }
168
169 pub fn empty_at_start(span: &Span) -> Span {
173 Span::new(
174 span.src().clone(),
175 span.start(),
176 span.start(),
177 span.source_id().copied(),
178 )
179 .expect("the existing `span` is a valid `Span`")
180 }
181
182 pub fn empty_at_end(span: &Span) -> Span {
186 Span::new(
187 span.src().clone(),
188 span.end(),
189 span.end(),
190 span.source_id().copied(),
191 )
192 .expect("the existing `span` is a valid `Span`")
193 }
194
195 pub fn from_string(source: String) -> Span {
196 let len = source.len();
197 Span::new(Source::new(&source), 0, len, None).unwrap()
198 }
199
200 pub fn src(&self) -> &Source {
201 &self.src
202 }
203
204 pub fn source_id(&self) -> Option<&SourceId> {
205 self.source_id.as_ref()
206 }
207
208 pub fn start(&self) -> usize {
209 self.start
210 }
211
212 pub fn end(&self) -> usize {
213 self.end
214 }
215
216 pub fn start_line_col_one_index(&self) -> LineCol {
218 self.src.line_col_one_index(self.start)
219 }
220
221 pub fn end_line_col_one_index(&self) -> LineCol {
223 self.src.line_col_one_index(self.end)
224 }
225
226 pub fn start_span(&self) -> Span {
228 Self::empty_at_start(self)
229 }
230
231 pub fn end_span(&self) -> Span {
233 Self::empty_at_end(self)
234 }
235
236 pub fn str(self) -> String {
237 self.as_str().to_owned()
238 }
239
240 pub fn as_str(&self) -> &str {
241 &self.src.text[self.start..self.end]
242 }
243
244 pub fn input(&self) -> &str {
245 &self.src.text
246 }
247
248 pub fn trim(self) -> Span {
249 let start_delta = self.as_str().len() - self.as_str().trim_start().len();
250 let end_delta = self.as_str().len() - self.as_str().trim_end().len();
251 Span {
252 src: self.src,
253 start: self.start + start_delta,
254 end: self.end - end_delta,
255 source_id: self.source_id,
256 }
257 }
258
259 pub fn next_char_utf8(&self) -> Option<Span> {
267 let char = self.src.text[self.end..].chars().next()?;
268 Some(Span {
269 src: self.src.clone(),
270 source_id: self.source_id,
271 start: self.end,
272 end: self.end + char.len_utf8(),
273 })
274 }
275
276 pub fn join(s1: Span, s2: &Span) -> Span {
279 assert!(
280 Arc::ptr_eq(&s1.src.text, &s2.src.text) && s1.source_id == s2.source_id,
281 "Spans from different files cannot be joined.",
282 );
283
284 Span {
285 src: s1.src,
286 start: cmp::min(s1.start, s2.start),
287 end: cmp::max(s1.end, s2.end),
288 source_id: s1.source_id,
289 }
290 }
291
292 pub fn join_all(spans: impl IntoIterator<Item = Span>) -> Span {
293 spans
294 .into_iter()
295 .reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
296 .unwrap_or_else(Span::dummy)
297 }
298
299 pub fn line_col_one_index(&self) -> LineColRange {
301 LineColRange {
302 start: self.start_line_col_one_index(),
303 end: self.end_line_col_one_index(),
304 }
305 }
306
307 pub fn is_dummy(&self) -> bool {
308 self.eq(&DUMMY_SPAN)
309 }
310
311 pub fn is_empty(&self) -> bool {
312 self.start == self.end
313 }
314
315 pub fn contains(&self, other: &Span) -> bool {
317 Arc::ptr_eq(&self.src.text, &other.src.text)
318 && self.source_id == other.source_id
319 && self.start <= other.start
320 && self.end >= other.end
321 }
322
323 pub fn subset_first_of(&self, needle: &str) -> Option<Span> {
325 let text = &self.src().text;
326 let needle_offset = text[self.start..].find(needle)?;
327 Span::new(
328 self.src().clone(),
329 self.start,
330 self.start + needle_offset,
331 self.source_id().cloned(),
332 )
333 }
334}
335
336impl fmt::Debug for Span {
337 #[cfg(not(feature = "no-span-debug"))]
338 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
339 fmt.debug_struct("Span")
340 .field("src (ptr)", &self.src.text.as_ptr())
341 .field("source_id", &self.source_id)
342 .field("start", &self.start)
343 .field("end", &self.end)
344 .field("as_str()", &self.as_str())
345 .finish()
346 }
347 #[cfg(feature = "no-span-debug")]
348 fn fmt(&self, _fmt: &mut fmt::Formatter) -> fmt::Result {
349 Ok(())
350 }
351}
352
353pub trait Spanned {
354 fn span(&self) -> Span;
355}
356
357impl<T: Spanned> Spanned for Box<T> {
358 fn span(&self) -> Span {
359 (**self).span()
360 }
361}
362
363#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
364pub struct LineCol {
365 pub line: usize,
366 pub col: usize,
367}
368
369pub struct LineColRange {
370 pub start: LineCol,
371 pub end: LineCol,
372}
373
374impl Display for LineColRange {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 f.write_fmt(format_args!("({}, {})", self.start, self.end))
377 }
378}
379
380impl Display for LineCol {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 f.write_fmt(format_args!("line {}:{}", self.line, self.col))
383 }
384}