Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance ELS stability #451

Merged
merged 10 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 61 additions & 31 deletions crates/els/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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> {
Expand All @@ -124,7 +154,7 @@ macro_rules! impl_sendable {
.as_ref()
.unwrap()
.$receiver
.send((id, params))
.send($crate::channels::WorkerMessage::Request(id, params))
.unwrap();
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/els/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>) {
Expand Down
54 changes: 46 additions & 8 deletions crates/els/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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) => {
Expand Down Expand Up @@ -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())
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -261,22 +293,28 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
"params": params,
}))
.unwrap();
sleep(Duration::from_secs(10));
sleep(INTERVAL);
}
},
"start_client_health_checker_sender",
);
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");
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/els/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
4 changes: 4 additions & 0 deletions crates/els/file_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(());
Expand Down
Loading
Loading