Fix unnecessarily large edits emitted from multi buffer on diff recalculation (#23753)
This fixes an issue introduced in #22994 where soft wrap would recalculate for the entire buffer when editing. Release Notes: - N/A --------- Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
parent
5331418f3a
commit
ee5f270f3f
7 changed files with 402 additions and 251 deletions
|
@ -35,6 +35,7 @@ util.workspace = true
|
|||
unindent.workspace = true
|
||||
serde_json.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
text = {workspace = true, features = ["test-support"]}
|
||||
|
||||
[features]
|
||||
test-support = []
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use rope::Rope;
|
||||
use std::{iter, ops::Range};
|
||||
use std::{cmp, iter, ops::Range};
|
||||
use sum_tree::SumTree;
|
||||
use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point};
|
||||
|
||||
|
@ -25,7 +25,7 @@ pub struct DiffHunk {
|
|||
}
|
||||
|
||||
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct InternalDiffHunk {
|
||||
buffer_range: Range<Anchor>,
|
||||
diff_base_byte_range: Range<usize>,
|
||||
|
@ -187,6 +187,69 @@ impl BufferDiff {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn compare(&self, old: &Self, new_snapshot: &BufferSnapshot) -> Option<Range<Anchor>> {
|
||||
let mut new_cursor = self.tree.cursor::<()>(new_snapshot);
|
||||
let mut old_cursor = old.tree.cursor::<()>(new_snapshot);
|
||||
old_cursor.next(new_snapshot);
|
||||
new_cursor.next(new_snapshot);
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
|
||||
loop {
|
||||
match (new_cursor.item(), old_cursor.item()) {
|
||||
(Some(new_hunk), Some(old_hunk)) => {
|
||||
match new_hunk
|
||||
.buffer_range
|
||||
.start
|
||||
.cmp(&old_hunk.buffer_range.start, new_snapshot)
|
||||
{
|
||||
cmp::Ordering::Less => {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
new_cursor.next(new_snapshot);
|
||||
}
|
||||
cmp::Ordering::Equal => {
|
||||
if new_hunk != old_hunk {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
if old_hunk
|
||||
.buffer_range
|
||||
.end
|
||||
.cmp(&new_hunk.buffer_range.end, new_snapshot)
|
||||
.is_ge()
|
||||
{
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
} else {
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
}
|
||||
}
|
||||
|
||||
new_cursor.next(new_snapshot);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
start.get_or_insert(old_hunk.buffer_range.start);
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(new_hunk), None) => {
|
||||
start.get_or_insert(new_hunk.buffer_range.start);
|
||||
end.replace(new_hunk.buffer_range.end);
|
||||
new_cursor.next(new_snapshot);
|
||||
}
|
||||
(None, Some(old_hunk)) => {
|
||||
start.get_or_insert(old_hunk.buffer_range.start);
|
||||
end.replace(old_hunk.buffer_range.end);
|
||||
old_cursor.next(new_snapshot);
|
||||
}
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
start.zip(end).map(|(start, end)| start..end)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn clear(&mut self, buffer: &text::BufferSnapshot) {
|
||||
self.tree = SumTree::new(buffer);
|
||||
|
@ -427,4 +490,128 @@ mod tests {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_diff_compare() {
|
||||
let base_text = "
|
||||
zero
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let buffer_text_1 = "
|
||||
one
|
||||
three
|
||||
four
|
||||
five
|
||||
SIX
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text_1);
|
||||
|
||||
let empty_diff = BufferDiff::new(&buffer);
|
||||
let diff_1 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_1.compare(&empty_diff, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
|
||||
|
||||
// Edit does not affect the diff.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
three
|
||||
four
|
||||
five
|
||||
«SIX.5»
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_2 = BufferDiff::build(&base_text, &buffer);
|
||||
assert_eq!(None, diff_2.compare(&diff_1, &buffer));
|
||||
|
||||
// Edit turns a deletion hunk into a modification.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
«THREE»
|
||||
four
|
||||
five
|
||||
SIX.5
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_3 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_3.compare(&diff_2, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));
|
||||
|
||||
// Edit turns a modification hunk into a deletion.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four
|
||||
five«»
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_4 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_4.compare(&diff_3, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));
|
||||
|
||||
// Edit introduces a new insertion hunk.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four«
|
||||
FOUR.5
|
||||
»five
|
||||
seven
|
||||
eight
|
||||
NINE
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_5 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_5.compare(&diff_4, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));
|
||||
|
||||
// Edit removes a hunk.
|
||||
buffer.edit_via_marked_text(
|
||||
&"
|
||||
one
|
||||
THREE
|
||||
four
|
||||
FOUR.5
|
||||
five
|
||||
seven
|
||||
eight
|
||||
«nine»
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
let diff_6 = BufferDiff::build(&base_text, &buffer);
|
||||
let range = diff_6.compare(&diff_5, &buffer).unwrap();
|
||||
assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue