Extract resolve_hint

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Richard Feldman 2025-07-07 16:48:33 -04:00
parent a61e478152
commit 17f7312fc0
No known key found for this signature in database
2 changed files with 182 additions and 137 deletions

View file

@ -121,6 +121,22 @@ impl Editor {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let hovered_link_modifier = Editor::multi_cursor_modifier(false, &modifiers, cx); 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() { if !hovered_link_modifier || self.has_pending_selection() {
self.hide_hovered_link(cx); self.hide_hovered_link(cx);
return; return;
@ -137,15 +153,7 @@ impl Editor {
show_link_definition(modifiers.shift, self, trigger_point, snapshot, window, cx); show_link_definition(modifiers.shift, self, trigger_point, snapshot, window, cx);
} }
None => { None => {
update_inlay_link_and_hover_points( // This case is now handled above
snapshot,
point_for_position,
self,
hovered_link_modifier,
modifiers.shift,
window,
cx,
);
} }
} }
} }
@ -319,129 +327,155 @@ pub fn update_inlay_link_and_hover_points(
let inlay_hint_cache = editor.inlay_hint_cache(); let inlay_hint_cache = editor.inlay_hint_cache();
let excerpt_id = previous_valid_anchor.excerpt_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) { 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(_, _) => { ResolveState::CanResolve(_, _) => {
if let Some(buffer_id) = previous_valid_anchor.buffer_id { // Check if the hint already has the data we need (tooltip in label parts)
inlay_hint_cache.spawn_hint_resolve( if let project::InlayHintLabel::LabelParts(label_parts) = &cached_hint.label
buffer_id, {
excerpt_id, let has_tooltip_parts =
hovered_hint.id, label_parts.iter().any(|part| part.tooltip.is_some());
window, if has_tooltip_parts {
cx, 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 => { ResolveState::Resolved => {
let mut extra_shift_left = 0; true // Process the hint
let mut extra_shift_right = 0; }
if cached_hint.padding_left { ResolveState::Resolving => {
extra_shift_left += 1; false // Don't process yet
extra_shift_right += 1; }
};
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 { project::InlayHintLabel::LabelParts(label_parts) => {
extra_shift_right += 1; let hint_start = snapshot.anchor_to_inlay_offset(hovered_hint.position);
} if let Some((hovered_hint_part, part_range)) =
match cached_hint.label { hover_popover::find_hovered_hint_part(
project::InlayHintLabel::String(_) => { label_parts,
if let Some(tooltip) = cached_hint.tooltip { 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( hover_popover::hover_at_inlay(
editor, editor,
InlayHover { InlayHover {
tooltip: match tooltip { tooltip: match tooltip {
InlayHintTooltip::String(text) => HoverBlock { InlayHintLabelPartTooltip::String(text) => {
text,
kind: HoverBlockKind::PlainText,
},
InlayHintTooltip::MarkupContent(content) => {
HoverBlock { HoverBlock {
text: content.value, text,
kind: content.kind, kind: HoverBlockKind::PlainText,
} }
} }
InlayHintLabelPartTooltip::MarkupContent(
content,
) => HoverBlock {
text: content.value,
kind: content.kind,
},
}, },
range: InlayHighlight { range: highlight.clone(),
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: extra_shift_left
..hovered_hint.text.len() + extra_shift_right,
},
}, },
window, window,
cx, cx,
); );
hover_updated = true; hover_updated = true;
} }
} if let Some((language_server_id, location)) =
project::InlayHintLabel::LabelParts(label_parts) => { hovered_hint_part.location
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 = if secondary_held && !editor.has_pending_nonempty_selection() {
(part_range.start - hint_start).0 + extra_shift_left; go_to_definition_updated = true;
let highlight_end = show_link_definition(
(part_range.end - hint_start).0 + extra_shift_right; shift_held,
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, editor,
InlayHover { TriggerPoint::InlayHint(
tooltip: match tooltip { highlight,
InlayHintLabelPartTooltip::String(text) => { location,
HoverBlock { language_server_id,
text, ),
kind: HoverBlockKind::PlainText, snapshot,
}
}
InlayHintLabelPartTooltip::MarkupContent(
content,
) => HoverBlock {
text: content.value,
kind: content.kind,
},
},
range: highlight.clone(),
},
window, window,
cx, 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 => {}
} }
} }
} }

View file

@ -21,6 +21,7 @@ use clock::Global;
use futures::future; use futures::future;
use gpui::{AppContext as _, AsyncApp, Context, Entity, Task, Window}; use gpui::{AppContext as _, AsyncApp, Context, Entity, Task, Window};
use language::{Buffer, BufferSnapshot, language_settings::InlayHintKind}; use language::{Buffer, BufferSnapshot, language_settings::InlayHintKind};
use lsp::LanguageServerId;
use parking_lot::RwLock; use parking_lot::RwLock;
use project::{InlayHint, ResolveState}; use project::{InlayHint, ResolveState};
@ -622,45 +623,55 @@ impl InlayHintCache {
let mut guard = excerpt_hints.write(); let mut guard = excerpt_hints.write();
if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) { if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state { if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state {
let hint_to_resolve = cached_hint.clone();
let server_id = *server_id; let server_id = *server_id;
let mut cached_hint = cached_hint.clone();
cached_hint.resolve_state = ResolveState::Resolving; cached_hint.resolve_state = ResolveState::Resolving;
drop(guard); drop(guard);
cx.spawn_in(window, async move |editor, cx| { self.resolve_hint(server_id, buffer_id, cached_hint, window, cx)
let resolved_hint_task = editor.update(cx, |editor, cx| { .detach_and_log_err(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);
} }
} }
} }
} }
fn resolve_hint(
&self,
server_id: LanguageServerId,
buffer_id: BufferId,
hint_to_resolve: InlayHint,
window: &mut Window,
cx: &mut Context<Editor>,
) -> Task<anyhow::Result<()>> {
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<Duration> { fn debounce_value(debounce_ms: u64) -> Option<Duration> {