mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-03 10:23:20 +00:00
commit
01fae890a4
12 changed files with 244 additions and 67 deletions
|
@ -16,23 +16,35 @@ use lsp_types::{
|
|||
|
||||
use crate::server::Server;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WorkerMessage<P> {
|
||||
Request(i64, P),
|
||||
Kill,
|
||||
}
|
||||
|
||||
impl<P> From<(i64, P)> for WorkerMessage<P> {
|
||||
fn from((id, params): (i64, P)) -> Self {
|
||||
Self::Request(id, params)
|
||||
}
|
||||
}
|
||||
|
||||
#[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)>,
|
||||
inlay_hint_resolve: mpsc::Sender<(i64, InlayHint)>,
|
||||
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)>,
|
||||
pub(crate) health_check: mpsc::Sender<()>,
|
||||
completion: mpsc::Sender<WorkerMessage<CompletionParams>>,
|
||||
resolve_completion: mpsc::Sender<WorkerMessage<CompletionItem>>,
|
||||
goto_definition: mpsc::Sender<WorkerMessage<GotoDefinitionParams>>,
|
||||
semantic_tokens_full: mpsc::Sender<WorkerMessage<SemanticTokensParams>>,
|
||||
inlay_hint: mpsc::Sender<WorkerMessage<InlayHintParams>>,
|
||||
inlay_hint_resolve: mpsc::Sender<WorkerMessage<InlayHint>>,
|
||||
hover: mpsc::Sender<WorkerMessage<HoverParams>>,
|
||||
references: mpsc::Sender<WorkerMessage<ReferenceParams>>,
|
||||
code_lens: mpsc::Sender<WorkerMessage<CodeLensParams>>,
|
||||
code_action: mpsc::Sender<WorkerMessage<CodeActionParams>>,
|
||||
code_action_resolve: mpsc::Sender<WorkerMessage<CodeAction>>,
|
||||
signature_help: mpsc::Sender<WorkerMessage<SignatureHelpParams>>,
|
||||
will_rename_files: mpsc::Sender<WorkerMessage<RenameFilesParams>>,
|
||||
execute_command: mpsc::Sender<WorkerMessage<ExecuteCommandParams>>,
|
||||
pub(crate) health_check: mpsc::Sender<WorkerMessage<()>>,
|
||||
}
|
||||
|
||||
impl SendChannels {
|
||||
|
@ -89,25 +101,43 @@ impl SendChannels {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn close(&self) {
|
||||
self.completion.send(WorkerMessage::Kill).unwrap();
|
||||
self.resolve_completion.send(WorkerMessage::Kill).unwrap();
|
||||
self.goto_definition.send(WorkerMessage::Kill).unwrap();
|
||||
self.semantic_tokens_full.send(WorkerMessage::Kill).unwrap();
|
||||
self.inlay_hint.send(WorkerMessage::Kill).unwrap();
|
||||
self.inlay_hint_resolve.send(WorkerMessage::Kill).unwrap();
|
||||
self.hover.send(WorkerMessage::Kill).unwrap();
|
||||
self.references.send(WorkerMessage::Kill).unwrap();
|
||||
self.code_lens.send(WorkerMessage::Kill).unwrap();
|
||||
self.code_action.send(WorkerMessage::Kill).unwrap();
|
||||
self.code_action_resolve.send(WorkerMessage::Kill).unwrap();
|
||||
self.signature_help.send(WorkerMessage::Kill).unwrap();
|
||||
self.will_rename_files.send(WorkerMessage::Kill).unwrap();
|
||||
self.execute_command.send(WorkerMessage::Kill).unwrap();
|
||||
self.health_check.send(WorkerMessage::Kill).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[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) inlay_hint_resolve: mpsc::Receiver<(i64, InlayHint)>,
|
||||
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(crate) health_check: mpsc::Receiver<()>,
|
||||
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) semantic_tokens_full: mpsc::Receiver<WorkerMessage<SemanticTokensParams>>,
|
||||
pub(crate) inlay_hint: mpsc::Receiver<WorkerMessage<InlayHintParams>>,
|
||||
pub(crate) inlay_hint_resolve: mpsc::Receiver<WorkerMessage<InlayHint>>,
|
||||
pub(crate) hover: mpsc::Receiver<WorkerMessage<HoverParams>>,
|
||||
pub(crate) references: mpsc::Receiver<WorkerMessage<ReferenceParams>>,
|
||||
pub(crate) code_lens: mpsc::Receiver<WorkerMessage<CodeLensParams>>,
|
||||
pub(crate) code_action: mpsc::Receiver<WorkerMessage<CodeActionParams>>,
|
||||
pub(crate) code_action_resolve: mpsc::Receiver<WorkerMessage<CodeAction>>,
|
||||
pub(crate) signature_help: mpsc::Receiver<WorkerMessage<SignatureHelpParams>>,
|
||||
pub(crate) will_rename_files: mpsc::Receiver<WorkerMessage<RenameFilesParams>>,
|
||||
pub(crate) execute_command: mpsc::Receiver<WorkerMessage<ExecuteCommandParams>>,
|
||||
pub(crate) health_check: mpsc::Receiver<WorkerMessage<()>>,
|
||||
}
|
||||
|
||||
pub trait Sendable<R: lsp_types::request::Request + 'static> {
|
||||
|
@ -124,7 +154,7 @@ macro_rules! impl_sendable {
|
|||
.as_ref()
|
||||
.unwrap()
|
||||
.$receiver
|
||||
.send((id, params))
|
||||
.send($crate::channels::WorkerMessage::Request(id, params))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -423,8 +423,8 @@ impl CompletionCache {
|
|||
self.cache.borrow_mut().insert(namespace, items);
|
||||
}
|
||||
|
||||
pub fn _clear(&self, namespace: &str) {
|
||||
self.cache.borrow_mut().remove(namespace);
|
||||
pub fn clear(&self) {
|
||||
self.cache.borrow_mut().clear();
|
||||
}
|
||||
|
||||
pub fn _append(&self, cache: Dict<String, Vec<CompletionItem>>) {
|
||||
|
|
|
@ -19,9 +19,12 @@ use lsp_types::{
|
|||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::_log;
|
||||
use crate::channels::WorkerMessage;
|
||||
use crate::diff::{ASTDiff, HIRDiff};
|
||||
use crate::server::{
|
||||
send, send_log, AnalysisResult, DefaultFeatures, ELSResult, Server, HEALTH_CHECKER_ID,
|
||||
send, send_log, AnalysisResult, DefaultFeatures, ELSResult, Server, ASK_AUTO_SAVE_ID,
|
||||
HEALTH_CHECKER_ID,
|
||||
};
|
||||
use crate::util::{self, NormalizedUrl};
|
||||
|
||||
|
@ -43,6 +46,12 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
} else {
|
||||
"exec"
|
||||
};
|
||||
if let Some((old, new)) = self.analysis_result.get_ast(&uri).zip(self.get_ast(&uri)) {
|
||||
if ASTDiff::diff(old, &new).is_nop() {
|
||||
crate::_log!("no changes: {uri}");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let mut checker = self.get_checker(path.clone());
|
||||
let artifact = match checker.build(code.into(), mode) {
|
||||
Ok(artifact) => {
|
||||
|
@ -198,7 +207,8 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
}
|
||||
let params = PublishDiagnosticsParams::new(uri, diagnostics, None);
|
||||
if self
|
||||
.client_capas
|
||||
.init_params
|
||||
.capabilities
|
||||
.text_document
|
||||
.as_ref()
|
||||
.map(|doc| doc.publish_diagnostics.is_some())
|
||||
|
@ -223,6 +233,25 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
move || {
|
||||
let mut file_vers = Dict::<NormalizedUrl, i32>::new();
|
||||
loop {
|
||||
if _self
|
||||
.client_answers
|
||||
.borrow()
|
||||
.get(&ASK_AUTO_SAVE_ID)
|
||||
.is_none()
|
||||
{
|
||||
_self.ask_auto_save().unwrap();
|
||||
} else if _self
|
||||
.client_answers
|
||||
.borrow()
|
||||
.get(&ASK_AUTO_SAVE_ID)
|
||||
.is_some_and(|val| {
|
||||
val["result"].as_array().and_then(|a| a[0].as_str())
|
||||
== Some("afterDelay")
|
||||
})
|
||||
{
|
||||
_log!("Auto saving is enabled");
|
||||
break;
|
||||
}
|
||||
for uri in _self.file_cache.entries() {
|
||||
let Some(latest_ver) = _self.file_cache.get_ver(&uri) else {
|
||||
continue;
|
||||
|
@ -247,8 +276,11 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
|
||||
/// Send an empty `workspace/configuration` request periodically.
|
||||
/// If there is no response to the request within a certain period of time, terminate the server.
|
||||
pub fn start_client_health_checker(&self, receiver: Receiver<()>) {
|
||||
pub fn start_client_health_checker(&self, receiver: Receiver<WorkerMessage<()>>) {
|
||||
const INTERVAL: Duration = Duration::from_secs(5);
|
||||
const TIMEOUT: Duration = Duration::from_secs(10);
|
||||
// let mut self_ = self.clone();
|
||||
// FIXME: close this thread when the server is restarted
|
||||
spawn_new_thread(
|
||||
move || {
|
||||
loop {
|
||||
|
@ -261,7 +293,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
"params": params,
|
||||
}))
|
||||
.unwrap();
|
||||
sleep(Duration::from_secs(10));
|
||||
sleep(INTERVAL);
|
||||
}
|
||||
},
|
||||
"start_client_health_checker_sender",
|
||||
|
@ -269,14 +301,20 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
spawn_new_thread(
|
||||
move || {
|
||||
loop {
|
||||
match receiver.recv_timeout(Duration::from_secs(20)) {
|
||||
match receiver.recv_timeout(TIMEOUT) {
|
||||
Ok(WorkerMessage::Kill) => {
|
||||
break;
|
||||
}
|
||||
Ok(_) => {
|
||||
// send_log("client health check passed").unwrap();
|
||||
}
|
||||
Err(_) => {
|
||||
// send_log("client health check timed out").unwrap();
|
||||
lsp_log!("client health check timed out");
|
||||
std::process::exit(1);
|
||||
lsp_log!("Client health check timed out");
|
||||
// lsp_log!("Restart the server");
|
||||
// _log!("Restart the server");
|
||||
// send_error_info("Something went wrong, ELS has been restarted").unwrap();
|
||||
// self_.restart();
|
||||
panic!("Client health check timed out");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ impl ASTDiff {
|
|||
.unwrap_or(Self::Nop),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_nop(&self) -> bool {
|
||||
matches!(self, Self::Nop)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -58,6 +58,10 @@ impl FileCache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.files.borrow_mut().clear();
|
||||
}
|
||||
|
||||
fn load_once(&self, uri: &NormalizedUrl) -> ELSResult<()> {
|
||||
if self.files.borrow_mut().get(uri).is_some() {
|
||||
return Ok(());
|
||||
|
|
|
@ -32,19 +32,20 @@ use lsp_types::request::{
|
|||
WillRenameFiles,
|
||||
};
|
||||
use lsp_types::{
|
||||
ClientCapabilities, CodeActionKind, CodeActionOptions, CodeActionProviderCapability,
|
||||
CodeLensOptions, CompletionOptions, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
|
||||
ExecuteCommandOptions, HoverProviderCapability, InitializeResult, InlayHintOptions,
|
||||
InlayHintServerCapabilities, OneOf, Position, SemanticTokenType, SemanticTokensFullOptions,
|
||||
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensServerCapabilities,
|
||||
ServerCapabilities, SignatureHelpOptions, WorkDoneProgressOptions,
|
||||
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};
|
||||
use serde_json::json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::channels::{SendChannels, Sendable};
|
||||
use crate::channels::{SendChannels, Sendable, WorkerMessage};
|
||||
use crate::completion::CompletionCache;
|
||||
use crate::file_cache::FileCache;
|
||||
use crate::hir_visitor::HIRVisitor;
|
||||
|
@ -52,6 +53,7 @@ use crate::message::{ErrorMessage, LSPResult, LogMessage, ShowMessage};
|
|||
use crate::util::{self, NormalizedUrl};
|
||||
|
||||
pub const HEALTH_CHECKER_ID: i64 = 10000;
|
||||
pub const ASK_AUTO_SAVE_ID: i64 = 10001;
|
||||
|
||||
pub type ELSResult<T> = Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
|
@ -312,7 +314,8 @@ pub struct Server<Checker: BuildRunnable = HIRBuilder, Parser: Parsable = Simple
|
|||
pub(crate) cfg: ErgConfig,
|
||||
pub(crate) home: PathBuf,
|
||||
pub(crate) erg_path: PathBuf,
|
||||
pub(crate) client_capas: ClientCapabilities,
|
||||
pub(crate) init_params: InitializeParams,
|
||||
pub(crate) client_answers: Shared<Dict<i64, Value>>,
|
||||
pub(crate) disabled_features: Vec<DefaultFeatures>,
|
||||
pub(crate) opt_features: Vec<OptionalFeatures>,
|
||||
pub(crate) file_cache: FileCache,
|
||||
|
@ -332,7 +335,8 @@ impl<C: BuildRunnable, P: Parsable> Clone for Server<C, P> {
|
|||
cfg: self.cfg.clone(),
|
||||
home: self.home.clone(),
|
||||
erg_path: self.erg_path.clone(),
|
||||
client_capas: self.client_capas.clone(),
|
||||
init_params: self.init_params.clone(),
|
||||
client_answers: self.client_answers.clone(),
|
||||
disabled_features: self.disabled_features.clone(),
|
||||
opt_features: self.opt_features.clone(),
|
||||
file_cache: self.file_cache.clone(),
|
||||
|
@ -354,7 +358,8 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
cfg,
|
||||
home: normalize_path(std::env::current_dir().unwrap_or_default()),
|
||||
erg_path: erg_path().clone(), // already normalized
|
||||
client_capas: ClientCapabilities::default(),
|
||||
init_params: InitializeParams::default(),
|
||||
client_answers: Shared::new(Dict::new()),
|
||||
disabled_features: vec![],
|
||||
opt_features: vec![],
|
||||
file_cache: FileCache::new(),
|
||||
|
@ -390,7 +395,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
send_log("initializing ELS")?;
|
||||
// #[allow(clippy::collapsible_if)]
|
||||
if msg.get("params").is_some() && msg["params"].get("capabilities").is_some() {
|
||||
self.client_capas = ClientCapabilities::deserialize(&msg["params"]["capabilities"])?;
|
||||
self.init_params = InitializeParams::deserialize(&msg["params"])?;
|
||||
// send_log(format!("set client capabilities: {:?}", self.client_capas))?;
|
||||
}
|
||||
let mut args = self.cfg.runtime_args.iter();
|
||||
|
@ -503,7 +508,22 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
capabilities
|
||||
}
|
||||
|
||||
fn init_services(&mut self) {
|
||||
pub(crate) fn ask_auto_save(&self) -> ELSResult<()> {
|
||||
let params = ConfigurationParams {
|
||||
items: vec![ConfigurationItem {
|
||||
scope_uri: None,
|
||||
section: Some("files.autoSave".to_string()),
|
||||
}],
|
||||
};
|
||||
send(&json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": ASK_AUTO_SAVE_ID,
|
||||
"method": "workspace/configuration",
|
||||
"params": params,
|
||||
}))
|
||||
}
|
||||
|
||||
fn start_language_services(&mut self) {
|
||||
let (senders, receivers) = SendChannels::new();
|
||||
self.channels = Some(senders);
|
||||
self.start_service::<Completion>(receivers.completion, Self::handle_completion);
|
||||
|
@ -544,10 +564,14 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
receivers.execute_command,
|
||||
Self::handle_execute_command,
|
||||
);
|
||||
self.start_auto_diagnostics();
|
||||
self.start_client_health_checker(receivers.health_check);
|
||||
}
|
||||
|
||||
fn init_services(&mut self) {
|
||||
self.start_language_services();
|
||||
self.start_auto_diagnostics();
|
||||
}
|
||||
|
||||
fn exit(&self) -> ELSResult<()> {
|
||||
send_log("exiting ELS")?;
|
||||
std::process::exit(0);
|
||||
|
@ -562,6 +586,17 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
}))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn restart(&mut self) {
|
||||
self.file_cache.clear();
|
||||
self.comp_cache.clear();
|
||||
self.modules = ModuleCache::new();
|
||||
self.analysis_result = AnalysisResultCache::new();
|
||||
self.current_sig = None;
|
||||
self.channels.as_ref().unwrap().close();
|
||||
self.start_language_services();
|
||||
}
|
||||
|
||||
/// Copied and modified from RLS, https://github.com/rust-lang/rls/blob/master/rls/src/server/io.rs
|
||||
fn read_message(&self) -> Result<Value, io::Error> {
|
||||
// Read in the "Content-Length: xx" part.
|
||||
|
@ -654,7 +689,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
|
||||
fn start_service<R>(
|
||||
&self,
|
||||
receiver: mpsc::Receiver<(i64, R::Params)>,
|
||||
receiver: mpsc::Receiver<WorkerMessage<R::Params>>,
|
||||
handler: Handler<Server<Checker, Parser>, R::Params, R::Result>,
|
||||
) where
|
||||
R: lsp_types::request::Request + 'static,
|
||||
|
@ -664,8 +699,15 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
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()));
|
||||
let msg = receiver.recv().unwrap();
|
||||
match msg {
|
||||
WorkerMessage::Request(id, params) => {
|
||||
let _ = send(&LSPResult::new(id, handler(&mut _self, params).unwrap()));
|
||||
}
|
||||
WorkerMessage::Kill => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
fn_name!(),
|
||||
);
|
||||
|
@ -742,16 +784,25 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
fn handle_response(&mut self, id: i64, msg: &Value) -> ELSResult<()> {
|
||||
match id {
|
||||
HEALTH_CHECKER_ID => {
|
||||
self.channels.as_ref().unwrap().health_check.send(())?;
|
||||
self.channels
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.health_check
|
||||
.send(WorkerMessage::Request(0, ()))?;
|
||||
}
|
||||
_ => {
|
||||
_log!("received a unknown response: {msg}");
|
||||
// ignore at this time
|
||||
_log!("msg: {msg}");
|
||||
if msg.get("error").is_none() {
|
||||
self.client_answers.borrow_mut().insert(id, msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// TODO: Reuse cache.
|
||||
/// Because of the difficulty of caching "transitional types" such as assert casting and mutable dependent types,
|
||||
/// the cache is deleted after each analysis.
|
||||
pub(crate) fn get_checker(&self, path: PathBuf) -> Checker {
|
||||
if let Some(shared) = self.get_shared() {
|
||||
let shared = shared.clone();
|
||||
|
@ -892,9 +943,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
|
|||
if let Some(module) = self.modules.remove(uri) {
|
||||
let shared = module.context.shared();
|
||||
let path = util::uri_to_path(uri);
|
||||
shared.mod_cache.remove(&path);
|
||||
shared.index.remove_path(&path);
|
||||
shared.graph.remove(&path);
|
||||
shared.clear(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,3 +50,8 @@ where
|
|||
.spawn(run)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn safe_yield() {
|
||||
std::thread::yield_now();
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
|
|
@ -254,6 +254,10 @@ pub trait Stream<T>: Sized {
|
|||
fn split_off(&mut self, at: usize) -> Vec<T> {
|
||||
self.ref_mut_payload().split_off(at)
|
||||
}
|
||||
|
||||
fn retain(&mut self, f: impl FnMut(&T) -> bool) {
|
||||
self.ref_mut_payload().retain(f);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
@ -54,27 +54,37 @@ impl SharedCompilerResource {
|
|||
_self
|
||||
}
|
||||
|
||||
/// Clear all but builtin modules
|
||||
pub fn clear_all(&self) {
|
||||
self.mod_cache.initialize();
|
||||
self.py_mod_cache.initialize();
|
||||
self.index.initialize();
|
||||
self.graph.initialize();
|
||||
self.trait_impls.initialize();
|
||||
// self.trait_impls.initialize();
|
||||
self.promises.initialize();
|
||||
self.errors.clear();
|
||||
self.warns.clear();
|
||||
}
|
||||
|
||||
/// Clear all information about the module. All child modules are also cleared.
|
||||
pub fn clear(&self, path: &Path) {
|
||||
for child in self.graph.children(path) {
|
||||
self.clear(&child);
|
||||
}
|
||||
self.mod_cache.remove(path);
|
||||
self.py_mod_cache.remove(path);
|
||||
self.index.remove_path(path);
|
||||
self.graph.remove(path);
|
||||
self.promises.remove(path);
|
||||
// self.errors.remove(path);
|
||||
// self.warns.remove(path);
|
||||
}
|
||||
|
||||
pub fn rename_path(&self, old: &Path, new: PathBuf) {
|
||||
self.mod_cache.rename_path(old, new.clone());
|
||||
self.py_mod_cache.rename_path(old, new.clone());
|
||||
self.index.rename_path(old, new.clone());
|
||||
self.graph.rename_path(old, new);
|
||||
self.graph.rename_path(old, new.clone());
|
||||
self.promises.rename(old, new);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,7 @@ impl ModuleGraph {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Do not erase relationships with modules that depend on `path`
|
||||
pub fn remove(&mut self, path: &Path) {
|
||||
let path = NormalizedPathBuf::new(path.to_path_buf());
|
||||
self.0.retain(|n| n.id != path);
|
||||
|
|
|
@ -61,6 +61,15 @@ impl TraitImpls {
|
|||
self.cache.remove(path)
|
||||
}
|
||||
|
||||
pub fn rename<Q: Eq + Hash + ?Sized>(&mut self, old: &Q, new: Str)
|
||||
where
|
||||
Str: Borrow<Q>,
|
||||
{
|
||||
if let Some(impls) = self.remove(old) {
|
||||
self.register(new, impls);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self) {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
@ -123,6 +132,13 @@ impl SharedTraitImpls {
|
|||
self.0.borrow_mut().remove(path)
|
||||
}
|
||||
|
||||
pub fn rename<Q: Eq + Hash + ?Sized>(&self, old: &Q, new: Str)
|
||||
where
|
||||
Str: Borrow<Q>,
|
||||
{
|
||||
self.0.borrow_mut().rename(old, new);
|
||||
}
|
||||
|
||||
pub fn ref_inner(&self) -> MappedRwLockReadGuard<Dict<Str, Set<TraitImpl>>> {
|
||||
RwLockReadGuard::map(self.0.borrow(), |tis| &tis.cache)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::thread::{current, JoinHandle, ThreadId};
|
|||
use erg_common::dict::Dict;
|
||||
use erg_common::pathutil::NormalizedPathBuf;
|
||||
use erg_common::shared::Shared;
|
||||
use erg_common::spawn::safe_yield;
|
||||
|
||||
use super::SharedModuleGraph;
|
||||
|
||||
|
@ -102,6 +103,21 @@ impl SharedPromises {
|
|||
.insert(path, Promise::running(handle));
|
||||
}
|
||||
|
||||
pub fn remove(&self, path: &Path) {
|
||||
self.promises.borrow_mut().remove(path);
|
||||
}
|
||||
|
||||
pub fn initialize(&self) {
|
||||
self.promises.borrow_mut().clear();
|
||||
}
|
||||
|
||||
pub fn rename(&self, old: &Path, new: PathBuf) {
|
||||
let Some(promise) = self.promises.borrow_mut().remove(old) else {
|
||||
return;
|
||||
};
|
||||
self.promises.borrow_mut().insert(new.into(), promise);
|
||||
}
|
||||
|
||||
pub fn is_registered(&self, path: &Path) -> bool {
|
||||
self.promises.borrow().get(path).is_some()
|
||||
}
|
||||
|
@ -140,7 +156,7 @@ impl SharedPromises {
|
|||
.get(path)
|
||||
.is_some_and(|p| !p.is_finished())
|
||||
{
|
||||
std::thread::yield_now();
|
||||
safe_yield();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -152,7 +168,7 @@ impl SharedPromises {
|
|||
|
||||
pub fn join(&self, path: &Path) -> std::thread::Result<()> {
|
||||
while let Some(Promise::Joining) | None = self.promises.borrow().get(path) {
|
||||
std::thread::yield_now();
|
||||
safe_yield();
|
||||
}
|
||||
let promise = self.promises.borrow_mut().get_mut(path).unwrap().take();
|
||||
self.join_checked(path, promise)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue