diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6304a56b66585f..90773e735425d1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -12207,11 +12207,19 @@ impl Editor { cx: &mut Context<'_, Editor>, ) { self.buffer.update(cx, |buffer, cx| { - if buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx) { - buffer.collapse_diff_hunks(ranges, cx) - } else { - buffer.expand_diff_hunks(ranges, cx) - } + let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx); + buffer.expand_or_collapse_diff_hunks(ranges, expand, cx); + }) + } + + fn toggle_diff_hunks_in_ranges_narrow( + &mut self, + ranges: Vec>, + cx: &mut Context<'_, Editor>, + ) { + 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); }) } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4669f5d57fbba7..f6790a84c4f05b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -558,7 +558,7 @@ impl EditorElement { let mut modifiers = event.modifiers; if let Some(hovered_hunk) = hovered_hunk { - editor.toggle_diff_hunks_in_ranges(vec![hovered_hunk], cx); + editor.toggle_diff_hunks_in_ranges_narrow(vec![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 1986944a1d6e95..38958e91f4d491 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2412,24 +2412,17 @@ impl MultiBuffer { false } - fn expand_or_collapse_diff_hunks( + fn expand_or_collapse_diff_hunks_internal( &mut self, - ranges: Vec>, + ranges: impl Iterator, ExcerptId)>, expand: bool, cx: &mut Context, ) { self.sync(cx); let mut snapshot = self.snapshot.borrow_mut(); let mut excerpt_edits = Vec::new(); - for range in ranges.iter() { - 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); - }; - - for diff_hunk in snapshot.diff_hunks_in_range(range.start..peek_end) { + for (range, end_excerpt_id) in ranges { + for diff_hunk in snapshot.diff_hunks_in_range(range) { if diff_hunk.excerpt_id.cmp(&end_excerpt_id, &snapshot).is_gt() { continue; } @@ -2464,6 +2457,44 @@ 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>, + expand: bool, + 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 && range.end != range.start { + peek_end = Point::new(range.end.row + 1, 0); + }; + (range.start..peek_end, end_excerpt_id) + }), + expand, + cx, + ); + } + pub fn resize_excerpt( &mut self, id: ExcerptId, diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 25c3a4cf912467..3679fce9683f4a 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -493,6 +493,76 @@ fn test_diff_hunks_in_range(cx: &mut TestAppContext) { ); } +#[gpui::test] +fn test_toggle_adjacent_hunks(cx: &mut TestAppContext) { + let base_text = "b\n"; + let text = "a\nb\nc\n"; + + let buffer = cx.new(|cx| Buffer::local(text, cx)); + let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(&base_text, &buffer, cx)); + let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx)); + + let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.add_change_set(change_set.clone(), cx); + (multibuffer.snapshot(cx), multibuffer.subscribe()) + }); + + assert_new_snapshot( + &multibuffer, + &mut snapshot, + &mut subscription, + cx, + "a\nb\nc\n", + ); + + multibuffer.update(cx, |multibuffer, cx| { + let start = multibuffer + .buffer_point_to_anchor(&buffer, Point::new(0, 0), cx) + .unwrap(); + multibuffer.expand_diff_hunks(vec![start..start], cx); + }); + + assert_new_snapshot( + &multibuffer, + &mut snapshot, + &mut subscription, + cx, + "+ a\n b\n c\n", + ); +} + +#[gpui::test] +fn test_toggle_adjacent_hunks_2(cx: &mut TestAppContext) { + let base_text = "a\nb\n"; + let text = "b\nc\n"; + + let buffer = cx.new(|cx| Buffer::local(text, cx)); + let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(&base_text, &buffer, cx)); + let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx)); + + let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.add_change_set(change_set.clone(), cx); + (multibuffer.snapshot(cx), multibuffer.subscribe()) + }); + + assert_new_snapshot(&multibuffer, &mut snapshot, &mut subscription, cx, "b\nc\n"); + + multibuffer.update(cx, |multibuffer, cx| { + let start = multibuffer + .buffer_point_to_anchor(&buffer, Point::new(0, 0), cx) + .unwrap(); + multibuffer.expand_diff_hunks(vec![start..start], cx); + }); + + assert_new_snapshot( + &multibuffer, + &mut snapshot, + &mut subscription, + cx, + "- a\n b\n c\n", + ); +} + #[gpui::test] fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) { let base_text = "one\ntwo\nfour\nfive\nsix\nseven\n";