From 6f4bfb5875377a52cd9bb35c324258ea0d23cbb5 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Fri, 5 Sep 2025 21:01:56 +0200 Subject: [PATCH] New plan, call hover_docs on the type where the inlay hint points to Co-authored-by: Richard Feldman --- crates/editor/src/hover_links.rs | 168 +++++++++++-------------------- 1 file changed, 57 insertions(+), 111 deletions(-) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 9971fcc66a541bcb1d5f883d88ebd0fd6738e04d..e5cb36c83050276d0782bb95901deeebf7a51d8d 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -1,7 +1,6 @@ use crate::{ Anchor, Editor, EditorSettings, EditorSnapshot, FindAllReferences, GoToDefinition, GoToTypeDefinition, GotoDefinitionKind, InlayId, Navigated, PointForPosition, SelectPhase, - display_map::InlayOffset, editor_settings::GoToDefinitionFallback, hover_popover::{self, InlayHover}, scroll::ScrollAmount, @@ -16,7 +15,7 @@ use project::{ }; use settings::Settings; use std::ops::Range; -use text; +use text::{self, Point}; use theme::ActiveTheme as _; use util::{ResultExt, TryFutureExt as _, maybe}; @@ -445,8 +444,8 @@ pub fn update_inlay_link_and_hover_points( let location = location.clone(); get_docs_then_show_hover( - window, cx, highlight, hint_value, location, - project, + editor, window, cx, highlight, hint_value, + location, project, ); } @@ -486,7 +485,20 @@ pub fn update_inlay_link_and_hover_points( } } +/// todo dvdsk: This extracts doc comments and shows them, a fine fallback +/// though maybe we should instead alsk the LSP for info on the type?. Lets figure +/// out what normal hoverdocs do. +/// +/// issue with Entity. Needs one more (expensive) step before rendering +/// which we need to cache. +/// +/// --- PLAN --- +/// - use location to call hover_popover and extract the text +/// - then use claude written 'existing' hover method to see if that works +/// - try use or adapt InfoPopover to get scrollbar/nice rendering (note issue above) +/// - get the unwraps and as_refs out of here fn get_docs_then_show_hover( + editor: &mut Editor, window: &mut Window, cx: &mut Context<'_, Editor>, highlight: InlayHighlight, @@ -494,128 +506,62 @@ fn get_docs_then_show_hover( location: lsp::Location, project: Entity, ) { + let provider = editor.semantics_provider.clone().unwrap(); cx.spawn_in(window, async move |editor, cx| { async move { - // Small delay to show the loading message first - cx.background_executor() - .timer(std::time::Duration::from_millis(50)) - .await; - // Convert LSP URL to file path let file_path = location .uri .to_file_path() .map_err(|_| anyhow::anyhow!("Invalid file URL"))?; - // Open the definition file let definition_buffer = project .update(cx, |project, cx| project.open_local_buffer(file_path, cx))? .await?; + let location = Point::new(location.range.start.line, location.range.start.character); + let buffer_position = definition_buffer + .update(cx, |buffer, _| buffer.snapshot().anchor_after(location)) + .unwrap(); - // Extract documentation directly from the source - let documentation = definition_buffer.update(cx, |buffer, _| { - let line_number = location.range.start.line as usize; - - // Get the text of the buffer - let text = buffer.text(); - let lines: Vec<&str> = text.lines().collect(); - - // Look backwards from the definition line to find doc comments - let mut doc_lines = Vec::new(); - let mut current_line = line_number.saturating_sub(1); - - // Skip any attributes like #[derive(...)] - while current_line > 0 - && lines.get(current_line).map_or(false, |line| { - let trimmed = line.trim(); - trimmed.starts_with("#[") || trimmed.is_empty() - }) - { - current_line = current_line.saturating_sub(1); - } - - // Collect doc comments - while current_line > 0 { - if let Some(line) = lines.get(current_line) { - let trimmed = line.trim(); - if trimmed.starts_with("///") { - // Remove the /// and any leading space - let doc_text = trimmed - .strip_prefix("///") - .unwrap_or("") - .strip_prefix(" ") - .unwrap_or_else(|| trimmed.strip_prefix("///").unwrap_or("")); - doc_lines.push(doc_text.to_string()); - } else if !trimmed.is_empty() { - // Stop at the first non-doc, non-empty line - break; - } - } - current_line = current_line.saturating_sub(1); - } - - // Reverse to get correct order - doc_lines.reverse(); - - // Also get the actual definition line - let definition = lines - .get(line_number) - .map(|s| s.trim().to_string()) - .unwrap_or_else(|| hint_value.clone()); - - if doc_lines.is_empty() { - None - } else { - let docs = doc_lines.join("\n"); - Some((definition, docs)) - } - })?; + // debounce the lsp request + cx.background_executor() + .timer(std::time::Duration::from_millis(50)) + .await; + let hover_request = + cx.update(|_, cx| provider.hover(&definition_buffer, buffer_position, cx))?; + let hovers_response = if let Some(hover_request) = hover_request { + hover_request.await.unwrap_or_default() + } else { + Vec::new() + }; - if let Some((definition, docs)) = documentation { - // Format as markdown with the definition as a code block - let formatted_docs = format!("```rust\n{}\n```\n\n{}", definition, docs); + let text = hovers_response + .first() + .as_ref() + .unwrap() + .contents + .first() + .as_ref() + .unwrap() + .text + .clone(); - editor - .update_in(cx, |editor, window, cx| { - hover_popover::hover_at_inlay( - editor, - InlayHover { - tooltip: HoverBlock { - text: formatted_docs, - kind: HoverBlockKind::Markdown, - }, - range: highlight, - }, - window, - cx, - ); - }) - .log_err(); - } else { - // Fallback to showing just the location info - let fallback_text = format!( - "{}\n\nDefined in at line {}", - hint_value.trim(), - // filename, // TODO - location.range.start.line + 1 - ); - editor - .update_in(cx, |editor, window, cx| { - hover_popover::hover_at_inlay( - editor, - InlayHover { - tooltip: HoverBlock { - text: fallback_text, - kind: HoverBlockKind::PlainText, - }, - range: highlight, + editor + .update_in(cx, |editor, window, cx| { + hover_popover::hover_at_inlay( + editor, + InlayHover { + tooltip: HoverBlock { + text, + kind: HoverBlockKind::Markdown, }, - window, - cx, - ); - }) - .log_err(); - } + range: highlight, + }, + window, + cx, + ); + }) + .log_err(); anyhow::Ok(()) }