From 82537224e7eb71843bf8bb7df935b7f4b210d381 Mon Sep 17 00:00:00 2001 From: sofia Date: Sun, 3 Aug 2025 20:57:56 +0300 Subject: [PATCH] Add go-to-definition --- reid-lsp/src/analysis.rs | 65 +++++++++++++++++++++++++++------------- reid-lsp/src/main.rs | 61 ++++++++++++++++++++++++++++++++----- 2 files changed, 99 insertions(+), 27 deletions(-) diff --git a/reid-lsp/src/analysis.rs b/reid-lsp/src/analysis.rs index 968c247..808475c 100644 --- a/reid-lsp/src/analysis.rs +++ b/reid-lsp/src/analysis.rs @@ -45,7 +45,7 @@ pub struct StaticAnalysis { } #[derive(Debug, Clone)] -pub struct TokenAnalysis { +pub struct SemanticToken { pub ty: Option, pub autocomplete: Vec, pub symbol: Option, @@ -80,15 +80,17 @@ impl ToString for AutocompleteKind { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SymbolId(usize); #[derive(Debug, Clone)] pub struct AnalysisState { /// TokenID -> Analysis map, containing SymbolIDs - pub map: HashMap, + pub map: HashMap, /// SymbolID -> Symbol symbol_table: Vec, + /// SymbolID -> Symbol + pub symbol_to_token: HashMap, } impl AnalysisState { @@ -97,18 +99,12 @@ impl AnalysisState { } } -#[derive(Debug, Clone)] -pub struct Symbol { - pub kind: SemanticKind, - pub definition: usize, -} - impl AnalysisState { pub fn init_types(&mut self, meta: &mir::Metadata, ty: Option) { for token in meta.range.start..=meta.range.end { self.map.insert( token, - TokenAnalysis { + SemanticToken { ty: ty.clone(), autocomplete: Vec::new(), symbol: Default::default(), @@ -123,7 +119,7 @@ impl AnalysisState { } else { self.map.insert( token_idx, - TokenAnalysis { + SemanticToken { ty: None, autocomplete: autocomplete.clone(), symbol: Default::default(), @@ -133,12 +129,13 @@ impl AnalysisState { } 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) { token.symbol = Some(symbol); } else { self.map.insert( idx, - TokenAnalysis { + SemanticToken { ty: None, autocomplete: Vec::new(), symbol: Some(symbol), @@ -152,6 +149,20 @@ impl AnalysisState { self.symbol_table.push(Symbol { kind, definition }); id } + + pub fn find_definition(&self, id: &SymbolId) -> SymbolId { + let symbol = self.get_symbol(*id); + match symbol.kind { + SemanticKind::Reference(idx) => self.find_definition(&idx), + _ => *id, + } + } +} + +#[derive(Debug, Clone)] +pub struct Symbol { + pub kind: SemanticKind, + pub definition: usize, } pub struct AnalysisScope<'a> { @@ -298,6 +309,7 @@ pub fn analyze_context(context: &mir::Context, module: &mir::Module, error: Opti let mut state = AnalysisState { map: HashMap::new(), symbol_table: Vec::new(), + symbol_to_token: HashMap::new(), }; let mut scope = AnalysisScope { @@ -615,19 +627,32 @@ pub fn analyze_expr( analyze_expr(context, source_module, expr, scope); } } - mir::ExprKind::Struct(_, items) => { - let idx = scope + mir::ExprKind::Struct(struct_name, items) => { + let struct_type = TypeKind::CustomType(CustomTypeKey(struct_name.clone(), source_module.module_id)); + let struct_idx = scope .token_idx(&expr.1, |t| matches!(t, Token::Identifier(_))) .unwrap_or(expr.1.range.end); - let symbol = scope.state.new_symbol(idx, SemanticKind::Struct); - scope.state.set_symbol(idx, symbol); - for (_, expr, field_meta) in items { - let idx = scope + let struct_symbol = if let Some(symbol_id) = scope.types.get(&struct_type) { + scope.state.new_symbol(struct_idx, SemanticKind::Reference(*symbol_id)) + } else { + scope.state.new_symbol(struct_idx, SemanticKind::Struct) + }; + scope.state.set_symbol(struct_idx, struct_symbol); + + for (field_name, expr, field_meta) in items { + let field_idx = scope .token_idx(&field_meta, |t| matches!(t, Token::Identifier(_))) .unwrap_or(field_meta.range.end); - let symbol = scope.state.new_symbol(idx, SemanticKind::Property); - scope.state.set_symbol(idx, symbol); + + let field_symbol = + if let Some(symbol_id) = scope.properties.get(&(struct_type.clone(), field_name.clone())) { + scope.state.new_symbol(field_idx, SemanticKind::Reference(*symbol_id)) + } else { + scope.state.new_symbol(field_idx, SemanticKind::Property) + }; + + scope.state.set_symbol(field_idx, field_symbol); analyze_expr(context, source_module, expr, scope); } diff --git a/reid-lsp/src/main.rs b/reid-lsp/src/main.rs index 4ae5c8b..e0129d3 100644 --- a/reid-lsp/src/main.rs +++ b/reid-lsp/src/main.rs @@ -7,13 +7,12 @@ use reid::mir::SourceModuleId; use reid::parse_module; use tower_lsp::lsp_types::{ self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity, - DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentFilter, DocumentSelector, Hover, HoverContents, - HoverParams, HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, MarkupContent, - MarkupKind, MessageType, OneOf, Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, - SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensRangeParams, - SemanticTokensRangeResult, SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities, - StaticRegistrationOptions, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability, - TextDocumentSyncKind, TextDocumentSyncOptions, WorkDoneProgressOptions, WorkspaceFoldersServerCapabilities, + DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentFilter, GotoDefinitionParams, + GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, + InitializeResult, InitializedParams, MarkupContent, MarkupKind, MessageType, OneOf, Range, SemanticToken, + SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult, + SemanticTokensServerCapabilities, ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions, + TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, }; use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc}; @@ -75,6 +74,7 @@ impl LanguageServer for Backend { static_registration_options: Default::default(), }, )), + definition_provider: Some(OneOf::Left(true)), ..Default::default() }; Ok(InitializeResult { @@ -248,6 +248,53 @@ impl LanguageServer for Backend { data: semantic_tokens, }))) } + + async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result> { + let path = PathBuf::from(params.text_document_position_params.text_document.uri.path()); + let file_name = path.file_name().unwrap().to_str().unwrap().to_owned(); + let analysis = self.analysis.get(&file_name); + let position = params.text_document_position_params.position; + + if let Some(analysis) = &analysis { + let token = analysis.tokens.iter().enumerate().find(|(_, tok)| { + tok.position.1 == position.line + 1 + && (tok.position.0 <= position.character + 1 + && (tok.position.0 + tok.token.len() as u32) > position.character + 1) + }); + + if let Some(token) = token { + dbg!(token); + if let Some(semantic_token) = analysis.state.map.get(&token.0) { + dbg!(semantic_token); + if let Some(symbol_id) = semantic_token.symbol { + dbg!(symbol_id); + let definition_id = analysis.state.find_definition(&symbol_id); + if let Some(def_token_idx) = analysis.state.symbol_to_token.get(&definition_id) { + dbg!(def_token_idx); + if let Some(def_token) = analysis.tokens.get(*def_token_idx) { + dbg!(def_token); + return Ok(Some(GotoDefinitionResponse::Scalar(lsp_types::Location { + uri: params.text_document_position_params.text_document.uri, + range: Range { + start: lsp_types::Position { + line: def_token.position.1.max(1) - 1, + character: def_token.position.0.max(1) - 1, + }, + end: lsp_types::Position { + line: def_token.position.1.max(1) - 1, + character: def_token.position.0.max(1) - 1 + def_token.token.len() as u32, + }, + }, + }))); + } + } + } + } + } + }; + + Ok(None) + } } impl Backend {