From 8ba4e4a9478c7661e67ea4bfac90779c03e4a228 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Fri, 21 Feb 2025 18:36:36 -0500 Subject: [PATCH] Fix issues with adjacent diff hunks --- crates/editor/src/editor.rs | 13 +- crates/editor/src/editor_tests.rs | 157 ++++++++++++++++++++++++ crates/editor/src/element.rs | 2 +- crates/multi_buffer/src/multi_buffer.rs | 60 +++++---- 4 files changed, 193 insertions(+), 39 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a3a4dfd6bbdb5c..5977c1b7310f0d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -12990,14 +12990,13 @@ impl Editor { }) } - fn toggle_diff_hunks_in_ranges_narrow( - &mut self, - ranges: Vec>, - cx: &mut Context<'_, Editor>, - ) { + fn toggle_single_diff_hunk(&mut self, range: Range, cx: &mut Context) { self.buffer.update(cx, |buffer, cx| { - let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx); - buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx); + let snapshot = buffer.snapshot(cx); + let excerpt_id = range.end.excerpt_id; + let point_range = range.to_point(&snapshot); + let expand = !buffer.single_hunk_is_expanded(range, cx); + buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx); }) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 030a07d3f20af4..ef2eb6e9434f07 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -14821,6 +14821,163 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) { ); } +#[gpui::test] +async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorTestContext::new(cx).await; + + let diff_base = r#" + a + b + c + "# + .unindent(); + + cx.set_state( + &r#" + ˇA + b + C + "# + .unindent(), + ); + cx.set_diff_base(&diff_base); + cx.update_editor(|editor, window, cx| { + editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx); + }); + executor.run_until_parked(); + + let both_hunks_expanded = r#" + - a + + ˇA + b + - c + + C + "# + .unindent(); + + cx.assert_state_with_diff(both_hunks_expanded.clone()); + + let hunk_ranges = cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + let hunks = editor + .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot) + .collect::>(); + let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0]; + let buffer_id = hunks[0].buffer_id; + hunks + .into_iter() + .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone())) + .collect::>() + }); + assert_eq!(hunk_ranges.len(), 2); + + cx.update_editor(|editor, _, cx| { + editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx); + }); + executor.run_until_parked(); + + let second_hunk_expanded = r#" + ˇA + b + - c + + C + "# + .unindent(); + + cx.assert_state_with_diff(second_hunk_expanded); + + cx.update_editor(|editor, _, cx| { + editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx); + }); + executor.run_until_parked(); + + cx.assert_state_with_diff(both_hunks_expanded.clone()); + + cx.update_editor(|editor, _, cx| { + editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx); + }); + executor.run_until_parked(); + + let first_hunk_expanded = r#" + - a + + ˇA + b + C + "# + .unindent(); + + cx.assert_state_with_diff(first_hunk_expanded); + + cx.update_editor(|editor, _, cx| { + editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx); + }); + executor.run_until_parked(); + + cx.assert_state_with_diff(both_hunks_expanded); + + cx.set_state( + &r#" + ˇA + b + "# + .unindent(), + ); + cx.run_until_parked(); + + // TODO this cursor position seems bad + cx.assert_state_with_diff( + r#" + - ˇa + + A + b + "# + .unindent(), + ); + + cx.update_editor(|editor, window, cx| { + editor.expand_all_diff_hunks(&ExpandAllHunkDiffs, window, cx); + }); + + cx.assert_state_with_diff( + r#" + - ˇa + + A + b + - c + "# + .unindent(), + ); + + let hunk_ranges = cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + let hunks = editor + .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot) + .collect::>(); + let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0]; + let buffer_id = hunks[0].buffer_id; + hunks + .into_iter() + .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone())) + .collect::>() + }); + assert_eq!(hunk_ranges.len(), 2); + + cx.update_editor(|editor, _, cx| { + editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx); + }); + executor.run_until_parked(); + + cx.assert_state_with_diff( + r#" + - ˇa + + A + b + "# + .unindent(), + ); +} + #[gpui::test] fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index c8e609b5a89f20..a18d8c5edf0d8e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -547,7 +547,7 @@ impl EditorElement { let mut modifiers = event.modifiers; if let Some(hovered_hunk) = hovered_hunk { - editor.toggle_diff_hunks_in_ranges_narrow(vec![hovered_hunk], cx); + editor.toggle_single_diff_hunk(hovered_hunk, cx); cx.notify(); return; } else if gutter_hitbox.is_hovered(window) { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 72a467c89f63c7..b7fadc92b4261b 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2388,6 +2388,23 @@ impl MultiBuffer { .is_some() } + pub fn single_hunk_is_expanded(&self, range: Range, cx: &App) -> bool { + let snapshot = self.read(cx); + let mut cursor = snapshot.diff_transforms.cursor::(&()); + let offset_range = range.to_offset(&snapshot); + cursor.seek(&offset_range.start, Bias::Right, &()); + while let Some(item) = cursor.item() { + if *cursor.start() >= offset_range.end && *cursor.start() > offset_range.start { + break; + } + if item.hunk_info().is_some() { + return true; + } + cursor.next(&()); + } + false + } + pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range], cx: &App) -> bool { let snapshot = self.read(cx); let mut cursor = snapshot.diff_transforms.cursor::(&()); @@ -2411,9 +2428,9 @@ impl MultiBuffer { false } - fn expand_or_collapse_diff_hunks_internal( + pub fn expand_or_collapse_diff_hunks_inner( &mut self, - ranges: impl Iterator, ExcerptId)>, + ranges: impl IntoIterator, ExcerptId)>, expand: bool, cx: &mut Context, ) { @@ -2459,22 +2476,6 @@ impl MultiBuffer { }); } - pub fn expand_or_collapse_diff_hunks_narrow( - &mut self, - ranges: Vec>, - expand: bool, - cx: &mut Context, - ) { - let snapshot = self.snapshot.borrow().clone(); - self.expand_or_collapse_diff_hunks_internal( - ranges - .iter() - .map(move |range| (range.to_point(&snapshot), range.end.excerpt_id)), - expand, - cx, - ); - } - pub fn expand_or_collapse_diff_hunks( &mut self, ranges: Vec>, @@ -2482,19 +2483,16 @@ impl MultiBuffer { cx: &mut Context, ) { let snapshot = self.snapshot.borrow().clone(); - self.expand_or_collapse_diff_hunks_internal( - ranges.iter().map(move |range| { - let end_excerpt_id = range.end.excerpt_id; - let range = range.to_point(&snapshot); - let mut peek_end = range.end; - if range.end.row < snapshot.max_row().0 { - peek_end = Point::new(range.end.row + 1, 0); - }; - (range.start..peek_end, end_excerpt_id) - }), - expand, - cx, - ); + let ranges = ranges.iter().map(move |range| { + let end_excerpt_id = range.end.excerpt_id; + let range = range.to_point(&snapshot); + let mut peek_end = range.end; + if range.end.row < snapshot.max_row().0 { + peek_end = Point::new(range.end.row + 1, 0); + }; + (range.start..peek_end, end_excerpt_id) + }); + self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx); } pub fn resize_excerpt(