Filter away new hints not in excerpt range

Kirill Bulatov created

Change summary

crates/editor/src/inlay_hint_cache.rs | 139 ++++++++++++++++++----------
1 file changed, 87 insertions(+), 52 deletions(-)

Detailed changes

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -1,13 +1,16 @@
-use std::cmp;
+use std::{cmp, ops::Range};
 
-use crate::{display_map::Inlay, editor_settings, Anchor, Editor, ExcerptId, InlayId, MultiBuffer};
+use crate::{
+    display_map::Inlay, editor_settings, Anchor, Editor, ExcerptId, InlayId, MultiBuffer,
+    MultiBufferSnapshot,
+};
 use anyhow::Context;
 use gpui::{ModelHandle, Task, ViewContext};
+use language::BufferSnapshot;
 use log::error;
 use project::{InlayHint, InlayHintKind};
 
 use collections::{hash_map, HashMap, HashSet};
-use text::BufferSnapshot;
 use util::post_inc;
 
 pub struct InlayHintCache {
@@ -39,6 +42,15 @@ pub struct HintsUpdateState {
     cache: Box<CacheSnapshot>,
 }
 
+#[derive(Debug, Clone)]
+struct ExcerptQuery {
+    buffer_id: u64,
+    excerpt_id: ExcerptId,
+    excerpt_range: Range<language::Anchor>,
+    cache_version: usize,
+    invalidate_cache: bool,
+}
+
 #[derive(Debug, Default)]
 pub struct InlaySplice {
     pub to_remove: Vec<InlayId>,
@@ -135,7 +147,7 @@ impl InlayHintCache {
                     let mut excerpts_to_query = editor
                         .excerpt_visible_offsets(cx)
                         .into_iter()
-                        .map(|(buffer, _, excerpt_id)| (excerpt_id, buffer.read(cx).remote_id()))
+                        .map(|(buffer, _, excerpt_id)| (excerpt_id, buffer))
                         .collect::<HashMap<_, _>>();
 
                     let update_state = get_update_state(editor, cx);
@@ -160,18 +172,41 @@ impl InlayHintCache {
                         }
                     });
 
-                    for (excerpt_id, buffer_id) in excerpts_to_query {
-                        update_tasks.insert(
-                            excerpt_id,
-                            new_update_task(
-                                buffer_id,
+                    for (excerpt_id, buffer_handle) in excerpts_to_query {
+                        let (multi_buffer_snapshot, excerpt_range) =
+                            editor.buffer.update(cx, |multi_buffer, cx| {
+                                let multi_buffer_snapshot = multi_buffer.snapshot(cx);
+                                (
+                                    multi_buffer_snapshot,
+                                    multi_buffer
+                                        .excerpts_for_buffer(&buffer_handle, cx)
+                                        .into_iter()
+                                        .find(|(id, _)| id == &excerpt_id)
+                                        .map(|(_, range)| range.context),
+                                )
+                            });
+
+                        if let Some(excerpt_range) = excerpt_range {
+                            let buffer = buffer_handle.read(cx);
+                            let buffer_snapshot = buffer.snapshot();
+                            let query = ExcerptQuery {
+                                buffer_id: buffer.remote_id(),
                                 excerpt_id,
+                                excerpt_range,
                                 cache_version,
-                                update_state.clone(),
                                 invalidate_cache,
-                                cx,
-                            ),
-                        );
+                            };
+                            update_tasks.insert(
+                                excerpt_id,
+                                new_update_task(
+                                    query,
+                                    update_state.clone(),
+                                    multi_buffer_snapshot,
+                                    buffer_snapshot,
+                                    cx,
+                                ),
+                            );
+                        }
                     }
                 })
                 .ok();
@@ -192,25 +227,16 @@ impl InlayHintCache {
 }
 
 fn new_update_task(
-    buffer_id: u64,
-    excerpt_id: ExcerptId,
-    cache_version: usize,
+    query: ExcerptQuery,
     state: HintsUpdateState,
-    invalidate_cache: bool,
+    multi_buffer_snapshot: MultiBufferSnapshot,
+    buffer_snapshot: BufferSnapshot,
     cx: &mut ViewContext<'_, '_, Editor>,
 ) -> InlayHintUpdateTask {
-    let hints_fetch_task = hints_fetch_task(buffer_id, excerpt_id, cx);
+    let hints_fetch_task = hints_fetch_task(query.clone(), cx);
     InlayHintUpdateTask {
-        version: cache_version,
+        version: query.cache_version,
         _task: cx.spawn(|editor, mut cx| async move {
-            let Some((multi_buffer_snapshot, buffer_snapshot)) = editor
-                .update(&mut cx, |editor, cx| {
-                    let multi_buffer = editor.buffer().read(cx);
-                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
-                    let buffer_snapshot = multi_buffer.buffer(buffer_id)?.read(cx).snapshot();
-                    Some((multi_buffer_snapshot, buffer_snapshot))
-                }).ok().flatten() else { return; };
-
             match hints_fetch_task.await {
                 Ok(Some(new_hints)) => {
                     let task_buffer_snapshot = buffer_snapshot.clone();
@@ -219,10 +245,11 @@ fn new_update_task(
                         .spawn(async move {
                             new_excerpt_hints_update_result(
                                 state,
-                                excerpt_id,
+                                query.excerpt_id,
                                 new_hints,
-                                invalidate_cache,
+                                query.invalidate_cache,
                                 &task_buffer_snapshot,
+                                query.excerpt_range,
                             )
                         })
                         .await
@@ -254,7 +281,7 @@ fn new_update_task(
 
                                 for new_hint in new_update.add_to_cache {
                                     let new_hint_position = multi_buffer_snapshot
-                                        .anchor_in_excerpt(excerpt_id, new_hint.position);
+                                        .anchor_in_excerpt(query.excerpt_id, new_hint.position);
                                     let new_inlay_id = InlayId(post_inc(&mut editor.next_inlay_id));
                                     if editor
                                         .inlay_hint_cache
@@ -299,7 +326,8 @@ fn new_update_task(
                 }
                 Ok(None) => {}
                 Err(e) => error!(
-                    "Failed to fecth hints for excerpt {excerpt_id:?} in buffer {buffer_id} : {e}"
+                    "Failed to fecth hints for excerpt {:?} in buffer {} : {}",
+                    query.excerpt_id, query.buffer_id, e
                 ),
             }
         }),
@@ -416,6 +444,7 @@ fn new_excerpt_hints_update_result(
     new_excerpt_hints: Vec<InlayHint>,
     invalidate_cache: bool,
     buffer_snapshot: &BufferSnapshot,
+    excerpt_range: Range<language::Anchor>,
 ) -> Option<ExcerptHintsUpdate> {
     let mut add_to_cache: Vec<InlayHint> = Vec::new();
     let cached_excerpt_hints = state.cache.hints.get(&excerpt_id);
@@ -454,6 +483,18 @@ fn new_excerpt_hints_update_result(
                 .visible_inlays
                 .iter()
                 .filter(|hint| hint.position.excerpt_id == excerpt_id)
+                .filter(|hint| {
+                    excerpt_range
+                        .start
+                        .cmp(&hint.position.text_anchor, buffer_snapshot)
+                        .is_le()
+                })
+                .filter(|hint| {
+                    excerpt_range
+                        .end
+                        .cmp(&hint.position.text_anchor, buffer_snapshot)
+                        .is_ge()
+                })
                 .map(|inlay_hint| inlay_hint.id)
                 .filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)),
         );
@@ -497,34 +538,28 @@ fn allowed_hint_types(
 }
 
 fn hints_fetch_task(
-    buffer_id: u64,
-    excerpt_id: ExcerptId,
+    query: ExcerptQuery,
     cx: &mut ViewContext<'_, '_, Editor>,
 ) -> Task<anyhow::Result<Option<Vec<InlayHint>>>> {
     cx.spawn(|editor, mut cx| async move {
-        let Ok(task) = editor
+        let task = editor
             .update(&mut cx, |editor, cx| {
-                Some({
-                    let multi_buffer = editor.buffer().read(cx);
-                    let buffer_handle = multi_buffer.buffer(buffer_id)?;
-                    let (_, excerpt_range) = multi_buffer
-                        .excerpts_for_buffer(&buffer_handle, cx)
-                        .into_iter()
-                        .find(|(id, _)| id == &excerpt_id)?;
-                    editor.project.as_ref()?.update(cx, |project, cx| {
-                        project.inlay_hints(
-                            buffer_handle,
-                            excerpt_range.context,
-                            cx,
-                        )
+                editor
+                    .buffer()
+                    .read(cx)
+                    .buffer(query.buffer_id)
+                    .and_then(|buffer| {
+                        let project = editor.project.as_ref()?;
+                        Some(project.update(cx, |project, cx| {
+                            project.inlay_hints(buffer, query.excerpt_range, cx)
+                        }))
                     })
-                })
-            }) else {
-                return Ok(None);
-            };
+            })
+            .ok()
+            .flatten();
         Ok(match task {
             Some(task) => Some(task.await.context("inlays for buffer task")?),
-            None => Some(Vec::new()),
+            None => None,
         })
     })
 }