lsp: Serialize LSP notifications on background threads (#39403)

This should reduce hiccups when opening large files.

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz
2025-10-08 19:48:40 +00:00
committed by GitHub
parent cd656485c8
commit ef423148fc
14 changed files with 186 additions and 147 deletions
+34 -39
View File
@@ -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::<lsp::notification::DidChangeConfiguration>(
&did_change_configuration_params,
)
.ok();
language_server.notify::<lsp::notification::DidChangeConfiguration>(
did_change_configuration_params,
)?;
anyhow::Ok(language_server)
}
@@ -7206,7 +7203,7 @@ impl LspStore {
language_server
.notify::<lsp::notification::DidChangeTextDocument>(
&lsp::DidChangeTextDocumentParams {
lsp::DidChangeTextDocumentParams {
text_document: lsp::VersionedTextDocumentIdentifier::new(
uri.clone(),
next_version,
@@ -7243,7 +7240,7 @@ impl LspStore {
};
server
.notify::<lsp::notification::DidSaveTextDocument>(
&lsp::DidSaveTextDocumentParams {
lsp::DidSaveTextDocumentParams {
text_document: text_document.clone(),
text,
},
@@ -7314,7 +7311,7 @@ impl LspStore {
.ok()?;
server
.notify::<lsp::notification::DidChangeConfiguration>(
&lsp::DidChangeConfigurationParams { settings },
lsp::DidChangeConfigurationParams { settings },
)
.ok()?;
Some(())
@@ -8536,15 +8533,16 @@ impl LspStore {
cx: AsyncApp,
) -> Result<proto::Ack> {
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::<lsp_store::lsp_ext_command::LspExtCancelFlycheck>(&())
.context("handling lsp ext cancel flycheck")
Some(server.notify::<lsp_store::lsp_ext_command::LspExtCancelFlycheck>(()))
} 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::LspExtRunFlycheck>(
&lsp_store::lsp_ext_command::RunFlycheckParams { text_document },
)
.context("handling lsp ext run flycheck")
} else {
anyhow::Ok(())
server.notify::<lsp_store::lsp_ext_command::LspExtRunFlycheck>(
lsp_store::lsp_ext_command::RunFlycheckParams { text_document },
)?;
}
anyhow::Ok(())
})??;
Ok(proto::Ack {})
@@ -8597,15 +8592,15 @@ impl LspStore {
cx: AsyncApp,
) -> Result<proto::Ack> {
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::<lsp_store::lsp_ext_command::LspExtClearFlycheck>(&())
.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::<lsp_store::lsp_ext_command::LspExtClearFlycheck>(()))
} 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::<DidRenameFiles>(&RenameFilesParams {
.notify::<DidRenameFiles>(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::notification::DidChangeWatchedFiles>(
&lsp::DidChangeWatchedFilesParams { changes },
lsp::DidChangeWatchedFilesParams { changes },
)
.ok();
}
@@ -10668,7 +10663,7 @@ impl LspStore {
if progress.is_cancellable {
server
.notify::<lsp::notification::WorkDoneProgressCancel>(
&WorkDoneProgressCancelParams {
WorkDoneProgressCancelParams {
token: lsp::NumberOrString::String(token.clone()),
},
)
@@ -10799,7 +10794,7 @@ impl LspStore {
};
if !params.changes.is_empty() {
server
.notify::<lsp::notification::DidChangeWatchedFiles>(&params)
.notify::<lsp::notification::DidChangeWatchedFiles>(params)
.ok();
}
}
@@ -42,7 +42,7 @@ impl lsp::notification::Notification for SchemaContentsChanged {
type Params = String;
}
pub fn notify_schema_changed(lsp_store: Entity<LspStore>, uri: &String, cx: &App) {
pub fn notify_schema_changed(lsp_store: Entity<LspStore>, 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<LspStore>, 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::<SchemaContentsChanged>(uri).ok();
server.notify::<SchemaContentsChanged>(uri.clone()).ok();
}
}
@@ -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::<lsp_store::lsp_ext_command::LspExtCancelFlycheck>(&())?;
server.notify::<lsp_store::lsp_ext_command::LspExtCancelFlycheck>(())
} 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::LspExtRunFlycheck>(
&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::<lsp_store::lsp_ext_command::LspExtClearFlycheck>(&())?;
server.notify::<lsp_store::lsp_ext_command::LspExtClearFlycheck>(())
} else {
Ok(())
}
anyhow::Ok(())
})?
.context("lsp ext clear flycheck")?;
})
.context("lsp ext clear flycheck")??;
};
anyhow::Ok(())
})
+8 -8
View File
@@ -1820,7 +1820,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
}
);
fake_server.notify::<lsp::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(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::notification::PublishDiagnostics>(&lsp::PublishDiagnosticsParams {
fake_server.notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
uri: lsp::Uri::from_file_path(path!("/dir/a.rs")).unwrap(),
version: Some(change_notification_2.text_document.version),
diagnostics: vec![