From 156dd32a35f3aac84b94bc467101538586c1a687 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 4 Apr 2025 12:22:02 -0700 Subject: [PATCH] Fix panic or bad hunks when expanding hunks w/ multiple ranges in 1 hunk (#28117) Release Notes: - Fixed a crash that could happen when expanding diff hunks with multiple cursors in one hunk. --- crates/multi_buffer/src/multi_buffer.rs | 5 ++ crates/multi_buffer/src/multi_buffer_tests.rs | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e7ddd27a93..8d3363b55c 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2649,11 +2649,15 @@ impl MultiBuffer { self.sync(cx); let mut snapshot = self.snapshot.borrow_mut(); let mut excerpt_edits = Vec::new(); + let mut last_hunk_row = None; 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; } + if last_hunk_row.map_or(false, |row| row >= diff_hunk.row_range.start) { + continue; + } let start = Anchor::in_buffer( diff_hunk.excerpt_id, diff_hunk.buffer_id, @@ -2666,6 +2670,7 @@ impl MultiBuffer { ); let start = snapshot.excerpt_offset_for_anchor(&start); let end = snapshot.excerpt_offset_for_anchor(&end); + last_hunk_row = Some(diff_hunk.row_range.start); excerpt_edits.push(text::Edit { old: start..end, new: start..end, diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 6ade1dc9e5..c3e8a69652 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -1380,6 +1380,7 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) { " one four + five six " ); @@ -1413,6 +1414,7 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) { + TWO + THREE four + - five + FIVE six " @@ -1440,6 +1442,58 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) { + TWO + THREE four + - five + + FIVE + six + " + ), + ); + + // Now collapse all diff hunks + multibuffer.update(cx, |multibuffer, cx| { + multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx); + }); + + assert_new_snapshot( + &multibuffer, + &mut snapshot, + &mut subscription, + cx, + indoc!( + " + one + TWO + THREE + four + FIVE + six + " + ), + ); + + // Expand the hunks again, but this time provide two ranges that are both within the same hunk + // Target the first hunk which is between "one" and "four" + multibuffer.update(cx, |multibuffer, cx| { + multibuffer.expand_diff_hunks( + vec![ + snapshot.anchor_before(Point::new(4, 0))..snapshot.anchor_before(Point::new(4, 0)), + snapshot.anchor_before(Point::new(4, 2))..snapshot.anchor_before(Point::new(4, 2)), + ], + cx, + ); + }); + assert_new_snapshot( + &multibuffer, + &mut snapshot, + &mut subscription, + cx, + indoc!( + " + one + TWO + THREE + four + - five + FIVE six "