Skip to content

Commit 5cada82

Browse files
committed
Vendor SourceLocation from ruff
1 parent 1d366d5 commit 5cada82

File tree

8 files changed

+1738
-8
lines changed

8 files changed

+1738
-8
lines changed

Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ include = ["LICENSE", "Cargo.toml", "src/**/*.rs"]
1212
resolver = "2"
1313
members = [
1414
"ast", "core", "literal", "parser",
15-
"ruff_text_size",
15+
"ruff_text_size", "ruff_source_location",
1616
]
1717

1818
[workspace.dependencies]
1919
rustpython-ast = { path = "ast", version = "0.2.0" }
2020
rustpython-parser-core = { path = "core", version = "0.2.0" }
2121
rustpython-literal = { path = "literal", version = "0.2.0" }
22+
ruff_text_size = { path = "ruff_text_size" }
23+
ruff_source_location = { path = "ruff_source_location" }
2224

2325
ahash = "0.7.6"
2426
anyhow = "1.0.45"
@@ -32,9 +34,8 @@ num-traits = "0.2"
3234
rand = "0.8.5"
3335
serde = "1.0"
3436
static_assertions = "1.1"
37+
once_cell = "1.17.1"
3538
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
36-
ruff_python_ast = { git = "https://github.com/youknowone/ruff.git", rev = "088958e8fda2f74f1ebf315c75db13c232409b13" }
37-
# ruff_python_ast = { path = "../ruff/crates/ruff_python_ast" }
3839

3940
[profile.dev.package."*"]
4041
opt-level = 3

core/Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ license = "MIT"
1111
itertools = { workspace = true }
1212
num-bigint = { workspace = true }
1313
num-complex = { workspace = true }
14-
serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] }
15-
ruff_text_size = { path = "../ruff_text_size" }
16-
ruff_python_ast = { workspace = true }
14+
ruff_text_size = { workspace = true }
15+
ruff_source_location = { workspace = true }
1716

17+
serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] }
1818
lz4_flex = "0.9.2"
1919

2020
[features]

core/src/source_code.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// re-export our public interface
2-
pub use ruff_python_ast::source_code::*;
2+
pub use ruff_source_location::*;
33

4-
pub type LineNumber = ruff_python_ast::source_code::OneIndexed;
4+
pub type LineNumber = OneIndexed;
55

66
#[derive(Debug)]
77
pub struct SourceRange {

ruff_source_location/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# NOTE: RUSTPYTHON
2+
# This crate is not a real crate of ruff, but cut off a part of `ruff_python_ast` and vendored it to avoid cross dependency
3+
4+
[package]
5+
name = "ruff_source_location"
6+
version = "0.0.0"
7+
publish = false
8+
edition = { workspace = true }
9+
rust-version = { workspace = true }
10+
11+
[lib]
12+
13+
[dependencies]
14+
ruff_text_size = { workspace = true, features = ["serde"] }
15+
16+
memchr = "2.5.0"
17+
once_cell = { workspace = true }

ruff_source_location/src/lib.rs

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

0 commit comments

Comments
 (0)