diff --git a/crates/els/channels.rs b/crates/els/channels.rs new file mode 100644 index 000000000..b46a4b2e2 --- /dev/null +++ b/crates/els/channels.rs @@ -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 { + fn send(&self, id: i64, params: R::Params); +} + +macro_rules! impl_sendable { + ($Request: ident, $Params: ident, $receiver: ident) => { + impl Sendable<$Request> + for Server + { + 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); diff --git a/crates/els/completion.rs b/crates/els/completion.rs index 80321c6b3..8caa412c6 100644 --- a/crates/els/completion.rs +++ b/crates/els/completion.rs @@ -179,7 +179,7 @@ impl<'b> CompletionOrderSetter<'b> { type Cache = Shared>>; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CompletionCache { cache: Cache, } diff --git a/crates/els/lib.rs b/crates/els/lib.rs index ed7eb9635..e97afd6cf 100644 --- a/crates/els/lib.rs +++ b/crates/els/lib.rs @@ -1,3 +1,4 @@ +mod channels; mod code_action; mod code_lens; mod command; diff --git a/crates/els/main.rs b/crates/els/main.rs index 8992beb07..079af54e3 100644 --- a/crates/els/main.rs +++ b/crates/els/main.rs @@ -1,3 +1,4 @@ +mod channels; mod code_action; mod code_lens; mod command; diff --git a/crates/els/server.rs b/crates/els/server.rs index 5dc5bac15..d6e5f0f4d 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -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>); impl AnalysisResultCache { @@ -265,6 +268,41 @@ impl AnalysisResultCache { } } +#[derive(Debug, Clone, Default)] +pub struct ModuleCache(Shared>); + +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 { + self.0.borrow_mut().remove(uri) + } + + pub fn values(&self) -> std::collections::hash_map::Values { + 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 { @@ -276,12 +314,33 @@ pub struct Server, pub(crate) file_cache: FileCache, pub(crate) comp_cache: CompletionCache, - // TODO: ModuleContextCache - pub(crate) modules: Dict, + pub(crate) modules: ModuleCache, pub(crate) analysis_result: AnalysisResultCache, pub(crate) current_sig: Option, - pub(crate) _parser: std::marker::PhantomData, - pub(crate) _checker: std::marker::PhantomData, + pub(crate) channels: Option, + pub(crate) _parser: std::marker::PhantomData Parser>, + pub(crate) _checker: std::marker::PhantomData Checker>, +} + +impl Clone for Server { + 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 Server { @@ -295,9 +354,10 @@ impl Server { 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 Server { 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 Server { })) } + fn init_services(&mut self) { + let (senders, receivers) = SendChannels::new(); + self.channels = Some(senders); + self.start_service::(receivers.completion, Self::handle_completion); + self.start_service::( + receivers.resolve_completion, + Self::handle_resolve_completion, + ); + self.start_service::( + receivers.goto_definition, + Self::handle_goto_definition, + ); + self.start_service::( + receivers.semantic_tokens_full, + Self::handle_semantic_tokens_full, + ); + self.start_service::(receivers.inlay_hint, Self::handle_inlay_hint); + self.start_service::(receivers.hover, Self::handle_hover); + self.start_service::(receivers.references, Self::handle_references); + self.start_service::(receivers.code_lens, Self::handle_code_lens); + self.start_service::(receivers.code_action, Self::handle_code_action); + self.start_service::( + receivers.code_action_resolve, + Self::handle_code_action_resolve, + ); + self.start_service::( + receivers.signature_help, + Self::handle_signature_help, + ); + self.start_service::( + receivers.will_rename_files, + Self::handle_will_rename_files, + ); + self.start_service::( + receivers.execute_command, + Self::handle_execute_command, + ); + } + fn exit(&self) -> ELSResult<()> { send_log("exiting ELS")?; std::process::exit(0); @@ -523,18 +623,34 @@ impl Server { } } - fn wrap( - &mut self, - id: i64, - msg: &Value, - handler: Handler, R::Params, R::Result>, - ) -> ELSResult<()> + fn parse_send(&self, id: i64, msg: &Value) -> ELSResult<()> where R: lsp_types::request::Request + 'static, R::Result: Serialize, + Server: Sendable, { let params = R::Params::deserialize(&msg["params"])?; - send(&LSPResult::new(id, handler(self, params)?)) + self.send(id, params); + Ok(()) + } + + fn start_service( + &self, + receiver: mpsc::Receiver<(i64, R::Params)>, + handler: Handler, 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 Server { "initialize" => self.init(msg, id), "shutdown" => self.shutdown(id), Rename::METHOD => self.rename(msg), - Completion::METHOD => self.wrap::(id, msg, Self::handle_completion), - ResolveCompletionItem::METHOD => { - self.wrap::(id, msg, Self::handle_resolve_completion) - } - GotoDefinition::METHOD => { - self.wrap::(id, msg, Self::handle_goto_definition) - } - HoverRequest::METHOD => self.wrap::(id, msg, Self::handle_hover), - References::METHOD => self.wrap::(id, msg, Self::handle_references), + Completion::METHOD => self.parse_send::(id, msg), + ResolveCompletionItem::METHOD => self.parse_send::(id, msg), + GotoDefinition::METHOD => self.parse_send::(id, msg), + HoverRequest::METHOD => self.parse_send::(id, msg), + References::METHOD => self.parse_send::(id, msg), SemanticTokensFullRequest::METHOD => { - self.wrap::(id, msg, Self::handle_semantic_tokens_full) - } - InlayHintRequest::METHOD => { - self.wrap::(id, msg, Self::handle_inlay_hint) - } - CodeActionRequest::METHOD => { - self.wrap::(id, msg, Self::handle_code_action) + self.parse_send::(id, msg) } + InlayHintRequest::METHOD => self.parse_send::(id, msg), + CodeActionRequest::METHOD => self.parse_send::(id, msg), CodeActionResolveRequest::METHOD => { - self.wrap::(id, msg, Self::handle_code_action_resolve) - } - SignatureHelpRequest::METHOD => { - self.wrap::(id, msg, Self::handle_signature_help) - } - CodeLensRequest::METHOD => { - self.wrap::(id, msg, Self::handle_code_lens) - } - WillRenameFiles::METHOD => { - self.wrap::(id, msg, Self::handle_will_rename_files) - } - ExecuteCommand::METHOD => { - self.wrap::(id, msg, Self::handle_execute_command) + self.parse_send::(id, msg) } + SignatureHelpRequest::METHOD => self.parse_send::(id, msg), + CodeLensRequest::METHOD => self.parse_send::(id, msg), + WillRenameFiles::METHOD => self.parse_send::(id, msg), + ExecuteCommand::METHOD => self.parse_send::(id, msg), other => send_error(Some(id), -32600, format!("{other} is not supported")), } } diff --git a/crates/erg_compiler/artifact.rs b/crates/erg_compiler/artifact.rs index d73e6ce22..3a5de6417 100644 --- a/crates/erg_compiler/artifact.rs +++ b/crates/erg_compiler/artifact.rs @@ -103,4 +103,4 @@ pub trait Buildable { fn get_context(&self) -> Option<&ModuleContext>; } -pub trait BuildRunnable: Buildable + Runnable {} +pub trait BuildRunnable: Buildable + Runnable + 'static {} diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index a7624c1e3..4045bae2f 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -98,7 +98,7 @@ macro_rules! expect_pop { }; } -pub trait Parsable { +pub trait Parsable: 'static { fn parse(code: String) -> Result>; }