Skip to content

Commit

Permalink
feat(els): add call hierarchy
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Sep 1, 2023
1 parent 0c89ca9 commit d4699cf
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 18 deletions.
1 change: 1 addition & 0 deletions crates/els/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ELS is a language server for the [Erg](https://github.com/erg-lang/erg) programm
- [x] show trait implementations
- [x] Signature help
- [x] Workspace symbol
- [x] Call hierarchy

## Installation

Expand Down
99 changes: 99 additions & 0 deletions crates/els/call_hierarchy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::str::FromStr;

use erg_compiler::artifact::BuildRunnable;
use erg_compiler::erg_parser::parse::Parsable;

use erg_compiler::hir::Def;
use erg_compiler::varinfo::{AbsLocation, VarInfo};
use lsp_types::{
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
};

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

fn hierarchy_item(name: String, vi: &VarInfo) -> Option<CallHierarchyItem> {
let loc = abs_loc_to_lsp_loc(&vi.def_loc)?;
Some(CallHierarchyItem {
name,
kind: symbol_kind(vi),
tags: None,
detail: Some(vi.t.to_string()),
uri: loc.uri,
range: loc.range,
selection_range: loc.range,
data: Some(vi.def_loc.to_string().into()),
})
}

impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
pub(crate) fn handle_call_hierarchy_incoming(
&mut self,
params: CallHierarchyIncomingCallsParams,
) -> ELSResult<Option<Vec<CallHierarchyIncomingCall>>> {
let mut res = vec![];
_log!("call hierarchy incoming calls requested: {params:?}");
let data = params.item.data.as_ref().unwrap().as_str().unwrap();
let loc = AbsLocation::from_str(data).unwrap();
let shared = self.get_shared().unwrap();
if let Some(refs) = shared.index.get_refs(&loc) {
for referrer_loc in refs.referrers.iter() {
let Some(uri) = referrer_loc
.module
.as_ref()
.and_then(|path| NormalizedUrl::from_file_path(path).ok())
else {
continue;
};
let Some(pos) = loc_to_pos(referrer_loc.loc) else {
continue;
};
if let Some(def) = self.get_min::<Def>(&uri, pos) {
if def.sig.is_subr() {
let Some(from) =
hierarchy_item(def.sig.inspect().to_string(), &def.sig.ident().vi)
else {
continue;
};
let call = CallHierarchyIncomingCall {
from,
from_ranges: vec![],
};
res.push(call);
}
}
}
}
Ok(Some(res))
}

pub(crate) fn handle_call_hierarchy_outgoing(
&mut self,
params: CallHierarchyOutgoingCallsParams,
) -> ELSResult<Option<Vec<CallHierarchyOutgoingCall>>> {
_log!("call hierarchy outgoing calls requested: {params:?}");
Ok(None)
}

pub(crate) fn handle_call_hierarchy_prepare(
&mut self,
params: CallHierarchyPrepareParams,
) -> ELSResult<Option<Vec<CallHierarchyItem>>> {
_log!("call hierarchy prepare requested: {params:?}");
let mut res = vec![];
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)? {
let Some(item) = hierarchy_item(token.content.to_string(), &vi) else {
return Ok(None);
};
res.push(item);
}
}
Ok(Some(res))
}
}
43 changes: 43 additions & 0 deletions crates/els/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use erg_compiler::artifact::BuildRunnable;
use erg_compiler::erg_parser::parse::Parsable;

use lsp_types::request::{
CallHierarchyIncomingCalls, CallHierarchyOutgoingCalls, CallHierarchyPrepare,
CodeActionRequest, CodeActionResolveRequest, CodeLensRequest, Completion, ExecuteCommand,
GotoDefinition, HoverRequest, InlayHintRequest, InlayHintResolveRequest, References,
ResolveCompletionItem, SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles,
WorkspaceSymbol,
};
use lsp_types::{
CallHierarchyIncomingCallsParams, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
CodeAction, CodeActionParams, CodeLensParams, CompletionItem, CompletionParams,
ExecuteCommandParams, GotoDefinitionParams, HoverParams, InlayHint, InlayHintParams,
ReferenceParams, RenameFilesParams, SemanticTokensParams, SignatureHelpParams,
Expand Down Expand Up @@ -47,6 +49,9 @@ pub struct SendChannels {
will_rename_files: mpsc::Sender<WorkerMessage<RenameFilesParams>>,
execute_command: mpsc::Sender<WorkerMessage<ExecuteCommandParams>>,
workspace_symbol: mpsc::Sender<WorkerMessage<WorkspaceSymbolParams>>,
call_hierarchy_prepare: mpsc::Sender<WorkerMessage<CallHierarchyPrepareParams>>,
call_hierarchy_incoming: mpsc::Sender<WorkerMessage<CallHierarchyIncomingCallsParams>>,
call_hierarchy_outgoing: mpsc::Sender<WorkerMessage<CallHierarchyOutgoingCallsParams>>,
pub(crate) health_check: mpsc::Sender<WorkerMessage<()>>,
}

Expand All @@ -67,6 +72,9 @@ impl SendChannels {
let (tx_will_rename_files, rx_will_rename_files) = mpsc::channel();
let (tx_execute_command, rx_execute_command) = mpsc::channel();
let (tx_workspace_symbol, rx_workspace_symbol) = mpsc::channel();
let (tx_call_hierarchy_prepare, rx_call_hierarchy_prepare) = mpsc::channel();
let (tx_call_hierarchy_incoming, rx_call_hierarchy_incoming) = mpsc::channel();
let (tx_call_hierarchy_outgoing, rx_call_hierarchy_outgoing) = mpsc::channel();
let (tx_health_check, rx_health_check) = mpsc::channel();
(
Self {
Expand All @@ -85,6 +93,9 @@ impl SendChannels {
will_rename_files: tx_will_rename_files,
execute_command: tx_execute_command,
workspace_symbol: tx_workspace_symbol,
call_hierarchy_prepare: tx_call_hierarchy_prepare,
call_hierarchy_incoming: tx_call_hierarchy_incoming,
call_hierarchy_outgoing: tx_call_hierarchy_outgoing,
health_check: tx_health_check,
},
ReceiveChannels {
Expand All @@ -103,6 +114,9 @@ impl SendChannels {
will_rename_files: rx_will_rename_files,
execute_command: rx_execute_command,
workspace_symbol: rx_workspace_symbol,
call_hierarchy_prepare: rx_call_hierarchy_prepare,
call_hierarchy_incoming: rx_call_hierarchy_incoming,
call_hierarchy_outgoing: rx_call_hierarchy_outgoing,
health_check: rx_health_check,
},
)
Expand All @@ -124,6 +138,15 @@ impl SendChannels {
self.will_rename_files.send(WorkerMessage::Kill).unwrap();
self.execute_command.send(WorkerMessage::Kill).unwrap();
self.workspace_symbol.send(WorkerMessage::Kill).unwrap();
self.call_hierarchy_prepare
.send(WorkerMessage::Kill)
.unwrap();
self.call_hierarchy_incoming
.send(WorkerMessage::Kill)
.unwrap();
self.call_hierarchy_outgoing
.send(WorkerMessage::Kill)
.unwrap();
self.health_check.send(WorkerMessage::Kill).unwrap();
}
}
Expand All @@ -145,6 +168,11 @@ pub struct ReceiveChannels {
pub(crate) will_rename_files: mpsc::Receiver<WorkerMessage<RenameFilesParams>>,
pub(crate) execute_command: mpsc::Receiver<WorkerMessage<ExecuteCommandParams>>,
pub(crate) workspace_symbol: mpsc::Receiver<WorkerMessage<WorkspaceSymbolParams>>,
pub(crate) call_hierarchy_prepare: mpsc::Receiver<WorkerMessage<CallHierarchyPrepareParams>>,
pub(crate) call_hierarchy_incoming:
mpsc::Receiver<WorkerMessage<CallHierarchyIncomingCallsParams>>,
pub(crate) call_hierarchy_outgoing:
mpsc::Receiver<WorkerMessage<CallHierarchyOutgoingCallsParams>>,
pub(crate) health_check: mpsc::Receiver<WorkerMessage<()>>,
}

Expand Down Expand Up @@ -188,3 +216,18 @@ impl_sendable!(SignatureHelpRequest, SignatureHelpParams, signature_help);
impl_sendable!(WillRenameFiles, RenameFilesParams, will_rename_files);
impl_sendable!(ExecuteCommand, ExecuteCommandParams, execute_command);
impl_sendable!(WorkspaceSymbol, WorkspaceSymbolParams, workspace_symbol);
impl_sendable!(
CallHierarchyPrepare,
CallHierarchyPrepareParams,
call_hierarchy_prepare
);
impl_sendable!(
CallHierarchyIncomingCalls,
CallHierarchyIncomingCallsParams,
call_hierarchy_incoming
);
impl_sendable!(
CallHierarchyOutgoingCalls,
CallHierarchyOutgoingCallsParams,
call_hierarchy_outgoing
);
21 changes: 16 additions & 5 deletions crates/els/hir_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,32 @@ impl PosLocational for Position {
}
}

pub trait GetExprKind {
const KIND: ExprKind;
}
impl GetExprKind for Expr {
const KIND: ExprKind = ExprKind::Expr;
}
impl GetExprKind for Call {
const KIND: ExprKind = ExprKind::Call;
}
impl GetExprKind for Def {
const KIND: ExprKind = ExprKind::Def;
}

#[derive(Debug)]
pub enum ExprKind {
Call,
#[allow(dead_code)]
SubrDef,
// Def,
Def,
Expr,
}

impl ExprKind {
pub const fn matches(&self, expr: &Expr) -> bool {
match (self, expr) {
(ExprKind::Call, Expr::Call(_)) | (ExprKind::Expr, _) => true,
// (ExprKind::Def, Expr::Def(_)) => true,
(ExprKind::SubrDef, Expr::Def(def)) => def.sig.is_subr(),
(ExprKind::Def, Expr::Def(_)) => true,
// (ExprKind::Def, Expr::Def(def)) => def.sig.is_subr(),
_ => false,
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/els/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod call_hierarchy;
mod channels;
mod code_action;
mod code_lens;
Expand Down
1 change: 1 addition & 0 deletions crates/els/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod call_hierarchy;
mod channels;
mod code_action;
mod code_lens;
Expand Down
35 changes: 28 additions & 7 deletions crates/els/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@ use erg_compiler::module::{SharedCompilerResource, SharedModuleGraph, SharedModu
use erg_compiler::ty::HasType;

use lsp_types::request::{
CallHierarchyIncomingCalls, CallHierarchyOutgoingCalls, CallHierarchyPrepare,
CodeActionRequest, CodeActionResolveRequest, CodeLensRequest, Completion, ExecuteCommand,
GotoDefinition, HoverRequest, InlayHintRequest, InlayHintResolveRequest, References, Rename,
Request, ResolveCompletionItem, SemanticTokensFullRequest, SignatureHelpRequest,
WillRenameFiles, WorkspaceSymbol,
};
use lsp_types::{
CodeActionKind, CodeActionOptions, CodeActionProviderCapability, CodeLensOptions,
CompletionOptions, ConfigurationItem, ConfigurationParams, DidChangeTextDocumentParams,
DidOpenTextDocumentParams, ExecuteCommandOptions, HoverProviderCapability, InitializeParams,
InitializeResult, InlayHintOptions, InlayHintServerCapabilities, OneOf, Position,
SemanticTokenType, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelpOptions,
WorkDoneProgressOptions,
CallHierarchyServerCapability, CodeActionKind, CodeActionOptions, CodeActionProviderCapability,
CodeLensOptions, CompletionOptions, ConfigurationItem, ConfigurationParams,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, ExecuteCommandOptions,
HoverProviderCapability, InitializeParams, InitializeResult, InlayHintOptions,
InlayHintServerCapabilities, OneOf, Position, SemanticTokenType, SemanticTokensFullOptions,
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensServerCapabilities,
ServerCapabilities, SignatureHelpOptions, WorkDoneProgressOptions,
};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -503,6 +504,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
resolve_provider: Some(false),
});
capabilities.workspace_symbol_provider = Some(OneOf::Left(true));
capabilities.call_hierarchy_provider = Some(CallHierarchyServerCapability::Simple(true));
capabilities
}

Expand Down Expand Up @@ -566,6 +568,18 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
receivers.workspace_symbol,
Self::handle_workspace_symbol,
);
self.start_service::<CallHierarchyPrepare>(
receivers.call_hierarchy_prepare,
Self::handle_call_hierarchy_prepare,
);
self.start_service::<CallHierarchyIncomingCalls>(
receivers.call_hierarchy_incoming,
Self::handle_call_hierarchy_incoming,
);
self.start_service::<CallHierarchyOutgoingCalls>(
receivers.call_hierarchy_outgoing,
Self::handle_call_hierarchy_outgoing,
);
self.start_client_health_checker(receivers.health_check);
}

Expand Down Expand Up @@ -746,6 +760,13 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
WillRenameFiles::METHOD => self.parse_send::<WillRenameFiles>(id, msg),
ExecuteCommand::METHOD => self.parse_send::<ExecuteCommand>(id, msg),
WorkspaceSymbol::METHOD => self.parse_send::<WorkspaceSymbol>(id, msg),
CallHierarchyIncomingCalls::METHOD => {
self.parse_send::<CallHierarchyIncomingCalls>(id, msg)
}
CallHierarchyOutgoingCalls::METHOD => {
self.parse_send::<CallHierarchyOutgoingCalls>(id, msg)
}
CallHierarchyPrepare::METHOD => self.parse_send::<CallHierarchyPrepare>(id, msg),
other => send_error(Some(id), -32600, format!("{other} is not supported")),
}
}
Expand Down
15 changes: 10 additions & 5 deletions crates/els/sig_help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use lsp_types::{
SignatureHelpParams, SignatureHelpTriggerKind, SignatureInformation,
};

use crate::hir_visitor::ExprKind;
use crate::hir_visitor::GetExprKind;
use crate::server::{send_log, ELSResult, Server};
use crate::util::{loc_to_pos, pos_to_loc, NormalizedUrl};

Expand Down Expand Up @@ -92,9 +92,14 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
None
}

pub(crate) fn get_min_call(&self, uri: &NormalizedUrl, pos: Position) -> Option<Expr> {
self.get_searcher(uri, ExprKind::Call)
pub(crate) fn get_min<E: TryFrom<Expr> + GetExprKind>(
&self,
uri: &NormalizedUrl,
pos: Position,
) -> Option<E> {
self.get_searcher(uri, E::KIND)
.and_then(|visitor| visitor.get_min_expr(pos).cloned())
.and_then(|expr| E::try_from(expr).ok())
}

pub(crate) fn nth(&self, uri: &NormalizedUrl, call: &Call, pos: Position) -> usize {
Expand Down Expand Up @@ -136,7 +141,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
) -> Option<SignatureHelp> {
if let Some(token) = self.file_cache.get_token(uri, pos) {
crate::_log!("token: {token}");
if let Some(Expr::Call(call)) = self.get_min_call(uri, pos) {
if let Some(call) = self.get_min::<Call>(uri, pos) {
if call.ln_begin() > token.ln_begin() || call.ln_end() < token.ln_end() {
return None;
}
Expand All @@ -161,7 +166,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
}

fn get_continuous_help(&mut self, uri: &NormalizedUrl, pos: Position) -> Option<SignatureHelp> {
if let Some(Expr::Call(call)) = self.get_min_call(uri, pos) {
if let Some(call) = self.get_min::<Call>(uri, pos) {
let nth = self.nth(uri, &call, pos) as u32 + 1;
let help = self.make_sig_help(call.obj.as_ref(), nth);
return help;
Expand Down
2 changes: 1 addition & 1 deletion crates/els/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::_log;
use crate::server::{ELSResult, Server};
use crate::util::abs_loc_to_lsp_loc;

fn symbol_kind(vi: &VarInfo) -> SymbolKind {
pub(crate) fn symbol_kind(vi: &VarInfo) -> SymbolKind {
match &vi.t {
Type::Subr(subr) if subr.self_t().is_some() => SymbolKind::METHOD,
Type::Quantified(quant) if quant.self_t().is_some() => SymbolKind::METHOD,
Expand Down

0 comments on commit d4699cf

Please sign in to comment.