Move InlayId generation back to InlayCache

Kirill Bulatov created

Change summary

crates/editor/src/display_map.rs           |  13 
crates/editor/src/display_map/fold_map.rs  |   3 
crates/editor/src/display_map/inlay_map.rs |  54 +++---
crates/editor/src/display_map/tab_map.rs   |   2 
crates/editor/src/display_map/wrap_map.rs  |   4 
crates/editor/src/editor.rs                |  27 ++-
crates/editor/src/inlay_hint_cache.rs      | 177 ++++++++++++-----------
7 files changed, 147 insertions(+), 133 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -4,7 +4,7 @@ mod inlay_map;
 mod tab_map;
 mod wrap_map;
 
-use crate::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
+use crate::{Anchor, AnchorRangeExt, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
 pub use block_map::{BlockMap, BlockPoint};
 use collections::{HashMap, HashSet};
 use fold_map::FoldMap;
@@ -28,7 +28,7 @@ pub use block_map::{
     BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
 };
 
-pub use self::inlay_map::{Inlay, InlayId, InlayProperties};
+pub use self::inlay_map::{Inlay, InlayProperties};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum FoldStatus {
@@ -249,11 +249,11 @@ impl DisplayMap {
     pub fn splice_inlays<T: Into<Rope>>(
         &mut self,
         to_remove: Vec<InlayId>,
-        to_insert: Vec<(Option<InlayId>, InlayProperties<T>)>,
+        to_insert: Vec<(InlayId, InlayProperties<T>)>,
         cx: &mut ModelContext<Self>,
-    ) -> Vec<InlayId> {
+    ) {
         if to_remove.is_empty() && to_insert.is_empty() {
-            return Vec::new();
+            return;
         }
 
         let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
@@ -267,14 +267,13 @@ impl DisplayMap {
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
 
-        let (snapshot, edits, new_inlay_ids) = self.inlay_map.splice(to_remove, to_insert);
+        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
         let (snapshot, edits) = self
             .wrap_map
             .update(cx, |map, cx| map.sync(snapshot, edits, cx));
         self.block_map.read(snapshot, edits);
-        new_inlay_ids
     }
 
     fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {

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

@@ -1475,6 +1475,7 @@ mod tests {
             Arc::new((HighlightStyle::default(), highlight_ranges)),
         );
 
+        let mut next_inlay_id = 0;
         for _ in 0..operations {
             log::info!("text: {:?}", buffer_snapshot.text());
             let mut buffer_edits = Vec::new();
@@ -1484,7 +1485,7 @@ mod tests {
                     snapshot_edits.extend(map.randomly_mutate(&mut rng));
                 }
                 40..=59 => {
-                    let (_, edits) = inlay_map.randomly_mutate(&mut rng);
+                    let (_, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
                     inlay_edits = edits;
                 }
                 _ => buffer.update(cx, |buffer, cx| {

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

@@ -1,6 +1,6 @@
 use crate::{
     multi_buffer::{MultiBufferChunks, MultiBufferRows},
-    Anchor, MultiBufferSnapshot, ToOffset,
+    Anchor, InlayId, MultiBufferSnapshot, ToOffset,
 };
 use collections::{BTreeSet, HashMap};
 use gpui::fonts::HighlightStyle;
@@ -12,13 +12,11 @@ use std::{
 };
 use sum_tree::{Bias, Cursor, SumTree};
 use text::Patch;
-use util::post_inc;
 
 pub struct InlayMap {
     snapshot: Mutex<InlaySnapshot>,
     inlays_by_id: HashMap<InlayId, Inlay>,
     inlays: Vec<Inlay>,
-    next_inlay_id: usize,
 }
 
 #[derive(Clone)]
@@ -34,9 +32,6 @@ enum Transform {
     Inlay(Inlay),
 }
 
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct InlayId(usize);
-
 #[derive(Debug, Clone)]
 pub struct Inlay {
     pub id: InlayId,
@@ -316,7 +311,6 @@ impl InlayMap {
                 snapshot: Mutex::new(snapshot.clone()),
                 inlays_by_id: HashMap::default(),
                 inlays: Vec::new(),
-                next_inlay_id: 0,
             },
             snapshot,
         )
@@ -461,8 +455,8 @@ impl InlayMap {
     pub fn splice<T: Into<Rope>>(
         &mut self,
         to_remove: Vec<InlayId>,
-        to_insert: Vec<(Option<InlayId>, InlayProperties<T>)>,
-    ) -> (InlaySnapshot, Vec<InlayEdit>, Vec<InlayId>) {
+        to_insert: Vec<(InlayId, InlayProperties<T>)>,
+    ) -> (InlaySnapshot, Vec<InlayEdit>) {
         let snapshot = self.snapshot.lock();
         let mut edits = BTreeSet::new();
 
@@ -474,16 +468,12 @@ impl InlayMap {
             }
         }
 
-        let mut new_inlay_ids = Vec::with_capacity(to_insert.len());
         for (existing_id, properties) in to_insert {
             let inlay = Inlay {
-                id: existing_id.unwrap_or_else(|| InlayId(post_inc(&mut self.next_inlay_id))),
+                id: existing_id,
                 position: properties.position,
                 text: properties.text.into(),
             };
-            if existing_id.is_none() {
-                new_inlay_ids.push(inlay.id);
-            }
             self.inlays_by_id.insert(inlay.id, inlay.clone());
             match self
                 .inlays
@@ -508,7 +498,7 @@ impl InlayMap {
         let buffer_snapshot = snapshot.buffer.clone();
         drop(snapshot);
         let (snapshot, edits) = self.sync(buffer_snapshot, buffer_edits);
-        (snapshot, edits, new_inlay_ids)
+        (snapshot, edits)
     }
 
     pub fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
@@ -518,9 +508,11 @@ impl InlayMap {
     #[cfg(test)]
     pub(crate) fn randomly_mutate(
         &mut self,
+        next_inlay_id: &mut usize,
         rng: &mut rand::rngs::StdRng,
     ) -> (InlaySnapshot, Vec<InlayEdit>) {
         use rand::prelude::*;
+        use util::post_inc;
 
         let mut to_remove = Vec::new();
         let mut to_insert = Vec::new();
@@ -541,7 +533,7 @@ impl InlayMap {
                     text
                 );
                 to_insert.push((
-                    None,
+                    InlayId(post_inc(next_inlay_id)),
                     InlayProperties {
                         position: snapshot.buffer.anchor_at(position, bias),
                         text,
@@ -554,7 +546,7 @@ impl InlayMap {
         log::info!("removing inlays: {:?}", to_remove);
 
         drop(snapshot);
-        let (snapshot, edits, _) = self.splice(to_remove, to_insert);
+        let (snapshot, edits) = self.splice(to_remove, to_insert);
         (snapshot, edits)
     }
 }
@@ -860,12 +852,13 @@ fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::MultiBuffer;
+    use crate::{InlayId, MultiBuffer};
     use gpui::AppContext;
     use rand::prelude::*;
     use settings::SettingsStore;
     use std::env;
     use text::Patch;
+    use util::post_inc;
 
     #[gpui::test]
     fn test_basic_inlays(cx: &mut AppContext) {
@@ -873,11 +866,12 @@ mod tests {
         let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
         assert_eq!(inlay_snapshot.text(), "abcdefghi");
+        let mut next_inlay_id = 0;
 
-        let (inlay_snapshot, _, _) = inlay_map.splice(
+        let (inlay_snapshot, _) = inlay_map.splice(
             Vec::new(),
             vec![(
-                None,
+                InlayId(post_inc(&mut next_inlay_id)),
                 InlayProperties {
                     position: buffer.read(cx).snapshot(cx).anchor_after(3),
                     text: "|123|",
@@ -952,18 +946,18 @@ mod tests {
         );
         assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
 
-        let (inlay_snapshot, _, _) = inlay_map.splice(
+        let (inlay_snapshot, _) = inlay_map.splice(
             Vec::new(),
             vec![
                 (
-                    None,
+                    InlayId(post_inc(&mut next_inlay_id)),
                     InlayProperties {
                         position: buffer.read(cx).snapshot(cx).anchor_before(3),
                         text: "|123|",
                     },
                 ),
                 (
-                    None,
+                    InlayId(post_inc(&mut next_inlay_id)),
                     InlayProperties {
                         position: buffer.read(cx).snapshot(cx).anchor_after(3),
                         text: "|456|",
@@ -982,7 +976,7 @@ mod tests {
         assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
 
         // The inlays can be manually removed.
-        let (inlay_snapshot, _, _) = inlay_map
+        let (inlay_snapshot, _) = inlay_map
             .splice::<String>(inlay_map.inlays_by_id.keys().copied().collect(), Vec::new());
         assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi");
     }
@@ -992,26 +986,27 @@ mod tests {
         let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
         assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
+        let mut next_inlay_id = 0;
 
-        let (inlay_snapshot, _, _) = inlay_map.splice(
+        let (inlay_snapshot, _) = inlay_map.splice(
             Vec::new(),
             vec![
                 (
-                    None,
+                    InlayId(post_inc(&mut next_inlay_id)),
                     InlayProperties {
                         position: buffer.read(cx).snapshot(cx).anchor_before(0),
                         text: "|123|\n",
                     },
                 ),
                 (
-                    None,
+                    InlayId(post_inc(&mut next_inlay_id)),
                     InlayProperties {
                         position: buffer.read(cx).snapshot(cx).anchor_before(4),
                         text: "|456|",
                     },
                 ),
                 (
-                    None,
+                    InlayId(post_inc(&mut next_inlay_id)),
                     InlayProperties {
                         position: buffer.read(cx).snapshot(cx).anchor_before(7),
                         text: "\n|567|\n",
@@ -1044,6 +1039,7 @@ mod tests {
             MultiBuffer::build_random(&mut rng, cx)
         };
         let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
+        let mut next_inlay_id = 0;
         log::info!("buffer text: {:?}", buffer_snapshot.text());
 
         let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
@@ -1054,7 +1050,7 @@ mod tests {
             let mut buffer_edits = Vec::new();
             match rng.gen_range(0..=100) {
                 0..=50 => {
-                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut rng);
+                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
                     log::info!("mutated text: {:?}", snapshot.text());
                     inlay_edits = Patch::new(edits);
                 }

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

@@ -708,7 +708,7 @@ mod tests {
         fold_map.randomly_mutate(&mut rng);
         let (fold_snapshot, _) = fold_map.read(inlay_snapshot, vec![]);
         log::info!("FoldMap text: {:?}", fold_snapshot.text());
-        let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut rng);
+        let (inlay_snapshot, _) = inlay_map.randomly_mutate(&mut 0, &mut rng);
         log::info!("InlayMap text: {:?}", inlay_snapshot.text());
 
         let (tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);

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

@@ -1119,6 +1119,7 @@ mod tests {
         );
         log::info!("Wrapped text: {:?}", actual_text);
 
+        let mut next_inlay_id = 0;
         let mut edits = Vec::new();
         for _i in 0..operations {
             log::info!("{} ==============================================", _i);
@@ -1146,7 +1147,8 @@ mod tests {
                     }
                 }
                 40..=59 => {
-                    let (inlay_snapshot, inlay_edits) = inlay_map.randomly_mutate(&mut rng);
+                    let (inlay_snapshot, inlay_edits) =
+                        inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
                     let (tabs_snapshot, tab_edits) =
                         tab_map.sync(fold_snapshot, fold_edits, tab_size);

crates/editor/src/editor.rs 🔗

@@ -185,6 +185,9 @@ pub struct GutterHover {
     pub hovered: bool,
 }
 
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct InlayId(usize);
+
 actions!(
     editor,
     [
@@ -541,6 +544,7 @@ pub struct Editor {
     link_go_to_definition_state: LinkGoToDefinitionState,
     copilot_state: CopilotState,
     inlay_hint_cache: InlayHintCache,
+    next_inlay_id: usize,
     _subscriptions: Vec<Subscription>,
 }
 
@@ -1339,6 +1343,7 @@ impl Editor {
                 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
             completion_tasks: Default::default(),
             next_completion_id: 0,
+            next_inlay_id: 0,
             available_code_actions: Default::default(),
             code_actions_task: Default::default(),
             document_highlights_task: Default::default(),
@@ -2664,7 +2669,7 @@ impl Editor {
                             to_insert,
                         } = editor
                             .update(&mut cx, |editor, cx| {
-                                editor.inlay_hint_cache.append_inlays(
+                                editor.inlay_hint_cache.append_hints(
                                     multi_buffer_handle,
                                     std::iter::once(updated_range_query),
                                     cx,
@@ -2693,7 +2698,7 @@ impl Editor {
                         to_insert,
                     } = editor
                         .update(&mut cx, |editor, cx| {
-                            editor.inlay_hint_cache.replace_inlays(
+                            editor.inlay_hint_cache.replace_hints(
                                 multi_buffer_handle,
                                 replacement_queries.into_iter(),
                                 currently_shown_inlay_hints,
@@ -2738,7 +2743,7 @@ impl Editor {
     fn splice_inlay_hints(
         &self,
         to_remove: Vec<InlayId>,
-        to_insert: Vec<(Option<InlayId>, Anchor, project::InlayHint)>,
+        to_insert: Vec<(InlayId, Anchor, project::InlayHint)>,
         cx: &mut ViewContext<Self>,
     ) {
         let buffer = self.buffer.read(cx).read(cx);
@@ -3514,23 +3519,19 @@ impl Editor {
                 to_remove.push(suggestion.id);
             }
 
+            let suggestion_inlay_id = self.next_inlay_id();
             let to_insert = vec![(
-                None,
+                suggestion_inlay_id,
                 InlayProperties {
                     position: cursor,
                     text: text.clone(),
                 },
             )];
-            let new_inlay_ids = self.display_map.update(cx, move |map, cx| {
+            self.display_map.update(cx, move |map, cx| {
                 map.splice_inlays(to_remove, to_insert, cx)
             });
-            assert_eq!(
-                new_inlay_ids.len(),
-                1,
-                "Expecting only copilot suggestion id generated"
-            );
             self.copilot_state.suggestion = Some(Inlay {
-                id: new_inlay_ids.into_iter().next().unwrap(),
+                id: suggestion_inlay_id,
                 position: cursor,
                 text,
             });
@@ -7687,6 +7688,10 @@ impl Editor {
         let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
         cx.write_to_clipboard(ClipboardItem::new(lines));
     }
+
+    pub fn next_inlay_id(&mut self) -> InlayId {
+        InlayId(post_inc(&mut self.next_inlay_id))
+    }
 }
 
 fn inlay_hint_query(

crates/editor/src/inlay_hint_cache.rs 🔗

@@ -1,8 +1,7 @@
 use std::ops::Range;
 
 use crate::{
-    display_map::InlayId, editor_settings, scroll::ScrollAnchor, Anchor, Editor, ExcerptId,
-    MultiBuffer,
+    editor_settings, scroll::ScrollAnchor, Anchor, Editor, ExcerptId, InlayId, MultiBuffer,
 };
 use anyhow::Context;
 use clock::Global;
@@ -12,6 +11,7 @@ use log::error;
 use project::{InlayHint, InlayHintKind};
 
 use collections::{hash_map, HashMap, HashSet};
+use util::post_inc;
 
 #[derive(Debug, Copy, Clone)]
 pub enum InlayRefreshReason {
@@ -20,26 +20,39 @@ pub enum InlayRefreshReason {
     VisibleExcerptsChange,
 }
 
-#[derive(Debug, Clone, Default)]
+#[derive(Debug, Default)]
 pub struct InlayHintCache {
     inlay_hints: HashMap<InlayId, InlayHint>,
-    inlays_in_buffers: HashMap<u64, BufferInlays<(Anchor, InlayId)>>,
+    hints_in_buffers: HashMap<u64, BufferHints<(Anchor, InlayId)>>,
     allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
 }
 
-#[derive(Clone, Debug, Default)]
-struct BufferInlays<I> {
+#[derive(Clone, Debug)]
+struct BufferHints<I> {
     buffer_version: Global,
-    cached_ranges: HashMap<ExcerptId, Vec<Range<usize>>>,
-    excerpt_inlays: HashMap<ExcerptId, Vec<I>>,
+    hints_per_excerpt: HashMap<ExcerptId, ExcerptHints<I>>,
+}
+
+#[derive(Clone, Debug)]
+struct ExcerptHints<I> {
+    cached_excerpt_offsets: Vec<Range<usize>>,
+    hints: Vec<I>,
+}
+
+impl<I> Default for ExcerptHints<I> {
+    fn default() -> Self {
+        Self {
+            cached_excerpt_offsets: Vec::new(),
+            hints: Vec::new(),
+        }
+    }
 }
 
-impl<I> BufferInlays<I> {
+impl<I> BufferHints<I> {
     fn new(buffer_version: Global) -> Self {
         Self {
             buffer_version,
-            excerpt_inlays: HashMap::default(),
-            cached_ranges: HashMap::default(),
+            hints_per_excerpt: HashMap::default(),
         }
     }
 }
@@ -47,7 +60,7 @@ impl<I> BufferInlays<I> {
 #[derive(Debug, Default)]
 pub struct InlaySplice {
     pub to_remove: Vec<InlayId>,
-    pub to_insert: Vec<(Option<InlayId>, Anchor, InlayHint)>,
+    pub to_insert: Vec<(InlayId, Anchor, InlayHint)>,
 }
 
 pub struct InlayHintQuery {
@@ -61,7 +74,7 @@ impl InlayHintCache {
     pub fn new(inlay_hint_settings: editor_settings::InlayHints) -> Self {
         Self {
             allowed_hint_kinds: allowed_inlay_hint_types(inlay_hint_settings),
-            inlays_in_buffers: HashMap::default(),
+            hints_in_buffers: HashMap::default(),
             inlay_hints: HashMap::default(),
         }
     }
@@ -117,26 +130,27 @@ impl InlayHintCache {
             }
 
             let reenabled_hints = self
-                .inlays_in_buffers
+                .hints_in_buffers
                 .iter()
                 .filter_map(|(cached_buffer_id, cached_hints_per_excerpt)| {
                     let considered_hints_in_excerpts = considered_hints.get(cached_buffer_id)?;
-                    let not_considered_cached_inlays = cached_hints_per_excerpt
-                        .excerpt_inlays
+                    let not_considered_cached_hints = cached_hints_per_excerpt
+                        .hints_per_excerpt
                         .iter()
-                        .filter_map(|(cached_excerpt_id, cached_hints)| {
+                        .filter_map(|(cached_excerpt_id, cached_excerpt_hints)| {
                             let considered_excerpt_hints =
                                 considered_hints_in_excerpts.get(&cached_excerpt_id)?;
-                            let not_considered_cached_inlays = cached_hints
+                            let not_considered_cached_hints = cached_excerpt_hints
+                                .hints
                                 .iter()
                                 .filter(|(_, cached_hint_id)| {
                                     !considered_excerpt_hints.contains(cached_hint_id)
                                 })
                                 .copied();
-                            Some(not_considered_cached_inlays)
+                            Some(not_considered_cached_hints)
                         })
                         .flatten();
-                    Some(not_considered_cached_inlays)
+                    Some(not_considered_cached_hints)
                 })
                 .flatten()
                 .filter_map(|(cached_anchor, cached_inlay_id)| {
@@ -148,11 +162,7 @@ impl InlayHintCache {
                 })
                 .filter(|(_, _, cached_inlay)| self.allowed_hint_kinds.contains(&cached_inlay.kind))
                 .map(|(cached_anchor, cached_inlay_id, reenabled_inlay)| {
-                    (
-                        Some(cached_inlay_id),
-                        cached_anchor,
-                        reenabled_inlay.clone(),
-                    )
+                    (cached_inlay_id, cached_anchor, reenabled_inlay.clone())
                 });
             to_insert.extend(reenabled_hints);
 
@@ -173,25 +183,25 @@ impl InlayHintCache {
 
     pub fn clear(&mut self) -> Vec<InlayId> {
         let ids_to_remove = self.inlay_hints.drain().map(|(id, _)| id).collect();
-        self.inlays_in_buffers.clear();
+        self.hints_in_buffers.clear();
         ids_to_remove
     }
 
-    pub fn append_inlays(
+    pub fn append_hints(
         &mut self,
         multi_buffer: ModelHandle<MultiBuffer>,
         ranges_to_add: impl Iterator<Item = InlayHintQuery>,
         cx: &mut ViewContext<Editor>,
     ) -> Task<anyhow::Result<InlaySplice>> {
         let queries = ranges_to_add.filter_map(|additive_query| {
-            let Some(cached_buffer_inlays) = self.inlays_in_buffers.get(&additive_query.buffer_id)
+            let Some(cached_buffer_hints) = self.hints_in_buffers.get(&additive_query.buffer_id)
                 else { return Some(vec![additive_query]) };
-            if cached_buffer_inlays.buffer_version.changed_since(&additive_query.buffer_version) {
+            if cached_buffer_hints.buffer_version.changed_since(&additive_query.buffer_version) {
                 return None
             }
-            let Some(excerpt_cached_ranges) = cached_buffer_inlays.cached_ranges.get(&additive_query.excerpt_id)
+            let Some(excerpt_hints) = cached_buffer_hints.hints_per_excerpt.get(&additive_query.excerpt_id)
                 else { return Some(vec![additive_query]) };
-            let non_cached_ranges = missing_subranges(&excerpt_cached_ranges, &additive_query.excerpt_offset_query_range);
+            let non_cached_ranges = missing_subranges(&excerpt_hints.cached_excerpt_offsets, &additive_query.excerpt_offset_query_range);
             if non_cached_ranges.is_empty() {
                 None
             } else {
@@ -210,55 +220,59 @@ impl InlayHintCache {
             let new_hints = fetch_queries_task.await?;
             editor.update(&mut cx, |editor, cx| {
                 let multi_buffer_snapshot = task_multi_buffer.read(cx).snapshot(cx);
-                let inlay_hint_cache = &mut editor.inlay_hint_cache;
                 let mut to_insert = Vec::new();
                 for (new_buffer_id, new_hints_per_buffer) in new_hints {
-                    let cached_buffer_inlays = inlay_hint_cache
-                        .inlays_in_buffers
+                    let cached_buffer_hints = editor
+                        .inlay_hint_cache
+                        .hints_in_buffers
                         .entry(new_buffer_id)
                         .or_insert_with(|| {
-                            BufferInlays::new(new_hints_per_buffer.buffer_version.clone())
+                            BufferHints::new(new_hints_per_buffer.buffer_version.clone())
                         });
-                    if cached_buffer_inlays
+                    if cached_buffer_hints
                         .buffer_version
                         .changed_since(&new_hints_per_buffer.buffer_version)
                     {
                         continue;
                     }
 
-                    for (new_excerpt_id, new_ranges) in new_hints_per_buffer.cached_ranges {
-                        let cached_ranges = cached_buffer_inlays
-                            .cached_ranges
+                    for (new_excerpt_id, new_excerpt_hints) in
+                        new_hints_per_buffer.hints_per_excerpt
+                    {
+                        let cached_excerpt_hints = cached_buffer_hints
+                            .hints_per_excerpt
                             .entry(new_excerpt_id)
-                            .or_default();
-                        for new_range in new_ranges {
-                            insert_and_merge_ranges(cached_ranges, &new_range)
+                            .or_insert_with(|| ExcerptHints::default());
+                        for new_range in new_excerpt_hints.cached_excerpt_offsets {
+                            insert_and_merge_ranges(
+                                &mut cached_excerpt_hints.cached_excerpt_offsets,
+                                &new_range,
+                            )
                         }
-                    }
-                    for (new_excerpt_id, new_hints) in new_hints_per_buffer.excerpt_inlays {
-                        let cached_inlays = cached_buffer_inlays
-                            .excerpt_inlays
-                            .entry(new_excerpt_id)
-                            .or_default();
-                        for new_inlay_hint in new_hints {
-                            let new_inlay_id = todo!("TODO kb");
+                        for new_inlay_hint in new_excerpt_hints.hints {
                             let hint_anchor = multi_buffer_snapshot
                                 .anchor_in_excerpt(new_excerpt_id, new_inlay_hint.position);
-                            match cached_inlays.binary_search_by(|probe| {
-                                hint_anchor.cmp(&probe.0, &multi_buffer_snapshot)
-                            }) {
-                                Ok(ix) | Err(ix) => {
-                                    cached_inlays.insert(ix, (hint_anchor, new_inlay_id))
-                                }
-                            }
-                            inlay_hint_cache
+                            let insert_ix =
+                                match cached_excerpt_hints.hints.binary_search_by(|probe| {
+                                    hint_anchor.cmp(&probe.0, &multi_buffer_snapshot)
+                                }) {
+                                    Ok(ix) | Err(ix) => ix,
+                                };
+
+                            let new_inlay_id = InlayId(post_inc(&mut editor.next_inlay_id));
+                            cached_excerpt_hints
+                                .hints
+                                .insert(insert_ix, (hint_anchor, new_inlay_id));
+                            editor
+                                .inlay_hint_cache
                                 .inlay_hints
                                 .insert(new_inlay_id, new_inlay_hint.clone());
-                            if inlay_hint_cache
+                            if editor
+                                .inlay_hint_cache
                                 .allowed_hint_kinds
                                 .contains(&new_inlay_hint.kind)
                             {
-                                to_insert.push((Some(new_inlay_id), hint_anchor, new_inlay_hint));
+                                to_insert.push((new_inlay_id, hint_anchor, new_inlay_hint));
                             }
                         }
                     }
@@ -272,7 +286,7 @@ impl InlayHintCache {
         })
     }
 
-    pub fn replace_inlays(
+    pub fn replace_hints(
         &mut self,
         multi_buffer: ModelHandle<MultiBuffer>,
         new_ranges: impl Iterator<Item = InlayHintQuery>,
@@ -401,7 +415,7 @@ fn fetch_queries<'a, 'b>(
     multi_buffer: ModelHandle<MultiBuffer>,
     queries: impl Iterator<Item = InlayHintQuery>,
     cx: &mut ViewContext<'a, 'b, Editor>,
-) -> Task<anyhow::Result<HashMap<u64, BufferInlays<InlayHint>>>> {
+) -> Task<anyhow::Result<HashMap<u64, BufferHints<InlayHint>>>> {
     let mut inlay_fetch_tasks = Vec::new();
     for query in queries {
         let task_multi_buffer = multi_buffer.clone();
@@ -434,33 +448,30 @@ fn fetch_queries<'a, 'b>(
     }
 
     cx.spawn(|editor, cx| async move {
-        let mut inlay_updates: HashMap<u64, BufferInlays<InlayHint>> = HashMap::default();
+        let mut inlay_updates: HashMap<u64, BufferHints<InlayHint>> = HashMap::default();
         for task_result in futures::future::join_all(inlay_fetch_tasks).await {
             match task_result {
-                Ok((query, Some(response_inlays))) => {
+                Ok((query, Some(response_hints))) => {
                     let Some(buffer_snapshot) = editor.read_with(&cx, |editor, cx| {
                         editor.buffer().read(cx).buffer(query.buffer_id).map(|buffer| buffer.read(cx).snapshot())
                     })? else { continue; };
-                    let buffer_inlays = inlay_updates
+                    let buffer_hints = inlay_updates
                         .entry(query.buffer_id)
-                        .or_insert_with(|| BufferInlays::new(query.buffer_version.clone()));
-                    assert_eq!(buffer_inlays.buffer_version, query.buffer_version);
-                    {
-                        let cached_ranges = buffer_inlays
-                            .cached_ranges
-                            .entry(query.excerpt_id)
-                            .or_default();
-                        insert_and_merge_ranges(cached_ranges, &query.excerpt_offset_query_range);
-                        let excerpt_inlays = buffer_inlays
-                            .excerpt_inlays
-                            .entry(query.excerpt_id)
-                            .or_default();
-                        for inlay in response_inlays {
-                            match excerpt_inlays.binary_search_by(|probe| {
-                                inlay.position.cmp(&probe.position, &buffer_snapshot)
-                            }) {
-                                Ok(ix) | Err(ix) => excerpt_inlays.insert(ix, inlay),
-                            }
+                        .or_insert_with(|| BufferHints::new(query.buffer_version.clone()));
+                    if buffer_snapshot.version().changed_since(&buffer_hints.buffer_version) {
+                        continue;
+                    }
+                    let cached_excerpt_hints = buffer_hints
+                        .hints_per_excerpt
+                        .entry(query.excerpt_id)
+                        .or_default();
+                    insert_and_merge_ranges(&mut cached_excerpt_hints.cached_excerpt_offsets, &query.excerpt_offset_query_range);
+                    let excerpt_hints = &mut cached_excerpt_hints.hints;
+                    for inlay in response_hints {
+                        match excerpt_hints.binary_search_by(|probe| {
+                            inlay.position.cmp(&probe.position, &buffer_snapshot)
+                        }) {
+                            Ok(ix) | Err(ix) => excerpt_hints.insert(ix, inlay),
                         }
                     }
                 }