Fix more panics when removing excerpts (#28836)

Kirill Bulatov and Conrad Irwin created

Release Notes:

- Fixed a panic when an excerpt removed has an edit suggestion inlay in
it

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/editor/src/display_map.rs        | 19 +++++++++++++++++--
crates/editor/src/editor.rs             |  5 ++++-
crates/editor/src/inlay_hint_cache.rs   |  6 +++---
crates/multi_buffer/src/anchor.rs       |  2 +-
crates/multi_buffer/src/multi_buffer.rs |  2 +-
5 files changed, 26 insertions(+), 8 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -49,8 +49,8 @@ use language::{
 };
 use lsp::DiagnosticSeverity;
 use multi_buffer::{
-    Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
-    RowInfo, ToOffset, ToPoint,
+    Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferPoint, MultiBufferRow,
+    MultiBufferSnapshot, RowInfo, ToOffset, ToPoint,
 };
 use serde::Deserialize;
 use std::{
@@ -574,6 +574,21 @@ impl DisplayMap {
         self.block_map.read(snapshot, edits);
     }
 
+    pub fn remove_inlays_for_excerpts(&mut self, excerpts_removed: &[ExcerptId]) {
+        let to_remove = self
+            .inlay_map
+            .current_inlays()
+            .filter_map(|inlay| {
+                if excerpts_removed.contains(&inlay.position.excerpt_id) {
+                    Some(inlay.id)
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+        self.inlay_map.splice(&to_remove, Vec::new());
+    }
+
     fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
         let buffer = buffer.read(cx).as_singleton().map(|buffer| buffer.read(cx));
         let language = buffer

crates/editor/src/editor.rs 🔗

@@ -4170,10 +4170,13 @@ impl Editor {
                 if let Some(InlaySplice {
                     to_remove,
                     to_insert,
-                }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
+                }) = self.inlay_hint_cache.remove_excerpts(&excerpts_removed)
                 {
                     self.splice_inlays(&to_remove, to_insert, cx);
                 }
+                self.display_map.update(cx, |display_map, _| {
+                    display_map.remove_inlays_for_excerpts(&excerpts_removed)
+                });
                 return;
             }
             InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -555,12 +555,12 @@ impl InlayHintCache {
     /// Completely forget of certain excerpts that were removed from the multibuffer.
     pub(super) fn remove_excerpts(
         &mut self,
-        excerpts_removed: Vec<ExcerptId>,
+        excerpts_removed: &[ExcerptId],
     ) -> Option<InlaySplice> {
         let mut to_remove = Vec::new();
         for excerpt_to_remove in excerpts_removed {
-            self.update_tasks.remove(&excerpt_to_remove);
-            if let Some(cached_hints) = self.hints.remove(&excerpt_to_remove) {
+            self.update_tasks.remove(excerpt_to_remove);
+            if let Some(cached_hints) = self.hints.remove(excerpt_to_remove) {
                 let cached_hints = cached_hints.read();
                 to_remove.extend(cached_hints.ordered_hints.iter().copied());
             }

crates/multi_buffer/src/anchor.rs 🔗

@@ -71,7 +71,7 @@ impl Anchor {
         if self_excerpt_id == ExcerptId::min() || self_excerpt_id == ExcerptId::max() {
             return Ordering::Equal;
         }
-        if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
+        if let Some(excerpt) = snapshot.excerpt(self_excerpt_id) {
             let text_cmp = self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer);
             if text_cmp.is_ne() {
                 return text_cmp;