From ef423148fc606b35e6891e8593231e99b0777e86 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 8 Oct 2025 21:48:40 +0200 Subject: [PATCH] lsp: Serialize LSP notifications on background threads (#39403) This should reduce hiccups when opening large files. Release Notes: - N/A --- crates/collab/src/tests/editor_tests.rs | 12 +-- crates/collab/src/tests/integration_tests.rs | 14 +-- crates/copilot/src/copilot.rs | 30 ++++--- crates/editor/src/editor_tests.rs | 53 +++++------ crates/editor/src/inlay_hint_cache.rs | 4 +- .../src/test/editor_lsp_test_context.rs | 2 +- .../src/json_schema_store.rs | 4 +- crates/language_tools/src/lsp_log_view.rs | 2 +- .../language_tools/src/lsp_log_view_tests.rs | 2 +- crates/lsp/src/lsp.rs | 88 +++++++++++++------ crates/project/src/lsp_store.rs | 73 +++++++-------- .../src/lsp_store/json_language_server_ext.rs | 4 +- .../src/lsp_store/rust_analyzer_ext.rs | 29 +++--- crates/project/src/project_tests.rs | 16 ++-- 14 files changed, 186 insertions(+), 147 deletions(-) diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index 8dbefc8714f9797b116551419486507b1a742b5a..0614d66928710aeeda4a4a492508b92c5b4d35e0 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -1272,7 +1272,7 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes fake_language_server.start_progress("the-token").await; executor.advance_clock(SERVER_PROGRESS_THROTTLE_TIMEOUT); - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: lsp::NumberOrString::String("the-token".to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report( lsp::WorkDoneProgressReport { @@ -1306,7 +1306,7 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes }); executor.advance_clock(SERVER_PROGRESS_THROTTLE_TIMEOUT); - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: lsp::NumberOrString::String("the-token".to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report( lsp::WorkDoneProgressReport { @@ -2848,7 +2848,7 @@ async fn test_lsp_pull_diagnostics( }); fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/main.rs")).unwrap(), diagnostics: vec![lsp::Diagnostic { range: lsp::Range { @@ -2869,7 +2869,7 @@ async fn test_lsp_pull_diagnostics( }, ); fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/lib.rs")).unwrap(), diagnostics: vec![lsp::Diagnostic { range: lsp::Range { @@ -2891,7 +2891,7 @@ async fn test_lsp_pull_diagnostics( ); if should_stream_workspace_diagnostic { - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: expected_workspace_diagnostic_token.clone(), value: lsp::ProgressParamsValue::WorkspaceDiagnostic( lsp::WorkspaceDiagnosticReportResult::Report(lsp::WorkspaceDiagnosticReport { @@ -3073,7 +3073,7 @@ async fn test_lsp_pull_diagnostics( }); if should_stream_workspace_diagnostic { - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: expected_workspace_diagnostic_token.clone(), value: lsp::ProgressParamsValue::WorkspaceDiagnostic( lsp::WorkspaceDiagnosticReportResult::Report(lsp::WorkspaceDiagnosticReport { diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index d3cd87ad6b9bf81bb3804a88b943703dba8f19be..f6a106b7db5c77ba8e98b307cc3e562766fb4dd4 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -4077,7 +4077,7 @@ async fn test_collaborating_with_diagnostics( .receive_notification::() .await; fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { @@ -4097,7 +4097,7 @@ async fn test_collaborating_with_diagnostics( .await .unwrap(); fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { @@ -4171,7 +4171,7 @@ async fn test_collaborating_with_diagnostics( // Simulate a language server reporting more errors for a file. fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/a.rs")).unwrap(), version: None, diagnostics: vec![ @@ -4269,7 +4269,7 @@ async fn test_collaborating_with_diagnostics( // Simulate a language server reporting no errors for a file. fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/a/a.rs")).unwrap(), version: None, diagnostics: Vec::new(), @@ -4365,7 +4365,7 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering( .await .into_response() .unwrap(); - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: lsp::NumberOrString::String("the-disk-based-token".to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin( lsp::WorkDoneProgressBegin { @@ -4376,7 +4376,7 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering( }); for file_name in file_names { fake_language_server.notify::( - &lsp::PublishDiagnosticsParams { + lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(Path::new(path!("/test")).join(file_name)).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { @@ -4389,7 +4389,7 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering( }, ); } - fake_language_server.notify::(&lsp::ProgressParams { + fake_language_server.notify::(lsp::ProgressParams { token: lsp::NumberOrString::String("the-disk-based-token".to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End( lsp::WorkDoneProgressEnd { message: None }, diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index ffcae93b40ad07de9e577e1d2d1558505c8b6de2..d8fa8967a862053ccf2a820878f450c38ea18fad 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -270,7 +270,7 @@ impl RegisteredBuffer { server .lsp .notify::( - &lsp::DidChangeTextDocumentParams { + lsp::DidChangeTextDocumentParams { text_document: lsp::VersionedTextDocumentIdentifier::new( buffer.uri.clone(), buffer.snapshot_version, @@ -744,7 +744,7 @@ impl Copilot { let snapshot = buffer.read(cx).snapshot(); server .notify::( - &lsp::DidOpenTextDocumentParams { + lsp::DidOpenTextDocumentParams { text_document: lsp::TextDocumentItem { uri: uri.clone(), language_id: language_id.clone(), @@ -792,13 +792,14 @@ impl Copilot { server .lsp .notify::( - &lsp::DidSaveTextDocumentParams { + lsp::DidSaveTextDocumentParams { text_document: lsp::TextDocumentIdentifier::new( registered_buffer.uri.clone(), ), text: None, }, - )?; + ) + .ok(); } language::BufferEvent::FileHandleChanged | language::BufferEvent::LanguageChanged => { @@ -814,14 +815,15 @@ impl Copilot { server .lsp .notify::( - &lsp::DidCloseTextDocumentParams { + lsp::DidCloseTextDocumentParams { text_document: lsp::TextDocumentIdentifier::new(old_uri), }, - )?; + ) + .ok(); server .lsp .notify::( - &lsp::DidOpenTextDocumentParams { + lsp::DidOpenTextDocumentParams { text_document: lsp::TextDocumentItem::new( registered_buffer.uri.clone(), registered_buffer.language_id.clone(), @@ -829,7 +831,8 @@ impl Copilot { registered_buffer.snapshot.text(), ), }, - )?; + ) + .ok(); } } _ => {} @@ -846,7 +849,7 @@ impl Copilot { server .lsp .notify::( - &lsp::DidCloseTextDocumentParams { + lsp::DidCloseTextDocumentParams { text_document: lsp::TextDocumentIdentifier::new(buffer.uri), }, ) @@ -1151,9 +1154,12 @@ fn notify_did_change_config_to_server( } }); - server.notify::(&lsp::DidChangeConfigurationParams { - settings, - }) + server + .notify::(lsp::DidChangeConfigurationParams { + settings, + }) + .ok(); + Ok(()) } async fn clear_copilot_dir() { diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 3dfed1905f934bc8848642c5c6e769e87fce72da..875d62b23e26cc85fb1d112bbd9042688d0394b9 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -12416,11 +12416,6 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) { .join("\n"), ); - // Submit a format request. - let format = cx - .update_editor(|editor, window, cx| editor.format(&Format, window, cx)) - .unwrap(); - // Record which buffer changes have been sent to the language server let buffer_changes = Arc::new(Mutex::new(Vec::new())); cx.lsp @@ -12441,28 +12436,29 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) { .set_request_handler::({ let buffer_changes = buffer_changes.clone(); move |_, _| { - // When formatting is requested, trailing whitespace has already been stripped, - // and the trailing newline has already been added. - assert_eq!( - &buffer_changes.lock()[1..], - &[ - ( - lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)), - "".into() - ), - ( - lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)), - "".into() - ), - ( - lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)), - "\n".into() - ), - ] - ); - + let buffer_changes = buffer_changes.clone(); // Insert blank lines between each line of the buffer. async move { + // When formatting is requested, trailing whitespace has already been stripped, + // and the trailing newline has already been added. + assert_eq!( + &buffer_changes.lock()[1..], + &[ + ( + lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)), + "".into() + ), + ( + lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)), + "".into() + ), + ( + lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)), + "\n".into() + ), + ] + ); + Ok(Some(vec![ lsp::TextEdit { range: lsp::Range::new( @@ -12483,10 +12479,17 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) { } }); + // Submit a format request. + let format = cx + .update_editor(|editor, window, cx| editor.format(&Format, window, cx)) + .unwrap(); + + cx.run_until_parked(); // After formatting the buffer, the trailing whitespace is stripped, // a newline is appended, and the edits provided by the language server // have been applied. format.await.unwrap(); + cx.assert_editor_state( &[ "one", // diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 63d74c73e12ef0c56490a2a51371c34e3c356ae3..9a1e07ba3946d0f2b05e2096201287334dd02534 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1495,7 +1495,7 @@ pub mod tests { .into_response() .expect("work done progress create request failed"); cx.executor().run_until_parked(); - fake_server.notify::(&lsp::ProgressParams { + fake_server.notify::(lsp::ProgressParams { token: lsp::ProgressToken::String(progress_token.to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin( lsp::WorkDoneProgressBegin::default(), @@ -1515,7 +1515,7 @@ pub mod tests { }) .unwrap(); - fake_server.notify::(&lsp::ProgressParams { + fake_server.notify::(lsp::ProgressParams { token: lsp::ProgressToken::String(progress_token.to_string()), value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End( lsp::WorkDoneProgressEnd::default(), diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index a085221e71cc52e43fac3b652b57f2360e99fa14..72060a11f07d297f578f933b0f6fd809dc915bb5 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -440,7 +440,7 @@ impl EditorLspTestContext { } pub fn notify(&self, params: T::Params) { - self.lsp.notify::(¶ms); + self.lsp.notify::(params); } #[cfg(target_os = "windows")] diff --git a/crates/json_schema_store/src/json_schema_store.rs b/crates/json_schema_store/src/json_schema_store.rs index 87c4203047f1ca4e74562b7f3a22adf7cdc5b6d5..b44efb8b1b135850ab78460a428b5088e5fa0928 100644 --- a/crates/json_schema_store/src/json_schema_store.rs +++ b/crates/json_schema_store/src/json_schema_store.rs @@ -61,7 +61,9 @@ impl SchemaStore { return false; }; project::lsp_store::json_language_server_ext::notify_schema_changed( - lsp_store, &uri, cx, + lsp_store, + uri.clone(), + cx, ); true }) diff --git a/crates/language_tools/src/lsp_log_view.rs b/crates/language_tools/src/lsp_log_view.rs index 5c6b4faa14723107644fb22195f12952bba8ccb7..1c24bfdcf44c09a1729065835debd4ef5fbb2252 100644 --- a/crates/language_tools/src/lsp_log_view.rs +++ b/crates/language_tools/src/lsp_log_view.rs @@ -606,7 +606,7 @@ impl LspLogView { }); server - .notify::(&SetTraceParams { value: level }) + .notify::(SetTraceParams { value: level }) .ok(); } } diff --git a/crates/language_tools/src/lsp_log_view_tests.rs b/crates/language_tools/src/lsp_log_view_tests.rs index 2ef915fdc386b69f1af604bb22abad58abb91d3a..c521c03a2fe5fd457445ec0a42cebfd3db0010ba 100644 --- a/crates/language_tools/src/lsp_log_view_tests.rs +++ b/crates/language_tools/src/lsp_log_view_tests.rs @@ -73,7 +73,7 @@ async fn test_lsp_log_view(cx: &mut TestAppContext) { let log_view = window.root(cx).unwrap(); let mut cx = VisualTestContext::from_window(*window, cx); - language_server.notify::(&lsp::LogMessageParams { + language_server.notify::(lsp::LogMessageParams { message: "hello from the server".into(), typ: lsp::MessageType::INFO, }); diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 0af69a0fb1f50268c54b8f943a73a03ae54d3cae..84e5a95ed80e75bf7d338b589f5b1c1c6495a616 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -80,11 +80,14 @@ pub struct LanguageServerBinaryOptions { pub pre_release: bool, } +struct NotificationSerializer(Box String + Send + Sync>); + /// A running language server process. pub struct LanguageServer { server_id: LanguageServerId, next_id: AtomicI32, outbound_tx: channel::Sender, + notification_tx: channel::Sender, name: LanguageServerName, process_name: Arc, binary: LanguageServerBinary, @@ -477,9 +480,24 @@ impl LanguageServer { } .into(); + let (notification_tx, notification_rx) = channel::unbounded::(); + cx.background_spawn({ + let outbound_tx = outbound_tx.clone(); + async move { + while let Ok(serializer) = notification_rx.recv().await { + let serialized = (serializer.0)(); + let Ok(_) = outbound_tx.send(serialized).await else { + return; + }; + } + outbound_tx.close(); + } + }) + .detach(); Self { server_id, notification_handlers, + notification_tx, response_handlers, io_handlers, name: server_name, @@ -906,7 +924,7 @@ impl LanguageServer { self.capabilities = RwLock::new(response.capabilities); self.configuration = configuration; - self.notify::(&InitializedParams {})?; + self.notify::(InitializedParams {})?; Ok(Arc::new(self)) }) } @@ -918,11 +936,13 @@ impl LanguageServer { let next_id = AtomicI32::new(self.next_id.load(SeqCst)); let outbound_tx = self.outbound_tx.clone(); let executor = self.executor.clone(); + let notification_serializers = self.notification_tx.clone(); let mut output_done = self.output_done_rx.lock().take().unwrap(); let shutdown_request = Self::request_internal::( &next_id, &response_handlers, &outbound_tx, + ¬ification_serializers, &executor, (), ); @@ -956,8 +976,8 @@ impl LanguageServer { } response_handlers.lock().take(); - Self::notify_internal::(&outbound_tx, &()).ok(); - outbound_tx.close(); + Self::notify_internal::(¬ification_serializers, ()).ok(); + notification_serializers.close(); output_done.recv().await; server.lock().take().map(|mut child| child.kill()); drop(tasks); @@ -1179,6 +1199,7 @@ impl LanguageServer { &self.next_id, &self.response_handlers, &self.outbound_tx, + &self.notification_tx, &self.executor, params, ) @@ -1200,6 +1221,7 @@ impl LanguageServer { &self.next_id, &self.response_handlers, &self.outbound_tx, + &self.notification_tx, &self.executor, timer, params, @@ -1210,6 +1232,7 @@ impl LanguageServer { next_id: &AtomicI32, response_handlers: &Mutex>>, outbound_tx: &channel::Sender, + notification_serializers: &channel::Sender, executor: &BackgroundExecutor, timer: U, params: T::Params, @@ -1261,7 +1284,7 @@ impl LanguageServer { .try_send(message) .context("failed to write to language server's stdin"); - let outbound_tx = outbound_tx.downgrade(); + let notification_serializers = notification_serializers.downgrade(); let started = Instant::now(); LspRequest::new(id, async move { if let Err(e) = handle_response { @@ -1272,10 +1295,10 @@ impl LanguageServer { } let cancel_on_drop = util::defer(move || { - if let Some(outbound_tx) = outbound_tx.upgrade() { + if let Some(notification_serializers) = notification_serializers.upgrade() { Self::notify_internal::( - &outbound_tx, - &CancelParams { + ¬ification_serializers, + CancelParams { id: NumberOrString::Number(id), }, ) @@ -1310,6 +1333,7 @@ impl LanguageServer { next_id: &AtomicI32, response_handlers: &Mutex>>, outbound_tx: &channel::Sender, + notification_serializers: &channel::Sender, executor: &BackgroundExecutor, params: T::Params, ) -> impl LspRequestFuture + use @@ -1321,6 +1345,7 @@ impl LanguageServer { next_id, response_handlers, outbound_tx, + notification_serializers, executor, Self::default_request_timer(executor.clone()), params, @@ -1336,21 +1361,25 @@ impl LanguageServer { /// Sends a RPC notification to the language server. /// /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage) - pub fn notify(&self, params: &T::Params) -> Result<()> { - Self::notify_internal::(&self.outbound_tx, params) + pub fn notify(&self, params: T::Params) -> Result<()> { + let outbound = self.notification_tx.clone(); + Self::notify_internal::(&outbound, params) } fn notify_internal( - outbound_tx: &channel::Sender, - params: &T::Params, + outbound_tx: &channel::Sender, + params: T::Params, ) -> Result<()> { - let message = serde_json::to_string(&Notification { - jsonrpc: JSON_RPC_VERSION, - method: T::METHOD, - params, - }) - .unwrap(); - outbound_tx.try_send(message)?; + let serializer = NotificationSerializer(Box::new(move || { + serde_json::to_string(&Notification { + jsonrpc: JSON_RPC_VERSION, + method: T::METHOD, + params, + }) + .unwrap() + })); + + outbound_tx.send_blocking(serializer)?; Ok(()) } @@ -1385,7 +1414,7 @@ impl LanguageServer { removed: vec![], }, }; - self.notify::(¶ms).ok(); + self.notify::(params).ok(); } } @@ -1419,7 +1448,7 @@ impl LanguageServer { }], }, }; - self.notify::(¶ms).ok(); + self.notify::(params).ok(); } } pub fn set_workspace_folders(&self, folders: BTreeSet) { @@ -1451,7 +1480,7 @@ impl LanguageServer { let params = DidChangeWorkspaceFoldersParams { event: WorkspaceFoldersChangeEvent { added, removed }, }; - self.notify::(¶ms).ok(); + self.notify::(params).ok(); } } @@ -1469,14 +1498,14 @@ impl LanguageServer { version: i32, initial_text: String, ) { - self.notify::(&DidOpenTextDocumentParams { + self.notify::(DidOpenTextDocumentParams { text_document: TextDocumentItem::new(uri, language_id, version, initial_text), }) .ok(); } pub fn unregister_buffer(&self, uri: Uri) { - self.notify::(&DidCloseTextDocumentParams { + self.notify::(DidCloseTextDocumentParams { text_document: TextDocumentIdentifier::new(uri), }) .ok(); @@ -1692,7 +1721,7 @@ impl LanguageServer { #[cfg(any(test, feature = "test-support"))] impl FakeLanguageServer { /// See [`LanguageServer::notify`]. - pub fn notify(&self, params: &T::Params) { + pub fn notify(&self, params: T::Params) { self.server.notify::(params).ok(); } @@ -1801,7 +1830,7 @@ impl FakeLanguageServer { .await .into_response() .unwrap(); - self.notify::(&ProgressParams { + self.notify::(ProgressParams { token: NumberOrString::String(token), value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(progress)), }); @@ -1809,7 +1838,7 @@ impl FakeLanguageServer { /// Simulate that the server has completed work and notifies about that with the specified token. pub fn end_progress(&self, token: impl Into) { - self.notify::(&ProgressParams { + self.notify::(ProgressParams { token: NumberOrString::String(token.into()), value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())), }); @@ -1868,7 +1897,7 @@ mod tests { .await .unwrap(); server - .notify::(&DidOpenTextDocumentParams { + .notify::(DidOpenTextDocumentParams { text_document: TextDocumentItem::new( Uri::from_str("file://a/b").unwrap(), "rust".to_string(), @@ -1886,11 +1915,11 @@ mod tests { "file://a/b" ); - fake.notify::(&ShowMessageParams { + fake.notify::(ShowMessageParams { typ: MessageType::ERROR, message: "ok".to_string(), }); - fake.notify::(&PublishDiagnosticsParams { + fake.notify::(PublishDiagnosticsParams { uri: Uri::from_str("file://b/c").unwrap(), version: Some(5), diagnostics: vec![], @@ -1904,6 +1933,7 @@ mod tests { fake.set_request_handler::(|_, _| async move { Ok(()) }); drop(server); + cx.run_until_parked(); fake.receive_notification::().await; } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 998deb197fa0ac622da50f3e9969f4774921f63e..3ef419714c20654df72b901fcd5663b4f7b61338 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -406,15 +406,14 @@ impl LocalLspStore { adapter.clone(), ); - let did_change_configuration_params = - Arc::new(lsp::DidChangeConfigurationParams { - settings: workspace_config, - }); + let did_change_configuration_params = lsp::DidChangeConfigurationParams { + settings: workspace_config, + }; let language_server = cx .update(|cx| { language_server.initialize( initialization_params, - did_change_configuration_params.clone(), + Arc::new(did_change_configuration_params.clone()), cx, ) })? @@ -430,11 +429,9 @@ impl LocalLspStore { } })?; - language_server - .notify::( - &did_change_configuration_params, - ) - .ok(); + language_server.notify::( + did_change_configuration_params, + )?; anyhow::Ok(language_server) } @@ -7206,7 +7203,7 @@ impl LspStore { language_server .notify::( - &lsp::DidChangeTextDocumentParams { + lsp::DidChangeTextDocumentParams { text_document: lsp::VersionedTextDocumentIdentifier::new( uri.clone(), next_version, @@ -7243,7 +7240,7 @@ impl LspStore { }; server .notify::( - &lsp::DidSaveTextDocumentParams { + lsp::DidSaveTextDocumentParams { text_document: text_document.clone(), text, }, @@ -7314,7 +7311,7 @@ impl LspStore { .ok()?; server .notify::( - &lsp::DidChangeConfigurationParams { settings }, + lsp::DidChangeConfigurationParams { settings }, ) .ok()?; Some(()) @@ -8536,15 +8533,16 @@ impl LspStore { cx: AsyncApp, ) -> Result { let server_id = LanguageServerId(envelope.payload.language_server_id as usize); - lsp_store.read_with(&cx, |lsp_store, _| { + let task = lsp_store.read_with(&cx, |lsp_store, _| { if let Some(server) = lsp_store.language_server_for_id(server_id) { - server - .notify::(&()) - .context("handling lsp ext cancel flycheck") + Some(server.notify::(())) } else { - anyhow::Ok(()) + None } - })??; + })?; + if let Some(task) = task { + task.context("handling lsp ext cancel flycheck")?; + } Ok(proto::Ack {}) } @@ -8578,14 +8576,11 @@ impl LspStore { } else { None }; - server - .notify::( - &lsp_store::lsp_ext_command::RunFlycheckParams { text_document }, - ) - .context("handling lsp ext run flycheck") - } else { - anyhow::Ok(()) + server.notify::( + lsp_store::lsp_ext_command::RunFlycheckParams { text_document }, + )?; } + anyhow::Ok(()) })??; Ok(proto::Ack {}) @@ -8597,15 +8592,15 @@ impl LspStore { cx: AsyncApp, ) -> Result { let server_id = LanguageServerId(envelope.payload.language_server_id as usize); - lsp_store.read_with(&cx, |lsp_store, _| { - if let Some(server) = lsp_store.language_server_for_id(server_id) { - server - .notify::(&()) - .context("handling lsp ext clear flycheck") - } else { - anyhow::Ok(()) - } - })??; + lsp_store + .read_with(&cx, |lsp_store, _| { + if let Some(server) = lsp_store.language_server_for_id(server_id) { + Some(server.notify::(())) + } else { + None + } + }) + .context("handling lsp ext clear flycheck")?; Ok(proto::Ack {}) } @@ -8744,7 +8739,7 @@ impl LspStore { if filter.should_send_did_rename(&old_uri, is_dir) { language_server - .notify::(&RenameFilesParams { + .notify::(RenameFilesParams { files: vec![FileRename { old_uri: old_uri.clone(), new_uri: new_uri.clone(), @@ -8858,7 +8853,7 @@ impl LspStore { if !changes.is_empty() { server .notify::( - &lsp::DidChangeWatchedFilesParams { changes }, + lsp::DidChangeWatchedFilesParams { changes }, ) .ok(); } @@ -10668,7 +10663,7 @@ impl LspStore { if progress.is_cancellable { server .notify::( - &WorkDoneProgressCancelParams { + WorkDoneProgressCancelParams { token: lsp::NumberOrString::String(token.clone()), }, ) @@ -10799,7 +10794,7 @@ impl LspStore { }; if !params.changes.is_empty() { server - .notify::(¶ms) + .notify::(params) .ok(); } } diff --git a/crates/project/src/lsp_store/json_language_server_ext.rs b/crates/project/src/lsp_store/json_language_server_ext.rs index 7a8d67e79207f3bb22ecf88c6353e71b7a84ee54..78df7132734e9bf71bac8df176f92e15eec21361 100644 --- a/crates/project/src/lsp_store/json_language_server_ext.rs +++ b/crates/project/src/lsp_store/json_language_server_ext.rs @@ -42,7 +42,7 @@ impl lsp::notification::Notification for SchemaContentsChanged { type Params = String; } -pub fn notify_schema_changed(lsp_store: Entity, uri: &String, cx: &App) { +pub fn notify_schema_changed(lsp_store: Entity, uri: String, cx: &App) { zlog::trace!(LOGGER => "Notifying schema changed for URI: {:?}", uri); let servers = lsp_store.read_with(cx, |lsp_store, _| { let mut servers = Vec::new(); @@ -65,7 +65,7 @@ pub fn notify_schema_changed(lsp_store: Entity, uri: &String, cx: &App for server in servers { zlog::trace!(LOGGER => "Notifying server {:?} of schema change for URI: {:?}", server.server_id(), &uri); // TODO: handle errors - server.notify::(uri).ok(); + server.notify::(uri.clone()).ok(); } } diff --git a/crates/project/src/lsp_store/rust_analyzer_ext.rs b/crates/project/src/lsp_store/rust_analyzer_ext.rs index 54f63220b1ef8bab1db22a0808fd2ccb9277b73c..4d5f134e5f1682d53df3a0ab3f55a4b3676518f8 100644 --- a/crates/project/src/lsp_store/rust_analyzer_ext.rs +++ b/crates/project/src/lsp_store/rust_analyzer_ext.rs @@ -119,11 +119,12 @@ pub fn cancel_flycheck( lsp_store .read_with(cx, |lsp_store, _| { if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) { - server.notify::(&())?; + server.notify::(()) + } else { + Ok(()) } - anyhow::Ok(()) - })? - .context("lsp ext cancel flycheck")?; + }) + .context("lsp ext cancel flycheck")??; }; anyhow::Ok(()) }) @@ -173,14 +174,15 @@ pub fn run_flycheck( .read_with(cx, |lsp_store, _| { if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) { server.notify::( - &lsp_store::lsp_ext_command::RunFlycheckParams { + lsp_store::lsp_ext_command::RunFlycheckParams { text_document: None, }, - )?; + ) + } else { + Ok(()) } - anyhow::Ok(()) - })? - .context("lsp ext run flycheck")?; + }) + .context("lsp ext run flycheck")??; }; anyhow::Ok(()) }) @@ -224,11 +226,12 @@ pub fn clear_flycheck( lsp_store .read_with(cx, |lsp_store, _| { if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) { - server.notify::(&())?; + server.notify::(()) + } else { + Ok(()) } - anyhow::Ok(()) - })? - .context("lsp ext clear flycheck")?; + }) + .context("lsp ext clear flycheck")??; }; anyhow::Ok(()) }) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index d6711cd5b54f5bbdb4dd9777bf8b92f2f592249e..14bdc18fbf3f0956267fb7452b017e6a80369e39 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1820,7 +1820,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { } ); - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { @@ -1873,7 +1873,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { }); // Ensure publishing empty diagnostics twice only results in one update event. - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: Default::default(), @@ -1886,7 +1886,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { } ); - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: Default::default(), @@ -2018,7 +2018,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp // Publish diagnostics let fake_server = fake_servers.next().await.unwrap(); - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { @@ -2099,7 +2099,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T // Before restarting the server, report diagnostics with an unknown buffer version. let fake_server = fake_servers.next().await.unwrap(); - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(10000), diagnostics: Vec::new(), @@ -2350,7 +2350,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { assert!(change_notification_1.text_document.version > open_notification.text_document.version); // Report some diagnostics for the initial version of the buffer - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(open_notification.text_document.version), diagnostics: vec![ @@ -2438,7 +2438,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { }); // Ensure overlapping diagnostics are highlighted correctly. - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(open_notification.text_document.version), diagnostics: vec![ @@ -2532,7 +2532,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { ); // Handle out-of-order diagnostics - fake_server.notify::(&lsp::PublishDiagnosticsParams { + fake_server.notify::(lsp::PublishDiagnosticsParams { uri: lsp::Uri::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(change_notification_2.text_document.version), diagnostics: vec![