Add go-to-definition
This commit is contained in:
parent
79b3c6b3ef
commit
82537224e7
@ -45,7 +45,7 @@ pub struct StaticAnalysis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TokenAnalysis {
|
pub struct SemanticToken {
|
||||||
pub ty: Option<TypeKind>,
|
pub ty: Option<TypeKind>,
|
||||||
pub autocomplete: Vec<Autocomplete>,
|
pub autocomplete: Vec<Autocomplete>,
|
||||||
pub symbol: Option<SymbolId>,
|
pub symbol: Option<SymbolId>,
|
||||||
@ -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);
|
pub struct SymbolId(usize);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AnalysisState {
|
pub struct AnalysisState {
|
||||||
/// TokenID -> Analysis map, containing SymbolIDs
|
/// TokenID -> Analysis map, containing SymbolIDs
|
||||||
pub map: HashMap<usize, TokenAnalysis>,
|
pub map: HashMap<usize, SemanticToken>,
|
||||||
/// SymbolID -> Symbol
|
/// SymbolID -> Symbol
|
||||||
symbol_table: Vec<Symbol>,
|
symbol_table: Vec<Symbol>,
|
||||||
|
/// SymbolID -> Symbol
|
||||||
|
pub symbol_to_token: HashMap<SymbolId, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalysisState {
|
impl AnalysisState {
|
||||||
@ -97,18 +99,12 @@ impl AnalysisState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Symbol {
|
|
||||||
pub kind: SemanticKind,
|
|
||||||
pub definition: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AnalysisState {
|
impl AnalysisState {
|
||||||
pub fn init_types(&mut self, meta: &mir::Metadata, ty: Option<TypeKind>) {
|
pub fn init_types(&mut self, meta: &mir::Metadata, ty: Option<TypeKind>) {
|
||||||
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,
|
||||||
TokenAnalysis {
|
SemanticToken {
|
||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
autocomplete: Vec::new(),
|
autocomplete: Vec::new(),
|
||||||
symbol: Default::default(),
|
symbol: Default::default(),
|
||||||
@ -123,7 +119,7 @@ impl AnalysisState {
|
|||||||
} else {
|
} else {
|
||||||
self.map.insert(
|
self.map.insert(
|
||||||
token_idx,
|
token_idx,
|
||||||
TokenAnalysis {
|
SemanticToken {
|
||||||
ty: None,
|
ty: None,
|
||||||
autocomplete: autocomplete.clone(),
|
autocomplete: autocomplete.clone(),
|
||||||
symbol: Default::default(),
|
symbol: Default::default(),
|
||||||
@ -133,12 +129,13 @@ impl AnalysisState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_symbol(&mut self, idx: usize, symbol: SymbolId) {
|
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) {
|
if let Some(token) = self.map.get_mut(&idx) {
|
||||||
token.symbol = Some(symbol);
|
token.symbol = Some(symbol);
|
||||||
} else {
|
} else {
|
||||||
self.map.insert(
|
self.map.insert(
|
||||||
idx,
|
idx,
|
||||||
TokenAnalysis {
|
SemanticToken {
|
||||||
ty: None,
|
ty: None,
|
||||||
autocomplete: Vec::new(),
|
autocomplete: Vec::new(),
|
||||||
symbol: Some(symbol),
|
symbol: Some(symbol),
|
||||||
@ -152,6 +149,20 @@ impl AnalysisState {
|
|||||||
self.symbol_table.push(Symbol { kind, definition });
|
self.symbol_table.push(Symbol { kind, definition });
|
||||||
id
|
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> {
|
pub struct AnalysisScope<'a> {
|
||||||
@ -298,6 +309,7 @@ pub fn analyze_context(context: &mir::Context, module: &mir::Module, error: Opti
|
|||||||
let mut state = AnalysisState {
|
let mut state = AnalysisState {
|
||||||
map: HashMap::new(),
|
map: HashMap::new(),
|
||||||
symbol_table: Vec::new(),
|
symbol_table: Vec::new(),
|
||||||
|
symbol_to_token: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scope = AnalysisScope {
|
let mut scope = AnalysisScope {
|
||||||
@ -615,19 +627,32 @@ pub fn analyze_expr(
|
|||||||
analyze_expr(context, source_module, expr, scope);
|
analyze_expr(context, source_module, expr, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::ExprKind::Struct(_, items) => {
|
mir::ExprKind::Struct(struct_name, items) => {
|
||||||
let idx = scope
|
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(_)))
|
.token_idx(&expr.1, |t| matches!(t, Token::Identifier(_)))
|
||||||
.unwrap_or(expr.1.range.end);
|
.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 struct_symbol = if let Some(symbol_id) = scope.types.get(&struct_type) {
|
||||||
let idx = scope
|
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(_)))
|
.token_idx(&field_meta, |t| matches!(t, Token::Identifier(_)))
|
||||||
.unwrap_or(field_meta.range.end);
|
.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);
|
analyze_expr(context, source_module, expr, scope);
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,12 @@ use reid::mir::SourceModuleId;
|
|||||||
use reid::parse_module;
|
use reid::parse_module;
|
||||||
use tower_lsp::lsp_types::{
|
use tower_lsp::lsp_types::{
|
||||||
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
|
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
|
||||||
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentFilter, DocumentSelector, Hover, HoverContents,
|
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DocumentFilter, GotoDefinitionParams,
|
||||||
HoverParams, HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, MarkupContent,
|
GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams,
|
||||||
MarkupKind, MessageType, OneOf, Range, SemanticToken, SemanticTokenModifier, SemanticTokenType,
|
InitializeResult, InitializedParams, MarkupContent, MarkupKind, MessageType, OneOf, Range, SemanticToken,
|
||||||
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensRangeParams,
|
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensResult,
|
||||||
SemanticTokensRangeResult, SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities,
|
SemanticTokensServerCapabilities, ServerCapabilities, TextDocumentItem, TextDocumentRegistrationOptions,
|
||||||
StaticRegistrationOptions, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
|
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, WorkspaceFoldersServerCapabilities,
|
||||||
TextDocumentSyncKind, TextDocumentSyncOptions, WorkDoneProgressOptions, WorkspaceFoldersServerCapabilities,
|
|
||||||
WorkspaceServerCapabilities,
|
WorkspaceServerCapabilities,
|
||||||
};
|
};
|
||||||
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
|
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
|
||||||
@ -75,6 +74,7 @@ impl LanguageServer for Backend {
|
|||||||
static_registration_options: Default::default(),
|
static_registration_options: Default::default(),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
definition_provider: Some(OneOf::Left(true)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
Ok(InitializeResult {
|
Ok(InitializeResult {
|
||||||
@ -248,6 +248,53 @@ impl LanguageServer for Backend {
|
|||||||
data: semantic_tokens,
|
data: semantic_tokens,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result<Option<GotoDefinitionResponse>> {
|
||||||
|
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 {
|
impl Backend {
|
||||||
|
Loading…
Reference in New Issue
Block a user