Skip to content

Commit 53cbd03

Browse files
authored
Merge pull request #828 from posit-dev/bugfix/folding-range-log
Fix perf issues with folding ranges
2 parents 4d6666a + 101de48 commit 53cbd03

File tree

1 file changed

+26
-62
lines changed

1 file changed

+26
-62
lines changed

crates/ark/src/lsp/folding_range.rs

Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//
66
//
77

8+
use std::borrow::Cow;
89
use std::cmp::Ordering;
910
use std::sync::LazyLock;
1011

@@ -19,14 +20,7 @@ use crate::lsp::documents::Document;
1920
pub fn folding_range(document: &Document) -> anyhow::Result<Vec<FoldingRange>> {
2021
let mut folding_ranges: Vec<FoldingRange> = Vec::new();
2122

22-
// Activate the parser
23-
let mut parser = tree_sitter::Parser::new();
24-
parser
25-
.set_language(&tree_sitter_r::LANGUAGE.into())
26-
.unwrap();
27-
28-
let ast = parser.parse(&document.contents.to_string(), None).unwrap();
29-
23+
let ast = &document.ast;
3024
if ast.root_node().has_error() {
3125
tracing::error!("Folding range service: Parse error");
3226
return Err(anyhow::anyhow!("Parse error"));
@@ -88,15 +82,18 @@ fn parse_ts_node(
8882
}
8983

9084
// Nested comment section handling
91-
let comment_line = get_line_text(document, start.row, None, None);
92-
93-
if let Err(err) =
94-
nested_processor(comment_stack, folding_ranges, start.row, &comment_line)
95-
{
96-
lsp::log_error!("Can't process comment: {err:?}");
85+
if let Some(comment_line) = document.contents.get_line(start.row) {
86+
// O(n) if comment overlaps rope chunks, O(1) otherwise
87+
let comment_line: Cow<'_, str> = comment_line.into();
88+
89+
if let Err(err) =
90+
nested_processor(comment_stack, folding_ranges, start.row, &comment_line)
91+
{
92+
lsp::log_error!("Can't process comment: {err:?}");
93+
};
94+
region_processor(folding_ranges, region_marker, start.row, &comment_line);
95+
cell_processor(folding_ranges, cell_marker, start.row, &comment_line);
9796
};
98-
region_processor(folding_ranges, region_marker, start.row, &comment_line);
99-
cell_processor(folding_ranges, cell_marker, start.row, &comment_line);
10097
},
10198
_ => (),
10299
}
@@ -145,28 +142,20 @@ fn bracket_range(
145142
white_space_count: usize,
146143
) -> FoldingRange {
147144
let mut end_line: u32 = end_line as u32;
148-
let mut end_char: Option<u32> = Some(end_char as u32);
145+
let mut end_character = Some(end_char as u32);
149146

150-
let adjusted_end_char = end_char.and_then(|val| val.checked_sub(white_space_count as u32));
151-
152-
match adjusted_end_char {
153-
Some(0) => {
147+
if let Some(val) = end_char.checked_sub(white_space_count) {
148+
if val == 0 {
154149
end_line -= 1;
155-
end_char = None;
156-
},
157-
Some(_) => {},
158-
None => {
159-
tracing::error!(
160-
"Folding Range (bracket_range): adjusted_end_char should not be None here"
161-
);
162-
},
150+
end_character = None;
151+
}
163152
}
164153

165154
FoldingRange {
166155
start_line: start_line as u32,
167156
start_character: Some(start_char as u32),
168157
end_line,
169-
end_character: end_char,
158+
end_character,
170159
kind: Some(FoldingRangeKind::Region),
171160
collapsed_text: None,
172161
}
@@ -183,39 +172,14 @@ fn comment_range(start_line: usize, end_line: usize) -> FoldingRange {
183172
}
184173
}
185174

186-
fn get_line_text(
187-
document: &Document,
188-
line_num: usize,
189-
start_char: Option<usize>,
190-
end_char: Option<usize>,
191-
) -> String {
192-
let text = &document.contents;
193-
// Split the text into lines
194-
let lines: Vec<&str> = text.lines().filter_map(|line| line.as_str()).collect();
195-
196-
// Ensure the start_line is within bounds
197-
if line_num >= lines.len() {
198-
return String::new(); // Return an empty string if out of bounds
199-
}
200-
201-
// Get the line corresponding to start_line
202-
let line = lines[line_num];
203-
204-
// Determine the start and end character indices
205-
let start_idx = start_char.unwrap_or(0); // Default to 0 if None
206-
let end_idx = end_char.unwrap_or(line.len()); // Default to the line's length if None
207-
208-
// Ensure indices are within bounds for the line
209-
let start_idx = start_idx.min(line.len());
210-
let end_idx = end_idx.min(line.len());
211-
212-
// Extract the substring and return it
213-
line[start_idx..end_idx].to_string()
214-
}
215-
216175
fn count_leading_whitespaces(document: &Document, line_num: usize) -> usize {
217-
let line_text = get_line_text(document, line_num, None, None);
218-
line_text.chars().take_while(|c| c.is_whitespace()).count()
176+
let Some(line) = document.contents.get_line(line_num) else {
177+
return 0;
178+
};
179+
180+
line.bytes()
181+
.take_while(|&b| b == b' ' || b == b'\t')
182+
.count()
219183
}
220184

221185
pub static RE_COMMENT_SECTION: LazyLock<Regex> =

0 commit comments

Comments
 (0)