diff --git a/crates/oxc_language_server/src/backend.rs b/crates/oxc_language_server/src/backend.rs index c1a2f762fce6f..43689fc6a3872 100644 --- a/crates/oxc_language_server/src/backend.rs +++ b/crates/oxc_language_server/src/backend.rs @@ -251,7 +251,7 @@ impl LanguageServer for Backend { let mut removed_registrations = Vec::new(); for worker in &*self.workspace_workers.read().await { - let (uris, unregistrations) = worker.shutdown().await; + let (uris, unregistrations) = worker.shutdown(&*self.file_system.read().await).await; clearing_diagnostics.extend(uris); removed_registrations.extend(unregistrations); } @@ -439,7 +439,7 @@ impl LanguageServer for Backend { else { continue; }; - let (uris, unregistrations) = worker.shutdown().await; + let (uris, unregistrations) = worker.shutdown(&*self.file_system.read().await).await; cleared_diagnostics.extend(uris); removed_registrations.extend(unregistrations); workers.remove(index); diff --git a/crates/oxc_language_server/src/lib.rs b/crates/oxc_language_server/src/lib.rs index ba1ef900a7339..7d6065ba24dae 100644 --- a/crates/oxc_language_server/src/lib.rs +++ b/crates/oxc_language_server/src/lib.rs @@ -20,7 +20,7 @@ use crate::backend::Backend; pub use crate::formatter::ServerFormatterBuilder; #[cfg(feature = "linter")] pub use crate::linter::ServerLinterBuilder; -pub use crate::tool::{Tool, ToolBuilder, ToolRestartChanges, ToolShutdownChanges}; +pub use crate::tool::{Tool, ToolBuilder, ToolRestartChanges}; pub type ConcurrentHashMap = papaya::HashMap; diff --git a/crates/oxc_language_server/src/linter/server_linter.rs b/crates/oxc_language_server/src/linter/server_linter.rs index 23f64e0611144..7c927c377d728 100644 --- a/crates/oxc_language_server/src/linter/server_linter.rs +++ b/crates/oxc_language_server/src/linter/server_linter.rs @@ -34,7 +34,7 @@ use crate::{ isolated_lint_handler::{IsolatedLintHandler, IsolatedLintHandlerOptions}, options::{LintOptions as LSPLintOptions, Run, UnusedDisableDirectives}, }, - tool::{Tool, ToolBuilder, ToolRestartChanges, ToolShutdownChanges}, + tool::{Tool, ToolBuilder, ToolRestartChanges}, utils::normalize_path, }; @@ -310,10 +310,6 @@ impl Tool for ServerLinter { "linter" } - fn shutdown(&self) -> ToolShutdownChanges { - ToolShutdownChanges { uris_to_clear_diagnostics: Some(self.get_cached_uris()) } - } - /// # Panics /// Panics if the root URI cannot be converted to a file path. fn handle_configuration_change( @@ -552,10 +548,6 @@ impl ServerLinter { } } - fn get_cached_uris(&self) -> Vec { - self.code_actions.pin().keys().cloned().collect() - } - fn get_code_actions_for_uri(&self, uri: &Uri) -> Option> { if let Some(cached_code_actions) = self.code_actions.pin().get(uri) { cached_code_actions.clone() diff --git a/crates/oxc_language_server/src/tests.rs b/crates/oxc_language_server/src/tests.rs index 267093db58b3d..0a741994999be 100644 --- a/crates/oxc_language_server/src/tests.rs +++ b/crates/oxc_language_server/src/tests.rs @@ -280,6 +280,20 @@ impl TestServer { assert_eq!(shutdown_result.id(), &Id::Number(id)); } + async fn shutdown_with_diagnostics_clear(&mut self, id: i64) { + // shutdown request + self.send_request(shutdown_request(id)).await; + + // diagnostics clear expected + acknowledge_publish_diagnostics(self).await; + + // shutdown response + let shutdown_result = self.recv_response().await; + + assert!(shutdown_result.is_ok()); + assert_eq!(shutdown_result.id(), &Id::Number(id)); + } + async fn shutdown_with_watchers(&mut self, id: i64) { // shutdown request self.send_request(shutdown_request(id)).await; @@ -375,6 +389,16 @@ async fn acknowledge_unregistrations(server: &mut TestServer) { server.send_ack(unregister_request.id().unwrap()).await; } +async fn acknowledge_publish_diagnostics(server: &mut TestServer) { + let diagnostic_request = server.recv_notification().await; + assert_eq!(diagnostic_request.method(), "textDocument/publishDiagnostics"); + + if let Some(id) = diagnostic_request.id() { + // Acknowledge the diagnostics + server.send_ack(id).await; + } +} + async fn response_to_configuration( server: &mut TestServer, configurations: Vec, @@ -481,11 +505,12 @@ mod test_suite { use crate::{ backend::Backend, tests::{ - FAKE_COMMAND, FakeToolBuilder, TestServer, WORKSPACE, acknowledge_registrations, - acknowledge_unregistrations, code_action, did_change, did_change_configuration, - did_change_watched_files, did_close, did_open, did_save, execute_command_request, - initialize_request, initialized_notification, response_to_configuration, - shutdown_request, test_configuration_request, workspace_folders_changed, + FAKE_COMMAND, FakeToolBuilder, TestServer, WORKSPACE, acknowledge_publish_diagnostics, + acknowledge_registrations, acknowledge_unregistrations, code_action, did_change, + did_change_configuration, did_change_watched_files, did_close, did_open, did_save, + execute_command_request, initialize_request, initialized_notification, + response_to_configuration, shutdown_request, test_configuration_request, + workspace_folders_changed, }, }; @@ -1051,7 +1076,7 @@ mod test_suite { assert!(response.id() == &Id::Number(3)); assert!(response.result().is_some_and(|result| *result == Value::Null)); - server.shutdown(4).await; + server.shutdown_with_diagnostics_clear(4).await; } #[tokio::test] @@ -1076,7 +1101,7 @@ mod test_suite { assert_eq!(actions.len(), 1); assert_eq!(actions[0]["title"], "Code Action title"); - server.shutdown(4).await; + server.shutdown_with_diagnostics_clear(4).await; } #[tokio::test] @@ -1102,7 +1127,12 @@ mod test_suite { format!("Fake diagnostic for content: {content}") ); - server.shutdown(4).await; + if let Some(id) = diagnostic_response.id() { + // Acknowledge the diagnostics + server.send_ack(id).await; + } + + server.shutdown_with_diagnostics_clear(4).await; } #[tokio::test] @@ -1116,8 +1146,8 @@ mod test_suite { let file = format!("{WORKSPACE}/diagnostics.config"); let content = "new text"; server.send_request(did_open(&file, "old text")).await; - let diagnostic_response = server.recv_notification().await; - assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics"); + + acknowledge_publish_diagnostics(&mut server).await; server.send_request(did_change(&file, content)).await; @@ -1132,7 +1162,12 @@ mod test_suite { format!("Fake diagnostic for content: {content}") ); - server.shutdown(4).await; + if let Some(id) = diagnostic_response.id() { + // Acknowledge the diagnostics + server.send_ack(id).await; + } + + server.shutdown_with_diagnostics_clear(4).await; } #[tokio::test] @@ -1146,13 +1181,11 @@ mod test_suite { let file = format!("{WORKSPACE}/diagnostics.config"); let content = "new text"; server.send_request(did_open(&file, "old text")).await; - let diagnostic_response = server.recv_notification().await; - assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics"); + acknowledge_publish_diagnostics(&mut server).await; server.send_request(did_change(&file, content)).await; - let diagnostic_response = server.recv_notification().await; - assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics"); + acknowledge_publish_diagnostics(&mut server).await; server.send_request(did_save(&file, content)).await; @@ -1167,6 +1200,11 @@ mod test_suite { format!("Fake diagnostic for content: {content}") ); - server.shutdown(4).await; + if let Some(id) = diagnostic_response.id() { + // Acknowledge the diagnostics + server.send_ack(id).await; + } + + server.shutdown_with_diagnostics_clear(4).await; } } diff --git a/crates/oxc_language_server/src/tool.rs b/crates/oxc_language_server/src/tool.rs index 5667320e4546d..d7695257b268f 100644 --- a/crates/oxc_language_server/src/tool.rs +++ b/crates/oxc_language_server/src/tool.rs @@ -120,8 +120,8 @@ pub trait Tool: Send + Sync { } /// Shutdown the tool and return any necessary changes to be made after shutdown. - fn shutdown(&self) -> ToolShutdownChanges { - ToolShutdownChanges { uris_to_clear_diagnostics: None } + fn shutdown(&self) { + // Default implementation does nothing. } } @@ -133,8 +133,3 @@ pub struct ToolRestartChanges { /// Old patterns will be automatically unregistered pub watch_patterns: Option>, } - -pub struct ToolShutdownChanges { - /// The URIs that need to have their diagnostics removed after the tool shutdown - pub uris_to_clear_diagnostics: Option>, -} diff --git a/crates/oxc_language_server/src/worker.rs b/crates/oxc_language_server/src/worker.rs index fc9ebaa97d40f..02595324780a7 100644 --- a/crates/oxc_language_server/src/worker.rs +++ b/crates/oxc_language_server/src/worker.rs @@ -168,24 +168,25 @@ impl WorkspaceWorker { /// This includes clearing diagnostics and unregistering file watchers. pub async fn shutdown( &self, + fs: &LSPFileSystem, ) -> ( // The URIs that need to have their diagnostics removed after shutdown Vec, // Watchers that need to be unregistered Vec, ) { - let mut uris_to_clear_diagnostics = Vec::new(); let mut watchers_to_unregister = Vec::new(); for tool in self.tools.read().await.iter() { - let shutdown_changes = tool.shutdown(); - if let Some(uris) = shutdown_changes.uris_to_clear_diagnostics { - uris_to_clear_diagnostics.extend(uris); - } + tool.shutdown(); + watchers_to_unregister .push(unregistration_tool_watcher_id(tool.name(), &self.root_uri)); } - (uris_to_clear_diagnostics, watchers_to_unregister) + ( + fs.keys().into_iter().filter(|uri| self.is_responsible_for_uri(uri)).collect(), + watchers_to_unregister, + ) } /// Get code actions or commands for the given range.