nu_protocol/
span.rs

1//! [`Span`] to point to sections of source code and the [`Spanned`] wrapper type
2use crate::{FromValue, IntoValue, ShellError, SpanId, Value, record};
3use miette::SourceSpan;
4use serde::{Deserialize, Serialize};
5use std::{fmt, 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> Spanned<&T>
58where
59    T: ToOwned + ?Sized,
60{
61    /// Map the spanned to hold an owned value.
62    pub fn to_owned(&self) -> Spanned<T::Owned> {
63        Spanned {
64            item: self.item.to_owned(),
65            span: self.span,
66        }
67    }
68}
69
70impl<T> Spanned<T>
71where
72    T: AsRef<str>,
73{
74    /// Span the value as a string slice.
75    pub fn as_str(&self) -> Spanned<&str> {
76        Spanned {
77            item: self.item.as_ref(),
78            span: self.span,
79        }
80    }
81}
82
83impl<T, E> Spanned<Result<T, E>> {
84    /// Move the `Result` to the outside, resulting in a spanned `Ok` or unspanned `Err`.
85    pub fn transpose(self) -> Result<Spanned<T>, E> {
86        match self {
87            Spanned {
88                item: Ok(item),
89                span,
90            } => Ok(Spanned { item, span }),
91            Spanned {
92                item: Err(err),
93                span: _,
94            } => Err(err),
95        }
96    }
97}
98
99// With both Display and Into<SourceSpan> implemented on Spanned, we can use Spanned<String> in an
100// error in one field instead of splitting it into two fields
101
102impl<T: fmt::Display> fmt::Display for Spanned<T> {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        fmt::Display::fmt(&self.item, f)
105    }
106}
107
108impl<T> From<Spanned<T>> for SourceSpan {
109    fn from(value: Spanned<T>) -> Self {
110        value.span.into()
111    }
112}
113
114/// Helper trait to create [`Spanned`] more ergonomically.
115pub trait IntoSpanned: Sized {
116    /// Wrap items together with a span into [`Spanned`].
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// # use nu_protocol::{Span, IntoSpanned};
122    /// # let span = Span::test_data();
123    /// let spanned = "Hello, world!".into_spanned(span);
124    /// assert_eq!("Hello, world!", spanned.item);
125    /// assert_eq!(span, spanned.span);
126    /// ```
127    fn into_spanned(self, span: Span) -> Spanned<Self>;
128}
129
130impl<T> IntoSpanned for T {
131    fn into_spanned(self, span: Span) -> Spanned<Self> {
132        Spanned { item: self, span }
133    }
134}
135
136/// Spans are a global offset across all seen files, which are cached in the engine's state. The start and
137/// end offset together make the inclusive start/exclusive end pair for where to underline to highlight
138/// a given point of interest.
139#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
140pub struct Span {
141    pub start: usize,
142    pub end: usize,
143}
144
145impl Span {
146    pub fn new(start: usize, end: usize) -> Self {
147        debug_assert!(
148            end >= start,
149            "Can't create a Span whose end < start, start={start}, end={end}"
150        );
151
152        Self { start, end }
153    }
154
155    pub const fn unknown() -> Self {
156        Self { start: 0, end: 0 }
157    }
158
159    /// Span for testing purposes.
160    ///
161    /// The provided span does not point into any known source but is unequal to [`Span::unknown()`].
162    ///
163    /// Note: Only use this for test data, *not* live data, as it will point into unknown source
164    /// when used in errors
165    pub const fn test_data() -> Self {
166        Self {
167            start: usize::MAX / 2,
168            end: usize::MAX / 2,
169        }
170    }
171
172    pub fn offset(&self, offset: usize) -> Self {
173        Self::new(self.start - offset, self.end - offset)
174    }
175
176    /// Return length of the slice.
177    pub fn len(&self) -> usize {
178        self.end - self.start
179    }
180
181    /// Indicate if slice has length 0.
182    pub fn is_empty(&self) -> bool {
183        self.start == self.end
184    }
185
186    /// Return another span fully inside the [`Span`].
187    ///
188    /// `start` and `end` are relative to `self.start`, and must lie within the `Span`.
189    /// In other words, both `start` and `end` must be `<= self.len()`.
190    pub fn subspan(&self, offset_start: usize, offset_end: usize) -> Option<Self> {
191        let len = self.len();
192
193        if offset_start > len || offset_end > len || offset_start > offset_end {
194            None
195        } else {
196            Some(Self::new(
197                self.start + offset_start,
198                self.start + offset_end,
199            ))
200        }
201    }
202
203    /// Return two spans that split the ['Span'] at the given position.
204    pub fn split_at(&self, offset: usize) -> Option<(Self, Self)> {
205        if offset < self.len() {
206            Some((
207                Self::new(self.start, self.start + offset),
208                Self::new(self.start + offset, self.end),
209            ))
210        } else {
211            None
212        }
213    }
214
215    pub fn contains(&self, pos: usize) -> bool {
216        self.start <= pos && pos < self.end
217    }
218
219    pub fn contains_span(&self, span: Self) -> bool {
220        self.start <= span.start && span.end <= self.end && span.end != 0
221    }
222
223    /// Point to the space just past this span, useful for missing values
224    pub fn past(&self) -> Self {
225        Self {
226            start: self.end,
227            end: self.end,
228        }
229    }
230
231    /// Converts row and column in a String to a Span, assuming bytes (1-based rows)
232    pub fn from_row_column(row: usize, col: usize, contents: &str) -> Span {
233        let mut cur_row = 1;
234        let mut cur_col = 1;
235
236        for (offset, curr_byte) in contents.bytes().enumerate() {
237            if curr_byte == b'\n' {
238                cur_row += 1;
239                cur_col = 1;
240            } else if cur_row >= row && cur_col >= col {
241                return Span::new(offset, offset);
242            } else {
243                cur_col += 1;
244            }
245        }
246
247        Self {
248            start: contents.len(),
249            end: contents.len(),
250        }
251    }
252
253    /// Returns the minimal [`Span`] that encompasses both of the given spans.
254    ///
255    /// The two `Spans` can overlap in the middle,
256    /// but must otherwise be in order by satisfying:
257    /// - `self.start <= after.start`
258    /// - `self.end <= after.end`
259    ///
260    /// If this is not guaranteed to be the case, use [`Span::merge`] instead.
261    pub fn append(self, after: Self) -> Self {
262        debug_assert!(
263            self.start <= after.start && self.end <= after.end,
264            "Can't merge two Spans that are not in order"
265        );
266        Self {
267            start: self.start,
268            end: after.end,
269        }
270    }
271
272    /// Returns the minimal [`Span`] that encompasses both of the given spans.
273    ///
274    /// The spans need not be in order or have any relationship.
275    ///
276    /// [`Span::append`] is slightly more efficient if the spans are known to be in order.
277    pub fn merge(self, other: Self) -> Self {
278        Self {
279            start: usize::min(self.start, other.start),
280            end: usize::max(self.end, other.end),
281        }
282    }
283
284    /// Returns the minimal [`Span`] that encompasses all of the spans in the given slice.
285    ///
286    /// The spans are assumed to be in order, that is, all consecutive spans must satisfy:
287    /// - `spans[i].start <= spans[i + 1].start`
288    /// - `spans[i].end <= spans[i + 1].end`
289    ///
290    /// (Two consecutive spans can overlap as long as the above is true.)
291    ///
292    /// Use [`Span::merge_many`] if the spans are not known to be in order.
293    pub fn concat(spans: &[Self]) -> Self {
294        // TODO: enable assert below
295        // debug_assert!(!spans.is_empty());
296        debug_assert!(spans.windows(2).all(|spans| {
297            let &[a, b] = spans else {
298                return false;
299            };
300            a.start <= b.start && a.end <= b.end
301        }));
302        Self {
303            start: spans.first().map(|s| s.start).unwrap_or(0),
304            end: spans.last().map(|s| s.end).unwrap_or(0),
305        }
306    }
307
308    /// Returns the minimal [`Span`] that encompasses all of the spans in the given iterator.
309    ///
310    /// The spans need not be in order or have any relationship.
311    ///
312    /// [`Span::concat`] is more efficient if the spans are known to be in order.
313    pub fn merge_many(spans: impl IntoIterator<Item = Self>) -> Self {
314        spans
315            .into_iter()
316            .reduce(Self::merge)
317            .unwrap_or(Self::unknown())
318    }
319}
320
321impl IntoValue for Span {
322    fn into_value(self, span: Span) -> Value {
323        let record = record! {
324            "start" => Value::int(self.start as i64, self),
325            "end" => Value::int(self.end as i64, self),
326        };
327        record.into_value(span)
328    }
329}
330
331impl FromValue for Span {
332    fn from_value(value: Value) -> Result<Self, ShellError> {
333        let rec = value.as_record();
334        match rec {
335            Ok(val) => {
336                let Some(pre_start) = val.get("start") else {
337                    return Err(ShellError::GenericError {
338                        error: "Unable to parse Span.".into(),
339                        msg: "`start` must be an `int`".into(),
340                        span: Some(value.span()),
341                        help: None,
342                        inner: vec![],
343                    });
344                };
345                let Some(pre_end) = val.get("end") else {
346                    return Err(ShellError::GenericError {
347                        error: "Unable to parse Span.".into(),
348                        msg: "`end` must be an `int`".into(),
349                        span: Some(value.span()),
350                        help: None,
351                        inner: vec![],
352                    });
353                };
354                let start = pre_start.as_int()? as usize;
355                let end = pre_end.as_int()? as usize;
356                if start <= end {
357                    Ok(Self::new(start, end))
358                } else {
359                    Err(ShellError::GenericError {
360                        error: "Unable to parse Span.".into(),
361                        msg: "`end` must not be less than `start`".into(),
362                        span: Some(value.span()),
363                        help: None,
364                        inner: vec![],
365                    })
366                }
367            }
368            _ => Err(ShellError::TypeMismatch {
369                err_message: "Must be a record".into(),
370                span: value.span(),
371            }),
372        }
373    }
374}
375
376impl From<Span> for SourceSpan {
377    fn from(s: Span) -> Self {
378        Self::new(s.start.into(), s.end - s.start)
379    }
380}
381
382/// An extension trait for [`Result`], which adds a span to the error type.
383///
384/// This trait might be removed later, since the old [`Spanned<std::io::Error>`] to
385/// [`ShellError`](crate::ShellError) conversion was replaced by
386/// [`IoError`](crate::shell_error::io::IoError).
387pub trait ErrSpan {
388    type Result;
389
390    /// Adds the given span to the error type, turning it into a [`Spanned<E>`].
391    fn err_span(self, span: Span) -> Self::Result;
392}
393
394impl<T, E> ErrSpan for Result<T, E> {
395    type Result = Result<T, Spanned<E>>;
396
397    fn err_span(self, span: Span) -> Self::Result {
398        self.map_err(|err| err.into_spanned(span))
399    }
400}