Pass inlay highlight information

Kirill Bulatov created

Change summary

crates/editor/src/display_map.rs           |  39 +++++-
crates/editor/src/display_map/block_map.rs |  14 +
crates/editor/src/display_map/fold_map.rs  |  27 +++-
crates/editor/src/display_map/inlay_map.rs | 113 ++++++++++---------
crates/editor/src/display_map/tab_map.rs   |  36 ++++-
crates/editor/src/display_map/wrap_map.rs  |  16 +-
crates/editor/src/editor.rs                |  31 +++++
crates/editor/src/element.rs               |   9 +
crates/editor/src/link_go_to_definition.rs | 139 +++++++++++++++--------
9 files changed, 279 insertions(+), 145 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -4,7 +4,10 @@ mod inlay_map;
 mod tab_map;
 mod wrap_map;
 
-use crate::{Anchor, AnchorRangeExt, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
+use crate::{
+    link_go_to_definition::InlayCoordinates, Anchor, AnchorRangeExt, InlayId, MultiBuffer,
+    MultiBufferSnapshot, ToOffset, ToPoint,
+};
 pub use block_map::{BlockMap, BlockPoint};
 use collections::{HashMap, HashSet};
 use fold_map::FoldMap;
@@ -40,6 +43,7 @@ pub trait ToDisplayPoint {
 }
 
 type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
+type InlayHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<InlayCoordinates>)>>;
 
 pub struct DisplayMap {
     buffer: ModelHandle<MultiBuffer>,
@@ -50,6 +54,7 @@ pub struct DisplayMap {
     wrap_map: ModelHandle<WrapMap>,
     block_map: BlockMap,
     text_highlights: TextHighlights,
+    inlay_highlights: InlayHighlights,
     pub clip_at_line_ends: bool,
 }
 
@@ -85,6 +90,7 @@ impl DisplayMap {
             wrap_map,
             block_map,
             text_highlights: Default::default(),
+            inlay_highlights: Default::default(),
             clip_at_line_ends: false,
         }
     }
@@ -109,6 +115,7 @@ impl DisplayMap {
             wrap_snapshot,
             block_snapshot,
             text_highlights: self.text_highlights.clone(),
+            inlay_highlights: self.inlay_highlights.clone(),
             clip_at_line_ends: self.clip_at_line_ends,
         }
     }
@@ -215,6 +222,16 @@ impl DisplayMap {
             .insert(Some(type_id), Arc::new((style, ranges)));
     }
 
+    pub fn highlight_inlays(
+        &mut self,
+        type_id: TypeId,
+        ranges: Vec<InlayCoordinates>,
+        style: HighlightStyle,
+    ) {
+        self.inlay_highlights
+            .insert(Some(type_id), Arc::new((style, ranges)));
+    }
+
     pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {
         let highlights = self.text_highlights.get(&Some(type_id))?;
         Some((highlights.0, &highlights.1))
@@ -227,6 +244,13 @@ impl DisplayMap {
         self.text_highlights.remove(&Some(type_id))
     }
 
+    pub fn clear_inlay_highlights(
+        &mut self,
+        type_id: TypeId,
+    ) -> Option<Arc<(HighlightStyle, Vec<InlayCoordinates>)>> {
+        self.inlay_highlights.remove(&Some(type_id))
+    }
+
     pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool {
         self.wrap_map
             .update(cx, |map, cx| map.set_font(font_id, font_size, cx))
@@ -296,6 +320,7 @@ pub struct DisplaySnapshot {
     wrap_snapshot: wrap_map::WrapSnapshot,
     block_snapshot: block_map::BlockSnapshot,
     text_highlights: TextHighlights,
+    inlay_highlights: InlayHighlights,
     clip_at_line_ends: bool,
 }
 
@@ -421,6 +446,7 @@ impl DisplaySnapshot {
                 None,
                 None,
                 None,
+                None,
             )
             .map(|h| h.text)
     }
@@ -429,7 +455,7 @@ impl DisplaySnapshot {
     pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
         (0..=display_row).into_iter().rev().flat_map(|row| {
             self.block_snapshot
-                .chunks(row..row + 1, false, None, None, None)
+                .chunks(row..row + 1, false, None, None, None, None)
                 .map(|h| h.text)
                 .collect::<Vec<_>>()
                 .into_iter()
@@ -441,15 +467,16 @@ impl DisplaySnapshot {
         &self,
         display_rows: Range<u32>,
         language_aware: bool,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> DisplayChunks<'_> {
         self.block_snapshot.chunks(
             display_rows,
             language_aware,
             Some(&self.text_highlights),
-            hint_highlights,
-            suggestion_highlights,
+            Some(&self.inlay_highlights),
+            inlay_highlight_style,
+            suggestion_highlight_style,
         )
     }
 

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

@@ -1,6 +1,6 @@
 use super::{
     wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
-    TextHighlights,
+    InlayHighlights, TextHighlights,
 };
 use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
@@ -579,6 +579,7 @@ impl BlockSnapshot {
             None,
             None,
             None,
+            None,
         )
         .map(|chunk| chunk.text)
         .collect()
@@ -589,8 +590,9 @@ impl BlockSnapshot {
         rows: Range<u32>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlights: Option<&'a InlayHighlights>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> BlockChunks<'a> {
         let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
         let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
@@ -623,8 +625,9 @@ impl BlockSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
-                hint_highlights,
-                suggestion_highlights,
+                inlay_highlights,
+                inlay_highlight_style,
+                suggestion_highlight_style,
             ),
             input_chunk: Default::default(),
             transforms: cursor,
@@ -1504,6 +1507,7 @@ mod tests {
                         None,
                         None,
                         None,
+                        None,
                     )
                     .map(|chunk| chunk.text)
                     .collect::<String>();

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

@@ -1,6 +1,6 @@
 use super::{
     inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
-    TextHighlights,
+    InlayHighlights, TextHighlights,
 };
 use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
 use gpui::{color::Color, fonts::HighlightStyle};
@@ -475,7 +475,7 @@ pub struct FoldSnapshot {
 impl FoldSnapshot {
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(FoldOffset(0)..self.len(), false, None, None, None)
+        self.chunks(FoldOffset(0)..self.len(), false, None, None, None, None)
             .map(|c| c.text)
             .collect()
     }
@@ -652,8 +652,9 @@ impl FoldSnapshot {
         range: Range<FoldOffset>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlights: Option<&'a InlayHighlights>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> FoldChunks<'a> {
         let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>();
 
@@ -675,8 +676,9 @@ impl FoldSnapshot {
                 inlay_start..inlay_end,
                 language_aware,
                 text_highlights,
-                hint_highlights,
-                suggestion_highlights,
+                inlay_highlights,
+                inlay_highlight_style,
+                suggestion_highlight_style,
             ),
             inlay_chunk: None,
             inlay_offset: inlay_start,
@@ -687,8 +689,15 @@ impl FoldSnapshot {
     }
 
     pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
-        self.chunks(start.to_offset(self)..self.len(), false, None, None, None)
-            .flat_map(|chunk| chunk.text.chars())
+        self.chunks(
+            start.to_offset(self)..self.len(),
+            false,
+            None,
+            None,
+            None,
+            None,
+        )
+        .flat_map(|chunk| chunk.text.chars())
     }
 
     #[cfg(test)]
@@ -1496,7 +1505,7 @@ mod tests {
                 let text = &expected_text[start.0..end.0];
                 assert_eq!(
                     snapshot
-                        .chunks(start..end, false, None, None, None)
+                        .chunks(start..end, false, None, None, None, None)
                         .map(|c| c.text)
                         .collect::<String>(),
                     text,

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

@@ -15,7 +15,7 @@ use std::{
 use sum_tree::{Bias, Cursor, SumTree};
 use text::{Patch, Rope};
 
-use super::TextHighlights;
+use super::{InlayHighlights, TextHighlights};
 
 pub struct InlayMap {
     snapshot: InlaySnapshot,
@@ -973,8 +973,9 @@ impl InlaySnapshot {
         range: Range<InlayOffset>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlights: Option<&'a InlayHighlights>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> InlayChunks<'a> {
         let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
         cursor.seek(&range.start, Bias::Right, &());
@@ -983,53 +984,50 @@ impl InlaySnapshot {
         if let Some(text_highlights) = text_highlights {
             if !text_highlights.is_empty() {
                 while cursor.start().0 < range.end {
-                    if true {
-                        let transform_start = self.buffer.anchor_after(
-                            self.to_buffer_offset(cmp::max(range.start, cursor.start().0)),
-                        );
-
-                        let transform_end = {
-                            let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0);
-                            self.buffer.anchor_before(self.to_buffer_offset(cmp::min(
-                                cursor.end(&()).0,
-                                cursor.start().0 + overshoot,
-                            )))
-                        };
-
-                        for (tag, highlights) in text_highlights.iter() {
-                            let style = highlights.0;
-                            let ranges = &highlights.1;
-
-                            let start_ix = match ranges.binary_search_by(|probe| {
-                                let cmp = probe.end.cmp(&transform_start, &self.buffer);
-                                if cmp.is_gt() {
-                                    cmp::Ordering::Greater
-                                } else {
-                                    cmp::Ordering::Less
-                                }
-                            }) {
-                                Ok(i) | Err(i) => i,
-                            };
-                            // TODO kb add a way to highlight inlay hints through here.
-                            for range in &ranges[start_ix..] {
-                                if range.start.cmp(&transform_end, &self.buffer).is_ge() {
-                                    break;
-                                }
+                    let transform_start = self.buffer.anchor_after(
+                        self.to_buffer_offset(cmp::max(range.start, cursor.start().0)),
+                    );
 
-                                highlight_endpoints.push(HighlightEndpoint {
-                                    offset: self
-                                        .to_inlay_offset(range.start.to_offset(&self.buffer)),
-                                    is_start: true,
-                                    tag: *tag,
-                                    style,
-                                });
-                                highlight_endpoints.push(HighlightEndpoint {
-                                    offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)),
-                                    is_start: false,
-                                    tag: *tag,
-                                    style,
-                                });
+                    let transform_end = {
+                        let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0);
+                        self.buffer.anchor_before(self.to_buffer_offset(cmp::min(
+                            cursor.end(&()).0,
+                            cursor.start().0 + overshoot,
+                        )))
+                    };
+
+                    for (tag, highlights) in text_highlights.iter() {
+                        let style = highlights.0;
+                        let ranges = &highlights.1;
+
+                        let start_ix = match ranges.binary_search_by(|probe| {
+                            let cmp = probe.end.cmp(&transform_start, &self.buffer);
+                            if cmp.is_gt() {
+                                cmp::Ordering::Greater
+                            } else {
+                                cmp::Ordering::Less
                             }
+                        }) {
+                            Ok(i) | Err(i) => i,
+                        };
+                        // TODO kb add a way to highlight inlay hints through here.
+                        for range in &ranges[start_ix..] {
+                            if range.start.cmp(&transform_end, &self.buffer).is_ge() {
+                                break;
+                            }
+
+                            highlight_endpoints.push(HighlightEndpoint {
+                                offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)),
+                                is_start: true,
+                                tag: *tag,
+                                style,
+                            });
+                            highlight_endpoints.push(HighlightEndpoint {
+                                offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)),
+                                is_start: false,
+                                tag: *tag,
+                                style,
+                            });
                         }
                     }
 
@@ -1050,8 +1048,8 @@ impl InlaySnapshot {
             buffer_chunk: None,
             output_offset: range.start,
             max_output_offset: range.end,
-            hint_highlight_style: hint_highlights,
-            suggestion_highlight_style: suggestion_highlights,
+            hint_highlight_style: inlay_highlight_style,
+            suggestion_highlight_style,
             highlight_endpoints: highlight_endpoints.into_iter().peekable(),
             active_highlights: Default::default(),
             snapshot: self,
@@ -1060,9 +1058,16 @@ impl InlaySnapshot {
 
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(Default::default()..self.len(), false, None, None, None)
-            .map(|chunk| chunk.text)
-            .collect()
+        self.chunks(
+            Default::default()..self.len(),
+            false,
+            None,
+            None,
+            None,
+            None,
+        )
+        .map(|chunk| chunk.text)
+        .collect()
     }
 
     fn check_invariants(&self) {
@@ -1636,6 +1641,8 @@ mod tests {
                         InlayOffset(start)..InlayOffset(end),
                         false,
                         Some(&highlights),
+                        // TODO kb add tests
+                        None,
                         None,
                         None,
                     )

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

@@ -1,6 +1,6 @@
 use super::{
     fold_map::{self, FoldChunks, FoldEdit, FoldPoint, FoldSnapshot},
-    TextHighlights,
+    InlayHighlights, TextHighlights,
 };
 use crate::MultiBufferSnapshot;
 use gpui::fonts::HighlightStyle;
@@ -71,6 +71,7 @@ impl TabMap {
                     None,
                     None,
                     None,
+                    None,
                 ) {
                     for (ix, _) in chunk.text.match_indices('\t') {
                         let offset_from_edit = offset_from_edit + (ix as u32);
@@ -183,7 +184,7 @@ impl TabSnapshot {
             self.max_point()
         };
         for c in self
-            .chunks(range.start..line_end, false, None, None, None)
+            .chunks(range.start..line_end, false, None, None, None, None)
             .flat_map(|chunk| chunk.text.chars())
         {
             if c == '\n' {
@@ -203,6 +204,7 @@ impl TabSnapshot {
                     None,
                     None,
                     None,
+                    None,
                 )
                 .flat_map(|chunk| chunk.text.chars())
             {
@@ -223,9 +225,11 @@ impl TabSnapshot {
         &'a self,
         range: Range<TabPoint>,
         language_aware: bool,
+        // TODO kb extract into one struct
         text_highlights: Option<&'a TextHighlights>,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlights: Option<&'a InlayHighlights>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> TabChunks<'a> {
         let (input_start, expanded_char_column, to_next_stop) =
             self.to_fold_point(range.start, Bias::Left);
@@ -246,8 +250,9 @@ impl TabSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
-                hint_highlights,
-                suggestion_highlights,
+                inlay_highlights,
+                inlay_highlight_style,
+                suggestion_highlight_style,
             ),
             input_column,
             column: expanded_char_column,
@@ -270,9 +275,16 @@ impl TabSnapshot {
 
     #[cfg(test)]
     pub fn text(&self) -> String {
-        self.chunks(TabPoint::zero()..self.max_point(), false, None, None, None)
-            .map(|chunk| chunk.text)
-            .collect()
+        self.chunks(
+            TabPoint::zero()..self.max_point(),
+            false,
+            None,
+            None,
+            None,
+            None,
+        )
+        .map(|chunk| chunk.text)
+        .collect()
     }
 
     pub fn max_point(&self) -> TabPoint {
@@ -600,6 +612,7 @@ mod tests {
                         None,
                         None,
                         None,
+                        None,
                     )
                     .map(|c| c.text)
                     .collect::<String>(),
@@ -674,7 +687,8 @@ mod tests {
             let mut chunks = Vec::new();
             let mut was_tab = false;
             let mut text = String::new();
-            for chunk in snapshot.chunks(start..snapshot.max_point(), false, None, None, None) {
+            for chunk in snapshot.chunks(start..snapshot.max_point(), false, None, None, None, None)
+            {
                 if chunk.is_tab != was_tab {
                     if !text.is_empty() {
                         chunks.push((mem::take(&mut text), was_tab));
@@ -743,7 +757,7 @@ mod tests {
             let expected_summary = TextSummary::from(expected_text.as_str());
             assert_eq!(
                 tabs_snapshot
-                    .chunks(start..end, false, None, None, None)
+                    .chunks(start..end, false, None, None, None, None)
                     .map(|c| c.text)
                     .collect::<String>(),
                 expected_text,

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

@@ -1,7 +1,7 @@
 use super::{
     fold_map::FoldBufferRows,
     tab_map::{self, TabEdit, TabPoint, TabSnapshot},
-    TextHighlights,
+    InlayHighlights, TextHighlights,
 };
 use crate::MultiBufferSnapshot;
 use gpui::{
@@ -447,6 +447,7 @@ impl WrapSnapshot {
                     None,
                     None,
                     None,
+                    None,
                 );
                 let mut edit_transforms = Vec::<Transform>::new();
                 for _ in edit.new_rows.start..edit.new_rows.end {
@@ -576,8 +577,9 @@ impl WrapSnapshot {
         rows: Range<u32>,
         language_aware: bool,
         text_highlights: Option<&'a TextHighlights>,
-        hint_highlights: Option<HighlightStyle>,
-        suggestion_highlights: Option<HighlightStyle>,
+        inlay_highlights: Option<&'a InlayHighlights>,
+        inlay_highlight_style: Option<HighlightStyle>,
+        suggestion_highlight_style: Option<HighlightStyle>,
     ) -> WrapChunks<'a> {
         let output_start = WrapPoint::new(rows.start, 0);
         let output_end = WrapPoint::new(rows.end, 0);
@@ -595,8 +597,9 @@ impl WrapSnapshot {
                 input_start..input_end,
                 language_aware,
                 text_highlights,
-                hint_highlights,
-                suggestion_highlights,
+                inlay_highlights,
+                inlay_highlight_style,
+                suggestion_highlight_style,
             ),
             input_chunk: Default::default(),
             output_position: output_start,
@@ -1326,6 +1329,7 @@ mod tests {
                 None,
                 None,
                 None,
+                None,
             )
             .map(|h| h.text)
         }
@@ -1350,7 +1354,7 @@ mod tests {
                 }
 
                 let actual_text = self
-                    .chunks(start_row..end_row, true, None, None, None)
+                    .chunks(start_row..end_row, true, None, None, None, None)
                     .map(|c| c.text)
                     .collect::<String>();
                 assert_eq!(

crates/editor/src/editor.rs 🔗

@@ -64,7 +64,9 @@ use language::{
     Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
     OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
 };
-use link_go_to_definition::{hide_link_definition, show_link_definition, LinkGoToDefinitionState};
+use link_go_to_definition::{
+    hide_link_definition, show_link_definition, InlayCoordinates, LinkGoToDefinitionState,
+};
 use log::error;
 use multi_buffer::ToOffsetUtf16;
 pub use multi_buffer::{
@@ -7716,6 +7718,18 @@ impl Editor {
         cx.notify();
     }
 
+    pub fn highlight_inlays<T: 'static>(
+        &mut self,
+        ranges: Vec<InlayCoordinates>,
+        style: HighlightStyle,
+        cx: &mut ViewContext<Self>,
+    ) {
+        self.display_map.update(cx, |map, _| {
+            map.highlight_inlays(TypeId::of::<T>(), ranges, style)
+        });
+        cx.notify();
+    }
+
     pub fn text_highlights<'a, T: 'static>(
         &'a self,
         cx: &'a AppContext,
@@ -7736,6 +7750,19 @@ impl Editor {
         highlights
     }
 
+    pub fn clear_highlights<T: 'static>(
+        &mut self,
+        cx: &mut ViewContext<Self>,
+    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
+        let highlights = self
+            .display_map
+            .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
+        if highlights.is_some() {
+            cx.notify();
+        }
+        highlights
+    }
+
     pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
         self.blink_manager.read(cx).visible() && self.focused
     }
@@ -8327,7 +8354,7 @@ impl View for Editor {
 
             self.link_go_to_definition_state.task = None;
 
-            self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
+            self.clear_highlights::<LinkGoToDefinitionState>(cx);
         }
 
         false

crates/editor/src/element.rs 🔗

@@ -13,7 +13,7 @@ use crate::{
     },
     link_go_to_definition::{
         go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
-        GoToDefinitionTrigger,
+        GoToDefinitionTrigger, InlayCoordinates,
     },
     mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
 };
@@ -1927,7 +1927,12 @@ fn update_inlay_link_and_hover_points(
                                             update_go_to_definition_link(
                                                 editor,
                                                 GoToDefinitionTrigger::InlayHint(
-                                                    hovered_hint.position,
+                                                    InlayCoordinates {
+                                                        inlay_id: hovered_hint.id,
+                                                        inlay_position: hovered_hint.position,
+                                                        inlay_start: hint_start_offset,
+                                                        highlight_end: hovered_offset,
+                                                    },
                                                     LocationLink {
                                                         origin: Some(Location {
                                                             buffer,
@@ -1,4 +1,7 @@
-use crate::{element::PointForPosition, Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase};
+use crate::{
+    display_map::InlayOffset, element::PointForPosition, Anchor, DisplayPoint, Editor,
+    EditorSnapshot, InlayId, SelectPhase,
+};
 use gpui::{Task, ViewContext};
 use language::{Bias, ToOffset};
 use project::LocationLink;
@@ -7,8 +10,8 @@ use util::TryFutureExt;
 
 #[derive(Debug, Default)]
 pub struct LinkGoToDefinitionState {
-    pub last_trigger_point: Option<TriggerAnchor>,
-    pub symbol_range: Option<Range<Anchor>>,
+    pub last_trigger_point: Option<TriggerPoint>,
+    pub symbol_range: Option<SymbolRange>,
     pub kind: Option<LinkDefinitionKind>,
     pub definitions: Vec<LocationLink>,
     pub task: Option<Task<Option<()>>>,
@@ -16,34 +19,65 @@ pub struct LinkGoToDefinitionState {
 
 pub enum GoToDefinitionTrigger {
     Text(DisplayPoint),
-    InlayHint(Anchor, LocationLink),
+    InlayHint(InlayCoordinates, LocationLink),
     None,
 }
 
+#[derive(Debug, Clone, Copy)]
+pub struct InlayCoordinates {
+    pub inlay_id: InlayId,
+    pub inlay_position: Anchor,
+    pub inlay_start: InlayOffset,
+    pub highlight_end: InlayOffset,
+}
+
 #[derive(Debug, Clone)]
-pub enum TriggerAnchor {
+pub enum TriggerPoint {
     Text(Anchor),
-    InlayHint(Anchor, LocationLink),
+    InlayHint(InlayCoordinates, LocationLink),
+}
+
+#[derive(Debug, Clone)]
+pub enum SymbolRange {
+    Text(Range<Anchor>),
+    Inlay(InlayCoordinates),
 }
 
-impl TriggerAnchor {
+impl SymbolRange {
+    fn point_within_range(&self, trigger_point: &TriggerPoint, snapshot: &EditorSnapshot) -> bool {
+        match (self, trigger_point) {
+            (SymbolRange::Text(range), TriggerPoint::Text(point)) => {
+                let point_after_start = range.start.cmp(point, &snapshot.buffer_snapshot).is_le();
+                point_after_start && range.end.cmp(point, &snapshot.buffer_snapshot).is_ge()
+            }
+            (SymbolRange::Inlay(range), TriggerPoint::InlayHint(point, _)) => {
+                range.inlay_start.cmp(&point.highlight_end).is_le()
+                    && range.highlight_end.cmp(&point.highlight_end).is_ge()
+            }
+            (SymbolRange::Inlay(_), TriggerPoint::Text(_))
+            | (SymbolRange::Text(_), TriggerPoint::InlayHint(_, _)) => false,
+        }
+    }
+}
+
+impl TriggerPoint {
     fn anchor(&self) -> &Anchor {
         match self {
-            TriggerAnchor::Text(anchor) => anchor,
-            TriggerAnchor::InlayHint(anchor, _) => anchor,
+            TriggerPoint::Text(anchor) => anchor,
+            TriggerPoint::InlayHint(coordinates, _) => &coordinates.inlay_position,
         }
     }
 
     pub fn definition_kind(&self, shift: bool) -> LinkDefinitionKind {
         match self {
-            TriggerAnchor::Text(_) => {
+            TriggerPoint::Text(_) => {
                 if shift {
                     LinkDefinitionKind::Type
                 } else {
                     LinkDefinitionKind::Symbol
                 }
             }
-            TriggerAnchor::InlayHint(_, _) => LinkDefinitionKind::Type,
+            TriggerPoint::InlayHint(_, _) => LinkDefinitionKind::Type,
         }
     }
 }
@@ -61,11 +95,11 @@ pub fn update_go_to_definition_link(
     let snapshot = editor.snapshot(cx);
     let trigger_point = match origin {
         GoToDefinitionTrigger::Text(p) => {
-            Some(TriggerAnchor::Text(snapshot.buffer_snapshot.anchor_before(
+            Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before(
                 p.to_offset(&snapshot.display_snapshot, Bias::Left),
             )))
         }
-        GoToDefinitionTrigger::InlayHint(p, target) => Some(TriggerAnchor::InlayHint(p, target)),
+        GoToDefinitionTrigger::InlayHint(p, target) => Some(TriggerPoint::InlayHint(p, target)),
         GoToDefinitionTrigger::None => None,
     };
 
@@ -109,7 +143,7 @@ pub enum LinkDefinitionKind {
 pub fn show_link_definition(
     definition_kind: LinkDefinitionKind,
     editor: &mut Editor,
-    trigger_point: TriggerAnchor,
+    trigger_point: TriggerPoint,
     snapshot: EditorSnapshot,
     cx: &mut ViewContext<Editor>,
 ) {
@@ -122,7 +156,7 @@ pub fn show_link_definition(
         return;
     }
 
-    let trigger_anchor = trigger_point.anchor().clone();
+    let trigger_anchor = trigger_point.anchor();
     let (buffer, buffer_position) = if let Some(output) = editor
         .buffer
         .read(cx)
@@ -151,26 +185,15 @@ pub fn show_link_definition(
 
     // Don't request again if the location is within the symbol region of a previous request with the same kind
     if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range {
-        let point_after_start = symbol_range
-            .start
-            .cmp(&trigger_anchor, &snapshot.buffer_snapshot)
-            .is_le();
-
-        let point_before_end = symbol_range
-            .end
-            .cmp(&trigger_anchor, &snapshot.buffer_snapshot)
-            .is_ge();
-
-        let point_within_range = point_after_start && point_before_end;
-        if point_within_range && same_kind {
+        if same_kind && symbol_range.point_within_range(&trigger_point, &snapshot) {
             return;
         }
     }
 
     let task = cx.spawn(|this, mut cx| {
         async move {
-            let result = match trigger_point {
-                TriggerAnchor::Text(_) => {
+            let result = match &trigger_point {
+                TriggerPoint::Text(_) => {
                     // query the LSP for definition info
                     cx.update(|cx| {
                         project.update(cx, |project, cx| match definition_kind {
@@ -196,23 +219,22 @@ pub fn show_link_definition(
                                         .buffer_snapshot
                                         .anchor_in_excerpt(excerpt_id.clone(), origin.range.end);
 
-                                    start..end
+                                    SymbolRange::Text(start..end)
                                 })
                             }),
                             definition_result,
                         )
                     })
                 }
-                TriggerAnchor::InlayHint(trigger_source, trigger_target) => {
-                    // TODO kb range is wrong, should be in inlay coordinates have a proper inlay range.
-                    // Or highlight inlays differently, in their layer?
-                    Some((Some(trigger_source..trigger_source), vec![trigger_target]))
-                }
+                TriggerPoint::InlayHint(trigger_source, trigger_target) => Some((
+                    Some(SymbolRange::Inlay(trigger_source.clone())),
+                    vec![trigger_target.clone()],
+                )),
             };
 
             this.update(&mut cx, |this, cx| {
                 // Clear any existing highlights
-                this.clear_text_highlights::<LinkGoToDefinitionState>(cx);
+                this.clear_highlights::<LinkGoToDefinitionState>(cx);
                 this.link_go_to_definition_state.kind = Some(definition_kind);
                 this.link_go_to_definition_state.symbol_range = result
                     .as_ref()
@@ -248,22 +270,37 @@ pub fn show_link_definition(
                         });
 
                     if any_definition_does_not_contain_current_location {
-                        // If no symbol range returned from language server, use the surrounding word.
-                        let highlight_range = symbol_range.unwrap_or_else(|| {
-                            let snapshot = &snapshot.buffer_snapshot;
-                            let (offset_range, _) = snapshot.surrounding_word(trigger_anchor);
-
-                            snapshot.anchor_before(offset_range.start)
-                                ..snapshot.anchor_after(offset_range.end)
-                        });
-
                         // Highlight symbol using theme link definition highlight style
                         let style = theme::current(cx).editor.link_definition;
-                        this.highlight_text::<LinkGoToDefinitionState>(
-                            vec![highlight_range],
-                            style,
-                            cx,
-                        );
+                        let highlight_range = symbol_range.unwrap_or_else(|| match trigger_point {
+                            TriggerPoint::Text(trigger_anchor) => {
+                                let snapshot = &snapshot.buffer_snapshot;
+                                // If no symbol range returned from language server, use the surrounding word.
+                                let (offset_range, _) = snapshot.surrounding_word(trigger_anchor);
+                                SymbolRange::Text(
+                                    snapshot.anchor_before(offset_range.start)
+                                        ..snapshot.anchor_after(offset_range.end),
+                                )
+                            }
+                            TriggerPoint::InlayHint(inlay_trigger, _) => {
+                                SymbolRange::Inlay(inlay_trigger)
+                            }
+                        });
+
+                        match highlight_range {
+                            SymbolRange::Text(text_range) => this
+                                .highlight_text::<LinkGoToDefinitionState>(
+                                    vec![text_range],
+                                    style,
+                                    cx,
+                                ),
+                            SymbolRange::Inlay(inlay_coordinates) => this
+                                .highlight_inlays::<LinkGoToDefinitionState>(
+                                    vec![inlay_coordinates],
+                                    style,
+                                    cx,
+                                ),
+                        }
                     } else {
                         hide_link_definition(this, cx);
                     }
@@ -289,7 +326,7 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
 
     editor.link_go_to_definition_state.task = None;
 
-    editor.clear_text_highlights::<LinkGoToDefinitionState>(cx);
+    editor.clear_highlights::<LinkGoToDefinitionState>(cx);
 }
 
 pub fn go_to_fetched_definition(