fix: improve LSP hover to show function info when cursor is on fn keyword
When hovering on declaration keywords (fn, type, effect, let, trait), look ahead to find the declaration name and show that symbol's full info from the symbol table instead of generic keyword documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
73
src/lsp.rs
73
src/lsp.rs
@@ -356,12 +356,57 @@ impl LspServer {
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to hardcoded info
|
||||
|
||||
// Extract the word at the cursor position
|
||||
// Fall back: try looking up the declaration name when hovering on keywords
|
||||
let word = self.get_word_at_position(source, position)?;
|
||||
|
||||
// Look up rich documentation for known symbols
|
||||
// When hovering on a keyword like 'fn', 'type', 'effect', 'let', 'trait',
|
||||
// look ahead to find the declaration name and show that symbol's info
|
||||
if let Some(ref table) = doc.symbol_table {
|
||||
let decl_name = match word.as_str() {
|
||||
"fn" | "type" | "effect" | "let" | "trait" | "handler" | "impl" => {
|
||||
let offset = self.position_to_offset(source, position);
|
||||
self.find_next_ident(source, offset + word.len())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(name) = decl_name {
|
||||
// Look up the declaration name in the symbol table
|
||||
for sym in table.global_symbols() {
|
||||
if sym.name == name {
|
||||
let signature = sym.type_signature.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(&sym.name);
|
||||
let kind_str = match sym.kind {
|
||||
SymbolKind::Function => "function",
|
||||
SymbolKind::Variable => "variable",
|
||||
SymbolKind::Parameter => "parameter",
|
||||
SymbolKind::Type => "type",
|
||||
SymbolKind::TypeParameter => "type parameter",
|
||||
SymbolKind::Variant => "variant",
|
||||
SymbolKind::Effect => "effect",
|
||||
SymbolKind::EffectOperation => "effect operation",
|
||||
SymbolKind::Field => "field",
|
||||
SymbolKind::Module => "module",
|
||||
};
|
||||
let doc_str = sym.documentation.as_ref()
|
||||
.map(|d| format!("\n\n{}", d))
|
||||
.unwrap_or_default();
|
||||
let formatted_sig = format_signature_for_hover(signature);
|
||||
let property_docs = extract_property_docs(signature);
|
||||
|
||||
return Some(Hover {
|
||||
contents: HoverContents::Markup(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: format!("```lux\n{}\n```\n\n*{}*{}{}", formatted_sig, kind_str, property_docs, doc_str),
|
||||
}),
|
||||
range: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback: rich documentation for keywords
|
||||
let info = self.get_rich_symbol_info(&word)
|
||||
.or_else(|| self.get_symbol_info(&word).map(|(s, d)| (s.to_string(), d.to_string())));
|
||||
|
||||
@@ -402,6 +447,26 @@ impl LspServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the next identifier in source after the given offset (skipping whitespace)
|
||||
fn find_next_ident(&self, source: &str, start: usize) -> Option<String> {
|
||||
let chars: Vec<char> = source.chars().collect();
|
||||
let mut pos = start;
|
||||
// Skip whitespace
|
||||
while pos < chars.len() && (chars[pos] == ' ' || chars[pos] == '\t' || chars[pos] == '\n' || chars[pos] == '\r') {
|
||||
pos += 1;
|
||||
}
|
||||
// Collect identifier
|
||||
let ident_start = pos;
|
||||
while pos < chars.len() && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
|
||||
pos += 1;
|
||||
}
|
||||
if pos > ident_start {
|
||||
Some(chars[ident_start..pos].iter().collect())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_symbol_info(&self, word: &str) -> Option<(&'static str, &'static str)> {
|
||||
match word {
|
||||
// Keywords
|
||||
|
||||
Reference in New Issue
Block a user