Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions crates/multi_buffer/src/multi_buffer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5041,3 +5041,86 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
assert_eq!(ranges_unbounded_trailing[0].2, te_excerpt_1_id);
assert_eq!(ranges_unbounded_trailing[1].2, te_excerpt_2_id);
}

#[gpui::test]
fn test_cannot_seek_backward_after_excerpt_replacement(cx: &mut TestAppContext) {
let buffer_b_text: String = (0..50).map(|i| format!("line_b {i}\n")).collect();
let buffer_b = cx.new(|cx| Buffer::local(buffer_b_text, cx));

let buffer_c_text: String = (0..10).map(|i| format!("line_c {i}\n")).collect();
let buffer_c = cx.new(|cx| Buffer::local(buffer_c_text, cx));

let buffer_d_text: String = (0..10).map(|i| format!("line_d {i}\n")).collect();
let buffer_d = cx.new(|cx| Buffer::local(buffer_d_text, cx));

let path_b = PathKey::with_sort_prefix(0, rel_path("bbb.rs").into_arc());
let path_c = PathKey::with_sort_prefix(0, rel_path("ddd.rs").into_arc());
let path_d = PathKey::with_sort_prefix(0, rel_path("ccc.rs").into_arc());

let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));

multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
path_b.clone(),
buffer_b.clone(),
vec![
Point::row_range(0..3),
Point::row_range(15..18),
Point::row_range(30..33),
],
0,
cx,
);
});

multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
path_c.clone(),
buffer_c.clone(),
vec![Point::row_range(0..3)],
0,
cx,
);
});

let (anchor_in_e_b2, anchor_in_e_b3) = multibuffer.read_with(cx, |multibuffer, cx| {
let snapshot = multibuffer.snapshot(cx);
let excerpt_ids: Vec<ExcerptId> = snapshot.excerpts().map(|(id, _, _)| id).collect();
assert_eq!(excerpt_ids.len(), 4, "expected 4 excerpts (3×B + 1×C)");

let e_b2_id = excerpt_ids[1];
let e_b3_id = excerpt_ids[2];

let e_b2 = snapshot.excerpt(e_b2_id).expect("E_B2 should exist");
let e_b3 = snapshot.excerpt(e_b3_id).expect("E_B3 should exist");

let anchor_b2 = Anchor::in_buffer(e_b2_id, e_b2.range.context.start);
let anchor_b3 = Anchor::in_buffer(e_b3_id, e_b3.range.context.start);
(anchor_b2, anchor_b3)
});

multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
path_b.clone(),
buffer_b.clone(),
vec![Point::row_range(0..3), Point::row_range(28..36)],
0,
cx,
);
});

multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
path_d.clone(),
buffer_d.clone(),
vec![Point::row_range(0..3)],
0,
cx,
);
});

multibuffer.read_with(cx, |multibuffer, cx| {
let snapshot = multibuffer.snapshot(cx);
snapshot.summaries_for_anchors::<Point, _>(&[anchor_in_e_b2, anchor_in_e_b3]);
});
}
19 changes: 19 additions & 0 deletions crates/multi_buffer/src/path_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@ impl MultiBuffer {
(None, Some((existing_id, _))) => {
existing_iter.next();
to_remove.push(existing_id);
// Map removed excerpt to nearest surviving neighbor so stale
// anchors remap to a valid locator and maintain ordering.
let replacement = to_insert.last().map(|(id, _)| *id).unwrap_or(insert_after);
if replacement != ExcerptId::min() {
self.snapshot
.get_mut()
.replaced_excerpts
.insert(existing_id, replacement);
}
continue;
}
(Some(_), None) => {
Expand All @@ -388,6 +397,16 @@ impl MultiBuffer {
if existing_range.end < new.context.start {
let existing_id = existing_iter.next().unwrap();
to_remove.push(existing_id);
// Map removed excerpt to nearest surviving neighbor so stale
// anchors remap to a valid locator and maintain ordering.
let replacement =
to_insert.last().map(|(id, _)| *id).unwrap_or(insert_after);
if replacement != ExcerptId::min() {
self.snapshot
.get_mut()
.replaced_excerpts
.insert(existing_id, replacement);
}
continue;
} else if existing_range.start > new.context.end {
let new_id = next_excerpt_id();
Expand Down