Fix remove_excerpts when removing the last N excerpts, N > 1

Also, generalize the randomized test to remove multiple excerpts at a time
This commit is contained in:
Max Brunsfeld 2021-12-22 21:02:20 -08:00
parent 3e59c61a34
commit c47340000d

View file

@ -689,31 +689,52 @@ impl MultiBuffer {
let mut new_excerpts = SumTree::new(); let mut new_excerpts = SumTree::new();
let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>(); let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
let mut edits = Vec::new(); let mut edits = Vec::new();
for excerpt_id in excerpt_ids { let mut excerpt_ids = excerpt_ids.into_iter().peekable();
new_excerpts.push_tree(cursor.slice(&Some(excerpt_id), Bias::Left, &()), &());
if let Some(excerpt) = cursor.item() {
if excerpt.id == *excerpt_id {
let mut old_start = cursor.start().1;
let old_end = cursor.end(&()).1;
cursor.next(&());
while let Some(mut excerpt_id) = excerpt_ids.next() {
// Seek to the next excerpt to remove, preserving any preceding excerpts.
new_excerpts.push_tree(cursor.slice(&Some(excerpt_id), Bias::Left, &()), &());
if let Some(mut excerpt) = cursor.item() {
if excerpt.id != *excerpt_id {
continue;
}
let mut old_start = cursor.start().1;
// Skip over the removed excerpt.
loop {
if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) { if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
buffer_state.excerpts.retain(|id| id != excerpt_id); buffer_state.excerpts.retain(|id| id != excerpt_id);
} }
cursor.next(&());
// When removing the last excerpt, remove the trailing newline from // Skip over any subsequent excerpts that are also removed.
// the previous excerpt. if let Some(&next_excerpt_id) = excerpt_ids.peek() {
if cursor.item().is_none() && old_start > 0 { if let Some(next_excerpt) = cursor.item() {
old_start -= 1; if next_excerpt.id == *next_excerpt_id {
new_excerpts.update_last(|e| e.has_trailing_newline = false, &()); excerpt = next_excerpt;
excerpt_id = excerpt_ids.next().unwrap();
continue;
}
}
} }
let new_start = new_excerpts.summary().text.bytes; break;
edits.push(Edit {
old: old_start..old_end,
new: new_start..new_start,
});
} }
// When removing the last excerpt, remove the trailing newline from
// the previous excerpt.
if cursor.item().is_none() && old_start > 0 {
old_start -= 1;
new_excerpts.update_last(|e| e.has_trailing_newline = false, &());
}
// Push an edit for the removal of this run of excerpts.
let old_end = cursor.start().1;
let new_start = new_excerpts.summary().text.bytes;
edits.push(Edit {
old: old_start..old_end,
new: new_start..new_start,
});
} }
} }
new_excerpts.push_tree(cursor.suffix(&()), &()); new_excerpts.push_tree(cursor.suffix(&()), &());
@ -2311,18 +2332,26 @@ mod tests {
buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx)); buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
} }
20..=29 if !expected_excerpts.is_empty() => { 20..=29 if !expected_excerpts.is_empty() => {
let ix = rng.gen_range(0..expected_excerpts.len()); let mut ids_to_remove = vec![];
let id = excerpt_ids.remove(ix); for _ in 0..rng.gen_range(1..=3) {
let (buffer, range) = expected_excerpts.remove(ix); if expected_excerpts.is_empty() {
let buffer = buffer.read(cx); break;
log::info!( }
"Removing excerpt {}: {:?}",
ix, let ix = rng.gen_range(0..expected_excerpts.len());
buffer ids_to_remove.push(excerpt_ids.remove(ix));
.text_for_range(range.to_offset(&buffer)) let (buffer, range) = expected_excerpts.remove(ix);
.collect::<String>(), let buffer = buffer.read(cx);
); log::info!(
list.update(cx, |list, cx| list.remove_excerpts(&[id], cx)); "Removing excerpt {}: {:?}",
ix,
buffer
.text_for_range(range.to_offset(&buffer))
.collect::<String>(),
);
}
ids_to_remove.sort_unstable();
list.update(cx, |list, cx| list.remove_excerpts(&ids_to_remove, cx));
} }
_ => { _ => {
let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) { let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {