Skip to content

Commit 2a6655a

Browse files
committed
Auto merge of rust-lang#17825 - Veykril:server-things, r=Veykril
internal: Offload diagnostics serialization to the task pool
2 parents 7614de4 + 3e4632d commit 2a6655a

File tree

3 files changed

+70
-45
lines changed

3 files changed

+70
-45
lines changed

src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs

+43
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,49 @@ impl GlobalState {
560560
fn send(&self, message: lsp_server::Message) {
561561
self.sender.send(message).unwrap()
562562
}
563+
564+
pub(crate) fn publish_diagnostics(
565+
&mut self,
566+
uri: Url,
567+
version: Option<i32>,
568+
mut diagnostics: Vec<lsp_types::Diagnostic>,
569+
) {
570+
// We put this on a separate thread to avoid blocking the main thread with serialization work
571+
self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
572+
let sender = self.sender.clone();
573+
move |_| {
574+
// VSCode assumes diagnostic messages to be non-empty strings, so we need to patch
575+
// empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether
576+
// diagnostic messages are actually allowed to be empty or not and patching this
577+
// in the VSCode client does not work as the assertion happens in the protocol
578+
// conversion. So this hack is here to stay, and will be considered a hack
579+
// until the LSP decides to state that empty messages are allowed.
580+
581+
// See https://github.com/rust-lang/rust-analyzer/issues/11404
582+
// See https://github.com/rust-lang/rust-analyzer/issues/13130
583+
let patch_empty = |message: &mut String| {
584+
if message.is_empty() {
585+
" ".clone_into(message);
586+
}
587+
};
588+
589+
for d in &mut diagnostics {
590+
patch_empty(&mut d.message);
591+
if let Some(dri) = &mut d.related_information {
592+
for dri in dri {
593+
patch_empty(&mut dri.message);
594+
}
595+
}
596+
}
597+
598+
let not = lsp_server::Notification::new(
599+
<lsp_types::notification::PublishDiagnostics as lsp_types::notification::Notification>::METHOD.to_owned(),
600+
lsp_types::PublishDiagnosticsParams { uri, diagnostics, version },
601+
);
602+
_ = sender.send(not.into());
603+
}
604+
});
605+
}
563606
}
564607

565608
impl Drop for GlobalState {

src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs

+23-44
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ impl GlobalState {
179179
}
180180
}
181181

182-
while let Some(event) = self.next_event(&inbox) {
182+
while let Ok(event) = self.next_event(&inbox) {
183+
let Some(event) = event else {
184+
anyhow::bail!("client exited without proper shutdown sequence");
185+
};
183186
if matches!(
184187
&event,
185188
Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))
@@ -190,7 +193,7 @@ impl GlobalState {
190193
self.handle_event(event)?;
191194
}
192195

193-
anyhow::bail!("client exited without proper shutdown sequence")
196+
Err(anyhow::anyhow!("A receiver has been dropped, something panicked!"))
194197
}
195198

196199
fn register_did_save_capability(&mut self, additional_patterns: impl Iterator<Item = String>) {
@@ -237,37 +240,40 @@ impl GlobalState {
237240
);
238241
}
239242

240-
fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
243+
fn next_event(
244+
&self,
245+
inbox: &Receiver<lsp_server::Message>,
246+
) -> Result<Option<Event>, crossbeam_channel::RecvError> {
241247
select! {
242248
recv(inbox) -> msg =>
243-
msg.ok().map(Event::Lsp),
249+
return Ok(msg.ok().map(Event::Lsp)),
244250

245251
recv(self.task_pool.receiver) -> task =>
246-
Some(Event::Task(task.unwrap())),
252+
task.map(Event::Task),
247253

248254
recv(self.deferred_task_queue.receiver) -> task =>
249-
Some(Event::QueuedTask(task.unwrap())),
255+
task.map(Event::QueuedTask),
250256

251257
recv(self.fmt_pool.receiver) -> task =>
252-
Some(Event::Task(task.unwrap())),
258+
task.map(Event::Task),
253259

254260
recv(self.loader.receiver) -> task =>
255-
Some(Event::Vfs(task.unwrap())),
261+
task.map(Event::Vfs),
256262

257263
recv(self.flycheck_receiver) -> task =>
258-
Some(Event::Flycheck(task.unwrap())),
264+
task.map(Event::Flycheck),
259265

260266
recv(self.test_run_receiver) -> task =>
261-
Some(Event::TestResult(task.unwrap())),
267+
task.map(Event::TestResult),
262268

263269
recv(self.discover_receiver) -> task =>
264-
Some(Event::DiscoverProject(task.unwrap())),
270+
task.map(Event::DiscoverProject),
265271
}
272+
.map(Some)
266273
}
267274

268275
fn handle_event(&mut self, event: Event) -> anyhow::Result<()> {
269276
let loop_start = Instant::now();
270-
// NOTE: don't count blocking select! call as a loop-turn time
271277
let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered();
272278

273279
let event_dbg_msg = format!("{event:?}");
@@ -434,40 +440,13 @@ impl GlobalState {
434440
if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
435441
for file_id in diagnostic_changes {
436442
let uri = file_id_to_url(&self.vfs.read().0, file_id);
437-
let mut diagnostics =
438-
self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
439-
440-
// VSCode assumes diagnostic messages to be non-empty strings, so we need to patch
441-
// empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether
442-
// diagnostic messages are actually allowed to be empty or not and patching this
443-
// in the VSCode client does not work as the assertion happens in the protocol
444-
// conversion. So this hack is here to stay, and will be considered a hack
445-
// until the LSP decides to state that empty messages are allowed.
446-
447-
// See https://github.com/rust-lang/rust-analyzer/issues/11404
448-
// See https://github.com/rust-lang/rust-analyzer/issues/13130
449-
let patch_empty = |message: &mut String| {
450-
if message.is_empty() {
451-
" ".clone_into(message);
452-
}
453-
};
454-
455-
for d in &mut diagnostics {
456-
patch_empty(&mut d.message);
457-
if let Some(dri) = &mut d.related_information {
458-
for dri in dri {
459-
patch_empty(&mut dri.message);
460-
}
461-
}
462-
}
463-
464443
let version = from_proto::vfs_path(&uri)
465-
.map(|path| self.mem_docs.get(&path).map(|it| it.version))
466-
.unwrap_or_default();
444+
.ok()
445+
.and_then(|path| self.mem_docs.get(&path).map(|it| it.version));
467446

468-
self.send_notification::<lsp_types::notification::PublishDiagnostics>(
469-
lsp_types::PublishDiagnosticsParams { uri, diagnostics, version },
470-
);
447+
let diagnostics =
448+
self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
449+
self.publish_diagnostics(uri, version, diagnostics);
471450
}
472451
}
473452

src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ impl NotifyActor {
103103
let (watcher_sender, watcher_receiver) = unbounded();
104104
let watcher = log_notify_error(RecommendedWatcher::new(
105105
move |event| {
106-
watcher_sender.send(event).unwrap();
106+
// we don't care about the error. If sending fails that usually
107+
// means we were dropped, so unwrapping will just add to the
108+
// panic noise.
109+
_ = watcher_sender.send(event);
107110
},
108111
Config::default(),
109112
));

0 commit comments

Comments
 (0)