Detailed changes
@@ -1596,7 +1596,7 @@ mod tests {
.map(|range| {
buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_after(range.end)
})
- // TODO add inlay highlight tests
+ // TODO kb add inlay highlight tests
.map(DocumentRange::Text)
.collect::<Vec<_>>();
@@ -223,7 +223,6 @@ impl TabSnapshot {
&'a self,
range: Range<TabPoint>,
language_aware: bool,
- // TODO kb extract into one struct?
text_highlights: Option<&'a TextHighlights>,
inlay_highlight_style: Option<HighlightStyle>,
suggestion_highlight_style: Option<HighlightStyle>,
@@ -7511,6 +7511,22 @@ impl Editor {
cx.notify();
}
+ pub fn highlight_inlay_background<T: 'static>(
+ &mut self,
+ ranges: Vec<InlayRange>,
+ color_fetcher: fn(&Theme) -> Color,
+ cx: &mut ViewContext<Self>,
+ ) {
+ self.background_highlights.insert(
+ TypeId::of::<T>(),
+ (
+ color_fetcher,
+ ranges.into_iter().map(DocumentRange::Inlay).collect(),
+ ),
+ );
+ cx.notify();
+ }
+
pub fn clear_background_highlights<T: 'static>(
&mut self,
cx: &mut ViewContext<Self>,
@@ -7932,7 +7948,6 @@ impl Editor {
Some(
ranges
.iter()
- // TODO kb mark inlays too
.filter_map(|range| range.as_text_range())
.map(move |range| {
range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
@@ -8400,7 +8415,6 @@ impl View for Editor {
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
let snapshot = self.buffer.read(cx).read(cx);
let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
- // TODO kb mark inlays too
let range = range.as_text_range()?;
Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
}
@@ -8,8 +8,8 @@ use crate::{
editor_settings::ShowScrollbar,
git::{diff_hunk_to_display, DisplayDiffHunk},
hover_popover::{
- hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
- MIN_POPOVER_LINE_HEIGHT,
+ hide_hover, hover_at, hover_at_inlay, InlayHover, HOVER_POPOVER_GAP,
+ MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
},
link_go_to_definition::{
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
@@ -43,7 +43,8 @@ use language::{
};
use project::{
project_settings::{GitGutterSetting, ProjectSettings},
- InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState,
+ HoverBlock, HoverBlockKind, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip,
+ Location, LocationLink, ProjectPath, ResolveState,
};
use smallvec::SmallVec;
use std::{
@@ -1860,8 +1861,7 @@ fn update_inlay_link_and_hover_points(
None
};
if let Some(hovered_offset) = hovered_offset {
- let buffer = editor.buffer().read(cx);
- let snapshot = buffer.snapshot(cx);
+ let snapshot = editor.buffer().read(cx).snapshot(cx);
let previous_valid_anchor = snapshot.anchor_at(
point_for_position
.previous_valid
@@ -1885,15 +1885,14 @@ fn update_inlay_link_and_hover_points(
.max_by_key(|hint| hint.id)
{
let inlay_hint_cache = editor.inlay_hint_cache();
- if let Some(cached_hint) =
- inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
- {
+ let excerpt_id = previous_valid_anchor.excerpt_id;
+ if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
match cached_hint.resolve_state {
ResolveState::CanResolve(_, _) => {
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
inlay_hint_cache.spawn_hint_resolve(
buffer_id,
- previous_valid_anchor.excerpt_id,
+ excerpt_id,
hovered_hint.id,
cx,
);
@@ -1902,9 +1901,33 @@ fn update_inlay_link_and_hover_points(
ResolveState::Resolved => {
match cached_hint.label {
project::InlayHintLabel::String(_) => {
- if cached_hint.tooltip.is_some() {
- dbg!(&cached_hint.tooltip); // TODO kb
- // hover_at_point = Some(hovered_offset);
+ if let Some(tooltip) = cached_hint.tooltip {
+ hover_at_inlay(
+ editor,
+ InlayHover {
+ excerpt: excerpt_id,
+ tooltip: match tooltip {
+ InlayHintTooltip::String(text) => HoverBlock {
+ text,
+ kind: HoverBlockKind::PlainText,
+ },
+ InlayHintTooltip::MarkupContent(content) => {
+ HoverBlock {
+ text: content.value,
+ kind: content.kind,
+ }
+ }
+ },
+ triggered_from: hovered_offset,
+ range: InlayRange {
+ inlay_position: hovered_hint.position,
+ highlight_start: hint_start_offset,
+ highlight_end: hint_end_offset,
+ },
+ },
+ cx,
+ );
+ hover_updated = true;
}
}
project::InlayHintLabel::LabelParts(label_parts) => {
@@ -1915,15 +1938,41 @@ fn update_inlay_link_and_hover_points(
hovered_offset,
)
{
- if hovered_hint_part.tooltip.is_some() {
- dbg!(&hovered_hint_part.tooltip); // TODO kb
- // hover_at_point = Some(hovered_offset);
+ if let Some(tooltip) = hovered_hint_part.tooltip {
+ hover_at_inlay(
+ editor,
+ InlayHover {
+ excerpt: excerpt_id,
+ tooltip: match tooltip {
+ InlayHintLabelPartTooltip::String(text) => {
+ HoverBlock {
+ text,
+ kind: HoverBlockKind::PlainText,
+ }
+ }
+ InlayHintLabelPartTooltip::MarkupContent(
+ content,
+ ) => HoverBlock {
+ text: content.value,
+ kind: content.kind,
+ },
+ },
+ triggered_from: hovered_offset,
+ range: InlayRange {
+ inlay_position: hovered_hint.position,
+ highlight_start: part_range.start,
+ highlight_end: part_range.end,
+ },
+ },
+ cx,
+ );
+ hover_updated = true;
}
if let Some(location) = hovered_hint_part.location {
- if let Some(buffer) = cached_hint
- .position
- .buffer_id
- .and_then(|buffer_id| buffer.buffer(buffer_id))
+ if let Some(buffer) =
+ cached_hint.position.buffer_id.and_then(|buffer_id| {
+ editor.buffer().read(cx).buffer(buffer_id)
+ })
{
go_to_definition_updated = true;
update_go_to_definition_link(
@@ -1,6 +1,8 @@
use crate::{
- display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
- EditorSnapshot, EditorStyle, RangeToAnchorExt,
+ display_map::{InlayOffset, ToDisplayPoint},
+ link_go_to_definition::{DocumentRange, InlayRange},
+ Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
+ ExcerptId, RangeToAnchorExt,
};
use futures::FutureExt;
use gpui::{
@@ -46,6 +48,84 @@ pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewC
}
}
+pub struct InlayHover {
+ pub excerpt: ExcerptId,
+ pub triggered_from: InlayOffset,
+ pub range: InlayRange,
+ pub tooltip: HoverBlock,
+}
+
+pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
+ if settings::get::<EditorSettings>(cx).hover_popover_enabled {
+ if editor.pending_rename.is_some() {
+ return;
+ }
+
+ let Some(project) = editor.project.clone() else {
+ return;
+ };
+
+ if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
+ if let DocumentRange::Inlay(range) = symbol_range {
+ if (range.highlight_start..=range.highlight_end)
+ .contains(&inlay_hover.triggered_from)
+ {
+ // Hover triggered from same location as last time. Don't show again.
+ return;
+ }
+ }
+ hide_hover(editor, cx);
+ }
+
+ let snapshot = editor.snapshot(cx);
+ // Don't request again if the location is the same as the previous request
+ if let Some(triggered_from) = editor.hover_state.triggered_from {
+ if inlay_hover.triggered_from
+ == snapshot
+ .display_snapshot
+ .anchor_to_inlay_offset(triggered_from)
+ {
+ return;
+ }
+ }
+
+ let task = cx.spawn(|this, mut cx| {
+ async move {
+ cx.background()
+ .timer(Duration::from_millis(HOVER_DELAY_MILLIS))
+ .await;
+ this.update(&mut cx, |this, _| {
+ this.hover_state.diagnostic_popover = None;
+ })?;
+
+ let hover_popover = InfoPopover {
+ project: project.clone(),
+ symbol_range: DocumentRange::Inlay(inlay_hover.range),
+ blocks: vec![inlay_hover.tooltip],
+ language: None,
+ rendered_content: None,
+ };
+
+ this.update(&mut cx, |this, cx| {
+ // Highlight the selected symbol using a background highlight
+ this.highlight_inlay_background::<HoverState>(
+ vec![inlay_hover.range],
+ |theme| theme.editor.hover_popover.highlight,
+ cx,
+ );
+ this.hover_state.info_popover = Some(hover_popover);
+ cx.notify();
+ })?;
+
+ anyhow::Ok(())
+ }
+ .log_err()
+ });
+
+ editor.hover_state.info_task = Some(task);
+ }
+}
+
/// Hides the type information popup.
/// Triggered by the `Hover` action when the cursor is not over a symbol or when the
/// selections changed.
@@ -110,8 +190,13 @@ fn show_hover(
if !ignore_timeout {
if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
if symbol_range
- .to_offset(&snapshot.buffer_snapshot)
- .contains(&multibuffer_offset)
+ .as_text_range()
+ .map(|range| {
+ range
+ .to_offset(&snapshot.buffer_snapshot)
+ .contains(&multibuffer_offset)
+ })
+ .unwrap_or(false)
{
// Hover triggered from same location as last time. Don't show again.
return;
@@ -219,7 +304,7 @@ fn show_hover(
Some(InfoPopover {
project: project.clone(),
- symbol_range: range,
+ symbol_range: DocumentRange::Text(range),
blocks: hover_result.contents,
language: hover_result.language,
rendered_content: None,
@@ -227,10 +312,13 @@ fn show_hover(
});
this.update(&mut cx, |this, cx| {
- if let Some(hover_popover) = hover_popover.as_ref() {
+ if let Some(symbol_range) = hover_popover
+ .as_ref()
+ .and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
+ {
// Highlight the selected symbol using a background highlight
this.highlight_background::<HoverState>(
- vec![hover_popover.symbol_range.clone()],
+ vec![symbol_range],
|theme| theme.editor.hover_popover.highlight,
cx,
);
@@ -497,7 +585,10 @@ impl HoverState {
.or_else(|| {
self.info_popover
.as_ref()
- .map(|info_popover| &info_popover.symbol_range.start)
+ .map(|info_popover| match &info_popover.symbol_range {
+ DocumentRange::Text(range) => &range.start,
+ DocumentRange::Inlay(range) => &range.inlay_position,
+ })
})?;
let point = anchor.to_display_point(&snapshot.display_snapshot);
@@ -522,7 +613,7 @@ impl HoverState {
#[derive(Debug, Clone)]
pub struct InfoPopover {
pub project: ModelHandle<Project>,
- pub symbol_range: Range<Anchor>,
+ symbol_range: DocumentRange,
pub blocks: Vec<HoverBlock>,
language: Option<Arc<Language>>,
rendered_content: Option<RenderedInfo>,
@@ -2126,7 +2126,6 @@ impl InlayHints {
})
}
- // TODO kb instead, store all LSP data inside the project::InlayHint?
pub fn project_to_lsp_hint(
hint: InlayHint,
project: &ModelHandle<Project>,