multibuffer: Speed up anchor resolution by avoiding an excerpts seek (#53340)

Before, every creation of `AnchorSeekTarget` called
`MultiBufferSnapshot::buffer_for_path`, which seeks the excerpts sum
tree. Now we avoid that by

- looking up the buffer snapshot for the anchor's buffer in the
`buffers` map (keyed by `BufferId`)
- handling the case where the anchor's buffer doesn't exist at the
original path key with a separate `AnchorSeekTarget::Missing` variant

We also added a separate optimization to `AnchorSeekTarget::cmp`, to
skip the full `PathKey` comparison when the cursor's `PathKeyIndex` is
the same as the anchor's. This should help with singleton multibuffers.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
Cole MillerandAnthony Eid authored and GitHub committed 2026-04-07 22:37:00 +00:00
1 parent 755e6781a8
commit c945048d83
2 files changed
+76 -47

No files matched your search

+23 -21
View File
@@ -870,6 +870,7 @@ impl ExcerptRange<text::Anchor> {
#[derive(Clone, Debug)]
pub struct ExcerptSummary {
path_key: PathKey,
path_key_index: Option<PathKeyIndex>,
max_anchor: Option<text::Anchor>,
widest_line_number: u32,
text: MBTextSummary,
@@ -880,6 +881,7 @@ impl ExcerptSummary {
pub fn min() -> Self {
ExcerptSummary {
path_key: PathKey::min(),
path_key_index: None,
max_anchor: None,
widest_line_number: 0,
text: MBTextSummary::default(),
@@ -6386,11 +6388,11 @@ impl MultiBufferSnapshot {
self.buffers.get(&id).map(|state| &state.buffer_snapshot)
}
fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option<PathKey> {
self.path_keys_by_index.get(&anchor.path).cloned()
fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option<&PathKey> {
self.path_keys_by_index.get(&anchor.path)
}
pub fn path_for_anchor(&self, anchor: ExcerptAnchor) -> PathKey {
pub fn path_for_anchor(&self, anchor: ExcerptAnchor) -> &PathKey {
self.try_path_for_anchor(anchor)
.expect("invalid anchor: path was never added to multibuffer")
}
@@ -7327,6 +7329,7 @@ impl sum_tree::Item for Excerpt {
}
ExcerptSummary {
path_key: self.path_key.clone(),
path_key_index: Some(self.path_key_index),
max_anchor: Some(self.range.context.end),
widest_line_number: self.max_buffer_row,
text: text.into(),
@@ -7425,6 +7428,7 @@ impl sum_tree::ContextLessSummary for ExcerptSummary {
);
self.path_key = summary.path_key.clone();
self.path_key_index = summary.path_key_index;
self.max_anchor = summary.max_anchor;
self.text += summary.text;
self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number);
@@ -7432,38 +7436,36 @@ impl sum_tree::ContextLessSummary for ExcerptSummary {
}
}
impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for AnchorSeekTarget {
impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for AnchorSeekTarget<'_> {
fn cmp(
&self,
cursor_location: &ExcerptSummary,
_cx: <ExcerptSummary as sum_tree::Summary>::Context<'_>,
) -> cmp::Ordering {
match self {
AnchorSeekTarget::Missing { path_key } => {
// Want to end up after any excerpts for (a different buffer at) the original path
match Ord::cmp(*path_key, &cursor_location.path_key) {
Ordering::Less => Ordering::Less,
Ordering::Equal | Ordering::Greater => Ordering::Greater,
}
}
AnchorSeekTarget::Excerpt {
path_key,
path_key_index,
anchor,
snapshot,
} => {
let path_comparison = Ord::cmp(path_key, &cursor_location.path_key);
if path_comparison.is_ne() {
path_comparison
} else if let Some(snapshot) = snapshot {
if anchor.text_anchor.buffer_id != snapshot.remote_id() {
Ordering::Greater
} else if let Some(max_anchor) = cursor_location.max_anchor {
debug_assert_eq!(max_anchor.buffer_id, snapshot.remote_id());
anchor.text_anchor().cmp(&max_anchor, snapshot)
} else {
Ordering::Greater
}
if Some(*path_key_index) != cursor_location.path_key_index {
Ord::cmp(*path_key, &cursor_location.path_key)
} else if let Some(max_anchor) = cursor_location.max_anchor {
debug_assert_eq!(max_anchor.buffer_id, snapshot.remote_id());
anchor.cmp(&max_anchor, snapshot)
} else {
// shouldn't happen because we expect this buffer not to have any excerpts
// (otherwise snapshot would have been Some)
Ordering::Equal
Ordering::Greater
}
}
// This should be dead code because Empty is only constructed for an empty snapshot
AnchorSeekTarget::Empty => Ordering::Equal,
AnchorSeekTarget::Empty => Ordering::Greater,
}
}
}