Skip to content

Commit

Permalink
feat(els): add goto implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Sep 3, 2023
1 parent d8835fd commit d4a3942
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 53 deletions.
2 changes: 1 addition & 1 deletion crates/els/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ELS is a language server for the [Erg](https://github.com/erg-lang/erg) programm
- [x] Diagnostics
- [x] Hover
- [x] Go to definition
- [ ] Go to implementation
- [x] Go to implementation
- [x] Find references
- [x] Renaming
- [x] Inlay hint
Expand Down
17 changes: 14 additions & 3 deletions crates/els/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use erg_compiler::erg_parser::parse::Parsable;
use lsp_types::request::{
CallHierarchyIncomingCalls, CallHierarchyOutgoingCalls, CallHierarchyPrepare,
CodeActionRequest, CodeActionResolveRequest, CodeLensRequest, Completion,
DocumentSymbolRequest, ExecuteCommand, FoldingRangeRequest, GotoDefinition, HoverRequest,
InlayHintRequest, InlayHintResolveRequest, References, ResolveCompletionItem,
SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles, WorkspaceSymbol,
DocumentSymbolRequest, ExecuteCommand, FoldingRangeRequest, GotoDefinition, GotoImplementation,
GotoImplementationParams, HoverRequest, InlayHintRequest, InlayHintResolveRequest, References,
ResolveCompletionItem, SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles,
WorkspaceSymbol,
};
use lsp_types::{
CallHierarchyIncomingCallsParams, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
Expand Down Expand Up @@ -37,6 +38,7 @@ pub struct SendChannels {
completion: mpsc::Sender<WorkerMessage<CompletionParams>>,
resolve_completion: mpsc::Sender<WorkerMessage<CompletionItem>>,
goto_definition: mpsc::Sender<WorkerMessage<GotoDefinitionParams>>,
goto_implementation: mpsc::Sender<WorkerMessage<GotoImplementationParams>>,
semantic_tokens_full: mpsc::Sender<WorkerMessage<SemanticTokensParams>>,
inlay_hint: mpsc::Sender<WorkerMessage<InlayHintParams>>,
inlay_hint_resolve: mpsc::Sender<WorkerMessage<InlayHint>>,
Expand All @@ -62,6 +64,7 @@ impl SendChannels {
let (tx_completion, rx_completion) = mpsc::channel();
let (tx_resolve_completion, rx_resolve_completion) = mpsc::channel();
let (tx_goto_definition, rx_goto_definition) = mpsc::channel();
let (tx_goto_implementation, rx_goto_implementation) = mpsc::channel();
let (tx_semantic_tokens_full, rx_semantic_tokens_full) = mpsc::channel();
let (tx_inlay_hint, rx_inlay_hint) = mpsc::channel();
let (tx_inlay_hint_resolve, rx_inlay_hint_resolve) = mpsc::channel();
Expand All @@ -85,6 +88,7 @@ impl SendChannels {
completion: tx_completion,
resolve_completion: tx_resolve_completion,
goto_definition: tx_goto_definition,
goto_implementation: tx_goto_implementation,
semantic_tokens_full: tx_semantic_tokens_full,
inlay_hint: tx_inlay_hint,
inlay_hint_resolve: tx_inlay_hint_resolve,
Expand All @@ -108,6 +112,7 @@ impl SendChannels {
completion: rx_completion,
resolve_completion: rx_resolve_completion,
goto_definition: rx_goto_definition,
goto_implementation: rx_goto_implementation,
semantic_tokens_full: rx_semantic_tokens_full,
inlay_hint: rx_inlay_hint,
inlay_hint_resolve: rx_inlay_hint_resolve,
Expand Down Expand Up @@ -166,6 +171,7 @@ pub struct ReceiveChannels {
pub(crate) completion: mpsc::Receiver<WorkerMessage<CompletionParams>>,
pub(crate) resolve_completion: mpsc::Receiver<WorkerMessage<CompletionItem>>,
pub(crate) goto_definition: mpsc::Receiver<WorkerMessage<GotoDefinitionParams>>,
pub(crate) goto_implementation: mpsc::Receiver<WorkerMessage<GotoImplementationParams>>,
pub(crate) semantic_tokens_full: mpsc::Receiver<WorkerMessage<SemanticTokensParams>>,
pub(crate) inlay_hint: mpsc::Receiver<WorkerMessage<InlayHintParams>>,
pub(crate) inlay_hint_resolve: mpsc::Receiver<WorkerMessage<InlayHint>>,
Expand Down Expand Up @@ -212,6 +218,11 @@ macro_rules! impl_sendable {
impl_sendable!(Completion, CompletionParams, completion);
impl_sendable!(ResolveCompletionItem, CompletionItem, resolve_completion);
impl_sendable!(GotoDefinition, GotoDefinitionParams, goto_definition);
impl_sendable!(
GotoImplementation,
GotoImplementationParams,
goto_implementation
);
impl_sendable!(
SemanticTokensFullRequest,
SemanticTokensParams,
Expand Down
72 changes: 31 additions & 41 deletions crates/els/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use erg_compiler::artifact::BuildRunnable;
use erg_compiler::context::register::PylyzerStatus;
use erg_compiler::erg_parser::parse::Parsable;
use erg_compiler::erg_parser::token::{Token, TokenCategory};
use erg_compiler::hir::Expr;
use erg_compiler::hir::{Def, Expr};
use erg_compiler::ty::HasType;
use erg_compiler::varinfo::VarInfo;

use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse, Url};
use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse, Location, Position, Url};

use crate::server::{send_log, ELSResult, Server};
use crate::util::{self, NormalizedUrl};
Expand All @@ -30,58 +30,51 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
}
}

fn get_definition_response(
pub(crate) fn get_definition_location(
&self,
params: GotoDefinitionParams,
) -> ELSResult<GotoDefinitionResponse> {
let uri = NormalizedUrl::new(params.text_document_position_params.text_document.uri);
let pos = params.text_document_position_params.position;
if let Some(token) = self.file_cache.get_symbol(&uri, pos) {
if let Some(vi) = self.get_definition(&uri, &token)? {
uri: &NormalizedUrl,
pos: Position,
) -> ELSResult<Option<Location>> {
if let Some(token) = self.file_cache.get_symbol(uri, pos) {
if let Some(vi) = self.get_definition(uri, &token)? {
// If the target variable is an imported one, jump to the definition file.
// Else if the target variable is an alias, jump to the definition of it.
// `foo = import "foo"` => jump to `foo.er`
// `{x;} = import "foo"` => jump to `x` of `foo.er`
if vi.def_loc.module == Some(util::uri_to_path(&uri))
if vi.def_loc.module == Some(util::uri_to_path(uri))
&& vi.def_loc.loc == token.loc()
{
if let Some((_, Expr::Def(def))) = self.get_min_expr(&uri, pos, 0) {
if let Some(def) = self.get_min::<Def>(uri, pos) {
if def.def_kind().is_import() {
if vi.t.is_module() {
if let Some(path) = self
.get_local_ctx(&uri, pos)
.get_local_ctx(uri, pos)
.first()
.and_then(|ctx| ctx.get_path_with_mod_t(&vi.t))
{
let mod_uri = Url::from_file_path(path).unwrap();
let resp = GotoDefinitionResponse::Array(vec![
lsp_types::Location::new(
mod_uri,
lsp_types::Range::default(),
),
]);
return Ok(resp);
return Ok(Some(lsp_types::Location::new(
mod_uri,
lsp_types::Range::default(),
)));
}
} else {
// line of module member definitions may no longer match after the desugaring process
let mod_t = def.body.ref_t();
if let Some((_, vi)) = self
.get_local_ctx(&uri, pos)
.get_local_ctx(uri, pos)
.first()
.and_then(|ctx| ctx.get_mod_with_t(mod_t))
.and_then(|mod_ctx| mod_ctx.get_var_info(token.inspect()))
{
let Some(path) = vi.def_loc.module.as_ref() else {
return Ok(GotoDefinitionResponse::Array(vec![]));
return Ok(None);
};
let def_uri = Url::from_file_path(path).unwrap();
let resp = GotoDefinitionResponse::Array(vec![
lsp_types::Location::new(
def_uri,
util::loc_to_range(vi.def_loc.loc).unwrap(),
),
]);
return Ok(resp);
return Ok(Some(lsp_types::Location::new(
def_uri,
util::loc_to_range(vi.def_loc.loc).unwrap(),
)));
}
}
} else if let Expr::Accessor(acc) = def.body.block.last().unwrap() {
Expand All @@ -103,14 +96,11 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
path.clone()
};
let def_uri = Url::from_file_path(def_file).unwrap();
let resp = GotoDefinitionResponse::Array(vec![
lsp_types::Location::new(def_uri, range),
]);
return Ok(resp);
return Ok(Some(lsp_types::Location::new(def_uri, range)));
}
_ => {
send_log("not found (maybe builtin)")?;
return Ok(GotoDefinitionResponse::Array(vec![]));
return Ok(None);
}
}
}
Expand All @@ -119,21 +109,19 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
match (vi.def_loc.module, util::loc_to_range(vi.def_loc.loc)) {
(Some(path), Some(range)) => {
let def_uri = Url::from_file_path(path).unwrap();
Ok(GotoDefinitionResponse::Array(vec![
lsp_types::Location::new(def_uri, range),
]))
Ok(Some(lsp_types::Location::new(def_uri, range)))
}
_ => {
send_log("not found (maybe builtin)")?;
Ok(GotoDefinitionResponse::Array(vec![]))
Ok(None)
}
}
} else {
Ok(GotoDefinitionResponse::Array(vec![]))
Ok(None)
}
} else {
send_log("lex error occurred")?;
Ok(GotoDefinitionResponse::Array(vec![]))
Ok(None)
}
}
Expand All @@ -142,7 +130,9 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
params: GotoDefinitionParams,
) -> ELSResult<Option<GotoDefinitionResponse>> {
send_log(format!("definition requested: {params:?}"))?;
let result = self.get_definition_response(params)?;
Ok(Some(result))
let uri = NormalizedUrl::new(params.text_document_position_params.text_document.uri);
let pos = params.text_document_position_params.position;
let result = self.get_definition_location(&uri, pos)?;
Ok(result.map(GotoDefinitionResponse::Scalar))
}
}
39 changes: 39 additions & 0 deletions crates/els/implementation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use erg_compiler::artifact::BuildRunnable;
use erg_compiler::erg_parser::parse::Parsable;
use lsp_types::request::{GotoImplementationParams, GotoImplementationResponse};

use crate::_log;
use crate::server::{ELSResult, Server};
use crate::util::{loc_to_pos, NormalizedUrl};

impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
pub(crate) fn handle_goto_implementation(
&mut self,
params: GotoImplementationParams,
) -> ELSResult<Option<GotoImplementationResponse>> {
_log!("implementation requested: {params:?}");
let uri = NormalizedUrl::new(params.text_document_position_params.text_document.uri);
let pos = params.text_document_position_params.position;
let Some(symbol) = self.file_cache.get_symbol(&uri, pos) else {
return Ok(None);
};
// {x #[ ← this is not the impl ]#;} = import "foo"
// print! x # ← symbol
if let Some(vi) = self.get_definition(&uri, &symbol)? {
let Some(uri) = vi
.def_loc
.module
.and_then(|p| NormalizedUrl::from_file_path(p).ok())
else {
return Ok(None);
};
let Some(pos) = loc_to_pos(vi.def_loc.loc) else {
return Ok(None);
};
if let Some(location) = self.get_definition_location(&uri, pos)? {
return Ok(Some(GotoImplementationResponse::Scalar(location)));
}
}
Ok(None)
}
}
1 change: 1 addition & 0 deletions crates/els/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod file_cache;
mod folding_range;
mod hir_visitor;
mod hover;
mod implementation;
mod inlay_hint;
mod message;
mod references;
Expand Down
1 change: 1 addition & 0 deletions crates/els/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod file_cache;
mod folding_range;
mod hir_visitor;
mod hover;
mod implementation;
mod inlay_hint;
mod message;
mod references;
Expand Down
23 changes: 15 additions & 8 deletions crates/els/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ use erg_compiler::ty::HasType;
use lsp_types::request::{
CallHierarchyIncomingCalls, CallHierarchyOutgoingCalls, CallHierarchyPrepare,
CodeActionRequest, CodeActionResolveRequest, CodeLensRequest, Completion,
DocumentSymbolRequest, ExecuteCommand, FoldingRangeRequest, GotoDefinition, HoverRequest,
InlayHintRequest, InlayHintResolveRequest, References, Rename, Request, ResolveCompletionItem,
SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles, WorkspaceSymbol,
DocumentSymbolRequest, ExecuteCommand, FoldingRangeRequest, GotoDefinition, GotoImplementation,
HoverRequest, InlayHintRequest, InlayHintResolveRequest, References, Rename, Request,
ResolveCompletionItem, SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles,
WorkspaceSymbol,
};
use lsp_types::{
CallHierarchyServerCapability, CodeActionKind, CodeActionOptions, CodeActionProviderCapability,
CodeLensOptions, CompletionOptions, ConfigurationItem, ConfigurationParams,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, ExecuteCommandOptions,
FoldingRangeProviderCapability, HoverProviderCapability, InitializeParams, InitializeResult,
InlayHintOptions, InlayHintServerCapabilities, OneOf, Position, SemanticTokenType,
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelpOptions,
WorkDoneProgressOptions,
FoldingRangeProviderCapability, HoverProviderCapability, ImplementationProviderCapability,
InitializeParams, InitializeResult, InlayHintOptions, InlayHintServerCapabilities, OneOf,
Position, SemanticTokenType, SemanticTokensFullOptions, SemanticTokensLegend,
SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities,
SignatureHelpOptions, WorkDoneProgressOptions,
};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -430,6 +431,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
capabilities.rename_provider = Some(OneOf::Left(true));
capabilities.references_provider = Some(OneOf::Left(true));
capabilities.definition_provider = Some(OneOf::Left(true));
capabilities.implementation_provider = Some(ImplementationProviderCapability::Simple(true));
capabilities.hover_provider = self
.disabled_features
.contains(&DefaultFeatures::Hover)
Expand Down Expand Up @@ -538,6 +540,10 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
receivers.goto_definition,
Self::handle_goto_definition,
);
self.start_service::<GotoImplementation>(
receivers.goto_implementation,
Self::handle_goto_implementation,
);
self.start_service::<SemanticTokensFullRequest>(
receivers.semantic_tokens_full,
Self::handle_semantic_tokens_full,
Expand Down Expand Up @@ -755,6 +761,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
Completion::METHOD => self.parse_send::<Completion>(id, msg),
ResolveCompletionItem::METHOD => self.parse_send::<ResolveCompletionItem>(id, msg),
GotoDefinition::METHOD => self.parse_send::<GotoDefinition>(id, msg),
GotoImplementation::METHOD => self.parse_send::<GotoImplementation>(id, msg),
HoverRequest::METHOD => self.parse_send::<HoverRequest>(id, msg),
References::METHOD => self.parse_send::<References>(id, msg),
SemanticTokensFullRequest::METHOD => {
Expand Down

0 comments on commit d4a3942

Please sign in to comment.