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}