Compare commits
32 commits
main
...
inlay-hint
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1a31961dac | ||
![]() |
78ca73e0b9 | ||
![]() |
c70178b7ea | ||
![]() |
4df010d33a | ||
![]() |
c44f9dfb17 | ||
![]() |
7c5738142a | ||
![]() |
23c9eb875a | ||
![]() |
81578c0d9e | ||
![]() |
70918609d8 | ||
![]() |
835651fc08 | ||
![]() |
267e44e513 | ||
![]() |
7ef2a8211f | ||
![]() |
a509ae241a | ||
![]() |
0bb1a5f98a | ||
![]() |
79f376d752 | ||
![]() |
fe8b3fe53d | ||
![]() |
2cd812e54f | ||
![]() |
6adf082e43 | ||
![]() |
e4963e70cc | ||
![]() |
e7c6f228d5 | ||
![]() |
0d6232b373 | ||
![]() |
ca4df68f31 | ||
![]() |
c96b6a06f0 | ||
![]() |
b1cd20a435 | ||
![]() |
509375c83c | ||
![]() |
b6bd9c0682 | ||
![]() |
8ee82395b8 | ||
![]() |
a322aa33c7 | ||
![]() |
2ff30d20e3 | ||
![]() |
01d7b3345b | ||
![]() |
17f7312fc0 | ||
![]() |
a61e478152 |
1 changed files with 187 additions and 18 deletions
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Anchor, Editor, EditorSettings, EditorSnapshot, FindAllReferences, GoToDefinition,
|
Anchor, Editor, EditorSettings, EditorSnapshot, FindAllReferences, GoToDefinition,
|
||||||
GoToTypeDefinition, GotoDefinitionKind, InlayId, Navigated, PointForPosition, SelectPhase,
|
GoToTypeDefinition, GotoDefinitionKind, InlayId, Navigated, PointForPosition, SelectPhase,
|
||||||
|
display_map::InlayOffset,
|
||||||
editor_settings::GoToDefinitionFallback,
|
editor_settings::GoToDefinitionFallback,
|
||||||
hover_popover::{self, InlayHover},
|
hover_popover::{self, InlayHover},
|
||||||
scroll::ScrollAmount,
|
scroll::ScrollAmount,
|
||||||
|
@ -15,6 +16,7 @@ use project::{
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use text;
|
||||||
use theme::ActiveTheme as _;
|
use theme::ActiveTheme as _;
|
||||||
use util::{ResultExt, TryFutureExt as _, maybe};
|
use util::{ResultExt, TryFutureExt as _, maybe};
|
||||||
|
|
||||||
|
@ -121,13 +123,25 @@ 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);
|
||||||
if !hovered_link_modifier || self.has_pending_selection() {
|
|
||||||
|
// When you're dragging to select, and you release the drag to create the selection,
|
||||||
|
// if you happened to end over something hoverable (including an inlay hint), don't
|
||||||
|
// have the hovered link appear. That would be annoying, because all you're trying
|
||||||
|
// to do is to create a selection, not hover to see a hovered link.
|
||||||
|
if self.has_pending_selection() {
|
||||||
self.hide_hovered_link(cx);
|
self.hide_hovered_link(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match point_for_position.as_valid() {
|
match point_for_position.as_valid() {
|
||||||
Some(point) => {
|
Some(point) => {
|
||||||
|
// Hide the underline unless you're holding the modifier key on the keyboard
|
||||||
|
// which will perform a goto definition.
|
||||||
|
if !hovered_link_modifier {
|
||||||
|
self.hide_hovered_link(cx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let trigger_point = TriggerPoint::Text(
|
let trigger_point = TriggerPoint::Text(
|
||||||
snapshot
|
snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
|
@ -319,6 +333,7 @@ pub fn update_inlay_link_and_hover_points(
|
||||||
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) {
|
||||||
|
// Check if we should process this hint for hover
|
||||||
match cached_hint.resolve_state {
|
match cached_hint.resolve_state {
|
||||||
ResolveState::CanResolve(_, _) => {
|
ResolveState::CanResolve(_, _) => {
|
||||||
if let Some(buffer_id) = snapshot
|
if let Some(buffer_id) = snapshot
|
||||||
|
@ -419,31 +434,46 @@ pub fn update_inlay_link_and_hover_points(
|
||||||
);
|
);
|
||||||
hover_updated = true;
|
hover_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((language_server_id, location)) =
|
if let Some((language_server_id, location)) =
|
||||||
hovered_hint_part.location
|
hovered_hint_part.location
|
||||||
&& secondary_held
|
|
||||||
&& !editor.has_pending_nonempty_selection()
|
|
||||||
{
|
{
|
||||||
go_to_definition_updated = true;
|
// Now perform the "Go to Definition" flow to get hover documentation
|
||||||
show_link_definition(
|
if let Some(project) = editor.project.clone() {
|
||||||
shift_held,
|
let highlight = highlight.clone();
|
||||||
editor,
|
let hint_value = hovered_hint_part.value.clone();
|
||||||
TriggerPoint::InlayHint(
|
let location = location.clone();
|
||||||
highlight,
|
|
||||||
location,
|
get_docs_then_show_hover(
|
||||||
language_server_id,
|
window, cx, highlight, hint_value, location,
|
||||||
),
|
project,
|
||||||
snapshot,
|
);
|
||||||
window,
|
}
|
||||||
cx,
|
|
||||||
);
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
ResolveState::Resolving => {}
|
ResolveState::Resolving => {}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,6 +486,145 @@ pub fn update_inlay_link_and_hover_points(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_docs_then_show_hover(
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<'_, Editor>,
|
||||||
|
highlight: InlayHighlight,
|
||||||
|
hint_value: String,
|
||||||
|
location: lsp::Location,
|
||||||
|
project: Entity<Project>,
|
||||||
|
) {
|
||||||
|
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?;
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
|
}
|
||||||
|
.log_err()
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn show_link_definition(
|
pub fn show_link_definition(
|
||||||
shift_held: bool,
|
shift_held: bool,
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue