Remove flashed black circle

This commit is contained in:
Richard Feldman 2025-07-10 17:54:22 -04:00
parent c96b6a06f0
commit ca4df68f31
No known key found for this signature in database

View file

@ -337,15 +337,15 @@ pub fn update_inlay_link_and_hover_points(
window,
cx,
);
// Don't clear hover while resolution is starting
hover_updated = true;
// 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 clear hover while resolving
hover_updated = true;
// Don't set hover_updated during resolution to prevent empty tooltip
// hover_updated = true;
false // Don't process further
}
};
@ -448,195 +448,159 @@ pub fn update_inlay_link_and_hover_points(
.unwrap_or("unknown")
.to_string();
// Prepare data needed for the async task
let project = editor.project.clone().unwrap();
let hint_value = part.value.clone();
let highlight = highlight.clone();
let filename = filename.clone();
hover_popover::hover_at_inlay(
editor,
InlayHover {
tooltip: HoverBlock {
text: "Loading documentation...".to_string(),
kind: HoverBlockKind::PlainText,
},
range: highlight.clone(),
},
window,
cx,
);
hover_updated = true;
// Spawn async task to fetch documentation
cx.spawn_in(window, async move |editor, cx| {
// Convert LSP URL to file path
let file_path = location.uri.to_file_path()
.map_err(|_| anyhow::anyhow!("Invalid file URL"))?;
// Now perform the "Go to Definition" flow to get hover documentation
if let Some(project) = editor.project.clone() {
let highlight = highlight.clone();
let hint_value = part.value.clone();
let location_uri = location.uri.clone();
// Open the definition file
let definition_buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(file_path, cx)
})?
.await?;
cx.spawn_in(window, async move |editor, cx| {
async move {
eprintln!("Starting async documentation fetch for {}", hint_value);
// Register the buffer with language servers
let _lsp_handle = project.update(cx, |project, cx| {
project.register_buffer_with_language_servers(&definition_buffer, cx)
})?;
// Small delay to show the loading message first
cx.background_executor()
.timer(std::time::Duration::from_millis(50))
.await;
// Give LSP a moment to process the didOpen notification
cx.background_executor()
.timer(std::time::Duration::from_millis(100))
.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);
// Try to get hover documentation from LSP
let hover_position = location.range.start;
// 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");
// Convert LSP position to a point
let hover_point = definition_buffer.update(cx, |buffer, _| {
let point_utf16 = point_from_lsp(hover_position);
let snapshot = buffer.snapshot();
let point = snapshot.clip_point_utf16(point_utf16, Bias::Left);
snapshot.anchor_after(point)
})?;
// 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);
let hover_response = project
.update(cx, |project, cx| {
project.hover(&definition_buffer, hover_point, cx)
})?
.await;
// Get the text of the buffer
let text = buffer.text();
let lines: Vec<&str> = text.lines().collect();
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();
// 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);
// 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");
}
// 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);
}
}
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,
// 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());
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(
editor,
InlayHover {
tooltip: HoverBlock {
text: formatted_docs,
kind: HoverBlockKind::Markdown,
},
range: highlight,
},
range: highlight,
},
window,
cx,
window,
cx,
);
}).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 {}",
hint_value.trim(),
filename,
location.range.start.line + 1
);
}).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;
// 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;
}
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();
}
current_line = current_line.saturating_sub(1);
eprintln!("Documentation fetch complete");
anyhow::Ok(())
}
// 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,
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(())
}).detach();
.log_err()
.await
}).detach();
}
}
if let Some((language_server_id, location)) = &part.location {