Add a hover when hovering over inlays
This commit is contained in:
parent
ca4df68f31
commit
0d6232b373
3 changed files with 94 additions and 32 deletions
|
@ -1238,8 +1238,8 @@ impl EditorElement {
|
|||
hover_at(editor, Some(anchor), window, cx);
|
||||
Self::update_visible_cursor(editor, point, position_map, window, cx);
|
||||
} else {
|
||||
// Don't call hover_at with None when we're over an inlay
|
||||
// The inlay hover is already handled by update_hovered_link
|
||||
// When over an inlay or invalid position, clear any existing hover
|
||||
hover_at(editor, None, window, cx);
|
||||
}
|
||||
} else {
|
||||
editor.hide_hovered_link(cx);
|
||||
|
|
|
@ -3,11 +3,11 @@ use crate::{
|
|||
GoToTypeDefinition, GotoDefinitionKind, InlayId, Navigated, PointForPosition, SelectPhase,
|
||||
display_map::InlayOffset,
|
||||
editor_settings::GoToDefinitionFallback,
|
||||
hover_popover::{self, InlayHover},
|
||||
hover_popover::{self, HoverState, InlayHover},
|
||||
scroll::ScrollAmount,
|
||||
};
|
||||
use gpui::{App, AsyncWindowContext, Context, Entity, Modifiers, Task, Window, px};
|
||||
use language::{Bias, ToOffset, point_from_lsp};
|
||||
use language::{Bias, ToOffset};
|
||||
use linkify::{LinkFinder, LinkKind};
|
||||
use lsp::LanguageServerId;
|
||||
use project::{
|
||||
|
@ -18,6 +18,7 @@ use settings::Settings;
|
|||
use std::ops::Range;
|
||||
use text;
|
||||
use theme::ActiveTheme as _;
|
||||
|
||||
use util::{ResultExt, TryFutureExt as _, maybe};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -164,6 +165,7 @@ impl Editor {
|
|||
pub(crate) fn hide_hovered_link(&mut self, cx: &mut Context<Self>) {
|
||||
self.hovered_link_state.take();
|
||||
self.clear_highlights::<HoveredLinkState>(cx);
|
||||
self.clear_background_highlights::<HoverState>(cx);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_click_hovered_link(
|
||||
|
@ -337,15 +339,11 @@ pub fn update_inlay_link_and_hover_points(
|
|||
window,
|
||||
cx,
|
||||
);
|
||||
// Don't set hover_updated during resolution to prevent empty tooltip
|
||||
// hover_updated = true;
|
||||
}
|
||||
false // Don't process unresolved hints
|
||||
}
|
||||
ResolveState::Resolved => true,
|
||||
ResolveState::Resolving => {
|
||||
// Don't set hover_updated during resolution to prevent empty tooltip
|
||||
// hover_updated = true;
|
||||
false // Don't process further
|
||||
}
|
||||
};
|
||||
|
@ -466,36 +464,96 @@ pub fn update_inlay_link_and_hover_points(
|
|||
if let Some(project) = editor.project.clone() {
|
||||
let highlight = highlight.clone();
|
||||
let hint_value = part.value.clone();
|
||||
let location_uri = location.uri.clone();
|
||||
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
async move {
|
||||
eprintln!("Starting async documentation fetch for {}", hint_value);
|
||||
|
||||
// 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
|
||||
eprintln!("Converting LSP URI to file path: {}", location_uri);
|
||||
let file_path = location.uri.to_file_path()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid file URL"))?;
|
||||
eprintln!("File path: {:?}", file_path);
|
||||
|
||||
// Open the definition file
|
||||
eprintln!("Opening definition file via project.open_local_buffer");
|
||||
let definition_buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer(file_path, cx)
|
||||
})?
|
||||
.await?;
|
||||
eprintln!("Successfully opened definition buffer");
|
||||
|
||||
// Extract documentation directly from the source
|
||||
// Register the buffer with language servers
|
||||
let _lsp_handle = project.update(cx, |project, cx| {
|
||||
project.register_buffer_with_language_servers(&definition_buffer, cx)
|
||||
})?;
|
||||
|
||||
// Give LSP a moment to process the didOpen notification
|
||||
cx.background_executor()
|
||||
.timer(std::time::Duration::from_millis(100))
|
||||
.await;
|
||||
|
||||
// Try to get hover documentation from LSP
|
||||
let hover_position = location.range.start;
|
||||
|
||||
// Convert LSP position to a point
|
||||
let hover_point = definition_buffer.update(cx, |buffer, _| {
|
||||
let point_utf16 = language::point_from_lsp(hover_position);
|
||||
let snapshot = buffer.snapshot();
|
||||
let point = snapshot.clip_point_utf16(point_utf16, Bias::Left);
|
||||
snapshot.anchor_after(point)
|
||||
})?;
|
||||
|
||||
let hover_response = project
|
||||
.update(cx, |project, cx| {
|
||||
project.hover(&definition_buffer, hover_point, cx)
|
||||
})?
|
||||
.await;
|
||||
|
||||
if !hover_response.is_empty() {
|
||||
// Get the first hover response
|
||||
let hover = &hover_response[0];
|
||||
if !hover.contents.is_empty() {
|
||||
// Format the hover blocks as markdown
|
||||
let mut formatted_docs = String::new();
|
||||
|
||||
// Add the type signature first
|
||||
formatted_docs.push_str(&format!("```rust\n{}\n```\n\n", hint_value.trim()));
|
||||
|
||||
// Add all the hover content
|
||||
for block in &hover.contents {
|
||||
match &block.kind {
|
||||
HoverBlockKind::Markdown => {
|
||||
formatted_docs.push_str(&block.text);
|
||||
formatted_docs.push_str("\n\n");
|
||||
}
|
||||
HoverBlockKind::Code { language } => {
|
||||
formatted_docs.push_str(&format!("```{}\n{}\n```\n\n", language, block.text));
|
||||
}
|
||||
HoverBlockKind::PlainText => {
|
||||
formatted_docs.push_str(&block.text);
|
||||
formatted_docs.push_str("\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
hover_popover::hover_at_inlay(
|
||||
editor,
|
||||
InlayHover {
|
||||
tooltip: HoverBlock {
|
||||
text: formatted_docs.trim().to_string(),
|
||||
kind: HoverBlockKind::Markdown,
|
||||
},
|
||||
range: highlight,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}).log_err();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Extract documentation directly from the source
|
||||
let documentation = definition_buffer.update(cx, |buffer, _| {
|
||||
let line_number = location.range.start.line as usize;
|
||||
eprintln!("Looking for documentation at line {}", line_number);
|
||||
|
||||
// Get the text of the buffer
|
||||
let text = buffer.text();
|
||||
|
@ -538,25 +596,20 @@ pub fn update_inlay_link_and_hover_points(
|
|||
.map(|s| s.trim().to_string())
|
||||
.unwrap_or_else(|| hint_value.clone());
|
||||
|
||||
eprintln!("Found {} doc lines", doc_lines.len());
|
||||
|
||||
if doc_lines.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let docs = doc_lines.join("\n");
|
||||
eprintln!("Extracted docs: {}", docs.chars().take(100).collect::<String>());
|
||||
Some((definition, docs))
|
||||
}
|
||||
})?;
|
||||
|
||||
if let Some((definition, docs)) = documentation {
|
||||
eprintln!("Got documentation from source!");
|
||||
|
||||
// Format as markdown with the definition as a code block
|
||||
let formatted_docs = format!("```rust\n{}\n```\n\n{}", definition, docs);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
hover_popover::hover_at_inlay(
|
||||
hover_popover::hover_at_inlay(
|
||||
editor,
|
||||
InlayHover {
|
||||
tooltip: HoverBlock {
|
||||
|
@ -570,7 +623,6 @@ pub fn update_inlay_link_and_hover_points(
|
|||
);
|
||||
}).log_err();
|
||||
} else {
|
||||
eprintln!("No documentation found in source, falling back to location info");
|
||||
// Fallback to showing just the location info
|
||||
let fallback_text = format!(
|
||||
"{}\n\nDefined in {} at line {}",
|
||||
|
@ -594,7 +646,6 @@ pub fn update_inlay_link_and_hover_points(
|
|||
}).log_err();
|
||||
}
|
||||
|
||||
eprintln!("Documentation fetch complete");
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err()
|
||||
|
@ -641,6 +692,7 @@ pub fn update_inlay_link_and_hover_points(
|
|||
}
|
||||
if !hover_updated {
|
||||
hover_popover::hover_at(editor, None, window, cx);
|
||||
editor.clear_background_highlights::<HoverState>(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
ActiveDiagnostic, Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings,
|
||||
EditorSnapshot, GlobalDiagnosticRenderer, Hover,
|
||||
EditorSnapshot, GlobalDiagnosticRenderer, HighlightStyle, Hover,
|
||||
display_map::{InlayOffset, ToDisplayPoint, invisibles::is_invisible},
|
||||
hover_links::{InlayHighlight, RangeInEditor},
|
||||
scroll::ScrollAmount,
|
||||
|
@ -193,7 +193,17 @@ pub fn hover_at_inlay(
|
|||
};
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
// TODO: no background highlights happen for inlays currently
|
||||
// Highlight the inlay using background highlighting
|
||||
let highlight_range = inlay_hover.range.clone();
|
||||
this.highlight_inlays::<HoverState>(
|
||||
vec![highlight_range],
|
||||
HighlightStyle {
|
||||
background_color: Some(cx.theme().colors().element_hover),
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
this.hover_state.info_popovers = vec![hover_popover];
|
||||
cx.notify();
|
||||
})?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue