Compute scrollbar markers asynchronously (#10080)

Refs #9647
Fixes https://github.com/zed-industries/zed/issues/9792

This pull request moves the computation of scrollbar markers off the
main thread, to prevent them from grinding the editor to a halt when we
have a lot of them (e.g., when there are lots of search results on a
large file). With these changes we also avoid generating multiple quads
for adjacent markers, thus fixing an issue where we stop drawing other
primitives because we've drawn too many quads in the scrollbar.

Release Notes:

- Improved editor performance when displaying lots of search results,
diagnostics, or symbol highlights in the scrollbar
([#9792](https://github.com/zed-industries/zed/issues/9792)).

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Thorsten Ball
2024-04-03 12:21:17 +02:00
committed by GitHub
co-authored by Antonio Scandurra Nathan
parent 7dbcace839
commit 3a0d3cee87
26 changed files with 532 additions and 379 deletions
+18 -14
View File
@@ -37,6 +37,7 @@ pub struct BlockMap {
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
#[derive(Clone)]
pub struct BlockSnapshot {
wrap_snapshot: WrapSnapshot,
transforms: SumTree<Transform>,
@@ -54,7 +55,7 @@ struct BlockRow(u32);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct WrapRow(u32);
pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
pub type RenderBlock = Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>;
pub struct Block {
id: BlockId,
@@ -65,15 +66,11 @@ pub struct Block {
disposition: BlockDisposition,
}
#[derive(Clone)]
pub struct BlockProperties<P>
where
P: Clone,
{
pub struct BlockProperties<P> {
pub position: P,
pub height: u8,
pub style: BlockStyle,
pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
pub render: Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>,
pub disposition: BlockDisposition,
}
@@ -1041,21 +1038,21 @@ mod tests {
position: buffer_snapshot.anchor_after(Point::new(1, 0)),
height: 1,
disposition: BlockDisposition::Above,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 2)),
height: 2,
disposition: BlockDisposition::Above,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(3, 3)),
height: 3,
disposition: BlockDisposition::Below,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
},
]);
@@ -1209,14 +1206,14 @@ mod tests {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 12)),
disposition: BlockDisposition::Above,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
height: 1,
},
BlockProperties {
style: BlockStyle::Fixed,
position: buffer_snapshot.anchor_after(Point::new(1, 1)),
disposition: BlockDisposition::Below,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
height: 1,
},
]);
@@ -1311,7 +1308,7 @@ mod tests {
position,
height,
disposition,
render: Arc::new(|_| div().into_any()),
render: Box::new(|_| div().into_any()),
}
})
.collect::<Vec<_>>();
@@ -1325,7 +1322,14 @@ mod tests {
wrap_map.sync(tab_snapshot, tab_edits, cx)
});
let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
let block_ids = block_map.insert(block_properties.clone());
let block_ids =
block_map.insert(block_properties.iter().map(|props| BlockProperties {
position: props.position,
height: props.height,
style: props.style,
render: Box::new(|_| div().into_any()),
disposition: props.disposition,
}));
for (block_id, props) in block_ids.into_iter().zip(block_properties) {
custom_blocks.push((block_id, props));
}
+30 -29
View File
@@ -1695,38 +1695,39 @@ mod tests {
while inlay_indices.len() < inlay_highlight_count {
inlay_indices.insert(rng.gen_range(0..inlays.len()));
}
let new_highlights = inlay_indices
.into_iter()
.filter_map(|i| {
let (_, inlay) = &inlays[i];
let inlay_text_len = inlay.text.len();
match inlay_text_len {
0 => None,
1 => Some(InlayHighlight {
inlay: inlay.id,
inlay_position: inlay.position,
range: 0..1,
}),
n => {
let inlay_text = inlay.text.to_string();
let mut highlight_end = rng.gen_range(1..n);
let mut highlight_start = rng.gen_range(0..highlight_end);
while !inlay_text.is_char_boundary(highlight_end) {
highlight_end += 1;
}
while !inlay_text.is_char_boundary(highlight_start) {
highlight_start -= 1;
}
Some(InlayHighlight {
let new_highlights = TreeMap::from_ordered_entries(
inlay_indices
.into_iter()
.filter_map(|i| {
let (_, inlay) = &inlays[i];
let inlay_text_len = inlay.text.len();
match inlay_text_len {
0 => None,
1 => Some(InlayHighlight {
inlay: inlay.id,
inlay_position: inlay.position,
range: highlight_start..highlight_end,
})
range: 0..1,
}),
n => {
let inlay_text = inlay.text.to_string();
let mut highlight_end = rng.gen_range(1..n);
let mut highlight_start = rng.gen_range(0..highlight_end);
while !inlay_text.is_char_boundary(highlight_end) {
highlight_end += 1;
}
while !inlay_text.is_char_boundary(highlight_start) {
highlight_start -= 1;
}
Some(InlayHighlight {
inlay: inlay.id,
inlay_position: inlay.position,
range: highlight_start..highlight_end,
})
}
}
}
})
.map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight)))
.collect();
})
.map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight))),
);
log::info!("highlighting inlay ranges {new_highlights:?}");
inlay_highlights.insert(TypeId::of::<()>(), new_highlights);
}