Skip to content

Commit ffca243

Browse files
committed
use line index over tokenizer
1 parent 4898181 commit ffca243

File tree

1 file changed

+121
-23
lines changed

1 file changed

+121
-23
lines changed

crates/ty_ide/src/folding_range.rs

Lines changed: 121 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use ruff_db::parsed::parsed_module;
33
use ruff_db::source::source_text;
44
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal, walk_body};
55
use ruff_python_ast::{AnyNodeRef, Decorator, Stmt, StmtClassDef, StmtFunctionDef};
6-
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
76
use ruff_source_file::{Line, UniversalNewlines};
87
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
98

@@ -49,9 +48,12 @@ pub fn folding_ranges(db: &dyn Db, file: File) -> Vec<FoldingRange> {
4948
let parsed = parsed_module(db, file).load(db);
5049
let source = source_text(db, file);
5150

51+
let line_index = ruff_db::source::line_index(db, file);
52+
5253
let mut visitor = FoldingRangeVisitor {
5354
source: source.as_str(),
5455
ranges: vec![],
56+
line_index: &line_index,
5557
};
5658
visitor.visit_body(parsed.suite());
5759

@@ -68,6 +70,7 @@ pub fn folding_ranges(db: &dyn Db, file: File) -> Vec<FoldingRange> {
6870
struct FoldingRangeVisitor<'a> {
6971
source: &'a str,
7072
ranges: Vec<FoldingRange>,
73+
line_index: &'a ruff_source_file::LineIndex,
7174
}
7275

7376
impl<'a> FoldingRangeVisitor<'a> {
@@ -233,38 +236,33 @@ impl<'a> FoldingRangeVisitor<'a> {
233236
}
234237

235238
/// Add a folding range for the function or class definition.
236-
///
237-
/// - target is `async` or `def` for functions, and `class` for classes
238239
fn add_def_range(
239240
&mut self,
240241
range: TextRange,
241242
decorator_list: &[Decorator],
242-
target: SimpleTokenKind,
243+
name_start: TextSize,
243244
) {
244-
if let Some(last) = decorator_list.last() {
245-
let tokenizer = SimpleTokenizer::starts_at(last.end(), self.source);
246-
if let Some(token) = tokenizer.skip_trivia().find(|token| token.kind == target) {
247-
let range = TextRange::new(token.start(), range.end());
248-
self.add_range(range);
249-
}
245+
if decorator_list.last().is_some() {
246+
// get the the beginning of the line containing the function or class name
247+
let line = self.line_index.line_index(name_start);
248+
let start = self.line_index.line_start(line, self.source);
249+
let range = TextRange::new(start, range.end());
250+
self.add_range(range);
250251
} else {
251252
self.add_range(range);
252253
}
253254
}
254255

255256
/// Add a folding range for function definitions, excluding decorators.
256257
fn add_function_def_range(&mut self, func: &StmtFunctionDef) {
257-
let target = if func.is_async {
258-
SimpleTokenKind::Async
259-
} else {
260-
SimpleTokenKind::Def
261-
};
262-
self.add_def_range(func.range(), &func.decorator_list, target);
258+
self.add_decorator_range(&func.decorator_list);
259+
self.add_def_range(func.range(), &func.decorator_list, func.name.start());
263260
}
264261

265262
/// Add a folding range for class definitions, excluding decorators.
266263
fn add_class_def_range(&mut self, class: &StmtClassDef) {
267-
self.add_def_range(class.range(), &class.decorator_list, SimpleTokenKind::Class);
264+
self.add_decorator_range(&class.decorator_list);
265+
self.add_def_range(class.range(), &class.decorator_list, class.name.start());
268266
}
269267
}
270268

@@ -273,7 +271,6 @@ impl SourceOrderVisitor<'_> for FoldingRangeVisitor<'_> {
273271
match node {
274272
// Compound statements that create folding regions
275273
AnyNodeRef::StmtFunctionDef(func) => {
276-
self.add_decorator_range(&func.decorator_list);
277274
self.add_function_def_range(func);
278275
// Note that this may be duplicative with folding
279276
// ranges added for string literals. But I don't think
@@ -285,7 +282,6 @@ impl SourceOrderVisitor<'_> for FoldingRangeVisitor<'_> {
285282
self.add_docstring_range(&func.body);
286283
}
287284
AnyNodeRef::StmtClassDef(class) => {
288-
self.add_decorator_range(&class.decorator_list);
289285
self.add_class_def_range(class);
290286
// See comment above for class docstrings about this
291287
// being duplicative with adding folding ranges for
@@ -1729,7 +1725,7 @@ with open("file.txt") as f:
17291725
}
17301726

17311727
#[test]
1732-
fn test_folding_range_decorated_function1() {
1728+
fn test_folding_range_decorated_function_single() {
17331729
let test = CursorTest::builder()
17341730
.source(
17351731
"main.py",
@@ -1755,7 +1751,7 @@ def my_function():
17551751
}
17561752

17571753
#[test]
1758-
fn test_folding_range_decorated_function2() {
1754+
fn test_folding_range_decorated_function_multiple() {
17591755
let test = CursorTest::builder()
17601756
.source(
17611757
"main.py",
@@ -1795,7 +1791,7 @@ def my_function():
17951791
}
17961792

17971793
#[test]
1798-
fn test_folding_range_decorated_class1() {
1794+
fn test_folding_range_decorated_class_single() {
17991795
let test = CursorTest::builder()
18001796
.source(
18011797
"main.py",
@@ -1824,7 +1820,7 @@ class MyClass:
18241820
}
18251821

18261822
#[test]
1827-
fn test_folding_range_decorated_class2() {
1823+
fn test_folding_range_decorated_class_multiple() {
18281824
let test = CursorTest::builder()
18291825
.source(
18301826
"main.py",
@@ -1861,6 +1857,108 @@ class MyClass:
18611857
");
18621858
}
18631859

1860+
#[test]
1861+
fn test_folding_range_decorated_async_function() {
1862+
let test = CursorTest::builder()
1863+
.source(
1864+
"main.py",
1865+
r#"
1866+
@decorator
1867+
async def my_async_function():
1868+
pass
1869+
<CURSOR>
1870+
"#,
1871+
)
1872+
.build();
1873+
1874+
assert_snapshot!(test.folding_ranges(), @r"
1875+
info[folding-range]: Folding Range
1876+
--> main.py:3:1
1877+
|
1878+
2 | @decorator
1879+
3 | / async def my_async_function():
1880+
4 | | pass
1881+
| |________^
1882+
|
1883+
");
1884+
}
1885+
1886+
#[test]
1887+
fn test_folding_range_decorated_nested_function() {
1888+
let test = CursorTest::builder()
1889+
.source(
1890+
"main.py",
1891+
r#"
1892+
def outer_function():
1893+
@decorator
1894+
def inner_function():
1895+
pass
1896+
<CURSOR>
1897+
"#,
1898+
)
1899+
.build();
1900+
1901+
assert_snapshot!(test.folding_ranges(), @"
1902+
info[folding-range]: Folding Range
1903+
--> main.py:2:1
1904+
|
1905+
2 | / def outer_function():
1906+
3 | | @decorator
1907+
4 | | def inner_function():
1908+
5 | | pass
1909+
| |____________^
1910+
|
1911+
1912+
info[folding-range]: Folding Range
1913+
--> main.py:4:1
1914+
|
1915+
2 | def outer_function():
1916+
3 | @decorator
1917+
4 | / def inner_function():
1918+
5 | | pass
1919+
| |____________^
1920+
|
1921+
");
1922+
}
1923+
1924+
#[test]
1925+
fn test_folding_range_decorated_async_method() {
1926+
let test = CursorTest::builder()
1927+
.source(
1928+
"main.py",
1929+
r#"
1930+
class MyClass:
1931+
@decorator
1932+
async def my_async_method(self):
1933+
pass
1934+
<CURSOR>
1935+
"#,
1936+
)
1937+
.build();
1938+
1939+
assert_snapshot!(test.folding_ranges(), @"
1940+
info[folding-range]: Folding Range
1941+
--> main.py:2:1
1942+
|
1943+
2 | / class MyClass:
1944+
3 | | @decorator
1945+
4 | | async def my_async_method(self):
1946+
5 | | pass
1947+
| |____________^
1948+
|
1949+
1950+
info[folding-range]: Folding Range
1951+
--> main.py:4:1
1952+
|
1953+
2 | class MyClass:
1954+
3 | @decorator
1955+
4 | / async def my_async_method(self):
1956+
5 | | pass
1957+
| |____________^
1958+
|
1959+
");
1960+
}
1961+
18641962
struct FoldingRangeDiagnostic {
18651963
file: File,
18661964
folding_range: FoldingRange,

0 commit comments

Comments
 (0)