diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index ddaddb7289..4d12ca90d1 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -1,7 +1,7 @@ use std::ops::Range; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; @@ -37,7 +37,6 @@ impl sum_tree::Item for DiffHunk { fn summary(&self) -> Self::Summary { DiffHunkSummary { buffer_range: self.buffer_range.clone(), - head_range: self.head_byte_range.clone(), } } } @@ -45,54 +44,17 @@ impl sum_tree::Item for DiffHunk { #[derive(Debug, Default, Clone)] pub struct DiffHunkSummary { buffer_range: Range, - head_range: Range, } impl sum_tree::Summary for DiffHunkSummary { type Context = text::BufferSnapshot; - fn add_summary(&mut self, other: &Self, _: &Self::Context) { - self.head_range.start = self.head_range.start.min(other.head_range.start); - self.head_range.end = self.head_range.end.max(other.head_range.end); - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkHeadEnd(usize); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) { - self.0 = summary.head_range.end; - } - - fn from_summary(summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) -> Self { - HunkHeadEnd(summary.head_range.end) - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkBufferStart(u32); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferStart { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { - self.0 = summary.buffer_range.start.to_point(buffer).row; - } - - fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { - HunkBufferStart(summary.buffer_range.start.to_point(buffer).row) - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkBufferEnd(u32); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { - self.0 = summary.buffer_range.end.to_point(buffer).row; - } - - fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { - HunkBufferEnd(summary.buffer_range.end.to_point(buffer).row) + fn add_summary(&mut self, other: &Self, buffer: &Self::Context) { + self.buffer_range.start = self + .buffer_range + .start + .min(&other.buffer_range.start, buffer); + self.buffer_range.end = self.buffer_range.end.max(&other.buffer_range.end, buffer); } } @@ -115,23 +77,30 @@ impl BufferDiff { query_row_range: Range, buffer: &'a BufferSnapshot, ) -> impl 'a + Iterator> { - self.tree.iter().filter_map(move |hunk| { - let range = hunk.buffer_range.to_point(&buffer); + let start = buffer.anchor_before(Point::new(query_row_range.start, 0)); + let end = buffer.anchor_after(Point::new(query_row_range.end, 0)); - if range.start.row <= query_row_range.end && query_row_range.start <= range.end.row { - let end_row = if range.end.column > 0 { - range.end.row + 1 - } else { - range.end.row - }; + let mut cursor = self.tree.filter::<_, DiffHunkSummary>(move |summary| { + let before_start = summary.buffer_range.end.cmp(&start, buffer).is_lt(); + let after_end = summary.buffer_range.start.cmp(&end, buffer).is_gt(); + !before_start && !after_end + }); - Some(DiffHunk { - buffer_range: range.start.row..end_row, - head_byte_range: hunk.head_byte_range.clone(), - }) + std::iter::from_fn(move || { + cursor.next(buffer); + let hunk = cursor.item()?; + + let range = hunk.buffer_range.to_point(buffer); + let end_row = if range.end.column > 0 { + range.end.row + 1 } else { - None - } + range.end.row + }; + + Some(DiffHunk { + buffer_range: range.start.row..end_row, + head_byte_range: hunk.head_byte_range.clone(), + }) }) } @@ -270,7 +239,7 @@ mod tests { let buffer_text = " one - hello + HELLO three " .unindent(); @@ -278,10 +247,78 @@ mod tests { let mut buffer = Buffer::new(0, 0, buffer_text); let mut diff = BufferDiff::new(); smol::block_on(diff.update(&head_text, &buffer)); - assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); + assert_hunks( + &diff, + &buffer, + &head_text, + &[(1..2, "two\n", "HELLO\n")], + None, + ); buffer.edit([(0..0, "point five\n")]); - assert_hunks(&diff, &buffer, &head_text, &[(2..3, "two\n")]); + smol::block_on(diff.update(&head_text, &buffer)); + assert_hunks( + &diff, + &buffer, + &head_text, + &[(0..1, "", "point five\n"), (2..3, "two\n", "HELLO\n")], + None, + ); + } + + #[test] + fn test_buffer_diff_range() { + let head_text = " + one + two + three + four + five + six + seven + eight + nine + ten + " + .unindent(); + + let buffer_text = " + A + one + B + two + C + three + HELLO + four + five + SIXTEEN + seven + eight + WORLD + nine + + ten + + " + .unindent(); + + let buffer = Buffer::new(0, 0, buffer_text); + let mut diff = BufferDiff::new(); + smol::block_on(diff.update(&head_text, &buffer)); + assert_eq!(diff.hunks(&buffer).count(), 8); + + assert_hunks( + &diff, + &buffer, + &head_text, + &[ + (6..7, "", "HELLO\n"), + (9..10, "six\n", "SIXTEEN\n"), + (12..13, "", "WORLD\n"), + ], + Some(7..12), + ); } #[track_caller] @@ -289,23 +326,30 @@ mod tests { diff: &BufferDiff, buffer: &BufferSnapshot, head_text: &str, - expected_hunks: &[(Range, &str)], + expected_hunks: &[(Range, &str, &str)], + range: Option>, ) { - let hunks = diff.hunks(buffer).collect::>(); - assert_eq!( - hunks.len(), - expected_hunks.len(), - "actual hunks are {hunks:#?}" - ); + let actual_hunks = diff + .hunks_in_range(range.unwrap_or(0..u32::MAX), buffer) + .map(|hunk| { + ( + hunk.buffer_range.clone(), + &head_text[hunk.head_byte_range], + buffer + .text_for_range( + Point::new(hunk.buffer_range.start, 0) + ..Point::new(hunk.buffer_range.end, 0), + ) + .collect::(), + ) + }) + .collect::>(); - let diff_iter = hunks.iter().enumerate(); - for ((index, hunk), (expected_range, expected_str)) in diff_iter.zip(expected_hunks) { - assert_eq!(&hunk.buffer_range, expected_range, "for hunk {index}"); - assert_eq!( - &head_text[hunk.head_byte_range.clone()], - *expected_str, - "for hunk {index}" - ); - } + let expected_hunks: Vec<_> = expected_hunks + .iter() + .map(|(r, s, h)| (r.clone(), *s, h.to_string())) + .collect(); + + assert_eq!(actual_hunks, expected_hunks); } } diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 9f70ae1cc7..ab0e1eeabc 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -26,7 +26,7 @@ impl Anchor { bias: Bias::Right, buffer_id: None, }; - + pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering { let fragment_id_comparison = if self.timestamp == other.timestamp { Ordering::Equal