Fix panic or bad hunks when expanding hunks w/ multiple ranges in 1 hunk (#28117)

Max Brunsfeld created

Release Notes:

- Fixed a crash that could happen when expanding diff hunks with
multiple cursors in one hunk.

Change summary

crates/multi_buffer/src/multi_buffer.rs       |  5 +
crates/multi_buffer/src/multi_buffer_tests.rs | 54 +++++++++++++++++++++
2 files changed, 59 insertions(+)

Detailed changes

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,

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
             "