multi_buffer: Remap excerpt ids to latest excerpt in excerpt fetching (#44229)

Lukas Wirth created

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored by: Cole Miller <cole@zed.dev>

Change summary

crates/editor/src/selections_collection.rs | 16 ++++++++++++----
crates/multi_buffer/src/multi_buffer.rs    |  5 +++--
crates/multi_buffer/src/path_key.rs        |  2 +-
3 files changed, 16 insertions(+), 7 deletions(-)

Detailed changes

crates/editor/src/selections_collection.rs 🔗

@@ -419,22 +419,30 @@ impl SelectionsCollection {
             mutable_collection.disjoint.iter().for_each(|selection| {
                 assert!(
                     snapshot.can_resolve(&selection.start),
-                    "disjoint selection start is not resolvable for the given snapshot:\n{selection:?}",
+                    "disjoint selection start is not resolvable for the given snapshot:\n{selection:?}, {excerpt:?}",
+                    excerpt = snapshot.buffer_for_excerpt(selection.start.excerpt_id).map(|snapshot| snapshot.remote_id()),
                 );
                 assert!(
                     snapshot.can_resolve(&selection.end),
-                    "disjoint selection end is not resolvable for the given snapshot: {selection:?}",
+                    "disjoint selection end is not resolvable for the given snapshot: {selection:?}, {excerpt:?}",
+                    excerpt = snapshot.buffer_for_excerpt(selection.end.excerpt_id).map(|snapshot| snapshot.remote_id()),
                 );
             });
             if let Some(pending) = &mutable_collection.pending {
                 let selection = &pending.selection;
                 assert!(
                     snapshot.can_resolve(&selection.start),
-                    "pending selection start is not resolvable for the given snapshot: {pending:?}",
+                    "pending selection start is not resolvable for the given snapshot: {pending:?}, {excerpt:?}",
+                    excerpt = snapshot
+                        .buffer_for_excerpt(selection.start.excerpt_id)
+                        .map(|snapshot| snapshot.remote_id()),
                 );
                 assert!(
                     snapshot.can_resolve(&selection.end),
-                    "pending selection end is not resolvable for the given snapshot: {pending:?}",
+                    "pending selection end is not resolvable for the given snapshot: {pending:?}, {excerpt:?}",
+                    excerpt = snapshot
+                        .buffer_for_excerpt(selection.end.excerpt_id)
+                        .map(|snapshot| snapshot.remote_id()),
                 );
             }
         }

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -6457,12 +6457,13 @@ impl MultiBufferSnapshot {
     }
 
     /// Returns the excerpt for the given id. The returned excerpt is guaranteed
-    /// to have the same excerpt id as the one passed in, with the exception of
-    /// `ExcerptId::max()`.
+    /// to have the latest excerpt id for the one passed in and will also remap
+    /// `ExcerptId::max()` to the corresponding excertp ID.
     ///
     /// Callers of this function should generally use the resulting excerpt's `id` field
     /// afterwards.
     fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> {
+        let excerpt_id = self.latest_excerpt_id(excerpt_id);
         let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
         let locator = self.excerpt_locator_for_id(excerpt_id);
         cursor.seek(&Some(locator), Bias::Left);

crates/multi_buffer/src/path_key.rs 🔗

@@ -305,7 +305,7 @@ impl MultiBuffer {
         let snapshot = self.snapshot(cx);
 
         let mut next_excerpt_id =
-            // is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
+            // todo(lw): is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
             if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
                 last_entry.id.0 + 1
             } else {