From 17f7312fc0ff783d4dba45cf212bb46758000465 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 7 Jul 2025 16:48:33 -0400 Subject: [PATCH] Extract resolve_hint Co-authored-by: Cole Miller --- crates/editor/src/hover_links.rs | 244 +++++++++++++++----------- crates/editor/src/inlay_hint_cache.rs | 75 ++++---- 2 files changed, 182 insertions(+), 137 deletions(-) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 02f93e6829..c39422fa04 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -121,6 +121,22 @@ impl Editor { cx: &mut Context, ) { let hovered_link_modifier = Editor::multi_cursor_modifier(false, &modifiers, cx); + + // Allow inlay hover points to be updated even without modifier key + if point_for_position.as_valid().is_none() { + // Hovering over inlay - check for hover tooltips + update_inlay_link_and_hover_points( + snapshot, + point_for_position, + self, + hovered_link_modifier, + modifiers.shift, + window, + cx, + ); + return; + } + if !hovered_link_modifier || self.has_pending_selection() { self.hide_hovered_link(cx); return; @@ -137,15 +153,7 @@ impl Editor { show_link_definition(modifiers.shift, self, trigger_point, snapshot, window, cx); } None => { - update_inlay_link_and_hover_points( - snapshot, - point_for_position, - self, - hovered_link_modifier, - modifiers.shift, - window, - cx, - ); + // This case is now handled above } } } @@ -319,129 +327,155 @@ pub fn update_inlay_link_and_hover_points( let inlay_hint_cache = editor.inlay_hint_cache(); 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 { + // Check if we should process this hint for hover + let should_process_hint = 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, - excerpt_id, - hovered_hint.id, - window, - cx, - ); + // Check if the hint already has the data we need (tooltip in label parts) + if let project::InlayHintLabel::LabelParts(label_parts) = &cached_hint.label + { + let has_tooltip_parts = + label_parts.iter().any(|part| part.tooltip.is_some()); + if has_tooltip_parts { + true // Process the hint + } else { + if let Some(buffer_id) = previous_valid_anchor.buffer_id { + inlay_hint_cache.spawn_hint_resolve( + buffer_id, + excerpt_id, + hovered_hint.id, + window, + cx, + ); + } + false // Don't process further + } + } else { + if let Some(buffer_id) = previous_valid_anchor.buffer_id { + inlay_hint_cache.spawn_hint_resolve( + buffer_id, + excerpt_id, + hovered_hint.id, + window, + cx, + ); + } + false // Don't process further } } ResolveState::Resolved => { - let mut extra_shift_left = 0; - let mut extra_shift_right = 0; - if cached_hint.padding_left { - extra_shift_left += 1; - extra_shift_right += 1; + true // Process the hint + } + ResolveState::Resolving => { + false // Don't process yet + } + }; + + if should_process_hint { + let mut extra_shift_left = 0; + let mut extra_shift_right = 0; + if cached_hint.padding_left { + extra_shift_left += 1; + extra_shift_right += 1; + } + if cached_hint.padding_right { + extra_shift_right += 1; + } + match cached_hint.label { + project::InlayHintLabel::String(_) => { + if let Some(tooltip) = cached_hint.tooltip { + hover_popover::hover_at_inlay( + editor, + InlayHover { + tooltip: match tooltip { + InlayHintTooltip::String(text) => HoverBlock { + text, + kind: HoverBlockKind::PlainText, + }, + InlayHintTooltip::MarkupContent(content) => { + HoverBlock { + text: content.value, + kind: content.kind, + } + } + }, + range: InlayHighlight { + inlay: hovered_hint.id, + inlay_position: hovered_hint.position, + range: extra_shift_left + ..hovered_hint.text.len() + extra_shift_right, + }, + }, + window, + cx, + ); + hover_updated = true; + } } - if cached_hint.padding_right { - extra_shift_right += 1; - } - match cached_hint.label { - project::InlayHintLabel::String(_) => { - if let Some(tooltip) = cached_hint.tooltip { + project::InlayHintLabel::LabelParts(label_parts) => { + let hint_start = snapshot.anchor_to_inlay_offset(hovered_hint.position); + if let Some((hovered_hint_part, part_range)) = + hover_popover::find_hovered_hint_part( + label_parts, + hint_start, + hovered_offset, + ) + { + let highlight_start = + (part_range.start - hint_start).0 + extra_shift_left; + let highlight_end = + (part_range.end - hint_start).0 + extra_shift_right; + let highlight = InlayHighlight { + inlay: hovered_hint.id, + inlay_position: hovered_hint.position, + range: highlight_start..highlight_end, + }; + if let Some(tooltip) = hovered_hint_part.tooltip { hover_popover::hover_at_inlay( editor, InlayHover { tooltip: match tooltip { - InlayHintTooltip::String(text) => HoverBlock { - text, - kind: HoverBlockKind::PlainText, - }, - InlayHintTooltip::MarkupContent(content) => { + InlayHintLabelPartTooltip::String(text) => { HoverBlock { - text: content.value, - kind: content.kind, + text, + kind: HoverBlockKind::PlainText, } } + InlayHintLabelPartTooltip::MarkupContent( + content, + ) => HoverBlock { + text: content.value, + kind: content.kind, + }, }, - range: InlayHighlight { - inlay: hovered_hint.id, - inlay_position: hovered_hint.position, - range: extra_shift_left - ..hovered_hint.text.len() + extra_shift_right, - }, + range: highlight.clone(), }, window, cx, ); hover_updated = true; } - } - project::InlayHintLabel::LabelParts(label_parts) => { - let hint_start = - snapshot.anchor_to_inlay_offset(hovered_hint.position); - if let Some((hovered_hint_part, part_range)) = - hover_popover::find_hovered_hint_part( - label_parts, - hint_start, - hovered_offset, - ) + if let Some((language_server_id, location)) = + hovered_hint_part.location { - let highlight_start = - (part_range.start - hint_start).0 + extra_shift_left; - let highlight_end = - (part_range.end - hint_start).0 + extra_shift_right; - let highlight = InlayHighlight { - inlay: hovered_hint.id, - inlay_position: hovered_hint.position, - range: highlight_start..highlight_end, - }; - if let Some(tooltip) = hovered_hint_part.tooltip { - hover_popover::hover_at_inlay( + if secondary_held && !editor.has_pending_nonempty_selection() { + go_to_definition_updated = true; + show_link_definition( + shift_held, editor, - InlayHover { - tooltip: match tooltip { - InlayHintLabelPartTooltip::String(text) => { - HoverBlock { - text, - kind: HoverBlockKind::PlainText, - } - } - InlayHintLabelPartTooltip::MarkupContent( - content, - ) => HoverBlock { - text: content.value, - kind: content.kind, - }, - }, - range: highlight.clone(), - }, + TriggerPoint::InlayHint( + highlight, + location, + language_server_id, + ), + snapshot, window, cx, ); - hover_updated = true; - } - if let Some((language_server_id, location)) = - hovered_hint_part.location - { - if secondary_held - && !editor.has_pending_nonempty_selection() - { - go_to_definition_updated = true; - show_link_definition( - shift_held, - editor, - TriggerPoint::InlayHint( - highlight, - location, - language_server_id, - ), - snapshot, - window, - cx, - ); - } } } } - }; - } - ResolveState::Resolving => {} + } + }; } } } diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index db01cc7ad1..98aae42169 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -21,6 +21,7 @@ use clock::Global; use futures::future; use gpui::{AppContext as _, AsyncApp, Context, Entity, Task, Window}; use language::{Buffer, BufferSnapshot, language_settings::InlayHintKind}; +use lsp::LanguageServerId; use parking_lot::RwLock; use project::{InlayHint, ResolveState}; @@ -622,45 +623,55 @@ impl InlayHintCache { let mut guard = excerpt_hints.write(); if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state { - let hint_to_resolve = cached_hint.clone(); let server_id = *server_id; + let mut cached_hint = cached_hint.clone(); cached_hint.resolve_state = ResolveState::Resolving; drop(guard); - cx.spawn_in(window, async move |editor, cx| { - let resolved_hint_task = editor.update(cx, |editor, cx| { - let buffer = editor.buffer().read(cx).buffer(buffer_id)?; - editor.semantics_provider.as_ref()?.resolve_inlay_hint( - hint_to_resolve, - buffer, - server_id, - cx, - ) - })?; - if let Some(resolved_hint_task) = resolved_hint_task { - let mut resolved_hint = - resolved_hint_task.await.context("hint resolve task")?; - editor.read_with(cx, |editor, _| { - if let Some(excerpt_hints) = - editor.inlay_hint_cache.hints.get(&excerpt_id) - { - let mut guard = excerpt_hints.write(); - if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { - if cached_hint.resolve_state == ResolveState::Resolving { - resolved_hint.resolve_state = ResolveState::Resolved; - *cached_hint = resolved_hint; - } - } - } - })?; - } - - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + self.resolve_hint(server_id, buffer_id, cached_hint, window, cx) + .detach_and_log_err(cx); } } } } + + fn resolve_hint( + &self, + server_id: LanguageServerId, + buffer_id: BufferId, + hint_to_resolve: InlayHint, + window: &mut Window, + cx: &mut Context, + ) -> Task> { + cx.spawn_in(window, async move |editor, cx| { + let resolved_hint_task = editor.update(cx, |editor, cx| { + let buffer = editor.buffer().read(cx).buffer(buffer_id)?; + editor.semantics_provider.as_ref()?.resolve_inlay_hint( + hint_to_resolve, + buffer, + server_id, + cx, + ) + })?; + if let Some(resolved_hint_task) = resolved_hint_task { + let mut resolved_hint = resolved_hint_task.await.context("hint resolve task")?; + editor.update(cx, |editor, cx| { + if let Some(excerpt_hints) = editor.inlay_hint_cache.hints.get(&excerpt_id) { + let mut guard = excerpt_hints.write(); + if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { + if cached_hint.resolve_state == ResolveState::Resolving { + resolved_hint.resolve_state = ResolveState::Resolved; + *cached_hint = resolved_hint; + } + } + } + // Notify to trigger UI update + cx.notify(); + })?; + } + + anyhow::Ok(()) + }) + } } fn debounce_value(debounce_ms: u64) -> Option {