rustpython_parser_vendored/source_location/
mod.rs

1mod line_index;
2// mod locator;
3pub mod newlines;
4
5pub use self::line_index::{LineIndex, OneIndexed};
6use crate::text_size::{TextRange, TextSize};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::fmt::{Debug, Formatter};
10use std::sync::Arc;
11
12/// Gives access to the source code of a file and allows mapping between [`TextSize`] and [`SourceLocation`].
13#[derive(Debug)]
14pub struct SourceCode<'src, 'index> {
15    text: &'src str,
16    index: &'index LineIndex,
17}
18
19impl<'src, 'index> SourceCode<'src, 'index> {
20    pub fn new(content: &'src str, index: &'index LineIndex) -> Self {
21        Self {
22            text: content,
23            index,
24        }
25    }
26
27    /// Computes the one indexed row and column numbers for `offset`.
28    #[inline]
29    pub fn source_location(&self, offset: TextSize) -> SourceLocation {
30        self.index.source_location(offset, self.text)
31    }
32
33    #[inline]
34    pub fn line_index(&self, offset: TextSize) -> OneIndexed {
35        self.index.line_index(offset)
36    }
37
38    /// Take the source code up to the given [`TextSize`].
39    #[inline]
40    pub fn up_to(&self, offset: TextSize) -> &'src str {
41        &self.text[TextRange::up_to(offset)]
42    }
43
44    /// Take the source code after the given [`TextSize`].
45    #[inline]
46    pub fn after(&self, offset: TextSize) -> &'src str {
47        &self.text[usize::from(offset)..]
48    }
49
50    /// Take the source code between the given [`TextRange`].
51    pub fn slice(&self, range: TextRange) -> &'src str {
52        &self.text[range]
53    }
54
55    pub fn line_start(&self, line: OneIndexed) -> TextSize {
56        self.index.line_start(line, self.text)
57    }
58
59    pub fn line_end(&self, line: OneIndexed) -> TextSize {
60        self.index.line_end(line, self.text)
61    }
62
63    pub fn line_range(&self, line: OneIndexed) -> TextRange {
64        self.index.line_range(line, self.text)
65    }
66
67    /// Returns the source text of the line with the given index
68    #[inline]
69    pub fn line_text(&self, index: OneIndexed) -> &'src str {
70        let range = self.index.line_range(index, self.text);
71        &self.text[range]
72    }
73
74    /// Returns the source text
75    pub fn text(&self) -> &'src str {
76        self.text
77    }
78
79    /// Returns the number of lines
80    #[inline]
81    pub fn line_count(&self) -> usize {
82        self.index.line_count()
83    }
84}
85
86impl PartialEq<Self> for SourceCode<'_, '_> {
87    fn eq(&self, other: &Self) -> bool {
88        self.text == other.text
89    }
90}
91
92impl Eq for SourceCode<'_, '_> {}
93
94/// A Builder for constructing a [`SourceFile`]
95pub struct SourceFileBuilder {
96    name: Box<str>,
97    code: Box<str>,
98    index: Option<LineIndex>,
99}
100
101impl SourceFileBuilder {
102    /// Creates a new builder for a file named `name`.
103    pub fn new<Name: Into<Box<str>>, Code: Into<Box<str>>>(name: Name, code: Code) -> Self {
104        Self {
105            name: name.into(),
106            code: code.into(),
107            index: None,
108        }
109    }
110
111    #[must_use]
112    pub fn line_index(mut self, index: LineIndex) -> Self {
113        self.index = Some(index);
114        self
115    }
116
117    pub fn set_line_index(&mut self, index: LineIndex) {
118        self.index = Some(index);
119    }
120
121    /// Consumes `self` and returns the [`SourceFile`].
122    pub fn finish(self) -> SourceFile {
123        let index = if let Some(index) = self.index {
124            once_cell::sync::OnceCell::with_value(index)
125        } else {
126            once_cell::sync::OnceCell::new()
127        };
128
129        SourceFile {
130            inner: Arc::new(SourceFileInner {
131                name: self.name,
132                code: self.code,
133                line_index: index,
134            }),
135        }
136    }
137}
138
139/// A source file that is identified by its name. Optionally stores the source code and [`LineIndex`].
140///
141/// Cloning a [`SourceFile`] is cheap, because it only requires bumping a reference count.
142#[derive(Clone, Eq, PartialEq)]
143pub struct SourceFile {
144    inner: Arc<SourceFileInner>,
145}
146
147impl Debug for SourceFile {
148    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
149        f.debug_struct("SourceFile")
150            .field("name", &self.name())
151            .field("code", &self.source_text())
152            .finish()
153    }
154}
155
156impl SourceFile {
157    /// Returns the name of the source file (filename).
158    #[inline]
159    pub fn name(&self) -> &str {
160        &self.inner.name
161    }
162
163    #[inline]
164    pub fn slice(&self, range: TextRange) -> &str {
165        &self.source_text()[range]
166    }
167
168    pub fn to_source_code(&self) -> SourceCode {
169        SourceCode {
170            text: self.source_text(),
171            index: self.index(),
172        }
173    }
174
175    fn index(&self) -> &LineIndex {
176        self.inner
177            .line_index
178            .get_or_init(|| LineIndex::from_source_text(self.source_text()))
179    }
180
181    /// Returns `Some` with the source text if set, or `None`.
182    #[inline]
183    pub fn source_text(&self) -> &str {
184        &self.inner.code
185    }
186}
187
188struct SourceFileInner {
189    name: Box<str>,
190    code: Box<str>,
191    line_index: once_cell::sync::OnceCell<LineIndex>,
192}
193
194impl PartialEq for SourceFileInner {
195    fn eq(&self, other: &Self) -> bool {
196        self.name == other.name && self.code == other.code
197    }
198}
199
200impl Eq for SourceFileInner {}
201
202#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Copy)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
204pub struct SourceLocation {
205    pub row: OneIndexed,
206    pub column: OneIndexed,
207}
208
209impl Default for SourceLocation {
210    fn default() -> Self {
211        Self {
212            row: OneIndexed::MIN,
213            column: OneIndexed::MIN,
214        }
215    }
216}
217
218impl Debug for SourceLocation {
219    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
220        f.debug_struct("SourceLocation")
221            .field("row", &self.row.get())
222            .field("column", &self.column.get())
223            .finish()
224    }
225}