nu_protocol/span.rs
1//! [`Span`] to point to sections of source code and the [`Spanned`] wrapper type
2use crate::SpanId;
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 pub fn contains(&self, pos: usize) -> bool {
136 self.start <= pos && pos < self.end
137 }
138
139 pub fn contains_span(&self, span: Self) -> bool {
140 self.start <= span.start && span.end <= self.end && span.end != 0
141 }
142
143 /// Point to the space just past this span, useful for missing values
144 pub fn past(&self) -> Self {
145 Self {
146 start: self.end,
147 end: self.end,
148 }
149 }
150
151 /// Converts row and column in a String to a Span, assuming bytes (1-based rows)
152 pub fn from_row_column(row: usize, col: usize, contents: &str) -> Span {
153 let mut cur_row = 1;
154 let mut cur_col = 1;
155
156 for (offset, curr_byte) in contents.bytes().enumerate() {
157 if curr_byte == b'\n' {
158 cur_row += 1;
159 cur_col = 1;
160 } else if cur_row >= row && cur_col >= col {
161 return Span::new(offset, offset);
162 } else {
163 cur_col += 1;
164 }
165 }
166
167 Self {
168 start: contents.len(),
169 end: contents.len(),
170 }
171 }
172
173 /// Returns the minimal [`Span`] that encompasses both of the given spans.
174 ///
175 /// The two `Spans` can overlap in the middle,
176 /// but must otherwise be in order by satisfying:
177 /// - `self.start <= after.start`
178 /// - `self.end <= after.end`
179 ///
180 /// If this is not guaranteed to be the case, use [`Span::merge`] instead.
181 pub fn append(self, after: Self) -> Self {
182 debug_assert!(
183 self.start <= after.start && self.end <= after.end,
184 "Can't merge two Spans that are not in order"
185 );
186 Self {
187 start: self.start,
188 end: after.end,
189 }
190 }
191
192 /// Returns the minimal [`Span`] that encompasses both of the given spans.
193 ///
194 /// The spans need not be in order or have any relationship.
195 ///
196 /// [`Span::append`] is slightly more efficient if the spans are known to be in order.
197 pub fn merge(self, other: Self) -> Self {
198 Self {
199 start: usize::min(self.start, other.start),
200 end: usize::max(self.end, other.end),
201 }
202 }
203
204 /// Returns the minimal [`Span`] that encompasses all of the spans in the given slice.
205 ///
206 /// The spans are assumed to be in order, that is, all consecutive spans must satisfy:
207 /// - `spans[i].start <= spans[i + 1].start`
208 /// - `spans[i].end <= spans[i + 1].end`
209 ///
210 /// (Two consecutive spans can overlap as long as the above is true.)
211 ///
212 /// Use [`Span::merge_many`] if the spans are not known to be in order.
213 pub fn concat(spans: &[Self]) -> Self {
214 // TODO: enable assert below
215 // debug_assert!(!spans.is_empty());
216 debug_assert!(spans.windows(2).all(|spans| {
217 let &[a, b] = spans else {
218 return false;
219 };
220 a.start <= b.start && a.end <= b.end
221 }));
222 Self {
223 start: spans.first().map(|s| s.start).unwrap_or(0),
224 end: spans.last().map(|s| s.end).unwrap_or(0),
225 }
226 }
227
228 /// Returns the minimal [`Span`] that encompasses all of the spans in the given iterator.
229 ///
230 /// The spans need not be in order or have any relationship.
231 ///
232 /// [`Span::concat`] is more efficient if the spans are known to be in order.
233 pub fn merge_many(spans: impl IntoIterator<Item = Self>) -> Self {
234 spans
235 .into_iter()
236 .reduce(Self::merge)
237 .unwrap_or(Self::unknown())
238 }
239}
240
241impl From<Span> for SourceSpan {
242 fn from(s: Span) -> Self {
243 Self::new(s.start.into(), s.end - s.start)
244 }
245}
246
247/// An extension trait for [`Result`], which adds a span to the error type.
248///
249/// This trait might be removed later, since the old [`Spanned<std::io::Error>`] to
250/// [`ShellError`](crate::ShellError) conversion was replaced by
251/// [`IoError`](crate::shell_error::io::IoError).
252pub trait ErrSpan {
253 type Result;
254
255 /// Adds the given span to the error type, turning it into a [`Spanned<E>`].
256 fn err_span(self, span: Span) -> Self::Result;
257}
258
259impl<T, E> ErrSpan for Result<T, E> {
260 type Result = Result<T, Spanned<E>>;
261
262 fn err_span(self, span: Span) -> Self::Result {
263 self.map_err(|err| err.into_spanned(span))
264 }
265}