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:
authored and GitHub committed 2026-02-17 22:09:27 +00:00
1 parent 2605757347
commit 76efc691e6
4 files changed
+97 -61

No files matched your search

+47 -13
View File
@@ -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(