nu_protocol/span.rs
1//! [`Span`] to point to sections of source code and the [`Spanned`] wrapper type
2use crate::{IntoValue, SpanId, Value, record};
3use miette::SourceSpan;
4use serde::{Deserialize, Serialize};
5use std::ops::Deref;
6
7pub trait GetSpan {
8 fn get_span(&self, span_id: SpanId) -> Span;
9}
10
11/// A spanned area of interest, generic over what kind of thing is of interest
12#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
13pub struct Spanned<T> {
14 pub item: T,
15 pub span: Span,
16}
17
18impl<T> Spanned<T> {
19 /// Map to a spanned reference of the inner type, i.e. `Spanned<T> -> Spanned<&T>`.
20 pub fn as_ref(&self) -> Spanned<&T> {
21 Spanned {
22 item: &self.item,
23 span: self.span,
24 }
25 }
26
27 /// Map to a mutable reference of the inner type, i.e. `Spanned<T> -> Spanned<&mut T>`.
28 pub fn as_mut(&mut self) -> Spanned<&mut T> {
29 Spanned {
30 item: &mut self.item,
31 span: self.span,
32 }
33 }
34
35 /// Map to the result of [`.deref()`](std::ops::Deref::deref) on the inner type.
36 ///
37 /// This can be used for example to turn `Spanned<Vec<T>>` into `Spanned<&[T]>`.
38 pub fn as_deref(&self) -> Spanned<&<T as Deref>::Target>
39 where
40 T: Deref,
41 {
42 Spanned {
43 item: self.item.deref(),
44 span: self.span,
45 }
46 }
47
48 /// Map the spanned item with a function.
49 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
50 Spanned {
51 item: f(self.item),
52 span: self.span,
53 }
54 }
55}
56
57impl<T, E> Spanned<Result<T, E>> {
58 /// Move the `Result` to the outside, resulting in a spanned `Ok` or unspanned `Err`.
59 pub fn transpose(self) -> Result<Spanned<T>, E> {
60 match self {
61 Spanned {
62 item: Ok(item),
63 span,
64 } => Ok(Spanned { item, span }),
65 Spanned {
66 item: Err(err),
67 span: _,
68 } => Err(err),
69 }
70 }
71}
72
73/// Helper trait to create [`Spanned`] more ergonomically.
74pub trait IntoSpanned: Sized {
75 /// Wrap items together with a span into [`Spanned`].
76 ///
77 /// # Example
78 ///
79 /// ```
80 /// # use nu_protocol::{Span, IntoSpanned};
81 /// # let span = Span::test_data();
82 /// let spanned = "Hello, world!".into_spanned(span);
83 /// assert_eq!("Hello, world!", spanned.item);
84 /// assert_eq!(span, spanned.span);
85 /// ```
86 fn into_spanned(self, span: Span) -> Spanned<Self>;
87}
88
89impl<T> IntoSpanned for T {
90 fn into_spanned(self, span: Span) -> Spanned<Self> {
91 Spanned { item: self, span }
92 }
93}
94
95/// Spans are a global offset across all seen files, which are cached in the engine's state. The start and
96/// end offset together make the inclusive start/exclusive end pair for where to underline to highlight
97/// a given point of interest.
98#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
99pub struct Span {
100 pub start: usize,
101 pub end: usize,
102}
103
104impl Span {
105 pub fn new(start: usize, end: usize) -> Self {
106 debug_assert!(
107 end >= start,
108 "Can't create a Span whose end < start, start={start}, end={end}"
109 );
110
111 Self { start, end }
112 }
113
114 pub const fn unknown() -> Self {
115 Self { start: 0, end: 0 }
116 }
117
118 /// Span for testing purposes.
119 ///
120 /// The provided span does not point into any known source but is unequal to [`Span::unknown()`].
121 ///
122 /// Note: Only use this for test data, *not* live data, as it will point into unknown source
123 /// when used in errors
124 pub const fn test_data() -> Self {
125 Self {
126 start: usize::MAX / 2,
127 end: usize::MAX / 2,
128 }
129 }
130
131 pub fn offset(&self, offset: usize) -> Self {
132 Self::new(self.start - offset, self.end - offset)
133 }
134
135 /// Return length of the slice.
136 pub fn len(&self) -> usize {
137 self.end - self.start
138 }
139
140 /// Indicate if slice has length 0.
141 pub fn is_empty(&self) -> bool {
142 self.start == self.end
143 }
144
145 /// Return another span fully inside the [`Span`].
146 ///
147 /// `start` and `end` are relative to `self.start`, and must lie within the `Span`.
148 /// In other words, both `start` and `end` must be `<= self.len()`.
149 pub fn subspan(&self, offset_start: usize, offset_end: usize) -> Option<Self> {
150 let len = self.len();
151
152 if offset_start > len || offset_end > len || offset_start > offset_end {
153 None
154 } else {
155 Some(Self::new(
156 self.start + offset_start,
157 self.start + offset_end,
158 ))
159 }
160 }
161
162 /// Return two spans that split the ['Span'] at the given position.
163 pub fn split_at(&self, offset: usize) -> Option<(Self, Self)> {
164 if offset < self.len() {
165 Some((
166 Self::new(self.start, self.start + offset),
167 Self::new(self.start + offset, self.end),
168 ))
169 } else {
170 None
171 }
172 }
173
174 pub fn contains(&self, pos: usize) -> bool {
175 self.start <= pos && pos < self.end
176 }
177
178 pub fn contains_span(&self, span: Self) -> bool {
179 self.start <= span.start && span.end <= self.end && span.end != 0
180 }
181
182 /// Point to the space just past this span, useful for missing values
183 pub fn past(&self) -> Self {
184 Self {
185 start: self.end,
186 end: self.end,
187 }
188 }
189
190 /// Converts row and column in a String to a Span, assuming bytes (1-based rows)
191 pub fn from_row_column(row: usize, col: usize, contents: &str) -> Span {
192 let mut cur_row = 1;
193 let mut cur_col = 1;
194
195 for (offset, curr_byte) in contents.bytes().enumerate() {
196 if curr_byte == b'\n' {
197 cur_row += 1;
198 cur_col = 1;
199 } else if cur_row >= row && cur_col >= col {
200 return Span::new(offset, offset);
201 } else {
202 cur_col += 1;
203 }
204 }
205
206 Self {
207 start: contents.len(),
208 end: contents.len(),
209 }
210 }
211
212 /// Returns the minimal [`Span`] that encompasses both of the given spans.
213 ///
214 /// The two `Spans` can overlap in the middle,
215 /// but must otherwise be in order by satisfying:
216 /// - `self.start <= after.start`
217 /// - `self.end <= after.end`
218 ///
219 /// If this is not guaranteed to be the case, use [`Span::merge`] instead.
220 pub fn append(self, after: Self) -> Self {
221 debug_assert!(
222 self.start <= after.start && self.end <= after.end,
223 "Can't merge two Spans that are not in order"
224 );
225 Self {
226 start: self.start,
227 end: after.end,
228 }
229 }
230
231 /// Returns the minimal [`Span`] that encompasses both of the given spans.
232 ///
233 /// The spans need not be in order or have any relationship.
234 ///
235 /// [`Span::append`] is slightly more efficient if the spans are known to be in order.
236 pub fn merge(self, other: Self) -> Self {
237 Self {
238 start: usize::min(self.start, other.start),
239 end: usize::max(self.end, other.end),
240 }
241 }
242
243 /// Returns the minimal [`Span`] that encompasses all of the spans in the given slice.
244 ///
245 /// The spans are assumed to be in order, that is, all consecutive spans must satisfy:
246 /// - `spans[i].start <= spans[i + 1].start`
247 /// - `spans[i].end <= spans[i + 1].end`
248 ///
249 /// (Two consecutive spans can overlap as long as the above is true.)
250 ///
251 /// Use [`Span::merge_many`] if the spans are not known to be in order.
252 pub fn concat(spans: &[Self]) -> Self {
253 // TODO: enable assert below
254 // debug_assert!(!spans.is_empty());
255 debug_assert!(spans.windows(2).all(|spans| {
256 let &[a, b] = spans else {
257 return false;
258 };
259 a.start <= b.start && a.end <= b.end
260 }));
261 Self {
262 start: spans.first().map(|s| s.start).unwrap_or(0),
263 end: spans.last().map(|s| s.end).unwrap_or(0),
264 }
265 }
266
267 /// Returns the minimal [`Span`] that encompasses all of the spans in the given iterator.
268 ///
269 /// The spans need not be in order or have any relationship.
270 ///
271 /// [`Span::concat`] is more efficient if the spans are known to be in order.
272 pub fn merge_many(spans: impl IntoIterator<Item = Self>) -> Self {
273 spans
274 .into_iter()
275 .reduce(Self::merge)
276 .unwrap_or(Self::unknown())
277 }
278}
279
280impl IntoValue for Span {
281 fn into_value(self, span: Span) -> Value {
282 let record = record! {
283 "start" => Value::int(self.start as i64, self),
284 "end" => Value::int(self.end as i64, self),
285 };
286 record.into_value(span)
287 }
288}
289
290impl From<Span> for SourceSpan {
291 fn from(s: Span) -> Self {
292 Self::new(s.start.into(), s.end - s.start)
293 }
294}
295
296/// An extension trait for [`Result`], which adds a span to the error type.
297///
298/// This trait might be removed later, since the old [`Spanned<std::io::Error>`] to
299/// [`ShellError`](crate::ShellError) conversion was replaced by
300/// [`IoError`](crate::shell_error::io::IoError).
301pub trait ErrSpan {
302 type Result;
303
304 /// Adds the given span to the error type, turning it into a [`Spanned<E>`].
305 fn err_span(self, span: Span) -> Self::Result;
306}
307
308impl<T, E> ErrSpan for Result<T, E> {
309 type Result = Result<T, Spanned<E>>;
310
311 fn err_span(self, span: Span) -> Self::Result {
312 self.map_err(|err| err.into_spanned(span))
313 }
314}