Preserve cursor position when resetting excerpts (#27850)

Antonio Scandurra , Nathan Sobo , and Conrad Irwin created

Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

assets/keymaps/default-linux.json                 |   7 
assets/keymaps/default-macos.json                 |   8 
crates/assistant/src/inline_assistant.rs          |   5 
crates/assistant2/src/inline_assistant.rs         |   5 
crates/collab/src/tests/following_tests.rs        |  18 
crates/copilot/src/copilot_completion_provider.rs |  20 
crates/diagnostics/src/diagnostics.rs             |   2 
crates/diagnostics/src/diagnostics_tests.rs       |   2 
crates/editor/src/display_map/block_map.rs        |  15 
crates/editor/src/editor_tests.rs                 | 333 +++-------------
crates/editor/src/element.rs                      |   5 
crates/editor/src/inlay_hint_cache.rs             |  70 --
crates/editor/src/items.rs                        |  23 
crates/editor/src/jsx_tag_auto_close.rs           |  15 
crates/editor/src/movement.rs                     |  10 
crates/editor/src/proposed_changes_editor.rs      |  12 
crates/editor/src/test/editor_test_context.rs     |   7 
crates/multi_buffer/src/multi_buffer.rs           | 219 ++++++++--
crates/multi_buffer/src/multi_buffer_tests.rs     | 146 +------
19 files changed, 302 insertions(+), 620 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -594,13 +594,6 @@
       "ctrl-:": "editor::ToggleInlayHints"
     }
   },
-  {
-    "context": "ProposedChangesEditor",
-    "bindings": {
-      "ctrl-shift-y": "editor::ApplyDiffHunk",
-      "ctrl-alt-a": "editor::ApplyAllDiffHunks"
-    }
-  },
   {
     "context": "Editor && jupyter && !ContextEditor",
     "bindings": {

assets/keymaps/default-macos.json 🔗

@@ -704,14 +704,6 @@
       "ctrl-:": "editor::ToggleInlayHints"
     }
   },
-  {
-    "context": "ProposedChangesEditor",
-    "use_key_equivalents": true,
-    "bindings": {
-      "cmd-shift-y": "editor::ApplyDiffHunk",
-      "cmd-shift-a": "editor::ApplyAllDiffHunks"
-    }
-  },
   {
     "context": "PromptEditor",
     "use_key_equivalents": true,

crates/assistant/src/inline_assistant.rs 🔗

@@ -1270,10 +1270,7 @@ impl InlineAssistant {
                     multi_buffer.update(cx, |multi_buffer, cx| {
                         multi_buffer.push_excerpts(
                             old_buffer.clone(),
-                            Some(ExcerptRange {
-                                context: buffer_start..buffer_end,
-                                primary: None,
-                            }),
+                            Some(ExcerptRange::new(buffer_start..buffer_end)),
                             cx,
                         );
                     });

crates/assistant2/src/inline_assistant.rs 🔗

@@ -1363,10 +1363,7 @@ impl InlineAssistant {
                     multi_buffer.update(cx, |multi_buffer, cx| {
                         multi_buffer.push_excerpts(
                             old_buffer.clone(),
-                            Some(ExcerptRange {
-                                context: buffer_start..buffer_end,
-                                primary: None,
-                            }),
+                            Some(ExcerptRange::new(buffer_start..buffer_end)),
                             cx,
                         );
                     });

crates/collab/src/tests/following_tests.rs 🔗

@@ -295,22 +295,8 @@ async fn test_basic_following(
                 .unwrap()
         });
         let mut result = MultiBuffer::new(Capability::ReadWrite);
-        result.push_excerpts(
-            buffer_a1,
-            [ExcerptRange {
-                context: 0..3,
-                primary: None,
-            }],
-            cx,
-        );
-        result.push_excerpts(
-            buffer_a2,
-            [ExcerptRange {
-                context: 4..7,
-                primary: None,
-            }],
-            cx,
-        );
+        result.push_excerpts(buffer_a1, [ExcerptRange::new(0..3)], cx);
+        result.push_excerpts(buffer_a2, [ExcerptRange::new(4..7)], cx);
         result
     });
     let multibuffer_editor_a = workspace_a.update_in(cx_a, |workspace, window, cx| {

crates/copilot/src/copilot_completion_provider.rs 🔗

@@ -729,18 +729,12 @@ mod tests {
             let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
             multibuffer.push_excerpts(
                 buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
                 cx,
             );
             multibuffer.push_excerpts(
                 buffer_2.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
                 cx,
             );
             multibuffer
@@ -981,18 +975,12 @@ mod tests {
             let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
             multibuffer.push_excerpts(
                 private_buffer.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(1, 0),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
                 cx,
             );
             multibuffer.push_excerpts(
                 public_buffer.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(6, 0),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 0)..Point::new(6, 0))],
                 cx,
             );
             multibuffer

crates/diagnostics/src/diagnostics.rs 🔗

@@ -513,7 +513,7 @@ impl ProjectDiagnosticsEditor {
                                         buffer.clone(),
                                         [ExcerptRange {
                                             context: context_range.clone(),
-                                            primary: Some(range.clone()),
+                                            primary: range.clone(),
                                         }],
                                         cx,
                                     )

crates/diagnostics/src/diagnostics_tests.rs 🔗

@@ -913,7 +913,7 @@ fn get_diagnostics_excerpts(
                     path: buffer.file().unwrap().path().to_path_buf(),
                     range: ExcerptRange {
                         context: range.context.to_point(buffer),
-                        primary: range.primary.map(|range| range.to_point(buffer)),
+                        primary: range.primary.to_point(buffer),
                     },
                     group_id: usize::MAX,
                     primary: false,

crates/editor/src/display_map/block_map.rs 🔗

@@ -2125,26 +2125,17 @@ mod tests {
             let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
             excerpt_ids.extend(multi_buffer.push_excerpts(
                 buffer1.clone(),
-                [ExcerptRange {
-                    context: 0..buffer1.read(cx).len(),
-                    primary: None,
-                }],
+                [ExcerptRange::new(0..buffer1.read(cx).len())],
                 cx,
             ));
             excerpt_ids.extend(multi_buffer.push_excerpts(
                 buffer2.clone(),
-                [ExcerptRange {
-                    context: 0..buffer2.read(cx).len(),
-                    primary: None,
-                }],
+                [ExcerptRange::new(0..buffer2.read(cx).len())],
                 cx,
             ));
             excerpt_ids.extend(multi_buffer.push_excerpts(
                 buffer3.clone(),
-                [ExcerptRange {
-                    context: 0..buffer3.read(cx).len(),
-                    primary: None,
-                }],
+                [ExcerptRange::new(0..buffer3.read(cx).len())],
                 cx,
             ));
 

crates/editor/src/editor_tests.rs 🔗

@@ -3165,18 +3165,12 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
         let mut multibuffer = MultiBuffer::new(ReadWrite);
         multibuffer.push_excerpts(
             toml_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(2, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
             cx,
         );
         multibuffer.push_excerpts(
             rust_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(1, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
             cx,
         );
         multibuffer
@@ -7859,54 +7853,27 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
         multi_buffer.push_excerpts(
             buffer_1.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_2.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_3.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
@@ -10685,14 +10652,8 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
         multibuffer.push_excerpts(
             buffer.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(0, 4),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(1, 0)..Point::new(1, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
+                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
             ],
             cx,
         );
@@ -10758,10 +10719,7 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
     );
     let excerpt_ranges = markers.into_iter().map(|marker| {
         let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
-        ExcerptRange {
-            context,
-            primary: None,
-        }
+        ExcerptRange::new(context.clone())
     });
     let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
     let multibuffer = cx.new(|cx| {
@@ -10829,14 +10787,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
             .push_excerpts(
                 buffer.clone(),
                 [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 4),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 4),
-                        primary: None,
-                    },
+                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
+                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
                 ],
                 cx,
             )
@@ -10920,14 +10872,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
             .push_excerpts(
                 buffer.clone(),
                 [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 4),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 4),
-                        primary: None,
-                    },
+                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
+                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
                 ],
                 cx,
             )
@@ -11398,34 +11344,16 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
             let excerpt_ids = multibuffer.push_excerpts(
                 buffer_1.clone(),
                 [
-                    ExcerptRange {
-                        context: 1..6,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 12..15,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 0..3,
-                        primary: None,
-                    },
+                    ExcerptRange::new(1..6),
+                    ExcerptRange::new(12..15),
+                    ExcerptRange::new(0..3),
                 ],
                 cx,
             );
             multibuffer.insert_excerpts_after(
                 excerpt_ids[0],
                 buffer_2.clone(),
-                [
-                    ExcerptRange {
-                        context: 8..12,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 0..6,
-                        primary: None,
-                    },
-                ],
+                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
                 cx,
             );
         });
@@ -13599,54 +13527,27 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
         multibuffer.push_excerpts(
             buffer_1.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_3.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
@@ -13766,54 +13667,27 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
         multibuffer.push_excerpts(
             buffer_1.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_3.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
@@ -14266,54 +14140,27 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
         multibuffer.push_excerpts(
             buffer_1.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 3),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 3),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
             ],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_3.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 3),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
             ],
             cx,
         );
@@ -14422,18 +14269,9 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
         multibuffer.push_excerpts(
             buffer.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(4, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 0),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
+                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
             ],
             cx,
         );
@@ -16613,54 +16451,27 @@ async fn test_folding_buffers(cx: &mut TestAppContext) {
         multi_buffer.push_excerpts(
             buffer_1.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_2.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_3.clone(),
             [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(3, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(5, 0)..Point::new(7, 0),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(9, 0)..Point::new(10, 4),
-                    primary: None,
-                },
+                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
+                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
+                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
             ],
             cx,
         );
@@ -16807,26 +16618,17 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
         let mut multi_buffer = MultiBuffer::new(ReadWrite);
         multi_buffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(3, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(3, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
             cx,
         );
         multi_buffer.push_excerpts(
             buffer_3.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(3, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
             cx,
         );
         multi_buffer
@@ -16939,14 +16741,13 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test
         let mut multi_buffer = MultiBuffer::new(ReadWrite);
         multi_buffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)
+            [ExcerptRange::new(
+                Point::new(0, 0)
                     ..Point::new(
                         sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
                         0,
                     ),
-                primary: None,
-            }],
+            )],
             cx,
         );
         multi_buffer

crates/editor/src/element.rs 🔗

@@ -5628,10 +5628,7 @@ fn header_jump_data(
 ) -> JumpData {
     let range = &for_excerpt.range;
     let buffer = &for_excerpt.buffer;
-    let jump_anchor = range
-        .primary
-        .as_ref()
-        .map_or(range.context.start, |primary| primary.start);
+    let jump_anchor = range.primary.start;
 
     let excerpt_start = range.context.start;
     let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -2565,60 +2565,24 @@ pub mod tests {
             multibuffer.push_excerpts(
                 buffer_1.clone(),
                 [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(2, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 0)..Point::new(11, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 0)..Point::new(33, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 0)..Point::new(55, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 0)..Point::new(66, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 0)..Point::new(77, 0),
-                        primary: None,
-                    },
+                    ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
+                    ExcerptRange::new(Point::new(4, 0)..Point::new(11, 0)),
+                    ExcerptRange::new(Point::new(22, 0)..Point::new(33, 0)),
+                    ExcerptRange::new(Point::new(44, 0)..Point::new(55, 0)),
+                    ExcerptRange::new(Point::new(56, 0)..Point::new(66, 0)),
+                    ExcerptRange::new(Point::new(67, 0)..Point::new(77, 0)),
                 ],
                 cx,
             );
             multibuffer.push_excerpts(
                 buffer_2.clone(),
                 [
-                    ExcerptRange {
-                        context: Point::new(0, 1)..Point::new(2, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 1)..Point::new(11, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 1)..Point::new(33, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 1)..Point::new(55, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 1)..Point::new(66, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 1)..Point::new(77, 1),
-                        primary: None,
-                    },
+                    ExcerptRange::new(Point::new(0, 1)..Point::new(2, 1)),
+                    ExcerptRange::new(Point::new(4, 1)..Point::new(11, 1)),
+                    ExcerptRange::new(Point::new(22, 1)..Point::new(33, 1)),
+                    ExcerptRange::new(Point::new(44, 1)..Point::new(55, 1)),
+                    ExcerptRange::new(Point::new(56, 1)..Point::new(66, 1)),
+                    ExcerptRange::new(Point::new(67, 1)..Point::new(77, 1)),
                 ],
                 cx,
             );
@@ -2907,18 +2871,12 @@ pub mod tests {
         let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
             let buffer_1_excerpts = multibuffer.push_excerpts(
                 buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
                 cx,
             );
             let buffer_2_excerpts = multibuffer.push_excerpts(
                 buffer_2.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 1)..Point::new(2, 1),
-                    primary: None,
-                }],
+                [ExcerptRange::new(Point::new(0, 1)..Point::new(2, 1))],
                 cx,
             );
             (buffer_1_excerpts, buffer_2_excerpts)

crates/editor/src/items.rs 🔗

@@ -200,14 +200,8 @@ impl FollowableItem for Editor {
                 buffer_id: buffer.remote_id().into(),
                 context_start: Some(serialize_text_anchor(&range.context.start)),
                 context_end: Some(serialize_text_anchor(&range.context.end)),
-                primary_start: range
-                    .primary
-                    .as_ref()
-                    .map(|range| serialize_text_anchor(&range.start)),
-                primary_end: range
-                    .primary
-                    .as_ref()
-                    .map(|range| serialize_text_anchor(&range.end)),
+                primary_start: Some(serialize_text_anchor(&range.primary.start)),
+                primary_end: Some(serialize_text_anchor(&range.primary.end)),
             })
             .collect();
 
@@ -481,14 +475,8 @@ fn serialize_excerpt(
         buffer_id: buffer_id.into(),
         context_start: Some(serialize_text_anchor(&range.context.start)),
         context_end: Some(serialize_text_anchor(&range.context.end)),
-        primary_start: range
-            .primary
-            .as_ref()
-            .map(|r| serialize_text_anchor(&r.start)),
-        primary_end: range
-            .primary
-            .as_ref()
-            .map(|r| serialize_text_anchor(&r.end)),
+        primary_start: Some(serialize_text_anchor(&range.primary.start)),
+        primary_end: Some(serialize_text_anchor(&range.primary.end)),
     })
 }
 
@@ -521,7 +509,8 @@ fn deserialize_excerpt_range(excerpt: proto::Excerpt) -> Option<ExcerptRange<lan
             let start = language::proto::deserialize_anchor(start)?;
             let end = language::proto::deserialize_anchor(end)?;
             Some(start..end)
-        });
+        })
+        .unwrap_or_else(|| context.clone());
     Some(ExcerptRange { context, primary })
 }
 

crates/editor/src/jsx_tag_auto_close.rs 🔗

@@ -814,26 +814,17 @@ mod jsx_tag_autoclose_tests {
             let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
             buf.push_excerpts(
                 buffer_a,
-                [ExcerptRange {
-                    context: text::Anchor::MIN..text::Anchor::MAX,
-                    primary: None,
-                }],
+                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
                 cx,
             );
             buf.push_excerpts(
                 buffer_b,
-                [ExcerptRange {
-                    context: text::Anchor::MIN..text::Anchor::MAX,
-                    primary: None,
-                }],
+                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
                 cx,
             );
             buf.push_excerpts(
                 buffer_c,
-                [ExcerptRange {
-                    context: text::Anchor::MIN..text::Anchor::MAX,
-                    primary: None,
-                }],
+                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
                 cx,
             );
             buf

crates/editor/src/movement.rs 🔗

@@ -1089,14 +1089,8 @@ mod tests {
                 multibuffer.push_excerpts(
                     buffer.clone(),
                     [
-                        ExcerptRange {
-                            context: Point::new(0, 0)..Point::new(1, 4),
-                            primary: None,
-                        },
-                        ExcerptRange {
-                            context: Point::new(2, 0)..Point::new(3, 2),
-                            primary: None,
-                        },
+                        ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
+                        ExcerptRange::new(Point::new(2, 0)..Point::new(3, 2)),
                     ],
                     cx,
                 );

crates/editor/src/proposed_changes_editor.rs 🔗

@@ -51,7 +51,7 @@ struct RecalculateDiff {
 struct BranchBufferSemanticsProvider(Rc<dyn SemanticsProvider>);
 
 impl ProposedChangesEditor {
-    pub fn new<T: ToOffset>(
+    pub fn new<T: Clone + ToOffset>(
         title: impl Into<SharedString>,
         locations: Vec<ProposedChangeLocation<T>>,
         project: Option<Entity<Project>>,
@@ -140,7 +140,7 @@ impl ProposedChangesEditor {
         cx.notify();
     }
 
-    pub fn reset_locations<T: ToOffset>(
+    pub fn reset_locations<T: Clone + ToOffset>(
         &mut self,
         locations: Vec<ProposedChangeLocation<T>>,
         window: &mut Window,
@@ -202,10 +202,10 @@ impl ProposedChangesEditor {
             self.multibuffer.update(cx, |multibuffer, cx| {
                 multibuffer.push_excerpts(
                     branch_buffer,
-                    location.ranges.into_iter().map(|range| ExcerptRange {
-                        context: range,
-                        primary: None,
-                    }),
+                    location
+                        .ranges
+                        .into_iter()
+                        .map(|range| ExcerptRange::new(range)),
                     cx,
                 );
             });

crates/editor/src/test/editor_test_context.rs 🔗

@@ -120,10 +120,9 @@ impl EditorTestContext {
                 let buffer = cx.new(|cx| Buffer::local(text, cx));
                 multibuffer.push_excerpts(
                     buffer,
-                    ranges.into_iter().map(|range| ExcerptRange {
-                        context: range,
-                        primary: None,
-                    }),
+                    ranges
+                        .into_iter()
+                        .map(|range| ExcerptRange::new(range.clone())),
                     cx,
                 );
             }

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -275,6 +275,7 @@ pub struct MultiBufferSnapshot {
     excerpt_ids: SumTree<ExcerptIdMapping>,
     diffs: TreeMap<BufferId, BufferDiffSnapshot>,
     diff_transforms: SumTree<DiffTransform>,
+    replaced_excerpts: TreeMap<ExcerptId, ExcerptId>,
     trailing_excerpt_update_count: usize,
     all_diff_hunks_expanded: bool,
     non_text_state_update_count: usize,
@@ -423,7 +424,16 @@ pub struct ExcerptRange<T> {
     pub context: Range<T>,
     /// The primary range of text to be highlighted in the excerpt.
     /// In a multi-buffer search, this would be the text that matched the search
-    pub primary: Option<Range<T>>,
+    pub primary: Range<T>,
+}
+
+impl<T: Clone> ExcerptRange<T> {
+    pub fn new(context: Range<T>) -> Self {
+        Self {
+            context: context.clone(),
+            primary: context,
+        }
+    }
 }
 
 #[derive(Clone, Debug, Default)]
@@ -666,10 +676,7 @@ impl MultiBuffer {
         this.singleton = true;
         this.push_excerpts(
             buffer,
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
+            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
             cx,
         );
         this.snapshot.borrow_mut().singleton = true;
@@ -1543,11 +1550,7 @@ impl MultiBuffer {
 
                 Some(ExcerptRange {
                     context,
-                    primary: excerpt
-                        .range
-                        .primary
-                        .as_ref()
-                        .map(|range| range.to_point(&excerpt.buffer)),
+                    primary: excerpt.range.primary.to_point(&excerpt.buffer),
                 })
             });
             let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
@@ -1592,8 +1595,71 @@ impl MultiBuffer {
     ) -> bool {
         let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
-        let (new, _) = build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
-        self.update_path_excerpts(path, buffer, &buffer_snapshot, new, cx)
+        let excerpt_ranges = ranges.into_iter().map(|range| {
+            let start = range
+                .start
+                .saturating_sub(Point::new(context_line_count, 0));
+            let end_row = (range.end.row + 2).min(buffer_snapshot.max_point().row);
+            let end = Point::new(end_row, buffer_snapshot.line_len(end_row));
+            ExcerptRange {
+                context: start..end,
+                primary: range,
+            }
+        });
+
+        let (_, is_new) =
+            self.set_excerpt_ranges_for_path(path, buffer, excerpt_ranges.collect(), cx);
+        is_new
+    }
+
+    /// Sets excerpts, returns `true` if at least one new excerpt was added.
+    pub fn set_excerpt_ranges_for_path(
+        &mut self,
+        path: PathKey,
+        buffer: Entity<Buffer>,
+        ranges: Vec<ExcerptRange<Point>>,
+        cx: &mut Context<Self>,
+    ) -> (Vec<Range<Anchor>>, bool) {
+        let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
+
+        let (new, counts) = self.merge_excerpt_ranges(&ranges);
+        let (excerpt_ids, added_a_new_excerpt) =
+            self.update_path_excerpts(path, buffer, &buffer_snapshot, new, cx);
+
+        let mut result = Vec::new();
+        let mut ranges = ranges.into_iter();
+        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
+            for range in ranges.by_ref().take(range_count) {
+                let range = Anchor::range_in_buffer(
+                    excerpt_id,
+                    buffer_snapshot.remote_id(),
+                    buffer_snapshot.anchor_before(&range.primary.start)
+                        ..buffer_snapshot.anchor_after(&range.primary.end),
+                );
+                result.push(range)
+            }
+        }
+        (result, added_a_new_excerpt)
+    }
+
+    fn merge_excerpt_ranges(
+        &self,
+        expanded_ranges: &Vec<ExcerptRange<Point>>,
+    ) -> (Vec<ExcerptRange<Point>>, Vec<usize>) {
+        let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
+        let mut counts: Vec<usize> = Vec::new();
+        for range in expanded_ranges {
+            if let Some(last_range) = merged_ranges.last_mut() {
+                if last_range.context.end >= range.context.start {
+                    last_range.context.end = range.context.end;
+                    *counts.last_mut().unwrap() += 1;
+                    continue;
+                }
+            }
+            merged_ranges.push(range.clone());
+            counts.push(1);
+        }
+        return (merged_ranges, counts);
     }
 
     fn update_path_excerpts(
@@ -1603,7 +1669,7 @@ impl MultiBuffer {
         buffer_snapshot: &BufferSnapshot,
         new: Vec<ExcerptRange<Point>>,
         cx: &mut Context<Self>,
-    ) -> bool {
+    ) -> (Vec<ExcerptId>, bool) {
         let mut insert_after = self
             .excerpts_by_path
             .range(..path.clone())
@@ -1620,12 +1686,21 @@ impl MultiBuffer {
         let mut new_iter = new.into_iter().peekable();
         let mut existing_iter = existing.into_iter().peekable();
 
-        let mut new_excerpt_ids = Vec::new();
+        let mut excerpt_ids = Vec::new();
         let mut to_remove = Vec::new();
-        let mut to_insert = Vec::new();
+        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
         let mut added_a_new_excerpt = false;
         let snapshot = self.snapshot(cx);
 
+        let mut next_excerpt_id =
+            if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
+                last_entry.id.0 + 1
+            } else {
+                1
+            };
+
+        let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
+
         let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(&());
         excerpts_cursor.next(&());
 
@@ -1634,12 +1709,29 @@ impl MultiBuffer {
                 (Some(new), Some(existing)) => (new, existing),
                 (None, None) => break,
                 (None, Some(_)) => {
-                    to_remove.push(existing_iter.next().unwrap());
+                    let existing_id = existing_iter.next().unwrap();
+                    let locator = snapshot.excerpt_locator_for_id(existing_id);
+                    let existing_excerpt = excerpts_cursor.item().unwrap();
+                    excerpts_cursor.seek_forward(&Some(locator), Bias::Left, &());
+                    let existing_end = existing_excerpt
+                        .range
+                        .context
+                        .end
+                        .to_point(&buffer_snapshot);
+                    if let Some((new_id, last)) = to_insert.last() {
+                        if existing_end <= last.context.end {
+                            self.snapshot
+                                .borrow_mut()
+                                .replaced_excerpts
+                                .insert(existing_id, *new_id);
+                        }
+                    }
+                    to_remove.push(existing_id);
                     continue;
                 }
                 (Some(_), None) => {
                     added_a_new_excerpt = true;
-                    to_insert.push(new_iter.next().unwrap());
+                    to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
                     continue;
                 }
             };
@@ -1647,12 +1739,12 @@ impl MultiBuffer {
             excerpts_cursor.seek_forward(&Some(locator), Bias::Left, &());
             let Some(existing_excerpt) = excerpts_cursor.item() else {
                 to_remove.push(existing_iter.next().unwrap());
-                to_insert.push(new_iter.next().unwrap());
+                to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
                 continue;
             };
             if existing_excerpt.buffer_id != buffer_snapshot.remote_id() {
                 to_remove.push(existing_iter.next().unwrap());
-                to_insert.push(new_iter.next().unwrap());
+                to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
                 continue;
             }
 
@@ -1668,49 +1760,61 @@ impl MultiBuffer {
                 .to_point(&buffer_snapshot);
 
             if existing_end < new.context.start {
-                to_remove.push(existing_iter.next().unwrap());
+                let existing_id = existing_iter.next().unwrap();
+                if let Some((new_id, last)) = to_insert.last() {
+                    if existing_end <= last.context.end {
+                        self.snapshot
+                            .borrow_mut()
+                            .replaced_excerpts
+                            .insert(existing_id, *new_id);
+                    }
+                }
+                to_remove.push(existing_id);
                 continue;
             } else if existing_start > new.context.end {
-                to_insert.push(new_iter.next().unwrap());
+                to_insert.push((next_excerpt_id(), new_iter.next().unwrap()));
                 continue;
             }
 
-            // maybe merge overlapping excerpts?
-            // it's hard to distinguish between a manually expanded excerpt, and one that
-            // got smaller because of a missing diff.
             if existing_start == new.context.start && existing_end == new.context.end {
-                new_excerpt_ids.append(&mut self.insert_excerpts_after(
+                excerpt_ids.extend(to_insert.iter().map(|(id, _)| id));
+                self.insert_excerpts_with_ids_after(
                     insert_after,
                     buffer.clone(),
                     mem::take(&mut to_insert),
                     cx,
-                ));
+                );
                 insert_after = existing_iter.next().unwrap();
-                new_excerpt_ids.push(insert_after);
+                excerpt_ids.push(insert_after);
                 new_iter.next();
             } else {
-                to_remove.push(existing_iter.next().unwrap());
-                to_insert.push(new_iter.next().unwrap());
-            }
-        }
-
-        new_excerpt_ids.append(&mut self.insert_excerpts_after(
-            insert_after,
-            buffer,
-            to_insert,
-            cx,
-        ));
+                let existing_id = existing_iter.next().unwrap();
+                let new_id = next_excerpt_id();
+                self.snapshot
+                    .borrow_mut()
+                    .replaced_excerpts
+                    .insert(existing_id, new_id);
+                to_remove.push(existing_id);
+                let mut range = new_iter.next().unwrap();
+                range.context.start = range.context.start.min(existing_start);
+                range.context.end = range.context.end.max(existing_end);
+                to_insert.push((new_id, range));
+            }
+        }
+
+        excerpt_ids.extend(to_insert.iter().map(|(id, _)| id));
+        self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
         self.remove_excerpts(to_remove, cx);
-        if new_excerpt_ids.is_empty() {
+        if excerpt_ids.is_empty() {
             self.excerpts_by_path.remove(&path);
         } else {
-            for excerpt_id in &new_excerpt_ids {
+            for excerpt_id in &excerpt_ids {
                 self.paths_by_excerpt.insert(*excerpt_id, path.clone());
             }
-            self.excerpts_by_path.insert(path, new_excerpt_ids);
+            self.excerpts_by_path.insert(path, excerpt_ids.clone());
         }
 
-        added_a_new_excerpt
+        (excerpt_ids, added_a_new_excerpt)
     }
 
     pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
@@ -1908,10 +2012,8 @@ impl MultiBuffer {
             let range = ExcerptRange {
                 context: buffer_snapshot.anchor_before(&range.context.start)
                     ..buffer_snapshot.anchor_after(&range.context.end),
-                primary: range.primary.map(|primary| {
-                    buffer_snapshot.anchor_before(&primary.start)
-                        ..buffer_snapshot.anchor_after(&primary.end)
-                }),
+                primary: buffer_snapshot.anchor_before(&range.primary.start)
+                    ..buffer_snapshot.anchor_after(&range.primary.end),
             };
             excerpts.push((id, range.clone()));
             let excerpt = Excerpt::new(
@@ -3391,10 +3493,9 @@ impl MultiBuffer {
         let multi = cx.new(|_| Self::new(Capability::ReadWrite));
         for (text, ranges) in excerpts {
             let buffer = cx.new(|cx| Buffer::local(text, cx));
-            let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
-                context: range,
-                primary: None,
-            });
+            let excerpt_ranges = ranges
+                .into_iter()
+                .map(|range| ExcerptRange::new(range.clone()));
             multi.update(cx, |multi, cx| {
                 multi.push_excerpts(buffer, excerpt_ranges, cx)
             });
@@ -3522,10 +3623,7 @@ impl MultiBuffer {
                         let end_ix =
                             buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
                         let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
-                        ExcerptRange {
-                            context: start_ix..end_ix,
-                            primary: None,
-                        }
+                        ExcerptRange::new(start_ix..end_ix)
                     })
                     .collect::<Vec<_>>();
                 log::info!(
@@ -4756,6 +4854,13 @@ impl MultiBufferSnapshot {
         position
     }
 
+    pub fn latest_excerpt_id(&self, mut excerpt_id: ExcerptId) -> ExcerptId {
+        while let Some(replacement) = self.replaced_excerpts.get(&excerpt_id) {
+            excerpt_id = *replacement;
+        }
+        return excerpt_id;
+    }
+
     pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
     where
         D: TextDimension + Ord + Sub<D, Output = D>,
@@ -4770,10 +4875,10 @@ impl MultiBufferSnapshot {
 
         let mut summaries = Vec::new();
         while let Some(anchor) = anchors.peek() {
-            let excerpt_id = anchor.excerpt_id;
+            let excerpt_id = self.latest_excerpt_id(anchor.excerpt_id);
             let excerpt_anchors = iter::from_fn(|| {
                 let anchor = anchors.peek()?;
-                if anchor.excerpt_id == excerpt_id {
+                if self.latest_excerpt_id(anchor.excerpt_id) == excerpt_id {
                     Some(anchors.next().unwrap())
                 } else {
                     None
@@ -7731,7 +7836,7 @@ where
 
         excerpt_ranges.push(ExcerptRange {
             context: excerpt_start..excerpt_end,
-            primary: Some(range),
+            primary: range,
         });
         range_counts.push(ranges_in_excerpt);
     }

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -120,10 +120,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
         let subscription = multibuffer.subscribe();
         multibuffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: Point::new(1, 2)..Point::new(2, 5),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(1, 2)..Point::new(2, 5))],
             cx,
         );
         assert_eq!(
@@ -136,18 +133,12 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
 
         multibuffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: Point::new(3, 3)..Point::new(4, 4),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(3, 3)..Point::new(4, 4))],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: Point::new(3, 1)..Point::new(3, 3),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(3, 1)..Point::new(3, 3))],
             cx,
         );
         assert_eq!(
@@ -657,31 +648,13 @@ fn test_excerpt_events(cx: &mut App) {
     leader_multibuffer.update(cx, |leader, cx| {
         leader.push_excerpts(
             buffer_1.clone(),
-            [
-                ExcerptRange {
-                    context: 0..8,
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: 12..16,
-                    primary: None,
-                },
-            ],
+            [ExcerptRange::new(0..8), ExcerptRange::new(12..16)],
             cx,
         );
         leader.insert_excerpts_after(
             leader.excerpt_ids()[0],
             buffer_2.clone(),
-            [
-                ExcerptRange {
-                    context: 0..5,
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: 10..15,
-                    primary: None,
-                },
-            ],
+            [ExcerptRange::new(0..5), ExcerptRange::new(10..15)],
             cx,
         )
     });
@@ -967,14 +940,7 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
 
     let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.push_excerpts(
-            buffer.clone(),
-            [ExcerptRange {
-                context: 0..0,
-                primary: None,
-            }],
-            cx,
-        );
+        multibuffer.push_excerpts(buffer.clone(), [ExcerptRange::new(0..0)], cx);
         multibuffer.set_all_diff_hunks_expanded(cx);
         multibuffer.add_diff(diff.clone(), cx);
     });
@@ -992,14 +958,7 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
 
     let buf2 = cx.new(|cx| Buffer::local("X", cx));
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.push_excerpts(
-            buf2,
-            [ExcerptRange {
-                context: 0..1,
-                primary: None,
-            }],
-            cx,
-        );
+        multibuffer.push_excerpts(buf2, [ExcerptRange::new(0..1)], cx);
     });
 
     buffer.update(cx, |buffer, cx| {
@@ -1053,22 +1012,8 @@ fn test_multibuffer_anchors(cx: &mut App) {
     let buffer_2 = cx.new(|cx| Buffer::local("efghi", cx));
     let multibuffer = cx.new(|cx| {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
-        multibuffer.push_excerpts(
-            buffer_1.clone(),
-            [ExcerptRange {
-                context: 0..4,
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer.push_excerpts(
-            buffer_2.clone(),
-            [ExcerptRange {
-                context: 0..5,
-                primary: None,
-            }],
-            cx,
-        );
+        multibuffer.push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..4)], cx);
+        multibuffer.push_excerpts(buffer_2.clone(), [ExcerptRange::new(0..5)], cx);
         multibuffer
     });
     let old_snapshot = multibuffer.read(cx).snapshot(cx);
@@ -1116,14 +1061,7 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
     buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
     let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
         multibuffer
-            .push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: 0..7,
-                    primary: None,
-                }],
-                cx,
-            )
+            .push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..7)], cx)
             .pop()
             .unwrap()
     });
@@ -1138,18 +1076,9 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
             .push_excerpts(
                 buffer_2.clone(),
                 [
-                    ExcerptRange {
-                        context: 0..4,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 6..10,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 12..16,
-                        primary: None,
-                    },
+                    ExcerptRange::new(0..4),
+                    ExcerptRange::new(6..10),
+                    ExcerptRange::new(12..16),
                 ],
                 cx,
             )
@@ -1197,10 +1126,7 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
             .insert_excerpts_after(
                 excerpt_id_2,
                 buffer_2.clone(),
-                [ExcerptRange {
-                    context: 5..8,
-                    primary: None,
-                }],
+                [ExcerptRange::new(5..8)],
                 cx,
             )
             .pop()
@@ -1613,11 +1539,13 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
             one
             two
             two.five
+            three
             -----
             four
             five
             six
             seven
+            eight
             -----
             nine
             ten
@@ -1651,11 +1579,11 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
              two
              two.five
              three
-             -----
              four
              five
              six
              seven
+             eight
              -----
              nine
              ten
@@ -1912,18 +1840,12 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
         let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
         multibuffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
+            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
+            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
             cx,
         );
         multibuffer.add_diff(diff_1.clone(), cx);
@@ -2667,10 +2589,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
                         .insert_excerpts_after(
                             prev_excerpt_id,
                             buffer_handle.clone(),
-                            [ExcerptRange {
-                                context: range,
-                                primary: None,
-                            }],
+                            [ExcerptRange::new(range.clone())],
                             cx,
                         )
                         .pop()
@@ -2875,18 +2794,12 @@ fn test_history(cx: &mut App) {
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: 0..buffer_1.read(cx).len(),
-                primary: None,
-            }],
+            [ExcerptRange::new(0..buffer_1.read(cx).len())],
             cx,
         );
         multibuffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: 0..buffer_2.read(cx).len(),
-                primary: None,
-            }],
+            [ExcerptRange::new(0..buffer_2.read(cx).len())],
             cx,
         );
     });
@@ -3129,18 +3042,12 @@ fn test_summaries_for_anchors(cx: &mut TestAppContext) {
         multibuffer.set_all_diff_hunks_expanded(cx);
         ids.extend(multibuffer.push_excerpts(
             buffer_1.clone(),
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
+            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
             cx,
         ));
         ids.extend(multibuffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
+            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
             cx,
         ));
         multibuffer.add_diff(diff_1.clone(), cx);
@@ -3235,10 +3142,7 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
     multibuffer.update(cx, |multibuffer, cx| {
         multibuffer.push_excerpts(
             buffer_2.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(1, 0),
-                primary: None,
-            }],
+            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
             cx,
         );
     });