Add documentation to hovers

This commit is contained in:
Sofia 2025-08-14 16:52:45 +03:00
parent 6dccab8b12
commit dcb4e76a40
2 changed files with 84 additions and 38 deletions

View File

@ -193,6 +193,33 @@ impl AnalysisState {
}
}
pub fn set_documentation(&mut self, token_idx: usize, documentation: Option<String>) {
if let Some(documentation) = documentation {
if let Some(token) = self.map.get_mut(&token_idx) {
if let Some(hover) = &mut token.hover {
hover.documentation = Some(documentation);
} else {
token.hover = Some(Hover {
documentation: Some(documentation),
kind: None,
});
}
} else {
self.map.insert(
token_idx,
SemanticToken {
hover: Some(Hover {
documentation: Some(documentation),
kind: None,
}),
autocomplete: Vec::new(),
symbol: Default::default(),
},
);
}
}
}
pub fn set_symbol(&mut self, idx: usize, symbol: SymbolId) {
self.symbol_to_token.insert(symbol, idx);
if let Some(token) = self.map.get_mut(&idx) {
@ -246,8 +273,8 @@ pub struct AnalysisScope<'a> {
tokens: &'a Vec<FullToken>,
variables: HashMap<String, SymbolId>,
types: HashMap<TypeKind, (SourceModuleId, SymbolId)>,
functions: HashMap<String, (SourceModuleId, SymbolId)>,
associated_functions: HashMap<(TypeKind, String), (SourceModuleId, SymbolId)>,
functions: HashMap<String, (SourceModuleId, SymbolId, Option<String>)>,
associated_functions: HashMap<(TypeKind, String), (SourceModuleId, SymbolId, Option<String>)>,
map: &'a StateMap,
}
@ -646,9 +673,10 @@ pub fn analyze_context(
if source_id != module.module_id {
if let Some(state) = map.get(&source_id) {
if let Some(symbol) = state.associated_functions.get(&(ty.clone(), function.name.clone())) {
scope
.associated_functions
.insert((ty.clone(), function.name.clone()), (source_id, *symbol));
scope.associated_functions.insert(
(ty.clone(), function.name.clone()),
(source_id, *symbol, function.documentation.clone()),
);
}
}
continue;
@ -664,9 +692,10 @@ pub fn analyze_context(
.state
.associated_functions
.insert((ty.clone(), function.name.clone()), symbol);
scope
.associated_functions
.insert((ty.clone(), function.name.clone()), (module.module_id, symbol));
scope.associated_functions.insert(
(ty.clone(), function.name.clone()),
(module.module_id, symbol, function.documentation.clone()),
);
}
for (_, function) in &module.associated_functions {
@ -692,7 +721,10 @@ pub fn analyze_context(
if source_id != module.module_id {
if let Some(state) = map.get(&source_id) {
if let Some(symbol) = state.functions.get(&function.name) {
scope.functions.insert(function.name.clone(), (source_id, *symbol));
scope.functions.insert(
function.name.clone(),
(source_id, *symbol, function.documentation.clone()),
);
}
}
continue;
@ -702,7 +734,7 @@ pub fn analyze_context(
scope.state.init_hover(
&function.signature(),
Some(HoverKind::Type(function.return_type.clone())),
None,
function.documentation.clone(),
);
let idx = scope
@ -711,9 +743,10 @@ pub fn analyze_context(
let function_symbol = scope.state.new_symbol(idx, SemanticKind::Function);
scope.state.set_symbol(idx, function_symbol);
scope.state.functions.insert(function.name.clone(), function_symbol);
scope
.functions
.insert(function.name.clone(), (module.module_id, function_symbol));
scope.functions.insert(
function.name.clone(),
(module.module_id, function_symbol, function.documentation.clone()),
);
}
for function in &module.functions {
@ -777,10 +810,12 @@ pub fn analyze_context(
}
}
let symbol = if let Some((source_id, symbol_id)) = scope.functions.get(&import_name) {
scope
let symbol = if let Some((source_id, symbol_id, doc)) = scope.functions.get(&import_name) {
let symbol_id = scope
.state
.new_symbol(import_idx, SemanticKind::Reference(*source_id, *symbol_id))
.new_symbol(import_idx, SemanticKind::Reference(*source_id, *symbol_id));
scope.state.set_documentation(import_idx, doc.clone());
symbol_id
} else if let Some(module_source) = scope.map.values().find(|s| s.module_name == *module_name) {
if let Some((source_id, symbol_id)) = scope.types.get(&TypeKind::CustomType(CustomTypeKey(
import_name.clone(),
@ -1070,10 +1105,12 @@ pub fn analyze_expr(
let idx = scope
.token_idx(&meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(meta.range.end);
let symbol = if let Some((module_id, symbol_id)) = scope.functions.get(name) {
scope
let symbol = if let Some((module_id, symbol_id, doc)) = scope.functions.get(name) {
let symbol = scope
.state
.new_symbol(idx, SemanticKind::Reference(*module_id, *symbol_id))
.new_symbol(idx, SemanticKind::Reference(*module_id, *symbol_id));
scope.state.set_documentation(idx, doc.clone());
symbol
} else {
scope.state.new_symbol(idx, SemanticKind::Function)
};
@ -1106,12 +1143,14 @@ pub fn analyze_expr(
let fn_idx = scope
.token_idx(&meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(meta.range.end);
let fn_symbol = if let Some((module_id, symbol_id)) =
let fn_symbol = if let Some((module_id, symbol_id, doc)) =
scope.associated_functions.get(&(invoked_ty.clone(), name.clone()))
{
scope
let symbol = scope
.state
.new_symbol(fn_idx, SemanticKind::Reference(*module_id, *symbol_id))
.new_symbol(fn_idx, SemanticKind::Reference(*module_id, *symbol_id));
scope.state.set_documentation(fn_idx, doc.clone());
symbol
} else {
scope.state.new_symbol(fn_idx, SemanticKind::Function)
};

View File

@ -12,12 +12,12 @@ use tower_lsp::lsp_types::{
self, CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic,
DiagnosticSeverity, DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams,
DocumentFilter, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, Location, MarkupContent,
MarkupKind, MessageType, OneOf, Range, ReferenceParams, RenameParams, SemanticToken, SemanticTokensLegend,
SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult, SemanticTokensServerCapabilities,
ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit, Url, WorkspaceEdit,
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, Location, MarkedString,
MarkupContent, MarkupKind, MessageType, OneOf, Range, ReferenceParams, RenameParams, SemanticToken,
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult,
SemanticTokensServerCapabilities, ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions,
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit,
Url, WorkspaceEdit, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
};
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
@ -199,7 +199,7 @@ impl LanguageServer for Backend {
None
};
let (range, ty) = if let Some((idx, token)) = token {
let (range, ty, documentation) = if let Some((idx, token)) = token {
if let Some(analysis) = self.analysis.get(&path).unwrap().state.map.get(&idx) {
let start = token.position;
let end = token.position.add(token.token.len() as u32);
@ -216,7 +216,9 @@ impl LanguageServer for Backend {
if let Some(hover) = analysis.hover.clone() {
if let Some(kind) = hover.kind {
match kind {
analysis::HoverKind::Type(type_kind) => (Some(range), format!("{}", type_kind)),
analysis::HoverKind::Type(type_kind) => {
(Some(range), format!("{}", type_kind), hover.documentation)
}
analysis::HoverKind::Function(name, function_params, return_type) => (
Some(range),
format!(
@ -229,25 +231,30 @@ impl LanguageServer for Backend {
.join(", "),
return_type
),
hover.documentation,
),
}
} else {
(Some(range), String::from("No type"))
(Some(range), String::from("No type"), hover.documentation)
}
} else {
(Some(range), String::from("No hover"))
(Some(range), String::from("No hover"), None)
}
} else {
(None, String::from("no type"))
(None, String::from("no type"), None)
}
} else {
(None, String::from("no token"))
(None, String::from("no token"), None)
};
let contents = HoverContents::Markup(MarkupContent {
let contents = if let Some(doc) = documentation {
HoverContents::Array(vec![MarkedString::String(doc), MarkedString::String(format!("`{ty}`"))])
} else {
HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("`{ty}`"),
});
})
};
Ok(Some(Hover { contents, range }))
}