wip - currently just shows a generic message, not the docs

This commit is contained in:
Richard Feldman 2025-07-09 16:37:34 -04:00
parent 2ff30d20e3
commit a322aa33c7
No known key found for this signature in database
3 changed files with 256 additions and 104 deletions

View file

@ -1222,6 +1222,7 @@ impl EditorElement {
// Don't trigger hover popover if mouse is hovering over context menu // Don't trigger hover popover if mouse is hovering over context menu
if text_hovered { if text_hovered {
eprintln!("mouse_moved: text_hovered=true, calling update_hovered_link");
editor.update_hovered_link( editor.update_hovered_link(
point_for_position, point_for_position,
&position_map.snapshot, &position_map.snapshot,
@ -1235,9 +1236,11 @@ impl EditorElement {
.snapshot .snapshot
.buffer_snapshot .buffer_snapshot
.anchor_before(point.to_offset(&position_map.snapshot, Bias::Left)); .anchor_before(point.to_offset(&position_map.snapshot, Bias::Left));
eprintln!("mouse_moved: Valid text position, calling hover_at with anchor");
hover_at(editor, Some(anchor), window, cx); hover_at(editor, Some(anchor), window, cx);
Self::update_visible_cursor(editor, point, position_map, window, cx); Self::update_visible_cursor(editor, point, position_map, window, cx);
} else { } else {
eprintln!("mouse_moved: Invalid position (inlay?), NOT calling hover_at");
// Don't call hover_at with None when we're over an inlay // Don't call hover_at with None when we're over an inlay
// The inlay hover is already handled by update_hovered_link // The inlay hover is already handled by update_hovered_link
} }

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,8 @@ 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 should not be reached anymore as we handle it above
snapshot, unreachable!("Invalid position should have been handled earlier");
point_for_position,
self,
hovered_link_modifier,
modifiers.shift,
window,
cx,
);
} }
} }
} }
@ -284,7 +293,10 @@ pub fn update_inlay_link_and_hover_points(
window: &mut Window, window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) { ) {
eprintln!("update_inlay_link_and_hover_points called");
// For inlay hints, we need to use the exact position where the mouse is
let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 { let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
// Use the exact unclipped position to get the correct offset within the inlay
Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left)) Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
} else { } else {
None None
@ -301,27 +313,69 @@ pub fn update_inlay_link_and_hover_points(
point_for_position.next_valid.to_point(snapshot), point_for_position.next_valid.to_point(snapshot),
Bias::Right, Bias::Right,
); );
if let Some(hovered_hint) = editor eprintln!(
.visible_inlay_hints(cx) "Looking for inlay hints between {:?} and {:?}",
previous_valid_anchor, next_valid_anchor
);
let visible_hints = editor.visible_inlay_hints(cx);
eprintln!("Total visible inlay hints: {}", visible_hints.len());
if let Some(hovered_hint) = visible_hints
.into_iter() .into_iter()
.skip_while(|hint| { .skip_while(|hint| {
hint.position let cmp = hint
.position
.cmp(&previous_valid_anchor, &buffer_snapshot) .cmp(&previous_valid_anchor, &buffer_snapshot)
.is_lt() .is_lt();
eprintln!(
"Checking hint {:?} at {:?} < prev {:?}: {}",
hint.id, hint.position, previous_valid_anchor, cmp
);
cmp
}) })
.take_while(|hint| { .take_while(|hint| {
hint.position let cmp = hint
.position
.cmp(&next_valid_anchor, &buffer_snapshot) .cmp(&next_valid_anchor, &buffer_snapshot)
.is_le() .is_le();
eprintln!(
"Checking hint {:?} at {:?} <= next {:?}: {}",
hint.id, hint.position, next_valid_anchor, cmp
);
cmp
}) })
.max_by_key(|hint| hint.id) .max_by_key(|hint| hint.id)
{ {
eprintln!("Found hovered inlay hint: {:?}", hovered_hint.id);
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 { eprintln!(
"Cached hint state: {:?}, has tooltip: {}, label: {:?}",
cached_hint.resolve_state,
cached_hint.tooltip.is_some(),
match &cached_hint.label {
project::InlayHintLabel::String(s) => format!("String({})", s),
project::InlayHintLabel::LabelParts(parts) =>
format!("LabelParts({} parts)", parts.len()),
}
);
// Debug full hint content
eprintln!("Full cached hint: {:?}", cached_hint);
if let project::InlayHintLabel::LabelParts(ref parts) = cached_hint.label {
for (i, part) in parts.iter().enumerate() {
eprintln!(
" Part {}: value='{}', tooltip={:?}, location={:?}",
i, part.value, part.tooltip, part.location
);
}
}
// Check if we should process this hint for hover
let should_process_hint = match cached_hint.resolve_state {
ResolveState::CanResolve(_, _) => { ResolveState::CanResolve(_, _) => {
// For unresolved hints, spawn resolution
if let Some(buffer_id) = previous_valid_anchor.buffer_id { if let Some(buffer_id) = previous_valid_anchor.buffer_id {
eprintln!("Spawning hint resolution for hint {:?}", hovered_hint.id);
inlay_hint_cache.spawn_hint_resolve( inlay_hint_cache.spawn_hint_resolve(
buffer_id, buffer_id,
excerpt_id, excerpt_id,
@ -329,128 +383,191 @@ pub fn update_inlay_link_and_hover_points(
window, window,
cx, cx,
); );
// Don't clear hover while resolution is starting
hover_updated = true;
} }
false // Don't process unresolved hints
} }
ResolveState::Resolved => { ResolveState::Resolved => true,
let mut extra_shift_left = 0; ResolveState::Resolving => {
let mut extra_shift_right = 0; // Don't clear hover while resolving
if cached_hint.padding_left { hover_updated = true;
extra_shift_left += 1; false // Don't process further
extra_shift_right += 1; }
};
eprintln!("Should process hint: {}", should_process_hint);
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(_) => {
eprintln!("Processing String label hint");
if let Some(tooltip) = cached_hint.tooltip {
eprintln!("Found tooltip on main hint, calling hover_at_inlay");
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; eprintln!(
} "Processing LabelParts hint with {} parts",
match cached_hint.label { label_parts.len()
project::InlayHintLabel::String(_) => { );
if let Some(tooltip) = cached_hint.tooltip { let hint_start = snapshot.anchor_to_inlay_offset(hovered_hint.position);
eprintln!("Hint start offset: {:?}", hint_start);
eprintln!("Hovered offset: {:?}", hovered_offset);
if let Some((hovered_hint_part, part_range)) =
hover_popover::find_hovered_hint_part(
label_parts.clone(),
hint_start,
hovered_offset,
)
{
eprintln!("Found hovered hint part: {:?}", hovered_hint_part.value);
eprintln!(
"Part has tooltip: {}",
hovered_hint_part.tooltip.is_some()
);
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 {
eprintln!("Found tooltip on hint part, calling hover_at_inlay");
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, window,
range: extra_shift_left cx,
..hovered_hint.text.len() + extra_shift_right, );
hover_updated = true;
} else if let Some((_language_server_id, location)) =
&hovered_hint_part.location
{
// Fallback: Show location info when no tooltip is available
eprintln!(
"No tooltip, but has location. Showing type definition info"
);
// Extract filename from the path
let filename =
location.uri.path().split('/').last().unwrap_or("unknown");
// Show information about where this type is defined
let hover_text = format!(
"{}\n\nDefined in {} at line {}",
hovered_hint_part.value.trim(),
filename,
location.range.start.line + 1
);
hover_popover::hover_at_inlay(
editor,
InlayHover {
tooltip: HoverBlock {
text: hover_text,
kind: HoverBlockKind::Markdown,
}, },
range: highlight.clone(),
}, },
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,
);
}
} }
} }
} else {
eprintln!("No hovered hint part found");
} }
}; }
} };
ResolveState::Resolving => {}
} }
} else {
eprintln!("No cached hint found for id {:?}", hovered_hint.id);
} }
} else {
eprintln!("No inlay hint found at hovered offset");
} }
} else {
eprintln!("No hovered offset calculated");
} }
if !go_to_definition_updated { if !go_to_definition_updated {
editor.hide_hovered_link(cx) editor.hide_hovered_link(cx)
} }
if !hover_updated { if !hover_updated {
eprintln!("No hover was updated, calling hover_at with None");
hover_popover::hover_at(editor, None, window, cx); hover_popover::hover_at(editor, None, window, cx);
} }
} }

View file

@ -48,6 +48,10 @@ pub fn hover_at(
window: &mut Window, window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) { ) {
eprintln!(
"hover_at called with anchor: {}",
if anchor.is_some() { "Some" } else { "None" }
);
if EditorSettings::get_global(cx).hover_popover_enabled { if EditorSettings::get_global(cx).hover_popover_enabled {
if show_keyboard_hover(editor, window, cx) { if show_keyboard_hover(editor, window, cx) {
return; return;
@ -110,7 +114,7 @@ pub fn find_hovered_hint_part(
let mut part_start = hint_start; let mut part_start = hint_start;
for part in label_parts { for part in label_parts {
let part_len = part.value.chars().count(); let part_len = part.value.chars().count();
if hovered_character > part_len { if hovered_character >= part_len {
hovered_character -= part_len; hovered_character -= part_len;
part_start.0 += part_len; part_start.0 += part_len;
} else { } else {
@ -128,6 +132,10 @@ pub fn hover_at_inlay(
window: &mut Window, window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) { ) {
eprintln!(
"hover_at_inlay called - inlay_id: {:?}, range: {:?}",
inlay_hover.range.inlay, inlay_hover.range.range
);
if EditorSettings::get_global(cx).hover_popover_enabled { if EditorSettings::get_global(cx).hover_popover_enabled {
if editor.pending_rename.is_some() { if editor.pending_rename.is_some() {
return; return;
@ -155,12 +163,18 @@ pub fn hover_at_inlay(
} }
let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay; let hover_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
eprintln!(
"hover_at_inlay: Creating task with {}ms delay",
hover_popover_delay
);
let task = cx.spawn_in(window, async move |this, cx| { let task = cx.spawn_in(window, async move |this, cx| {
async move { async move {
eprintln!("hover_at_inlay task: Starting delay");
cx.background_executor() cx.background_executor()
.timer(Duration::from_millis(hover_popover_delay)) .timer(Duration::from_millis(hover_popover_delay))
.await; .await;
eprintln!("hover_at_inlay task: Delay complete");
this.update(cx, |this, _| { this.update(cx, |this, _| {
this.hover_state.diagnostic_popover = None; this.hover_state.diagnostic_popover = None;
})?; })?;
@ -193,6 +207,7 @@ pub fn hover_at_inlay(
}; };
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
eprintln!("hover_at_inlay task: Setting hover popover and calling notify");
// TODO: no background highlights happen for inlays currently // TODO: no background highlights happen for inlays currently
this.hover_state.info_popovers = vec![hover_popover]; this.hover_state.info_popovers = vec![hover_popover];
cx.notify(); cx.notify();
@ -205,6 +220,7 @@ pub fn hover_at_inlay(
}); });
editor.hover_state.info_task = Some(task); editor.hover_state.info_task = Some(task);
eprintln!("hover_at_inlay: Task stored");
} }
} }
@ -212,6 +228,7 @@ pub fn hover_at_inlay(
/// Triggered by the `Hover` action when the cursor is not over a symbol or when the /// Triggered by the `Hover` action when the cursor is not over a symbol or when the
/// selections changed. /// selections changed.
pub fn hide_hover(editor: &mut Editor, cx: &mut Context<Editor>) -> bool { pub fn hide_hover(editor: &mut Editor, cx: &mut Context<Editor>) -> bool {
eprintln!("hide_hover called");
let info_popovers = editor.hover_state.info_popovers.drain(..); let info_popovers = editor.hover_state.info_popovers.drain(..);
let diagnostics_popover = editor.hover_state.diagnostic_popover.take(); let diagnostics_popover = editor.hover_state.diagnostic_popover.take();
let did_hide = info_popovers.count() > 0 || diagnostics_popover.is_some(); let did_hide = info_popovers.count() > 0 || diagnostics_popover.is_some();
@ -786,6 +803,12 @@ impl HoverState {
window: &mut Window, window: &mut Window,
cx: &mut Context<Editor>, cx: &mut Context<Editor>,
) -> Option<(DisplayPoint, Vec<AnyElement>)> { ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
let visible = self.visible();
eprintln!(
"HoverState::render - visible: {}, info_popovers: {}",
visible,
self.info_popovers.len()
);
// If there is a diagnostic, position the popovers based on that. // If there is a diagnostic, position the popovers based on that.
// Otherwise use the start of the hover range // Otherwise use the start of the hover range
let anchor = self let anchor = self
@ -809,9 +832,18 @@ impl HoverState {
}) })
})?; })?;
let point = anchor.to_display_point(&snapshot.display_snapshot); let point = anchor.to_display_point(&snapshot.display_snapshot);
eprintln!(
"HoverState::render - point: {:?}, visible_rows: {:?}",
point, visible_rows
);
// Don't render if the relevant point isn't on screen // Don't render if the relevant point isn't on screen
if !self.visible() || !visible_rows.contains(&point.row()) { if !self.visible() || !visible_rows.contains(&point.row()) {
eprintln!(
"HoverState::render - Not rendering: visible={}, point_in_range={}",
self.visible(),
visible_rows.contains(&point.row())
);
return None; return None;
} }