git: More performance improvements when toggling between diff views (#49400)
- Defer syncing block maps from `set_companion`, eliminating some redundant recomputations - Emit one large multibuffer edit from `set_show_deleted_hunks` instead of many small edits, to avoid bad case for block map This cuts hangs roughly in half when toggling between views in a large diff (1000 commits from the chromium repository). <!-- Before you mark this PR as ready for review, make sure that you have: - [ ] Added a solid test coverage and/or screenshots from doing manual testing - [ ] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) --> Release Notes: - Improved performance with large diffs when toggling between diff views. --------- Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
1 parent
2605757347
commit
76efc691e6
4 files changed
+97
-61
No files matched your search
@@ -473,7 +473,6 @@ impl DisplayMap {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(split-diff) figure out how to free the LHS from having to build a block map before this is called
|
||||
pub(crate) fn set_companion(
|
||||
&mut self,
|
||||
companion: Option<(Entity<DisplayMap>, Entity<Companion>)>,
|
||||
@@ -486,26 +485,31 @@ impl DisplayMap {
|
||||
let Some((_, companion)) = self.companion.take() else {
|
||||
return;
|
||||
};
|
||||
let (snapshot, edits) = self.sync_through_wrap(cx);
|
||||
let edits = edits.compose([text::Edit {
|
||||
old: WrapRow(0)..snapshot.max_point().row(),
|
||||
new: WrapRow(0)..snapshot.max_point().row(),
|
||||
assert_eq!(self.entity_id, companion.read(cx).rhs_display_map_id);
|
||||
let (snapshot, _edits) = self.sync_through_wrap(cx);
|
||||
let edits = Patch::new(vec![text::Edit {
|
||||
old: WrapRow(0)
|
||||
..self.block_map.wrap_snapshot.borrow().max_point().row() + WrapRow(1),
|
||||
new: WrapRow(0)..snapshot.max_point().row() + WrapRow(1),
|
||||
}]);
|
||||
self.block_map.write(snapshot, edits, None).remove(
|
||||
companion
|
||||
self.block_map.deferred_edits.set(edits);
|
||||
self.block_map.retain_blocks_raw(|block| {
|
||||
if companion
|
||||
.read(cx)
|
||||
.lhs_custom_block_to_balancing_block
|
||||
.borrow()
|
||||
.values()
|
||||
.copied()
|
||||
.collect(),
|
||||
);
|
||||
.any(|id| *id == block.id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
});
|
||||
return;
|
||||
};
|
||||
assert_eq!(self.entity_id, companion.read(cx).rhs_display_map_id);
|
||||
|
||||
// Note, throwing away the wrap edits because we're going to recompute the maximal range for the block map regardless
|
||||
let old_max_row = self.block_map.wrap_snapshot.borrow().max_point().row();
|
||||
// Note, throwing away the wrap edits because we defer spacer computation to the first render.
|
||||
let snapshot = {
|
||||
let edits = self.buffer_subscription.consume();
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
@@ -529,28 +533,16 @@ impl DisplayMap {
|
||||
snapshot
|
||||
};
|
||||
|
||||
let (companion_wrap_snapshot, companion_wrap_edits) =
|
||||
let (companion_wrap_snapshot, _companion_wrap_edits) =
|
||||
companion_display_map.update(cx, |dm, cx| dm.sync_through_wrap(cx));
|
||||
|
||||
let edits = Patch::new(
|
||||
[text::Edit {
|
||||
old: WrapRow(0)..old_max_row,
|
||||
new: WrapRow(0)..snapshot.max_point().row(),
|
||||
}]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
);
|
||||
let edits = Patch::new(vec![text::Edit {
|
||||
old: WrapRow(0)..self.block_map.wrap_snapshot.borrow().max_point().row() + WrapRow(1),
|
||||
new: WrapRow(0)..snapshot.max_point().row() + WrapRow(1),
|
||||
}]);
|
||||
self.block_map.deferred_edits.set(edits);
|
||||
|
||||
let reader = self.block_map.read(
|
||||
snapshot.clone(),
|
||||
edits.clone(),
|
||||
Some(CompanionView::new(
|
||||
self.entity_id,
|
||||
&companion_wrap_snapshot,
|
||||
&companion_wrap_edits,
|
||||
companion.read(cx),
|
||||
)),
|
||||
);
|
||||
let all_blocks: Vec<_> = self.block_map.blocks_raw().map(Clone::clone).collect();
|
||||
|
||||
companion_display_map.update(cx, |companion_display_map, cx| {
|
||||
for my_buffer in self.folded_buffers() {
|
||||
@@ -564,7 +556,7 @@ impl DisplayMap {
|
||||
.folded_buffers
|
||||
.insert(*their_buffer);
|
||||
}
|
||||
for block in reader.blocks {
|
||||
for block in all_blocks {
|
||||
let Some(their_block) = block_map::balancing_block(
|
||||
&block.properties(),
|
||||
snapshot.buffer(),
|
||||
@@ -584,16 +576,21 @@ impl DisplayMap {
|
||||
.insert(block.id, their_id);
|
||||
});
|
||||
}
|
||||
companion_display_map.block_map.read(
|
||||
companion_wrap_snapshot,
|
||||
companion_wrap_edits,
|
||||
Some(CompanionView::new(
|
||||
companion_display_map.entity_id,
|
||||
&snapshot,
|
||||
&edits,
|
||||
companion.read(cx),
|
||||
)),
|
||||
);
|
||||
let companion_edits = Patch::new(vec![text::Edit {
|
||||
old: WrapRow(0)
|
||||
..companion_display_map
|
||||
.block_map
|
||||
.wrap_snapshot
|
||||
.borrow()
|
||||
.max_point()
|
||||
.row()
|
||||
+ WrapRow(1),
|
||||
new: WrapRow(0)..companion_wrap_snapshot.max_point().row() + WrapRow(1),
|
||||
}]);
|
||||
companion_display_map
|
||||
.block_map
|
||||
.deferred_edits
|
||||
.set(companion_edits);
|
||||
companion_display_map.companion = Some((this, companion.clone()));
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use multi_buffer::{
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
cell::{Cell, RefCell},
|
||||
cmp::{self, Ordering},
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeInclusive},
|
||||
@@ -45,6 +45,7 @@ pub struct BlockMap {
|
||||
excerpt_header_height: u32,
|
||||
pub(super) folded_buffers: HashSet<BufferId>,
|
||||
buffers_with_disabled_headers: HashSet<BufferId>,
|
||||
pub(super) deferred_edits: Cell<Patch<WrapRow>>,
|
||||
}
|
||||
|
||||
pub struct BlockMapReader<'a> {
|
||||
@@ -620,6 +621,7 @@ impl BlockMap {
|
||||
wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
|
||||
buffer_header_height,
|
||||
excerpt_header_height,
|
||||
deferred_edits: Cell::default(),
|
||||
};
|
||||
map.sync(
|
||||
&wrap_snapshot,
|
||||
@@ -747,6 +749,11 @@ impl BlockMap {
|
||||
.retain(|id, _| !ids_to_remove.contains(id));
|
||||
}
|
||||
|
||||
// Warning: doesn't sync the block map, use advisedly
|
||||
pub(crate) fn blocks_raw(&self) -> impl Iterator<Item = &Arc<CustomBlock>> {
|
||||
self.custom_blocks.iter()
|
||||
}
|
||||
|
||||
#[ztracing::instrument(skip_all, fields(edits = ?edits))]
|
||||
fn sync(
|
||||
&self,
|
||||
@@ -756,6 +763,8 @@ impl BlockMap {
|
||||
) {
|
||||
let buffer = wrap_snapshot.buffer_snapshot();
|
||||
|
||||
edits = self.deferred_edits.take().compose(edits);
|
||||
|
||||
// Handle changing the last excerpt if it is empty.
|
||||
if buffer.trailing_excerpt_update_count()
|
||||
!= self
|
||||
|
||||
@@ -20821,11 +20821,7 @@ impl Editor {
|
||||
|
||||
fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
let excerpt_id = range.end.excerpt_id;
|
||||
let point_range = range.to_point(&snapshot);
|
||||
let expand = !buffer.single_hunk_is_expanded(range, cx);
|
||||
buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
|
||||
buffer.toggle_single_diff_hunk(range, cx);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2710,7 +2710,25 @@ impl MultiBuffer {
|
||||
|
||||
pub fn set_show_deleted_hunks(&mut self, show: bool, cx: &mut Context<Self>) {
|
||||
self.snapshot.get_mut().show_deleted_hunks = show;
|
||||
self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx);
|
||||
|
||||
self.sync_mut(cx);
|
||||
|
||||
let old_len = self.snapshot.borrow().len();
|
||||
|
||||
let ranges = std::iter::once((Point::zero()..Point::MAX, ExcerptId::max()));
|
||||
let _ = self.expand_or_collapse_diff_hunks_inner(ranges, true, cx);
|
||||
|
||||
let new_len = self.snapshot.borrow().len();
|
||||
|
||||
self.subscriptions.publish(vec![Edit {
|
||||
old: MultiBufferOffset(0)..old_len,
|
||||
new: MultiBufferOffset(0)..new_len,
|
||||
}]);
|
||||
|
||||
cx.emit(Event::DiffHunksToggled);
|
||||
cx.emit(Event::Edited {
|
||||
edited_buffer: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_use_extended_diff_range(&mut self, use_extended: bool, _cx: &mut Context<Self>) {
|
||||
@@ -2768,9 +2786,9 @@ impl MultiBuffer {
|
||||
ranges: impl IntoIterator<Item = (Range<Point>, ExcerptId)>,
|
||||
expand: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
) -> Vec<Edit<MultiBufferOffset>> {
|
||||
if self.snapshot.borrow().all_diff_hunks_expanded && !expand {
|
||||
return;
|
||||
return Vec::new();
|
||||
}
|
||||
self.sync_mut(cx);
|
||||
let mut snapshot = self.snapshot.get_mut();
|
||||
@@ -2796,18 +2814,11 @@ impl MultiBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
let edits = Self::sync_diff_transforms(
|
||||
Self::sync_diff_transforms(
|
||||
&mut snapshot,
|
||||
excerpt_edits,
|
||||
DiffChangeKind::ExpandOrCollapseHunks { expand },
|
||||
);
|
||||
if !edits.is_empty() {
|
||||
self.subscriptions.publish(edits);
|
||||
}
|
||||
cx.emit(Event::DiffHunksToggled);
|
||||
cx.emit(Event::Edited {
|
||||
edited_buffer: None,
|
||||
});
|
||||
)
|
||||
}
|
||||
|
||||
pub fn expand_or_collapse_diff_hunks(
|
||||
@@ -2826,7 +2837,14 @@ impl MultiBuffer {
|
||||
};
|
||||
(range.start..peek_end, end_excerpt_id)
|
||||
});
|
||||
self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
|
||||
let edits = self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
|
||||
if !edits.is_empty() {
|
||||
self.subscriptions.publish(edits);
|
||||
}
|
||||
cx.emit(Event::DiffHunksToggled);
|
||||
cx.emit(Event::Edited {
|
||||
edited_buffer: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn resize_excerpt(
|
||||
@@ -3629,6 +3647,22 @@ impl MultiBuffer {
|
||||
);
|
||||
did_extend
|
||||
}
|
||||
|
||||
pub fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
|
||||
let snapshot = self.snapshot(cx);
|
||||
let excerpt_id = range.end.excerpt_id;
|
||||
let point_range = range.to_point(&snapshot);
|
||||
let expand = !self.single_hunk_is_expanded(range, cx);
|
||||
let edits =
|
||||
self.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
|
||||
if !edits.is_empty() {
|
||||
self.subscriptions.publish(edits);
|
||||
}
|
||||
cx.emit(Event::DiffHunksToggled);
|
||||
cx.emit(Event::Edited {
|
||||
edited_buffer: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn build_excerpt_ranges(
|
||||
|
||||
Reference in new issue
Block a user