Fix editor rendering slowness with large folds (#31569)
Closes https://github.com/zed-industries/zed/issues/31565 * Looking up settings on every row was very slow in the case of large folds, especially if there was an `.editorconfig` file with numerous glob patterns * Checking whether each indent guide was within a fold was very slow, when a fold spanned many indent guides. Release Notes: - Fixed slowness that could happen when editing in the presence of large folds.
This commit is contained in:
parent
53849cf983
commit
97579662e6
3 changed files with 137 additions and 38 deletions
|
@ -16768,9 +16768,9 @@ fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -
|
|||
async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
&"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
}"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
}"
|
||||
.unindent(),
|
||||
cx,
|
||||
)
|
||||
|
@ -16783,10 +16783,10 @@ async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
|
|||
async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
&"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
}"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
}"
|
||||
.unindent(),
|
||||
cx,
|
||||
)
|
||||
|
@ -16799,14 +16799,14 @@ async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
|
|||
async fn test_indent_guide_nested(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
&"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
if a == 3 {
|
||||
let b = 2;
|
||||
} else {
|
||||
let c = 3;
|
||||
}
|
||||
}"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
if a == 3 {
|
||||
let b = 2;
|
||||
} else {
|
||||
let c = 3;
|
||||
}
|
||||
}"
|
||||
.unindent(),
|
||||
cx,
|
||||
)
|
||||
|
@ -16828,11 +16828,11 @@ async fn test_indent_guide_nested(cx: &mut TestAppContext) {
|
|||
async fn test_indent_guide_tab(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
&"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
let c = 3;
|
||||
}"
|
||||
fn main() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
let c = 3;
|
||||
}"
|
||||
.unindent(),
|
||||
cx,
|
||||
)
|
||||
|
@ -16962,6 +16962,72 @@ async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_guide_with_folds(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
&"
|
||||
fn main() {
|
||||
if a {
|
||||
b(
|
||||
c,
|
||||
d,
|
||||
)
|
||||
} else {
|
||||
e(
|
||||
f
|
||||
)
|
||||
}
|
||||
}"
|
||||
.unindent(),
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_indent_guides(
|
||||
0..11,
|
||||
vec![
|
||||
indent_guide(buffer_id, 1, 10, 0),
|
||||
indent_guide(buffer_id, 2, 5, 1),
|
||||
indent_guide(buffer_id, 7, 9, 1),
|
||||
indent_guide(buffer_id, 3, 4, 2),
|
||||
indent_guide(buffer_id, 8, 8, 2),
|
||||
],
|
||||
None,
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.fold_at(MultiBufferRow(2), window, cx);
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
"
|
||||
fn main() {
|
||||
if a {
|
||||
b(⋯
|
||||
)
|
||||
} else {
|
||||
e(
|
||||
f
|
||||
)
|
||||
}
|
||||
}"
|
||||
.unindent()
|
||||
);
|
||||
});
|
||||
|
||||
assert_indent_guides(
|
||||
0..11,
|
||||
vec![
|
||||
indent_guide(buffer_id, 1, 10, 0),
|
||||
indent_guide(buffer_id, 2, 5, 1),
|
||||
indent_guide(buffer_id, 7, 9, 1),
|
||||
indent_guide(buffer_id, 8, 8, 2),
|
||||
],
|
||||
None,
|
||||
&mut cx,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
|
||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::{ops::Range, time::Duration};
|
||||
use std::{cmp::Ordering, ops::Range, time::Duration};
|
||||
|
||||
use collections::HashSet;
|
||||
use gpui::{App, AppContext as _, Context, Task, Window};
|
||||
use language::language_settings::language_settings;
|
||||
use multi_buffer::{IndentGuide, MultiBufferRow};
|
||||
use multi_buffer::{IndentGuide, MultiBufferRow, ToPoint};
|
||||
use text::{LineIndent, Point};
|
||||
use util::ResultExt;
|
||||
|
||||
|
@ -154,12 +154,28 @@ pub fn indent_guides_in_range(
|
|||
snapshot: &DisplaySnapshot,
|
||||
cx: &App,
|
||||
) -> Vec<IndentGuide> {
|
||||
let start_anchor = snapshot
|
||||
let start_offset = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(Point::new(visible_buffer_range.start.0, 0));
|
||||
let end_anchor = snapshot
|
||||
.point_to_offset(Point::new(visible_buffer_range.start.0, 0));
|
||||
let end_offset = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_after(Point::new(visible_buffer_range.end.0, 0));
|
||||
.point_to_offset(Point::new(visible_buffer_range.end.0, 0));
|
||||
let start_anchor = snapshot.buffer_snapshot.anchor_before(start_offset);
|
||||
let end_anchor = snapshot.buffer_snapshot.anchor_after(end_offset);
|
||||
|
||||
let mut fold_ranges = Vec::<Range<Point>>::new();
|
||||
let mut folds = snapshot.folds_in_range(start_offset..end_offset).peekable();
|
||||
while let Some(fold) = folds.next() {
|
||||
let start = fold.range.start.to_point(&snapshot.buffer_snapshot);
|
||||
let end = fold.range.end.to_point(&snapshot.buffer_snapshot);
|
||||
if let Some(last_range) = fold_ranges.last_mut() {
|
||||
if last_range.end >= start {
|
||||
last_range.end = last_range.end.max(end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fold_ranges.push(start..end);
|
||||
}
|
||||
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
|
@ -169,15 +185,19 @@ pub fn indent_guides_in_range(
|
|||
return false;
|
||||
}
|
||||
|
||||
let start = MultiBufferRow(indent_guide.start_row.0.saturating_sub(1));
|
||||
// Filter out indent guides that are inside a fold
|
||||
// All indent guides that are starting "offscreen" have a start value of the first visible row minus one
|
||||
// Therefore checking if a line is folded at first visible row minus one causes the other indent guides that are not related to the fold to disappear as well
|
||||
let is_folded = snapshot.is_line_folded(start);
|
||||
let line_indent = snapshot.line_indent_for_buffer_row(start);
|
||||
let contained_in_fold =
|
||||
line_indent.len(indent_guide.tab_size) <= indent_guide.indent_level();
|
||||
!(is_folded && contained_in_fold)
|
||||
let has_containing_fold = fold_ranges
|
||||
.binary_search_by(|fold_range| {
|
||||
if fold_range.start >= Point::new(indent_guide.start_row.0, 0) {
|
||||
Ordering::Greater
|
||||
} else if fold_range.end < Point::new(indent_guide.end_row.0, 0) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
})
|
||||
.is_ok();
|
||||
|
||||
!has_containing_fold
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -5753,15 +5753,28 @@ impl MultiBufferSnapshot {
|
|||
let mut result = Vec::new();
|
||||
let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
|
||||
|
||||
let mut prev_settings = None;
|
||||
while let Some((first_row, mut line_indent, buffer)) = row_indents.next() {
|
||||
if first_row > end_row {
|
||||
break;
|
||||
}
|
||||
let current_depth = indent_stack.len() as u32;
|
||||
|
||||
let settings =
|
||||
language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx);
|
||||
let tab_size = settings.tab_size.get() as u32;
|
||||
// Avoid retrieving the language settings repeatedly for every buffer row.
|
||||
if let Some((prev_buffer_id, _)) = &prev_settings {
|
||||
if prev_buffer_id != &buffer.remote_id() {
|
||||
prev_settings.take();
|
||||
}
|
||||
}
|
||||
let settings = &prev_settings
|
||||
.get_or_insert_with(|| {
|
||||
(
|
||||
buffer.remote_id(),
|
||||
language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx),
|
||||
)
|
||||
})
|
||||
.1;
|
||||
let tab_size = settings.tab_size.get();
|
||||
|
||||
// When encountering empty, continue until found useful line indent
|
||||
// then add to the indent stack with the depth found
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue