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(Arc::from(""), 0, 0, None).unwrap();
13}
14
15pub struct Position<'a> {
16 input: &'a str,
17 pos: usize,
18}
19
20impl<'a> Position<'a> {
21 pub fn new(input: &'a str, pos: usize) -> Option<Position<'a>> {
22 input.get(pos..).map(|_| Position { input, pos })
23 }
24
25 pub fn line_col(&self) -> LineCol {
26 assert!(self.pos <= self.input.len(), "position out of bounds");
27
28 let newlines_up_to_pos = bytecount::count(&self.input.as_bytes()[..self.pos], b'\n');
30 let line = newlines_up_to_pos + 1;
31
32 let last_newline_pos = match self.input[..self.pos].rfind('\n') {
34 Some(pos) => pos + 1, None => 0, };
37
38 let col = self.pos - last_newline_pos + 1;
40 LineCol { line, col }
41 }
42}
43
44#[derive(Clone, Ord, PartialOrd, Serialize, Deserialize)]
46pub struct Span {
47 src: Arc<str>,
49 start: usize,
51 end: usize,
53 source_id: Option<SourceId>,
55}
56
57impl Hash for Span {
58 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
59 self.start.hash(state);
60 self.end.hash(state);
61 self.source_id.hash(state);
62 }
63}
64
65impl PartialEq for Span {
66 fn eq(&self, other: &Self) -> bool {
67 self.start == other.start && self.end == other.end && self.source_id == other.source_id
68 }
69}
70
71impl Eq for Span {}
72
73impl From<Span> for std::ops::Range<usize> {
74 fn from(value: Span) -> Self {
75 Self {
76 start: value.start,
77 end: value.end,
78 }
79 }
80}
81
82impl Span {
83 pub fn dummy() -> Span {
84 DUMMY_SPAN.clone()
85 }
86
87 pub fn new(src: Arc<str>, start: usize, end: usize, source: Option<SourceId>) -> Option<Span> {
88 let _ = src.get(start..end)?;
89 Some(Span {
90 src,
91 start,
92 end,
93 source_id: source,
94 })
95 }
96
97 pub fn empty_at_start(span: &Span) -> Span {
101 Span::new(
102 span.src().clone(),
103 span.start(),
104 span.start(),
105 span.source_id().copied(),
106 )
107 .expect("the existing `span` is a valid `Span`")
108 }
109
110 pub fn empty_at_end(span: &Span) -> Span {
114 Span::new(
115 span.src().clone(),
116 span.end(),
117 span.end(),
118 span.source_id().copied(),
119 )
120 .expect("the existing `span` is a valid `Span`")
121 }
122
123 pub fn from_string(source: String) -> Span {
124 let len = source.len();
125 Span::new(Arc::from(source), 0, len, None).unwrap()
126 }
127
128 pub fn src(&self) -> &Arc<str> {
129 &self.src
130 }
131
132 pub fn start(&self) -> usize {
133 self.start
134 }
135
136 pub fn end(&self) -> usize {
137 self.end
138 }
139
140 pub fn source_id(&self) -> Option<&SourceId> {
141 self.source_id.as_ref()
142 }
143
144 pub fn start_pos(&self) -> Position {
145 Position::new(&self.src, self.start).unwrap()
146 }
147
148 pub fn end_pos(&self) -> Position {
149 Position::new(&self.src, self.end).unwrap()
150 }
151
152 pub fn split(&self) -> (Position, Position) {
153 let start = self.start_pos();
154 let end = self.end_pos();
155 (start, end)
156 }
157
158 pub fn str(self) -> String {
159 self.as_str().to_owned()
160 }
161
162 pub fn as_str(&self) -> &str {
163 &self.src[self.start..self.end]
164 }
165
166 pub fn input(&self) -> &str {
167 &self.src
168 }
169
170 pub fn trim(self) -> Span {
171 let start_delta = self.as_str().len() - self.as_str().trim_start().len();
172 let end_delta = self.as_str().len() - self.as_str().trim_end().len();
173 Span {
174 src: self.src,
175 start: self.start + start_delta,
176 end: self.end - end_delta,
177 source_id: self.source_id,
178 }
179 }
180
181 pub fn next_char_utf8(&self) -> Option<Span> {
189 let char = self.src[self.end..].chars().next()?;
190 Some(Span {
191 src: self.src.clone(),
192 source_id: self.source_id,
193 start: self.end,
194 end: self.end + char.len_utf8(),
195 })
196 }
197
198 pub fn join(s1: Span, s2: &Span) -> Span {
201 assert!(
202 Arc::ptr_eq(&s1.src, &s2.src) && s1.source_id == s2.source_id,
203 "Spans from different files cannot be joined.",
204 );
205
206 Span {
207 src: s1.src,
208 start: cmp::min(s1.start, s2.start),
209 end: cmp::max(s1.end, s2.end),
210 source_id: s1.source_id,
211 }
212 }
213
214 pub fn join_all(spans: impl IntoIterator<Item = Span>) -> Span {
215 spans
216 .into_iter()
217 .reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
218 .unwrap_or_else(Span::dummy)
219 }
220
221 pub fn line_col(&self) -> LineColRange {
223 LineColRange {
224 start: self.start_pos().line_col(),
225 end: self.end_pos().line_col(),
226 }
227 }
228
229 pub fn is_dummy(&self) -> bool {
230 self.eq(&DUMMY_SPAN)
231 }
232
233 pub fn is_empty(&self) -> bool {
234 self.start == self.end
235 }
236
237 pub fn contains(&self, other: &Span) -> bool {
239 Arc::ptr_eq(&self.src, &other.src)
240 && self.source_id == other.source_id
241 && self.start <= other.start
242 && self.end >= other.end
243 }
244}
245
246impl fmt::Debug for Span {
247 #[cfg(not(feature = "no-span-debug"))]
248 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
249 fmt.debug_struct("Span")
250 .field("src (ptr)", &self.src.as_ptr())
251 .field("source_id", &self.source_id)
252 .field("start", &self.start)
253 .field("end", &self.end)
254 .field("as_str()", &self.as_str())
255 .finish()
256 }
257 #[cfg(feature = "no-span-debug")]
258 fn fmt(&self, _fmt: &mut fmt::Formatter) -> fmt::Result {
259 Ok(())
260 }
261}
262
263pub trait Spanned {
264 fn span(&self) -> Span;
265}
266
267impl<T: Spanned> Spanned for Box<T> {
268 fn span(&self) -> Span {
269 (**self).span()
270 }
271}
272
273#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
274pub struct LineCol {
275 pub line: usize,
276 pub col: usize,
277}
278
279pub struct LineColRange {
280 pub start: LineCol,
281 pub end: LineCol,
282}
283
284impl Display for LineColRange {
285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286 f.write_fmt(format_args!("({}, {})", self.start, self.end))
287 }
288}
289
290impl Display for LineCol {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 f.write_fmt(format_args!("line {}:{}", self.line, self.col))
293 }
294}