Add hover kind

This commit is contained in:
Sofia 2025-08-14 16:39:31 +03:00
parent 7b4f38406d
commit 6dccab8b12
3 changed files with 73 additions and 26 deletions

View File

@ -8,9 +8,8 @@ struct Otus {
} }
impl Otus { impl Otus {
/// Some test documentation /// Some test documentation here.
/// Here /// On a second line
/// qwe
fn test(&self) -> u32 { fn test(&self) -> u32 {
*self.field *self.field
} }

View File

@ -2,6 +2,7 @@ use std::{collections::HashMap, hash::Hash, path::PathBuf};
use reid::{ use reid::{
ast::{ ast::{
ReturnType,
lexer::{FullToken, Token}, lexer::{FullToken, Token},
token_stream::TokenRange, token_stream::TokenRange,
}, },
@ -84,7 +85,7 @@ impl StaticAnalysis {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SemanticToken { pub struct SemanticToken {
pub ty: Option<TypeKind>, pub hover: Option<Hover>,
pub autocomplete: Vec<Autocomplete>, pub autocomplete: Vec<Autocomplete>,
pub symbol: Option<SymbolId>, pub symbol: Option<SymbolId>,
} }
@ -103,6 +104,18 @@ pub enum AutocompleteKind {
Function(Vec<FunctionParam>, TypeKind), Function(Vec<FunctionParam>, TypeKind),
} }
#[derive(Debug, Clone)]
pub struct Hover {
pub documentation: Option<String>,
pub kind: Option<HoverKind>,
}
#[derive(Debug, Clone)]
pub enum HoverKind {
Type(TypeKind),
Function(String, Vec<FunctionParam>, TypeKind),
}
impl ToString for AutocompleteKind { impl ToString for AutocompleteKind {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
@ -149,12 +162,15 @@ impl AnalysisState {
} }
impl AnalysisState { impl AnalysisState {
pub fn init_types(&mut self, meta: &mir::Metadata, ty: Option<TypeKind>) { pub fn init_hover(&mut self, meta: &mir::Metadata, kind: Option<HoverKind>, documentation: Option<String>) {
for token in meta.range.start..=meta.range.end { for token in meta.range.start..=meta.range.end {
self.map.insert( self.map.insert(
token, token,
SemanticToken { SemanticToken {
ty: ty.clone(), hover: Some(Hover {
documentation: documentation.clone(),
kind: kind.clone(),
}),
autocomplete: Vec::new(), autocomplete: Vec::new(),
symbol: Default::default(), symbol: Default::default(),
}, },
@ -169,7 +185,7 @@ impl AnalysisState {
self.map.insert( self.map.insert(
token_idx, token_idx,
SemanticToken { SemanticToken {
ty: None, hover: None,
autocomplete: autocomplete.clone(), autocomplete: autocomplete.clone(),
symbol: Default::default(), symbol: Default::default(),
}, },
@ -185,7 +201,7 @@ impl AnalysisState {
self.map.insert( self.map.insert(
idx, idx,
SemanticToken { SemanticToken {
ty: None, hover: None,
autocomplete: Vec::new(), autocomplete: Vec::new(),
symbol: Some(symbol), symbol: Some(symbol),
}, },
@ -533,7 +549,7 @@ pub fn analyze_context(
.token_idx(&field.2, |t| matches!(t, Token::Identifier(_))) .token_idx(&field.2, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(field.2.range.end); .unwrap_or(field.2.range.end);
scope.state.init_types( scope.state.init_hover(
&Metadata { &Metadata {
source_module_id: field.2.source_module_id, source_module_id: field.2.source_module_id,
range: TokenRange { range: TokenRange {
@ -542,7 +558,8 @@ pub fn analyze_context(
}, },
position: None, position: None,
}, },
Some(field.1.clone()), Some(HoverKind::Type(field.1.clone())),
None,
); );
let field_symbol = scope.state.new_symbol(field_idx, SemanticKind::Property); let field_symbol = scope.state.new_symbol(field_idx, SemanticKind::Property);
@ -584,7 +601,7 @@ pub fn analyze_context(
scope.state.new_symbol(field_ty_idx, SemanticKind::Type) scope.state.new_symbol(field_ty_idx, SemanticKind::Type)
}; };
scope.state.init_types( scope.state.init_hover(
&Metadata { &Metadata {
source_module_id: field.2.source_module_id, source_module_id: field.2.source_module_id,
range: TokenRange { range: TokenRange {
@ -593,7 +610,8 @@ pub fn analyze_context(
}, },
position: None, position: None,
}, },
Some(field.1.clone()), Some(HoverKind::Type(field.1.clone())),
None,
); );
scope.state.set_symbol(field_ty_idx, field_ty_symbol); scope.state.set_symbol(field_ty_idx, field_ty_symbol);
} }
@ -604,7 +622,9 @@ pub fn analyze_context(
for binop in &module.binop_defs { for binop in &module.binop_defs {
if binop.meta.source_module_id == module.module_id { if binop.meta.source_module_id == module.module_id {
for param in [&binop.lhs, &binop.rhs] { for param in [&binop.lhs, &binop.rhs] {
scope.state.init_types(&param.meta, Some(param.ty.clone())); scope
.state
.init_hover(&param.meta, Some(HoverKind::Type(param.ty.clone())), None);
let idx = scope let idx = scope
.token_idx(&param.meta, |t| matches!(t, Token::Identifier(_))) .token_idx(&param.meta, |t| matches!(t, Token::Identifier(_)))
.unwrap_or(param.meta.range.end); .unwrap_or(param.meta.range.end);
@ -679,9 +699,11 @@ pub fn analyze_context(
} }
} }
scope scope.state.init_hover(
.state &function.signature(),
.init_types(&function.signature(), Some(function.return_type.clone())); Some(HoverKind::Type(function.return_type.clone())),
None,
);
let idx = scope let idx = scope
.token_idx(&function.signature(), |t| matches!(t, Token::Identifier(_))) .token_idx(&function.signature(), |t| matches!(t, Token::Identifier(_)))
@ -713,7 +735,7 @@ pub fn analyze_context(
} }
for import in &module.imports { for import in &module.imports {
scope.state.init_types(&import.1, None); scope.state.init_hover(&import.1, None, None);
if let Some((module_name, _)) = import.0.get(0) { if let Some((module_name, _)) = import.0.get(0) {
let module_idx = scope let module_idx = scope
.token_idx(&import.1, |t| matches!(t, Token::Identifier(_))) .token_idx(&import.1, |t| matches!(t, Token::Identifier(_)))
@ -793,7 +815,9 @@ pub fn analyze_function_parameters(
scope: &mut AnalysisScope, scope: &mut AnalysisScope,
) { ) {
for param in &function.parameters { for param in &function.parameters {
scope.state.init_types(&param.meta, Some(param.ty.clone())); scope
.state
.init_hover(&param.meta, Some(HoverKind::Type(param.ty.clone())), None);
if param.meta.source_module_id == module.module_id { if param.meta.source_module_id == module.module_id {
let param_var_idx = scope let param_var_idx = scope
@ -832,12 +856,14 @@ pub fn analyze_block(
for statement in &block.statements { for statement in &block.statements {
match &statement.0 { match &statement.0 {
mir::StmtKind::Let(named_variable_ref, _, expression) => { mir::StmtKind::Let(named_variable_ref, _, expression) => {
scope.state.init_types( scope.state.init_hover(
&named_variable_ref.2, &named_variable_ref.2,
expression expression
.return_type(&TypeRefs::unknown(), source_module.module_id) .return_type(&TypeRefs::unknown(), source_module.module_id)
.ok() .ok()
.map(|(_, ty)| ty), .map(|(_, ty)| ty)
.map(|t| HoverKind::Type(t)),
None,
); );
let idx = scope let idx = scope
.token_idx(&named_variable_ref.2, |t| matches!(t, Token::Identifier(_))) .token_idx(&named_variable_ref.2, |t| matches!(t, Token::Identifier(_)))
@ -888,16 +914,19 @@ pub fn analyze_expr(
expr: &mir::Expression, expr: &mir::Expression,
scope: &mut AnalysisScope, scope: &mut AnalysisScope,
) { ) {
scope.state.init_types( scope.state.init_hover(
&expr.1, &expr.1,
expr.return_type(&TypeRefs::unknown(), source_module.module_id) expr.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok() .ok()
.map(|(_, t)| t), .map(|(_, t)| HoverKind::Type(t)),
None,
); );
match &expr.0 { match &expr.0 {
mir::ExprKind::Variable(var_ref) => { mir::ExprKind::Variable(var_ref) => {
scope.state.init_types(&var_ref.2, Some(var_ref.0.clone())); scope
.state
.init_hover(&var_ref.2, Some(HoverKind::Type(var_ref.0.clone())), None);
let idx = scope let idx = scope
.token_idx(&var_ref.2, |t| matches!(t, Token::Identifier(_))) .token_idx(&var_ref.2, |t| matches!(t, Token::Identifier(_)))

View File

@ -213,10 +213,29 @@ impl LanguageServer for Backend {
character: (end.0 as i32 - 1).max(0) as u32, character: (end.0 as i32 - 1).max(0) as u32,
}, },
}; };
if let Some(ty) = analysis.ty.clone() { if let Some(hover) = analysis.hover.clone() {
(Some(range), format!("{}", ty)) if let Some(kind) = hover.kind {
match kind {
analysis::HoverKind::Type(type_kind) => (Some(range), format!("{}", type_kind)),
analysis::HoverKind::Function(name, function_params, return_type) => (
Some(range),
format!(
"{}({}) -> {}",
name,
function_params
.iter()
.map(|p| format!("{}: {}", p.name, p.ty))
.collect::<Vec<_>>()
.join(", "),
return_type
),
),
}
} else {
(Some(range), String::from("No type"))
}
} else { } else {
(Some(range), String::from("None type")) (Some(range), String::from("No hover"))
} }
} else { } else {
(None, String::from("no type")) (None, String::from("no type"))