1use dada_util::FromImpls;
2use salsa::Update;
3use serde::Serialize;
4
5use crate::{
6 ast::{AstAggregate, AstFunction},
7 inputs::SourceFile,
8};
9
10#[derive(
11 Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Update, FromImpls, Serialize,
12)]
13pub enum Anchor<'db> {
14 SourceFile(SourceFile),
15 Class(AstAggregate<'db>),
16 Function(AstFunction<'db>),
17}
18
19impl<'db> Anchor<'db> {
20 pub fn span(&self, db: &'db dyn crate::Db) -> Span<'db> {
21 match self {
22 Anchor::SourceFile(source_file) => Span {
23 anchor: *self,
24 start: Offset::ZERO,
25 end: Offset::from(source_file.contents_if_ok(db).len()),
26 },
27 Anchor::Class(data) => data.span(db),
28 Anchor::Function(data) => data.span(db),
29 }
30 }
31
32 pub fn absolute_span_of_contents(&self, db: &'db dyn crate::Db) -> AbsoluteSpan {
34 match self {
35 Anchor::SourceFile(source_file) => source_file.absolute_span(db),
36
37 Anchor::Class(data) => data.span(db).absolute_span(db).narrow(),
39 Anchor::Function(data) => data.span(db).absolute_span(db).narrow(),
40 }
41 }
42
43 pub fn source_file(&self, db: &dyn crate::Db) -> SourceFile {
44 match self {
45 Anchor::SourceFile(source_file) => *source_file,
46 Anchor::Class(ast_class_item) => ast_class_item.name_span(db).source_file(db),
47 Anchor::Function(ast_function) => ast_function.name(db).span.source_file(db),
48 }
49 }
50}
51
52#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Update)]
58pub struct Span<'db> {
59 pub anchor: Anchor<'db>,
60 pub start: Offset,
61 pub end: Offset,
62}
63
64impl serde::Serialize for Span<'_> {
65 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66 where
67 S: serde::Serializer,
68 {
69 salsa::with_attached_database(|db| {
70 let db: &dyn crate::Db = db.as_view();
71 serde::Serialize::serialize(&self.absolute_span(db), serializer)
72 })
73 .unwrap_or_else(|| panic!("cannot serialize without attached database"))
74 }
75}
76
77impl std::fmt::Debug for Span<'_> {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("Span")
80 .field("start", &self.start)
81 .field("end", &self.end)
82 .field("anchor", &"...")
83 .finish()
84 }
85}
86
87#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize)]
91pub struct AbsoluteSpan {
92 pub source_file: SourceFile,
93 pub start: AbsoluteOffset,
94 pub end: AbsoluteOffset,
95}
96
97impl AbsoluteSpan {
98 pub fn narrow(mut self) -> Self {
101 self.start = self.start + Offset::ONE;
102 self.end = self.end - Offset::ONE;
103 assert!(self.start <= self.end);
104 self
105 }
106
107 pub fn into_span(self, _db: &dyn crate::Db) -> Span<'_> {
109 Span {
110 anchor: self.source_file.into(),
111 start: Offset::from(self.start),
112 end: Offset::from(self.end),
113 }
114 }
115
116 pub fn contains(self, other: AbsoluteSpan) -> bool {
118 self.source_file == other.source_file && self.start <= other.start && self.end >= other.end
119 }
120}
121
122impl<'db> Span<'db> {
123 pub fn to(self, db: &'db dyn crate::Db, end: impl IntoOptionSpan<'db>) -> Span<'db> {
124 match end.into_opt_span() {
125 Some(end) => {
126 if self.anchor == end.anchor {
127 Span {
128 anchor: self.anchor,
129 start: self.start,
130 end: end.end,
131 }
132 } else {
133 self.absolute_span(db)
137 .into_span(db)
138 .to(db, end.absolute_span(db).into_span(db))
139 }
140 }
141 None => self,
142 }
143 }
144
145 pub fn start_from(self, start: impl IntoOptionSpan<'db>) -> Span<'db> {
146 match start.into_opt_span() {
147 Some(start) => {
148 assert!(self.anchor == start.anchor);
149 Span {
150 anchor: self.anchor,
151 start: start.start,
152 end: self.end,
153 }
154 }
155 None => self,
156 }
157 }
158
159 pub fn at_start(self) -> Span<'db> {
161 Span {
162 anchor: self.anchor,
163 start: self.end,
164 end: self.end,
165 }
166 }
167
168 pub fn at_end(self) -> Span<'db> {
170 Span {
171 anchor: self.anchor,
172 start: self.end,
173 end: self.end,
174 }
175 }
176
177 pub fn absolute_span(&self, db: &'db dyn crate::Db) -> AbsoluteSpan {
179 let anchor_span = self.anchor.absolute_span_of_contents(db);
180 AbsoluteSpan {
181 source_file: anchor_span.source_file,
182 start: anchor_span.start + self.start,
183 end: anchor_span.start + self.end,
184 }
185 }
186
187 pub fn source_file(&self, db: &dyn crate::Db) -> SourceFile {
188 self.anchor.source_file(db)
189 }
190}
191
192impl<'db> Spanned<'db> for Span<'db> {
193 fn span(&self, _db: &'db dyn crate::Db) -> Span<'db> {
194 *self
195 }
196}
197
198pub trait Spanned<'db> {
204 fn span(&self, db: &'db dyn crate::Db) -> Span<'db>;
205}
206
207pub trait SourceSpanned<'db> {
212 fn source_span(&self, db: &'db dyn crate::Db) -> Span<'db>;
213}
214
215pub trait IntoOptionSpan<'db> {
217 fn into_opt_span(self) -> Option<Span<'db>>;
218}
219
220impl<'db> IntoOptionSpan<'db> for Span<'db> {
221 fn into_opt_span(self) -> Option<Span<'db>> {
222 Some(self)
223 }
224}
225
226impl<'db> IntoOptionSpan<'db> for Option<Span<'db>> {
227 fn into_opt_span(self) -> Option<Span<'db>> {
228 self
229 }
230}
231
232#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
233pub struct Offset(u32);
234
235impl From<usize> for Offset {
236 fn from(offset: usize) -> Self {
237 assert!(offset < u32::MAX as usize);
238 Offset(offset as u32)
239 }
240}
241
242impl From<u32> for Offset {
243 fn from(offset: u32) -> Self {
244 Offset(offset)
245 }
246}
247
248impl From<AbsoluteOffset> for Offset {
249 fn from(offset: AbsoluteOffset) -> Self {
250 Offset(offset.0)
251 }
252}
253
254impl Offset {
255 pub const ZERO: Offset = Offset(0);
256 pub const ONE: Offset = Offset(1);
257
258 pub fn as_u32(&self) -> u32 {
259 self.0
260 }
261
262 pub fn as_usize(&self) -> usize {
263 self.0 as usize
264 }
265}
266
267impl std::ops::Add<usize> for Offset {
268 type Output = Offset;
269
270 fn add(self, rhs: usize) -> Self::Output {
271 assert!(rhs < u32::MAX as usize);
272 Offset(self.0.checked_add(rhs as u32).unwrap())
273 }
274}
275
276impl std::ops::Add<Offset> for Offset {
277 type Output = Offset;
278
279 fn add(self, rhs: Offset) -> Self::Output {
280 Offset(self.0.checked_add(rhs.0).unwrap())
281 }
282}
283
284impl std::ops::Sub<Offset> for Offset {
285 type Output = Offset;
286
287 fn sub(self, rhs: Offset) -> Self::Output {
288 Offset(self.0.checked_sub(rhs.0).unwrap())
289 }
290}
291
292#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
293pub struct AbsoluteOffset(u32);
294
295impl AbsoluteOffset {
296 pub const ZERO: AbsoluteOffset = AbsoluteOffset(0);
297
298 pub fn as_usize(self) -> usize {
299 self.0 as usize
300 }
301}
302
303impl From<usize> for AbsoluteOffset {
304 fn from(offset: usize) -> Self {
305 assert!(offset < u32::MAX as usize);
306 AbsoluteOffset(offset as u32)
307 }
308}
309
310impl From<u32> for AbsoluteOffset {
311 fn from(offset: u32) -> Self {
312 AbsoluteOffset(offset)
313 }
314}
315
316impl std::ops::Add<Offset> for AbsoluteOffset {
317 type Output = AbsoluteOffset;
318
319 fn add(self, rhs: Offset) -> Self::Output {
320 AbsoluteOffset(self.0.checked_add(rhs.0).unwrap())
321 }
322}
323
324impl std::ops::Sub<Offset> for AbsoluteOffset {
325 type Output = AbsoluteOffset;
326
327 fn sub(self, rhs: Offset) -> Self::Output {
328 AbsoluteOffset(self.0.checked_sub(rhs.0).unwrap())
329 }
330}
331
332impl std::ops::Sub<AbsoluteOffset> for AbsoluteOffset {
333 type Output = u32;
334
335 fn sub(self, rhs: AbsoluteOffset) -> Self::Output {
336 self.0.checked_sub(rhs.0).unwrap()
337 }
338}
339
340impl PartialOrd for AbsoluteSpan {
341 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
342 Some(Self::cmp(self, other))
343 }
344}
345
346impl Ord for AbsoluteSpan {
352 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
353 let AbsoluteSpan {
354 source_file,
355 start,
356 end,
357 } = self;
358 let AbsoluteSpan {
359 source_file: other_source_file,
360 start: other_start,
361 end: other_end,
362 } = other;
363
364 (source_file, end, other_start).cmp(&(other_source_file, other_end, start))
415 }
416}
417
418#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
420pub struct ZeroLine(u32);
421
422#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
424pub struct OneLine(u32);
425
426#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
428pub struct ZeroColumn(u32);
429
430#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize)]
432pub struct OneColumn(u32);
433
434macro_rules! methods {
435 ($t:ident) => {
436 impl $t {
437 pub fn as_u32(self) -> u32 {
438 self.0
439 }
440
441 pub fn as_usize(self) -> usize {
442 self.0 as usize
443 }
444 }
445
446 impl From<usize> for $t {
447 fn from(offset: usize) -> Self {
448 assert!(offset < u32::MAX as usize);
449 $t(offset as u32)
450 }
451 }
452
453 impl From<u32> for $t {
454 fn from(offset: u32) -> Self {
455 $t(offset)
456 }
457 }
458
459 impl std::ops::Add<$t> for $t {
460 type Output = $t;
461
462 fn add(self, rhs: $t) -> Self::Output {
463 $t(self.0.checked_add(rhs.0).unwrap())
464 }
465 }
466
467 impl std::ops::Sub<$t> for $t {
468 type Output = $t;
469
470 fn sub(self, rhs: $t) -> Self::Output {
471 $t(self.0.checked_sub(rhs.0).unwrap())
472 }
473 }
474
475 impl std::ops::Add<u32> for $t {
476 type Output = $t;
477
478 fn add(self, rhs: u32) -> Self::Output {
479 $t(self.0.checked_add(rhs).unwrap())
480 }
481 }
482
483 impl std::ops::Sub<u32> for $t {
484 type Output = $t;
485
486 fn sub(self, rhs: u32) -> Self::Output {
487 $t(self.0.checked_sub(rhs).unwrap())
488 }
489 }
490 };
491}
492
493methods!(ZeroColumn);
494methods!(ZeroLine);
495methods!(OneColumn);
496methods!(OneLine);