diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d4cd126f32..3f0aa887ef 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1222,6 +1222,7 @@ impl EditorElement { // Don't trigger hover popover if mouse is hovering over context menu if text_hovered { + eprintln!("mouse_moved: text_hovered=true, calling update_hovered_link"); editor.update_hovered_link( point_for_position, &position_map.snapshot, @@ -1235,9 +1236,11 @@ impl EditorElement { .snapshot .buffer_snapshot .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); Self::update_visible_cursor(editor, point, position_map, window, cx); } else { + eprintln!("mouse_moved: Invalid position (inlay?), NOT calling hover_at"); // Don't call hover_at with None when we're over an inlay // The inlay hover is already handled by update_hovered_link } diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 02f93e6829..f13036ca14 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,8 @@ 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 should not be reached anymore as we handle it above + unreachable!("Invalid position should have been handled earlier"); } } } @@ -284,7 +293,10 @@ pub fn update_inlay_link_and_hover_points( window: &mut Window, cx: &mut Context, ) { + 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 { + // 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)) } else { None @@ -301,27 +313,69 @@ pub fn update_inlay_link_and_hover_points( point_for_position.next_valid.to_point(snapshot), Bias::Right, ); - if let Some(hovered_hint) = editor - .visible_inlay_hints(cx) + eprintln!( + "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() .skip_while(|hint| { - hint.position + let cmp = hint + .position .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| { - hint.position + let cmp = hint + .position .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) { + eprintln!("Found hovered inlay hint: {:?}", hovered_hint.id); 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 { + 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(_, _) => { + // For unresolved hints, spawn resolution 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( buffer_id, excerpt_id, @@ -329,128 +383,191 @@ pub fn update_inlay_link_and_hover_points( window, cx, ); + // Don't clear hover while resolution is starting + hover_updated = true; } + false // Don't process unresolved hints } - 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; + ResolveState::Resolved => true, + ResolveState::Resolving => { + // Don't clear hover while resolving + hover_updated = true; + false // Don't process further + } + }; + + 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 { - extra_shift_right += 1; - } - match cached_hint.label { - project::InlayHintLabel::String(_) => { - if let Some(tooltip) = cached_hint.tooltip { + project::InlayHintLabel::LabelParts(label_parts) => { + eprintln!( + "Processing LabelParts hint with {} parts", + label_parts.len() + ); + 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( 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; + } 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, 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, - ); - } } } + } 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 { editor.hide_hovered_link(cx) } if !hover_updated { + eprintln!("No hover was updated, calling hover_at with None"); hover_popover::hover_at(editor, None, window, cx); } } diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index cae4789535..14f81522c0 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -48,6 +48,10 @@ pub fn hover_at( window: &mut Window, cx: &mut Context, ) { + eprintln!( + "hover_at called with anchor: {}", + if anchor.is_some() { "Some" } else { "None" } + ); if EditorSettings::get_global(cx).hover_popover_enabled { if show_keyboard_hover(editor, window, cx) { return; @@ -110,7 +114,7 @@ pub fn find_hovered_hint_part( let mut part_start = hint_start; for part in label_parts { let part_len = part.value.chars().count(); - if hovered_character > part_len { + if hovered_character >= part_len { hovered_character -= part_len; part_start.0 += part_len; } else { @@ -128,6 +132,10 @@ pub fn hover_at_inlay( window: &mut Window, cx: &mut Context, ) { + 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 editor.pending_rename.is_some() { return; @@ -155,12 +163,18 @@ pub fn hover_at_inlay( } 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| { async move { + eprintln!("hover_at_inlay task: Starting delay"); cx.background_executor() .timer(Duration::from_millis(hover_popover_delay)) .await; + eprintln!("hover_at_inlay task: Delay complete"); this.update(cx, |this, _| { this.hover_state.diagnostic_popover = None; })?; @@ -193,6 +207,7 @@ pub fn hover_at_inlay( }; this.update(cx, |this, cx| { + eprintln!("hover_at_inlay task: Setting hover popover and calling notify"); // TODO: no background highlights happen for inlays currently this.hover_state.info_popovers = vec![hover_popover]; cx.notify(); @@ -205,6 +220,7 @@ pub fn hover_at_inlay( }); 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 /// selections changed. pub fn hide_hover(editor: &mut Editor, cx: &mut Context) -> bool { + eprintln!("hide_hover called"); let info_popovers = editor.hover_state.info_popovers.drain(..); let diagnostics_popover = editor.hover_state.diagnostic_popover.take(); let did_hide = info_popovers.count() > 0 || diagnostics_popover.is_some(); @@ -786,6 +803,12 @@ impl HoverState { window: &mut Window, cx: &mut Context, ) -> Option<(DisplayPoint, Vec)> { + 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. // Otherwise use the start of the hover range let anchor = self @@ -809,9 +832,18 @@ impl HoverState { }) })?; 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 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; }