feat(els): parallelize Server

This commit is contained in:
Shunsuke Shibayama 2023-06-21 01:16:53 +09:00
parent 5aa73ef19b
commit d49e6fa70c
7 changed files with 298 additions and 56 deletions

140
crates/els/channels.rs Normal file
View file

@ -0,0 +1,140 @@
use std::sync::mpsc;
use erg_compiler::artifact::BuildRunnable;
use erg_compiler::erg_parser::parse::Parsable;
use lsp_types::request::{
CodeActionRequest, CodeActionResolveRequest, CodeLensRequest, Completion, ExecuteCommand,
GotoDefinition, HoverRequest, InlayHintRequest, References, ResolveCompletionItem,
SemanticTokensFullRequest, SignatureHelpRequest, WillRenameFiles,
};
use lsp_types::{
CodeAction, CodeActionParams, CodeLensParams, CompletionItem, CompletionParams,
ExecuteCommandParams, GotoDefinitionParams, HoverParams, InlayHintParams, ReferenceParams,
RenameFilesParams, SemanticTokensParams, SignatureHelpParams,
};
use crate::server::Server;
#[derive(Debug, Clone)]
pub struct SendChannels {
completion: mpsc::Sender<(i64, CompletionParams)>,
resolve_completion: mpsc::Sender<(i64, CompletionItem)>,
goto_definition: mpsc::Sender<(i64, GotoDefinitionParams)>,
semantic_tokens_full: mpsc::Sender<(i64, SemanticTokensParams)>,
inlay_hint: mpsc::Sender<(i64, InlayHintParams)>,
hover: mpsc::Sender<(i64, HoverParams)>,
references: mpsc::Sender<(i64, ReferenceParams)>,
code_lens: mpsc::Sender<(i64, CodeLensParams)>,
code_action: mpsc::Sender<(i64, CodeActionParams)>,
code_action_resolve: mpsc::Sender<(i64, CodeAction)>,
signature_help: mpsc::Sender<(i64, SignatureHelpParams)>,
will_rename_files: mpsc::Sender<(i64, RenameFilesParams)>,
execute_command: mpsc::Sender<(i64, ExecuteCommandParams)>,
}
impl SendChannels {
pub fn new() -> (Self, ReceiveChannels) {
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_semantic_tokens_full, rx_semantic_tokens_full) = mpsc::channel();
let (tx_inlay_hint, rx_inlay_hint) = mpsc::channel();
let (tx_hover, rx_hover) = mpsc::channel();
let (tx_references, rx_references) = mpsc::channel();
let (tx_code_lens, rx_code_lens) = mpsc::channel();
let (tx_code_action, rx_code_action) = mpsc::channel();
let (tx_code_action_resolve, rx_code_action_resolve) = mpsc::channel();
let (tx_sig_help, rx_sig_help) = mpsc::channel();
let (tx_will_rename_files, rx_will_rename_files) = mpsc::channel();
let (tx_execute_command, rx_execute_command) = mpsc::channel();
(
Self {
completion: tx_completion,
resolve_completion: tx_resolve_completion,
goto_definition: tx_goto_definition,
semantic_tokens_full: tx_semantic_tokens_full,
inlay_hint: tx_inlay_hint,
hover: tx_hover,
references: tx_references,
code_lens: tx_code_lens,
code_action: tx_code_action,
code_action_resolve: tx_code_action_resolve,
signature_help: tx_sig_help,
will_rename_files: tx_will_rename_files,
execute_command: tx_execute_command,
},
ReceiveChannels {
completion: rx_completion,
resolve_completion: rx_resolve_completion,
goto_definition: rx_goto_definition,
semantic_tokens_full: rx_semantic_tokens_full,
inlay_hint: rx_inlay_hint,
hover: rx_hover,
references: rx_references,
code_lens: rx_code_lens,
code_action: rx_code_action,
code_action_resolve: rx_code_action_resolve,
signature_help: rx_sig_help,
will_rename_files: rx_will_rename_files,
execute_command: rx_execute_command,
},
)
}
}
#[derive(Debug)]
pub struct ReceiveChannels {
pub(crate) completion: mpsc::Receiver<(i64, CompletionParams)>,
pub(crate) resolve_completion: mpsc::Receiver<(i64, CompletionItem)>,
pub(crate) goto_definition: mpsc::Receiver<(i64, GotoDefinitionParams)>,
pub(crate) semantic_tokens_full: mpsc::Receiver<(i64, SemanticTokensParams)>,
pub(crate) inlay_hint: mpsc::Receiver<(i64, InlayHintParams)>,
pub(crate) hover: mpsc::Receiver<(i64, HoverParams)>,
pub(crate) references: mpsc::Receiver<(i64, ReferenceParams)>,
pub(crate) code_lens: mpsc::Receiver<(i64, CodeLensParams)>,
pub(crate) code_action: mpsc::Receiver<(i64, CodeActionParams)>,
pub(crate) code_action_resolve: mpsc::Receiver<(i64, CodeAction)>,
pub(crate) signature_help: mpsc::Receiver<(i64, SignatureHelpParams)>,
pub(crate) will_rename_files: mpsc::Receiver<(i64, RenameFilesParams)>,
pub(crate) execute_command: mpsc::Receiver<(i64, ExecuteCommandParams)>,
}
pub trait Sendable<R: lsp_types::request::Request + 'static> {
fn send(&self, id: i64, params: R::Params);
}
macro_rules! impl_sendable {
($Request: ident, $Params: ident, $receiver: ident) => {
impl<Checker: BuildRunnable, Parser: Parsable> Sendable<$Request>
for Server<Checker, Parser>
{
fn send(&self, id: i64, params: $Params) {
self.channels
.as_ref()
.unwrap()
.$receiver
.send((id, params))
.unwrap();
}
}
};
}
impl_sendable!(Completion, CompletionParams, completion);
impl_sendable!(ResolveCompletionItem, CompletionItem, resolve_completion);
impl_sendable!(GotoDefinition, GotoDefinitionParams, goto_definition);
impl_sendable!(
SemanticTokensFullRequest,
SemanticTokensParams,
semantic_tokens_full
);
impl_sendable!(InlayHintRequest, InlayHintParams, inlay_hint);
impl_sendable!(HoverRequest, HoverParams, hover);
impl_sendable!(References, ReferenceParams, references);
impl_sendable!(CodeLensRequest, CodeLensParams, code_lens);
impl_sendable!(CodeActionRequest, CodeActionParams, code_action);
impl_sendable!(CodeActionResolveRequest, CodeAction, code_action_resolve);
impl_sendable!(SignatureHelpRequest, SignatureHelpParams, signature_help);
impl_sendable!(WillRenameFiles, RenameFilesParams, will_rename_files);
impl_sendable!(ExecuteCommand, ExecuteCommandParams, execute_command);

View file

@ -179,7 +179,7 @@ impl<'b> CompletionOrderSetter<'b> {
type Cache = Shared<Dict<String, Vec<CompletionItem>>>;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CompletionCache {
cache: Cache,
}

View file

@ -1,3 +1,4 @@
mod channels;
mod code_action;
mod code_lens;
mod command;

View file

@ -1,3 +1,4 @@
mod channels;
mod code_action;
mod code_lens;
mod command;

View file

@ -3,27 +3,25 @@ use std::io::{stdin, stdout, BufRead, Read, Write};
use std::ops::Not;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::mpsc;
use erg_common::config::ErgConfig;
use erg_common::consts::PYTHON_MODE;
use erg_common::dict::Dict;
use erg_common::env::erg_path;
use erg_common::shared::{
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLockReadGuard, RwLockWriteGuard, Shared,
};
use erg_compiler::erg_parser::ast::Module;
use erg_compiler::erg_parser::parse::{Parsable, SimpleParser};
use erg_compiler::lower::ASTLowerer;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use erg_common::config::ErgConfig;
use erg_common::dict::Dict;
use erg_common::env::erg_path;
use erg_common::normalize_path;
use erg_common::spawn::spawn_new_thread;
use erg_common::{fn_name, normalize_path};
use erg_compiler::artifact::{BuildRunnable, IncompleteArtifact};
use erg_compiler::build_hir::HIRBuilder;
use erg_compiler::context::{Context, ModuleContext};
use erg_compiler::erg_parser::ast::Module;
use erg_compiler::erg_parser::parse::{Parsable, SimpleParser};
use erg_compiler::hir::{Expr, HIR};
use erg_compiler::lower::ASTLowerer;
use erg_compiler::module::{SharedCompilerResource, SharedModuleIndex};
use erg_compiler::ty::HasType;
@ -41,6 +39,11 @@ use lsp_types::{
WorkDoneProgressOptions,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use crate::channels::{SendChannels, Sendable};
use crate::completion::CompletionCache;
use crate::file_cache::FileCache;
use crate::hir_visitor::HIRVisitor;
@ -191,7 +194,7 @@ impl AnalysisResult {
pub(crate) const TRIGGER_CHARS: [&str; 4] = [".", ":", "(", " "];
#[derive(Debug, Default)]
#[derive(Debug, Clone, Default)]
pub struct AnalysisResultCache(Shared<Dict<NormalizedUrl, AnalysisResult>>);
impl AnalysisResultCache {
@ -265,6 +268,41 @@ impl AnalysisResultCache {
}
}
#[derive(Debug, Clone, Default)]
pub struct ModuleCache(Shared<Dict<NormalizedUrl, ModuleContext>>);
impl ModuleCache {
pub fn new() -> Self {
Self(Shared::new(Dict::new()))
}
pub fn get(&self, uri: &NormalizedUrl) -> Option<&ModuleContext> {
let _ref = self.0.borrow();
let ref_ = unsafe { self.0.as_ptr().as_ref() };
ref_.unwrap().get(uri)
}
pub fn get_mut(&self, uri: &NormalizedUrl) -> Option<&mut ModuleContext> {
let _ref = self.0.borrow_mut();
let ref_ = unsafe { self.0.as_ptr().as_mut() };
ref_.unwrap().get_mut(uri)
}
pub fn insert(&self, uri: NormalizedUrl, module: ModuleContext) {
self.0.borrow_mut().insert(uri, module);
}
pub fn remove(&self, uri: &NormalizedUrl) -> Option<ModuleContext> {
self.0.borrow_mut().remove(uri)
}
pub fn values(&self) -> std::collections::hash_map::Values<NormalizedUrl, ModuleContext> {
let _ref = self.0.borrow();
let ref_ = unsafe { self.0.as_ptr().as_ref() };
ref_.unwrap().values()
}
}
/// A Language Server, which can be used any object implementing `BuildRunnable` internally by passing it as a generic parameter.
#[derive(Debug)]
pub struct Server<Checker: BuildRunnable = HIRBuilder, Parser: Parsable = SimpleParser> {
@ -276,12 +314,33 @@ pub struct Server<Checker: BuildRunnable = HIRBuilder, Parser: Parsable = Simple
pub(crate) opt_features: Vec<OptionalFeatures>,
pub(crate) file_cache: FileCache,
pub(crate) comp_cache: CompletionCache,
// TODO: ModuleContextCache
pub(crate) modules: Dict<NormalizedUrl, ModuleContext>,
pub(crate) modules: ModuleCache,
pub(crate) analysis_result: AnalysisResultCache,
pub(crate) current_sig: Option<Expr>,
pub(crate) _parser: std::marker::PhantomData<Parser>,
pub(crate) _checker: std::marker::PhantomData<Checker>,
pub(crate) channels: Option<SendChannels>,
pub(crate) _parser: std::marker::PhantomData<fn() -> Parser>,
pub(crate) _checker: std::marker::PhantomData<fn() -> Checker>,
}
impl<C: BuildRunnable, P: Parsable> Clone for Server<C, P> {
fn clone(&self) -> Self {
Self {
cfg: self.cfg.clone(),
home: self.home.clone(),
erg_path: self.erg_path.clone(),
client_capas: self.client_capas.clone(),
disabled_features: self.disabled_features.clone(),
opt_features: self.opt_features.clone(),
file_cache: self.file_cache.clone(),
comp_cache: self.comp_cache.clone(),
modules: self.modules.clone(),
analysis_result: self.analysis_result.clone(),
current_sig: self.current_sig.clone(),
channels: self.channels.clone(),
_parser: std::marker::PhantomData,
_checker: std::marker::PhantomData,
}
}
}
impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
@ -295,9 +354,10 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
disabled_features: vec![],
opt_features: vec![],
file_cache: FileCache::new(),
modules: Dict::new(),
modules: ModuleCache::new(),
analysis_result: AnalysisResultCache::new(),
current_sig: None,
channels: None,
_parser: std::marker::PhantomData,
_checker: std::marker::PhantomData,
}
@ -420,6 +480,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
result.capabilities.code_lens_provider = Some(CodeLensOptions {
resolve_provider: Some(false),
});
self.init_services();
send(&json!({
"jsonrpc": "2.0",
"id": id,
@ -427,6 +488,45 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
}))
}
fn init_services(&mut self) {
let (senders, receivers) = SendChannels::new();
self.channels = Some(senders);
self.start_service::<Completion>(receivers.completion, Self::handle_completion);
self.start_service::<ResolveCompletionItem>(
receivers.resolve_completion,
Self::handle_resolve_completion,
);
self.start_service::<GotoDefinition>(
receivers.goto_definition,
Self::handle_goto_definition,
);
self.start_service::<SemanticTokensFullRequest>(
receivers.semantic_tokens_full,
Self::handle_semantic_tokens_full,
);
self.start_service::<InlayHintRequest>(receivers.inlay_hint, Self::handle_inlay_hint);
self.start_service::<HoverRequest>(receivers.hover, Self::handle_hover);
self.start_service::<References>(receivers.references, Self::handle_references);
self.start_service::<CodeLensRequest>(receivers.code_lens, Self::handle_code_lens);
self.start_service::<CodeActionRequest>(receivers.code_action, Self::handle_code_action);
self.start_service::<CodeActionResolveRequest>(
receivers.code_action_resolve,
Self::handle_code_action_resolve,
);
self.start_service::<SignatureHelpRequest>(
receivers.signature_help,
Self::handle_signature_help,
);
self.start_service::<WillRenameFiles>(
receivers.will_rename_files,
Self::handle_will_rename_files,
);
self.start_service::<ExecuteCommand>(
receivers.execute_command,
Self::handle_execute_command,
);
}
fn exit(&self) -> ELSResult<()> {
send_log("exiting ELS")?;
std::process::exit(0);
@ -523,18 +623,34 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
}
}
fn wrap<R>(
&mut self,
id: i64,
msg: &Value,
handler: Handler<Server<Checker, Parser>, R::Params, R::Result>,
) -> ELSResult<()>
fn parse_send<R>(&self, id: i64, msg: &Value) -> ELSResult<()>
where
R: lsp_types::request::Request + 'static,
R::Result: Serialize,
Server<Checker, Parser>: Sendable<R>,
{
let params = R::Params::deserialize(&msg["params"])?;
send(&LSPResult::new(id, handler(self, params)?))
self.send(id, params);
Ok(())
}
fn start_service<R>(
&self,
receiver: mpsc::Receiver<(i64, R::Params)>,
handler: Handler<Server<Checker, Parser>, R::Params, R::Result>,
) where
R: lsp_types::request::Request + 'static,
R::Params: Send,
R::Result: Serialize,
{
let mut _self = self.clone();
spawn_new_thread(
move || loop {
let (id, params) = receiver.recv().unwrap();
let _ = send(&LSPResult::new(id, handler(&mut _self, params).unwrap()));
},
fn_name!(),
);
}
fn handle_request(&mut self, msg: &Value, id: i64, method: &str) -> ELSResult<()> {
@ -542,39 +658,23 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
"initialize" => self.init(msg, id),
"shutdown" => self.shutdown(id),
Rename::METHOD => self.rename(msg),
Completion::METHOD => self.wrap::<Completion>(id, msg, Self::handle_completion),
ResolveCompletionItem::METHOD => {
self.wrap::<ResolveCompletionItem>(id, msg, Self::handle_resolve_completion)
}
GotoDefinition::METHOD => {
self.wrap::<GotoDefinition>(id, msg, Self::handle_goto_definition)
}
HoverRequest::METHOD => self.wrap::<HoverRequest>(id, msg, Self::handle_hover),
References::METHOD => self.wrap::<References>(id, msg, Self::handle_references),
Completion::METHOD => self.parse_send::<Completion>(id, msg),
ResolveCompletionItem::METHOD => self.parse_send::<ResolveCompletionItem>(id, msg),
GotoDefinition::METHOD => self.parse_send::<GotoDefinition>(id, msg),
HoverRequest::METHOD => self.parse_send::<HoverRequest>(id, msg),
References::METHOD => self.parse_send::<References>(id, msg),
SemanticTokensFullRequest::METHOD => {
self.wrap::<SemanticTokensFullRequest>(id, msg, Self::handle_semantic_tokens_full)
}
InlayHintRequest::METHOD => {
self.wrap::<InlayHintRequest>(id, msg, Self::handle_inlay_hint)
}
CodeActionRequest::METHOD => {
self.wrap::<CodeActionRequest>(id, msg, Self::handle_code_action)
self.parse_send::<SemanticTokensFullRequest>(id, msg)
}
InlayHintRequest::METHOD => self.parse_send::<InlayHintRequest>(id, msg),
CodeActionRequest::METHOD => self.parse_send::<CodeActionRequest>(id, msg),
CodeActionResolveRequest::METHOD => {
self.wrap::<CodeActionResolveRequest>(id, msg, Self::handle_code_action_resolve)
}
SignatureHelpRequest::METHOD => {
self.wrap::<SignatureHelpRequest>(id, msg, Self::handle_signature_help)
}
CodeLensRequest::METHOD => {
self.wrap::<CodeLensRequest>(id, msg, Self::handle_code_lens)
}
WillRenameFiles::METHOD => {
self.wrap::<WillRenameFiles>(id, msg, Self::handle_will_rename_files)
}
ExecuteCommand::METHOD => {
self.wrap::<ExecuteCommand>(id, msg, Self::handle_execute_command)
self.parse_send::<CodeActionResolveRequest>(id, msg)
}
SignatureHelpRequest::METHOD => self.parse_send::<SignatureHelpRequest>(id, msg),
CodeLensRequest::METHOD => self.parse_send::<CodeLensRequest>(id, msg),
WillRenameFiles::METHOD => self.parse_send::<WillRenameFiles>(id, msg),
ExecuteCommand::METHOD => self.parse_send::<ExecuteCommand>(id, msg),
other => send_error(Some(id), -32600, format!("{other} is not supported")),
}
}

View file

@ -103,4 +103,4 @@ pub trait Buildable<T = HIR> {
fn get_context(&self) -> Option<&ModuleContext>;
}
pub trait BuildRunnable<T = HIR>: Buildable<T> + Runnable {}
pub trait BuildRunnable<T = HIR>: Buildable<T> + Runnable + 'static {}

View file

@ -98,7 +98,7 @@ macro_rules! expect_pop {
};
}
pub trait Parsable {
pub trait Parsable: 'static {
fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParseErrors>>;
}