Skip to main content

nu_protocol/
span.rs

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