Fix duplicated multi-buffer excerpts (#29193)

- **add test case**
- **Merge excerpts more aggressively**
- **Randomized test for set_excerpts_for_path**

Closes #ISSUE

Release Notes:

- Fixed duplicted excerpts (and resulting panics)

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
This commit is contained in:
Conrad Irwin 2025-04-21 23:25:09 -06:00 committed by GitHub
parent 458ffaa134
commit 3357736aea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 251 additions and 89 deletions

View file

@ -2476,6 +2476,81 @@ impl ReferenceMultibuffer {
}
}
#[gpui::test(iterations = 100)]
async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
let base_text = "a\n".repeat(100);
let buf = cx.update(|cx| cx.new(|cx| Buffer::local(base_text, cx)));
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
fn row_ranges(ranges: &Vec<Range<Point>>) -> Vec<Range<u32>> {
ranges
.iter()
.map(|range| range.start.row..range.end.row)
.collect()
}
for _ in 0..operations {
let snapshot = buf.update(cx, |buf, _| buf.snapshot());
let num_ranges = rng.gen_range(0..=10);
let max_row = snapshot.max_point().row;
let mut ranges = (0..num_ranges)
.map(|_| {
let start = rng.gen_range(0..max_row);
let end = rng.gen_range(start + 1..max_row + 1);
Point::row_range(start..end)
})
.collect::<Vec<_>>();
ranges.sort_by_key(|range| range.start);
log::info!("Setting ranges: {:?}", row_ranges(&ranges));
let (created, _) = multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
PathKey::for_buffer(&buf, cx),
buf.clone(),
ranges.clone(),
2,
cx,
)
});
assert_eq!(created.len(), ranges.len());
let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
let mut last_end = None;
let mut seen_ranges = Vec::default();
for (_, buf, range) in snapshot.excerpts() {
let start = range.context.start.to_point(&buf);
let end = range.context.end.to_point(&buf);
seen_ranges.push(start..end);
if let Some(last_end) = last_end.take() {
assert!(
start > last_end,
"multibuffer has out-of-order ranges: {:?}; {:?} <= {:?}",
row_ranges(&seen_ranges),
start,
last_end
)
}
ranges.retain(|range| range.start < start || range.end > end);
last_end = Some(end)
}
assert!(
ranges.is_empty(),
"multibuffer {:?} did not include all ranges: {:?}",
row_ranges(&seen_ranges),
row_ranges(&ranges)
);
}
}
#[gpui::test(iterations = 100)]
async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
let operations = env::var("OPERATIONS")