From a2e8fc79d9ff47686eb9b6442049f2f9331ba240 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 9 Sep 2022 17:32:19 -0400 Subject: [PATCH] Switch head range from row range to byte offset range Co-Authored-By: Max Brunsfeld --- crates/language/src/git.rs | 405 +++++++++++++++++++++++-------------- 1 file changed, 255 insertions(+), 150 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 09d74ad9f3..b3e2f7da51 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -16,7 +16,7 @@ pub enum DiffHunkStatus { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DiffHunk { pub buffer_range: Range, - pub head_range: Range, + pub head_range: Range, } impl DiffHunk { @@ -45,7 +45,7 @@ impl sum_tree::Item for DiffHunk { #[derive(Debug, Default, Clone)] pub struct DiffHunkSummary { buffer_range: Range, - head_range: Range, + head_range: Range, } impl sum_tree::Summary for DiffHunkSummary { @@ -58,7 +58,7 @@ impl sum_tree::Summary for DiffHunkSummary { } #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkHeadEnd(u32); +struct HunkHeadEnd(usize); impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) { @@ -83,55 +83,63 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } } -struct HunkIter<'a> { - index: usize, - patch: GitPatch<'a>, -} +// struct HunkIter<'a> { +// index: usize, +// patch: GitPatch<'a>, +// } -impl<'a> HunkIter<'a> { - fn diff(head: &'a [u8], current: &'a [u8]) -> Option { - let mut options = GitOptions::default(); - options.context_lines(0); - let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { - Ok(patch) => patch, - Err(_) => return None, - }; +// impl<'a> HunkIter<'a> { +// fn diff(head: &'a [u8], current: &'a [u8]) -> Option { +// let mut options = GitOptions::default(); +// options.context_lines(0); +// let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { +// Ok(patch) => patch, +// Err(_) => return None, +// }; - Some(HunkIter { index: 0, patch }) - } +// Some(HunkIter { index: 0, patch }) +// } - fn next(&mut self, buffer: &BufferSnapshot) -> Option> { - if self.index >= self.patch.num_hunks() { - return None; - } +// fn next(&mut self, buffer: &BufferSnapshot) -> Option> { +// if self.index >= self.patch.num_hunks() { +// return None; +// } - let (hunk, _) = match self.patch.hunk(self.index) { - Ok(it) => it, - Err(_) => return None, - }; +// let (hunk, _) = match self.patch.hunk(self.index) { +// Ok(it) => it, +// Err(_) => return None, +// }; +// let hunk_line_count = self.patch.num_lines_in_hunk(self.index).unwrap(); - let new_start = hunk.new_start() - 1; - let new_end = new_start + hunk.new_lines(); - let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); - let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); - let buffer_range = start_anchor..end_anchor; +// println!("{hunk:#?}"); +// for index in 0..hunk_line_count { +// println!("{:?}", self.patch.line_in_hunk(self.index, index)); +// } - //This is probably wrong? When does this trigger? Should buffer range also do this? - let head_range = if hunk.old_start() == 0 { - 0..0 - } else { - let old_start = hunk.old_start() - 1; - let old_end = old_start + hunk.old_lines(); - old_start..old_end - }; +// let new_start = hunk.new_start() - 1; +// let new_end = new_start + hunk.new_lines(); +// let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); +// let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); +// let buffer_range = start_anchor..end_anchor; - self.index += 1; - Some(DiffHunk { - buffer_range, - head_range, - }) - } -} +// //This is probably wrong? When does this trigger? Should buffer range also do this? +// let head_range = if hunk.old_start() == 0 { +// 0..0 +// } else { +// let old_start = hunk.old_start() - 1; +// let old_end = old_start + hunk.old_lines(); +// old_start..old_end +// }; + +// // let head_start_index = self.patch.line_in_hunk(self.index, 0) + +// self.index += 1; +// Some(DiffHunk { +// buffer_range, +// head_range, +// }) +// } +// } #[derive(Clone)] pub struct BufferDiffSnapshot { @@ -144,7 +152,7 @@ impl BufferDiffSnapshot { query_row_range: Range, buffer: &'a BufferSnapshot, ) -> impl 'a + Iterator> { - println!("{} hunks overall", self.tree.iter().count()); + // println!("{} hunks overall", self.tree.iter().count()); self.tree.iter().filter_map(move |hunk| { let range = hunk.buffer_range.to_point(&buffer); @@ -183,16 +191,100 @@ impl BufferDiff { let buffer_string = buffer.as_rope().to_string(); let buffer_bytes = buffer_string.as_bytes(); - let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); - if let Some(mut iter) = iter { - let mut hunks = SumTree::new(); - while let Some(hunk) = iter.next(buffer) { - hunks.push(hunk, buffer); + let mut options = GitOptions::default(); + options.context_lines(0); + let patch = match GitPatch::from_buffers( + head_text.as_bytes(), + None, + buffer_bytes, + None, + Some(&mut options), + ) { + Ok(patch) => patch, + Err(_) => todo!("This needs to be handled"), + }; + + let mut hunks = SumTree::>::new(); + let mut delta = 0i64; + for i in 0..patch.num_hunks() { + let diff_line_item_count = patch.num_lines_in_hunk(i).unwrap(); + + // if diff_line_item_count == 0 { + // continue; + // } + + // let calc_line_diff_hunk = || { + + // }; + + // let first_line = patch.line_in_hunk(0).unwrap(); + // let mut hunk = + + for j in 0..diff_line_item_count { + let line = patch.line_in_hunk(i, j).unwrap(); + + let hunk = match line.origin_value() { + libgit::DiffLineType::Addition => { + let buffer_start = line.content_offset(); + let buffer_end = buffer_start as usize + line.content().len(); + let head_offset = (buffer_start - delta) as usize; + delta += line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_start as usize) + ..buffer.anchor_after(buffer_end), + head_range: head_offset..head_offset, + } + } + libgit::DiffLineType::Deletion => { + let head_start = line.content_offset(); + let head_end = head_start as usize + line.content().len(); + let buffer_offset = (head_start + delta) as usize; + delta -= line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_offset) + ..buffer.anchor_after(buffer_offset), + head_range: (head_start as usize)..head_end, + } + } + + libgit::DiffLineType::AddEOFNL => todo!(), + libgit::DiffLineType::ContextEOFNL => todo!(), + libgit::DiffLineType::DeleteEOFNL => todo!(), + libgit::DiffLineType::Context => unreachable!(), + libgit::DiffLineType::FileHeader => continue, + libgit::DiffLineType::HunkHeader => continue, + libgit::DiffLineType::Binary => continue, + }; + + let mut combined = false; + hunks.update_last( + |last_hunk| { + if last_hunk.head_range.end == hunk.head_range.start { + last_hunk.head_range.end = hunk.head_range.end; + last_hunk.buffer_range.end = hunk.buffer_range.end; + combined = true; + } + }, + buffer, + ); + if !combined { + hunks.push(hunk, buffer); + } } - hunks - } else { - SumTree::new() } + + // let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); + // if let Some(mut iter) = iter { + // let mut hunks = SumTree::new(); + // while let Some(hunk) = iter.next(buffer) { + // hunks.push(hunk, buffer); + // } + // println!("========"); + // hunks + // } else { + // SumTree::new() + // } + hunks } else { SumTree::new() }; @@ -208,108 +300,108 @@ impl BufferDiff { } pub fn update(&mut self, head: &Rope, buffer: &text::BufferSnapshot) { - let expand_by = 20; - let combine_distance = 5; + // let expand_by = 20; + // let combine_distance = 5; - struct EditRange { - head_start: u32, - head_end: u32, - buffer_start: u32, - buffer_end: u32, - } + // struct EditRange { + // head_start: u32, + // head_end: u32, + // buffer_start: u32, + // buffer_end: u32, + // } - let mut ranges = Vec::::new(); + // let mut ranges = Vec::::new(); - for edit in buffer.edits_since::(&self.last_update_version) { - //This bit is extremely wrong, this is not where these row lines should come from - let head_start = edit.old.start.row.saturating_sub(expand_by); - let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); + // for edit in buffer.edits_since::(&self.last_update_version) { + // //This bit is extremely wrong, this is not where these row lines should come from + // let head_start = edit.old.start.row.saturating_sub(expand_by); + // let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); - let buffer_start = edit.new.start.row.saturating_sub(expand_by); - let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); + // let buffer_start = edit.new.start.row.saturating_sub(expand_by); + // let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); - if let Some(last_range) = ranges.last_mut() { - let head_distance = last_range.head_end.abs_diff(head_end); - let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); + // if let Some(last_range) = ranges.last_mut() { + // let head_distance = last_range.head_end.abs_diff(head_end); + // let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); - if head_distance <= combine_distance || buffer_distance <= combine_distance { - last_range.head_start = last_range.head_start.min(head_start); - last_range.head_end = last_range.head_end.max(head_end); + // if head_distance <= combine_distance || buffer_distance <= combine_distance { + // last_range.head_start = last_range.head_start.min(head_start); + // last_range.head_end = last_range.head_end.max(head_end); - last_range.buffer_start = last_range.buffer_start.min(buffer_start); - last_range.buffer_end = last_range.buffer_end.max(buffer_end); - } else { - ranges.push(EditRange { - head_start, - head_end, - buffer_start, - buffer_end, - }); - } - } else { - ranges.push(EditRange { - head_start, - head_end, - buffer_start, - buffer_end, - }); - } - } + // last_range.buffer_start = last_range.buffer_start.min(buffer_start); + // last_range.buffer_end = last_range.buffer_end.max(buffer_end); + // } else { + // ranges.push(EditRange { + // head_start, + // head_end, + // buffer_start, + // buffer_end, + // }); + // } + // } else { + // ranges.push(EditRange { + // head_start, + // head_end, + // buffer_start, + // buffer_end, + // }); + // } + // } - self.last_update_version = buffer.version().clone(); + // self.last_update_version = buffer.version().clone(); - let mut new_hunks = SumTree::new(); - let mut cursor = self.snapshot.tree.cursor::(); + // let mut new_hunks = SumTree::new(); + // let mut cursor = self.snapshot.tree.cursor::(); - for range in ranges { - let head_range = range.head_start..range.head_end; - let head_slice = head.slice_rows(head_range.clone()); - let head_str = head_slice.to_string(); + // for range in ranges { + // let head_range = range.head_start..range.head_end; + // let head_slice = head.slice_rows(head_range.clone()); + // let head_str = head_slice.to_string(); - let buffer_range = range.buffer_start..range.buffer_end; - let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); - let buffer_str = buffer_slice.to_string(); + // let buffer_range = range.buffer_start..range.buffer_end; + // let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); + // let buffer_str = buffer_slice.to_string(); - println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); + // println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); - let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { - Some(iter) => iter, - None => continue, - }; + // let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { + // Some(iter) => iter, + // None => continue, + // }; - while let Some(hunk) = iter.next(buffer) { - println!("hunk"); - let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - println!("prefix len: {}", prefix.iter().count()); - new_hunks.extend(prefix.iter().cloned(), buffer); + // while let Some(hunk) = iter.next(buffer) { + // println!("hunk"); + // let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); + // println!("prefix len: {}", prefix.iter().count()); + // new_hunks.extend(prefix.iter().cloned(), buffer); - new_hunks.push(hunk.clone(), buffer); + // new_hunks.push(hunk.clone(), buffer); - cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - println!("item: {:?}", cursor.item()); - if let Some(item) = cursor.item() { - if item.head_range.end <= hunk.head_range.end { - println!("skipping"); - cursor.next(buffer); - } - } - } - } + // cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); + // println!("item: {:?}", cursor.item()); + // if let Some(item) = cursor.item() { + // if item.head_range.end <= hunk.head_range.end { + // println!("skipping"); + // cursor.next(buffer); + // } + // } + // } + // } - new_hunks.extend( - cursor - .suffix(buffer) - .iter() - .map(|i| { - println!("extending with {i:?}"); - i - }) - .cloned(), - buffer, - ); - drop(cursor); + // new_hunks.extend( + // cursor + // .suffix(buffer) + // .iter() + // .map(|i| { + // println!("extending with {i:?}"); + // i + // }) + // .cloned(), + // buffer, + // ); + // drop(cursor); - self.snapshot.tree = new_hunks; + // self.snapshot.tree = new_hunks; } } @@ -354,22 +446,35 @@ mod tests { let mut buffer = Buffer::new(0, 0, buffer_text); let diff = BufferDiff::new(&Some(head_text.clone()), &buffer); - assert_eq!( - diff.snapshot.hunks(&buffer).collect::>(), - &[DiffHunk { - buffer_range: 1..2, - head_range: 1..2 - }] - ); + assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); buffer.edit([(0..0, "point five\n")]); + assert_hunks(&diff, &buffer, &head_text, &[(2..3, "two\n")]); + } + + #[track_caller] + fn assert_hunks( + diff: &BufferDiff, + buffer: &BufferSnapshot, + head_text: &str, + expected_hunks: &[(Range, &str)], + ) { + let hunks = diff.snapshot.hunks(buffer).collect::>(); assert_eq!( - diff.snapshot.hunks(&buffer).collect::>(), - &[DiffHunk { - buffer_range: 2..3, - head_range: 1..2 - }] + hunks.len(), + expected_hunks.len(), + "actual hunks are {hunks:#?}" ); + + 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_range.clone()], + *expected_str, + "for hunk {index}" + ); + } } // use rand::rngs::StdRng;