diff --git a/reid-lsp/src/analysis.rs b/reid-lsp/src/analysis.rs index ba5ec30..ae68821 100644 --- a/reid-lsp/src/analysis.rs +++ b/reid-lsp/src/analysis.rs @@ -193,6 +193,33 @@ impl AnalysisState { } } + pub fn set_documentation(&mut self, token_idx: usize, documentation: Option) { + 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, variables: HashMap, types: HashMap, - functions: HashMap, - associated_functions: HashMap<(TypeKind, String), (SourceModuleId, SymbolId)>, + functions: HashMap)>, + associated_functions: HashMap<(TypeKind, String), (SourceModuleId, SymbolId, Option)>, 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) }; diff --git a/reid-lsp/src/main.rs b/reid-lsp/src/main.rs index 5609ca5..3c85930 100644 --- a/reid-lsp/src/main.rs +++ b/reid-lsp/src/main.rs @@ -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 { - kind: MarkupKind::Markdown, - value: format!("`{ty}`"), - }); + 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 })) }