solar_interface/
span.rs

1use crate::{BytePos, SessionGlobals};
2use std::{cmp, fmt, ops::Range};
3
4/// A source code location.
5///
6/// Essentially a `lo..hi` range into a `SourceMap` file's source code.
7///
8/// Both `lo` and `hi` are offset by the file's starting position.
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct Span {
11    lo: BytePos,
12    hi: BytePos,
13}
14
15impl Default for Span {
16    #[inline(always)]
17    fn default() -> Self {
18        Self::DUMMY
19    }
20}
21
22impl Default for &Span {
23    #[inline(always)]
24    fn default() -> Self {
25        &Span::DUMMY
26    }
27}
28
29impl fmt::Debug for Span {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        // Use the global `SourceMap` to print the span. If that's not
32        // available, fall back to printing the raw values.
33
34        fn fallback(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35            write!(f, "Span({lo}..{hi})", lo = span.lo().0, hi = span.hi().0)
36        }
37
38        if SessionGlobals::is_set() {
39            SessionGlobals::with(|g: &SessionGlobals| {
40                let sm = g.source_map.lock();
41                if let Some(source_map) = &*sm {
42                    f.write_str(&source_map.span_to_diagnostic_string(*self))
43                } else {
44                    drop(sm);
45                    fallback(*self, f)
46                }
47            })
48        } else {
49            fallback(*self, f)
50        }
51    }
52}
53
54impl Span {
55    /// A dummy span.
56    pub const DUMMY: Self = Self { lo: BytePos(0), hi: BytePos(0) };
57
58    /// Creates a new span from two byte positions.
59    #[inline]
60    pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
61        if lo > hi {
62            std::mem::swap(&mut lo, &mut hi);
63        }
64        Self { lo, hi }
65    }
66
67    /// Returns the span as a `Range<usize>`.
68    #[inline]
69    pub fn to_range(self) -> Range<usize> {
70        self.lo().to_usize()..self.hi().to_usize()
71    }
72
73    /// Returns the span as a `Range<u32>`.
74    #[inline]
75    pub fn to_u32_range(self) -> Range<u32> {
76        self.lo().to_u32()..self.hi().to_u32()
77    }
78
79    /// Returns the span's start position.
80    #[inline(always)]
81    pub fn lo(self) -> BytePos {
82        self.lo
83    }
84
85    /// Creates a new span with the same hi position as this span and the given lo position.
86    #[inline]
87    pub fn with_lo(self, lo: BytePos) -> Self {
88        Self::new(lo, self.hi())
89    }
90
91    /// Returns the span's end position.
92    #[inline(always)]
93    pub fn hi(self) -> BytePos {
94        self.hi
95    }
96
97    /// Creates a new span with the same lo position as this span and the given hi position.
98    #[inline]
99    pub fn with_hi(self, hi: BytePos) -> Self {
100        Self::new(self.lo(), hi)
101    }
102
103    /// Creates a new span representing an empty span at the beginning of this span.
104    #[inline]
105    pub fn shrink_to_lo(self) -> Self {
106        Self::new(self.lo(), self.lo())
107    }
108
109    /// Creates a new span representing an empty span at the end of this span.
110    #[inline]
111    pub fn shrink_to_hi(self) -> Self {
112        Self::new(self.hi(), self.hi())
113    }
114
115    /// Returns `true` if this is a dummy span.
116    #[inline]
117    pub fn is_dummy(self) -> bool {
118        self == Self::DUMMY
119    }
120
121    /// Returns `true` if `self` fully encloses `other`.
122    #[inline]
123    pub fn contains(self, other: Self) -> bool {
124        self.lo() <= other.lo() && other.hi() <= self.hi()
125    }
126
127    /// Returns `true` if `self` touches `other`.
128    #[inline]
129    pub fn overlaps(self, other: Self) -> bool {
130        self.lo() < other.hi() && other.lo() < self.hi()
131    }
132
133    /// Returns `true` if `self` and `other` are equal.
134    #[inline]
135    pub fn is_empty(self, other: Self) -> bool {
136        self.lo() == other.lo() && self.hi() == other.hi()
137    }
138
139    /// Splits a span into two composite spans around a certain position.
140    #[inline]
141    pub fn split_at(self, pos: u32) -> (Self, Self) {
142        let len = self.hi().0 - self.lo().0;
143        debug_assert!(pos <= len);
144
145        let split_pos = BytePos(self.lo().0 + pos);
146        (Self::new(self.lo(), split_pos), Self::new(split_pos, self.hi()))
147    }
148
149    /// Returns a `Span` that would enclose both `self` and `end`.
150    ///
151    /// Note that this can also be used to extend the span "backwards":
152    /// `start.to(end)` and `end.to(start)` return the same `Span`.
153    ///
154    /// ```text
155    ///     ____             ___
156    ///     self lorem ipsum end
157    ///     ^^^^^^^^^^^^^^^^^^^^
158    /// ```
159    #[inline]
160    pub fn to(self, end: Self) -> Self {
161        Self::new(cmp::min(self.lo(), end.lo()), cmp::max(self.hi(), end.hi()))
162    }
163
164    /// Returns a `Span` between the end of `self` to the beginning of `end`.
165    ///
166    /// ```text
167    ///     ____             ___
168    ///     self lorem ipsum end
169    ///         ^^^^^^^^^^^^^
170    /// ```
171    #[inline]
172    pub fn between(self, end: Self) -> Self {
173        Self::new(self.hi(), end.lo())
174    }
175
176    /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
177    ///
178    /// ```text
179    ///     ____             ___
180    ///     self lorem ipsum end
181    ///     ^^^^^^^^^^^^^^^^^
182    /// ```
183    #[inline]
184    pub fn until(self, end: Self) -> Self {
185        Self::new(self.lo(), end.lo())
186    }
187
188    /// Joins all the spans in the given iterator using [`to`](Self::to).
189    #[inline]
190    pub fn join_many(spans: impl IntoIterator<Item = Self>) -> Self {
191        spans.into_iter().reduce(Self::to).unwrap_or_default()
192    }
193
194    /// Joins the first and last span in the given iterator.
195    #[inline]
196    pub fn join_first_last(
197        spans: impl IntoIterator<Item = Self, IntoIter: DoubleEndedIterator>,
198    ) -> Self {
199        let mut spans = spans.into_iter();
200        let first = spans.next().unwrap_or_default();
201        if let Some(last) = spans.next_back() {
202            first.to(last)
203        } else {
204            first
205        }
206    }
207}