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

Richard Feldman created

Change summary

crates/editor/src/element.rs       |   3 
crates/editor/src/hover_links.rs   | 323 +++++++++++++++++++++----------
crates/editor/src/hover_popover.rs |  34 +++
3 files changed, 256 insertions(+), 104 deletions(-)

Detailed changes

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
             }

crates/editor/src/hover_links.rs 🔗

@@ -121,6 +121,22 @@ impl Editor {
         cx: &mut Context<Self>,
     ) {
         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<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 {
+        // 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;
-                        }
-                        if cached_hint.padding_right {
-                            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;
+                            }
                         }
-                        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);
     }
 }

crates/editor/src/hover_popover.rs 🔗

@@ -48,6 +48,10 @@ pub fn hover_at(
     window: &mut Window,
     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 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<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 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<Editor>) -> 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<Editor>,
     ) -> 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.
         // 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;
         }