Skip to main content

ra_ap_rust_analyzer/
main_loop.rs

1//! The main loop of `rust-analyzer` responsible for dispatching LSP
2//! requests/replies and notifications back to the client.
3
4use std::{
5    fmt,
6    ops::Div as _,
7    panic::AssertUnwindSafe,
8    time::{Duration, Instant},
9};
10
11use crossbeam_channel::{Receiver, never, select};
12use ide_db::base_db::{SourceDatabase, VfsPath};
13use lsp_server::{Connection, Notification, Request};
14use lsp_types::{Notification as _, TextDocumentIdentifier};
15use stdx::thread::ThreadIntent;
16use tracing::{Level, error, span};
17use vfs::{AbsPathBuf, FileId, loader::LoadingProgress};
18
19use crate::{
20    config::Config,
21    diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics},
22    discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
23    flycheck::{self, ClearDiagnosticsKind, ClearScope, FlycheckMessage},
24    global_state::{
25        FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState,
26        file_id_to_url, url_to_file_id,
27    },
28    handlers::{
29        dispatch::{NotificationDispatcher, RequestDispatcher},
30        request::empty_diagnostic_report,
31    },
32    lsp::{
33        from_proto, to_proto,
34        utils::{Progress, notification_is},
35    },
36    lsp_ext,
37    reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
38    test_runner::{CargoTestMessage, CargoTestOutput, TestState},
39};
40
41pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> {
42    tracing::info!("initial config: {:#?}", config);
43
44    // Windows scheduler implements priority boosts: if thread waits for an
45    // event (like a condvar), and event fires, priority of the thread is
46    // temporary bumped. This optimization backfires in our case: each time the
47    // `main_loop` schedules a task to run on a threadpool, the worker threads
48    // gets a higher priority, and (on a machine with fewer cores) displaces the
49    // main loop! We work around this by marking the main loop as a
50    // higher-priority thread.
51    //
52    // https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities
53    // https://docs.microsoft.com/en-us/windows/win32/procthread/priority-boosts
54    // https://github.com/rust-lang/rust-analyzer/issues/2835
55    #[cfg(windows)]
56    unsafe {
57        use windows_sys::Win32::System::Threading::*;
58        let thread = GetCurrentThread();
59        let thread_priority_above_normal = 1;
60        SetThreadPriority(thread, thread_priority_above_normal);
61    }
62
63    #[cfg(feature = "dhat")]
64    {
65        if let Some(dhat_output_file) = config.dhat_output_file() {
66            *crate::DHAT_PROFILER.lock().unwrap() =
67                Some(dhat::Profiler::builder().file_name(&dhat_output_file).build());
68        }
69    }
70
71    GlobalState::new(connection.sender, config).run(connection.receiver)
72}
73
74enum Event {
75    Lsp(lsp_server::Message),
76    Task(Task),
77    DeferredTask(DeferredTask),
78    Vfs(vfs::loader::Message),
79    Flycheck(FlycheckMessage),
80    TestResult(CargoTestMessage),
81    DiscoverProject(DiscoverProjectMessage),
82    FetchWorkspaces(FetchWorkspaceRequest),
83}
84
85impl fmt::Display for Event {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            Event::Lsp(_) => write!(f, "Event::Lsp"),
89            Event::Task(_) => write!(f, "Event::Task"),
90            Event::Vfs(_) => write!(f, "Event::Vfs"),
91            Event::Flycheck(_) => write!(f, "Event::Flycheck"),
92            Event::DeferredTask(_) => write!(f, "Event::DeferredTask"),
93            Event::TestResult(_) => write!(f, "Event::TestResult"),
94            Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"),
95            Event::FetchWorkspaces(_) => write!(f, "Event::FetchWorkspaces"),
96        }
97    }
98}
99
100#[derive(Debug)]
101pub(crate) enum DeferredTask {
102    CheckIfIndexed(lsp_types::Uri),
103    CheckProcMacroSources(Vec<FileId>),
104}
105
106#[derive(Debug)]
107pub(crate) enum DiagnosticsTaskKind {
108    Syntax(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
109    Semantic(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
110}
111
112#[derive(Debug)]
113pub(crate) enum Task {
114    Response(lsp_server::Response),
115    DiscoverLinkedProjects(DiscoverProjectParam),
116    Retry(lsp_server::Request),
117    Diagnostics(DiagnosticsTaskKind),
118    DiscoverTest(lsp_ext::DiscoverTestResults),
119    PrimeCaches(PrimeCachesProgress),
120    FetchWorkspace(ProjectWorkspaceProgress),
121    FetchBuildData(BuildDataProgress),
122    LoadProcMacros(ProcMacroProgress),
123    // FIXME: Remove this in favor of a more general QueuedTask, see `handle_did_save_text_document`
124    BuildDepsHaveChanged,
125}
126
127#[derive(Debug)]
128pub(crate) enum DiscoverProjectParam {
129    Buildfile(AbsPathBuf),
130    Path(AbsPathBuf),
131}
132
133#[derive(Debug)]
134pub(crate) enum PrimeCachesProgress {
135    Begin,
136    Report(ide::ParallelPrimeCachesProgress),
137    End { cancelled: bool },
138}
139
140impl fmt::Debug for Event {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        let debug_non_verbose = |not: &Notification, f: &mut fmt::Formatter<'_>| {
143            f.debug_struct("Notification").field("method", &not.method).finish()
144        };
145
146        match self {
147            Event::Lsp(lsp_server::Message::Notification(not))
148                if (notification_is::<lsp_types::DidOpenTextDocumentNotification>(not)
149                    || notification_is::<lsp_types::DidChangeTextDocumentNotification>(not)) =>
150            {
151                return debug_non_verbose(not, f);
152            }
153            Event::Task(Task::Response(resp)) => {
154                return f
155                    .debug_struct("Response")
156                    .field("id", &resp.id)
157                    .field("error", &resp.error)
158                    .finish();
159            }
160            _ => (),
161        }
162
163        match self {
164            Event::Lsp(it) => fmt::Debug::fmt(it, f),
165            Event::Task(it) => fmt::Debug::fmt(it, f),
166            Event::DeferredTask(it) => fmt::Debug::fmt(it, f),
167            Event::Vfs(it) => fmt::Debug::fmt(it, f),
168            Event::Flycheck(it) => fmt::Debug::fmt(it, f),
169            Event::TestResult(it) => fmt::Debug::fmt(it, f),
170            Event::DiscoverProject(it) => fmt::Debug::fmt(it, f),
171            Event::FetchWorkspaces(it) => fmt::Debug::fmt(it, f),
172        }
173    }
174}
175
176impl GlobalState {
177    fn run(mut self, inbox: Receiver<lsp_server::Message>) -> anyhow::Result<()> {
178        self.update_status_or_notify();
179
180        if self.config.did_save_text_document_dynamic_registration() {
181            let additional_patterns = self
182                .config
183                .discover_workspace_config()
184                .map(|cfg| cfg.files_to_watch.clone().into_iter())
185                .into_iter()
186                .flatten()
187                .map(|f| format!("**/{f}"));
188            self.register_did_save_capability(additional_patterns);
189        }
190
191        if self.config.discover_workspace_config().is_none() {
192            self.fetch_workspaces_queue.request_op(
193                "startup".to_owned(),
194                FetchWorkspaceRequest { path: None, force_crate_graph_reload: false },
195            );
196            if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
197                self.fetch_workspaces_queue.should_start_op()
198            {
199                self.fetch_workspaces(cause, path, force_crate_graph_reload);
200            }
201        }
202
203        while let Ok(event) = self.next_event(&inbox) {
204            let Some(event) = event else {
205                anyhow::bail!("client exited without proper shutdown sequence");
206            };
207            if matches!(
208                &event,
209                Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))
210                if method == lsp_types::ExitNotification::METHOD.as_str()
211            ) {
212                return Ok(());
213            }
214            self.handle_event(event);
215        }
216
217        Err(anyhow::anyhow!("A receiver has been dropped, something panicked!"))
218    }
219
220    fn register_did_save_capability(&mut self, additional_patterns: impl Iterator<Item = String>) {
221        let additional_filters = additional_patterns.map(|pattern| {
222            lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(
223                lsp_types::TextDocumentFilterPattern {
224                    language: None,
225                    scheme: None,
226                    pattern: pattern.into(),
227                },
228            ))
229        });
230
231        let mut selectors = vec![
232            lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(
233                lsp_types::TextDocumentFilterPattern {
234                    language: None,
235                    scheme: None,
236                    pattern: "**/*.rs".to_owned().into(),
237                },
238            )),
239            lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(
240                lsp_types::TextDocumentFilterPattern {
241                    language: None,
242                    scheme: None,
243                    pattern: "**/Cargo.toml".to_owned().into(),
244                },
245            )),
246            lsp_types::DocumentFilter::TextDocumentFilter(lsp_types::TextDocumentFilter::Pattern(
247                lsp_types::TextDocumentFilterPattern {
248                    language: None,
249                    scheme: None,
250                    pattern: "**/Cargo.lock".to_owned().into(),
251                },
252            )),
253        ];
254        selectors.extend(additional_filters);
255
256        let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
257            save_options: lsp_types::SaveOptions { include_text: Some(false) },
258            text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
259                document_selector: Some(selectors),
260            },
261        };
262
263        let registration = lsp_types::Registration {
264            id: "textDocument/didSave".to_owned(),
265            method: "textDocument/didSave".to_owned(),
266            register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
267        };
268        self.send_request::<lsp_types::RegistrationRequest>(
269            lsp_types::RegistrationParams { registrations: vec![registration] },
270            |_, _| (),
271        );
272    }
273
274    fn next_event(
275        &mut self,
276        inbox: &Receiver<lsp_server::Message>,
277    ) -> Result<Option<Event>, crossbeam_channel::RecvError> {
278        // Make sure we reply to formatting requests ASAP so the editor doesn't block
279        if let Ok(task) = self.fmt_pool.receiver.try_recv() {
280            return Ok(Some(Event::Task(task)));
281        }
282
283        select! {
284            recv(inbox) -> msg =>
285                return Ok(msg.ok().map(Event::Lsp)),
286
287            recv(self.task_pool.receiver) -> task =>
288                task.map(Event::Task),
289
290            recv(self.deferred_task_queue.receiver) -> task =>
291                task.map(Event::DeferredTask),
292
293            recv(self.fmt_pool.receiver) -> task =>
294                task.map(Event::Task),
295
296            recv(self.loader.receiver) -> task =>
297                task.map(Event::Vfs),
298
299            recv(self.flycheck_receiver) -> task =>
300                task.map(Event::Flycheck),
301
302            recv(self.test_run_receiver) -> task =>
303                task.map(Event::TestResult),
304
305            recv(self.discover_receiver) -> task =>
306                task.map(Event::DiscoverProject),
307
308            recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => {
309                Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1))
310            },
311        }
312        .map(Some)
313    }
314
315    fn handle_event(&mut self, event: Event) {
316        let loop_start = Instant::now();
317        let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered();
318
319        let event_dbg_msg = format!("{event:?}");
320        tracing::debug!(?event, "handle_event");
321
322        let was_quiescent = self.is_quiescent();
323
324        let mut cancellation_time = None;
325        match event {
326            Event::Lsp(msg) => match msg {
327                lsp_server::Message::Request(req) => self.on_new_request(loop_start, req),
328                lsp_server::Message::Notification(not) => self.on_notification(not),
329                lsp_server::Message::Response(resp) => self.complete_request(resp),
330            },
331            Event::DeferredTask(task) => {
332                let _p = tracing::info_span!("GlobalState::handle_event/queued_task").entered();
333                self.handle_deferred_task(task);
334                // Coalesce multiple deferred task events into one loop turn
335                while loop_start.elapsed() < Duration::from_millis(50)
336                    && let Ok(task) = self.deferred_task_queue.receiver.try_recv()
337                {
338                    self.handle_deferred_task(task);
339                }
340            }
341            Event::Task(task) => {
342                let _p = tracing::info_span!("GlobalState::handle_event/task").entered();
343                let mut prime_caches_progress = Vec::new();
344
345                cancellation_time = self.handle_task(&mut prime_caches_progress, task);
346                // Coalesce multiple task events into one loop turn
347                while loop_start.elapsed() < Duration::from_millis(50)
348                    && let Ok(task) = self.task_pool.receiver.try_recv()
349                {
350                    self.handle_task(&mut prime_caches_progress, task);
351                }
352
353                let title = "Indexing";
354                let cancel_token = || Some("rustAnalyzer/cachePriming".to_owned());
355
356                let mut last_report = None;
357                for progress in prime_caches_progress {
358                    match progress {
359                        PrimeCachesProgress::Begin => {
360                            self.report_progress(
361                                title,
362                                Progress::Begin,
363                                None,
364                                Some(0.0),
365                                cancel_token(),
366                            );
367                        }
368                        PrimeCachesProgress::Report(report) => {
369                            let message = match &*report.crates_currently_indexing {
370                                [crate_name] => Some(format!(
371                                    "{}/{} ({})",
372                                    report.crates_done,
373                                    report.crates_total,
374                                    crate_name.as_str(),
375                                )),
376                                [crate_name, rest @ ..] => Some(format!(
377                                    "{}/{} ({} + {} more)",
378                                    report.crates_done,
379                                    report.crates_total,
380                                    crate_name.as_str(),
381                                    rest.len()
382                                )),
383                                _ => None,
384                            };
385
386                            // Don't send too many notifications while batching, sending progress reports
387                            // serializes notifications on the mainthread at the moment which slows us down
388                            last_report = Some((
389                                message,
390                                Progress::fraction(report.crates_done, report.crates_total),
391                                report.work_type,
392                            ));
393                        }
394                        PrimeCachesProgress::End { cancelled } => {
395                            self.analysis_host.trigger_garbage_collection();
396                            self.prime_caches_queue.op_completed(());
397                            if cancelled {
398                                self.prime_caches_queue
399                                    .request_op("restart after cancellation".to_owned(), ());
400                            } else if self.config.check_on_save(None)
401                                && self.config.flycheck_workspace(None)
402                                && !self.fetch_build_data_queue.op_requested()
403                            {
404                                // Priming finished; now run the deferred initial workspace flycheck
405                                // (kept off the critical path so `cargo check` doesn't contend with
406                                // cache priming for CPU).
407                                self.flycheck
408                                    .iter()
409                                    .for_each(|flycheck| flycheck.restart_workspace(None));
410                            }
411                            if let Some((message, fraction, title)) = last_report.take() {
412                                self.report_progress(
413                                    title,
414                                    Progress::Report,
415                                    message,
416                                    Some(fraction),
417                                    cancel_token(),
418                                );
419                            }
420                            self.report_progress(
421                                title,
422                                Progress::End,
423                                None,
424                                Some(1.0),
425                                cancel_token(),
426                            );
427                        }
428                    };
429                }
430                if let Some((message, fraction, title)) = last_report.take() {
431                    self.report_progress(
432                        title,
433                        Progress::Report,
434                        message,
435                        Some(fraction),
436                        cancel_token(),
437                    );
438                }
439            }
440            Event::Vfs(message) => {
441                let _p = tracing::info_span!("GlobalState::handle_event/vfs").entered();
442                let mut last_progress_report = None;
443                self.handle_vfs_msg(message, &mut last_progress_report);
444                // Coalesce many VFS event into a single loop turn
445                while loop_start.elapsed() < Duration::from_millis(50)
446                    && let Ok(message) = self.loader.receiver.try_recv()
447                {
448                    self.handle_vfs_msg(message, &mut last_progress_report);
449                }
450                if let Some((message, fraction)) = last_progress_report {
451                    self.report_progress(
452                        "Roots Scanned",
453                        Progress::Report,
454                        Some(message),
455                        Some(fraction),
456                        None,
457                    );
458                }
459            }
460            Event::Flycheck(message) => {
461                let mut cargo_finished = false;
462                self.handle_flycheck_msg(message, &mut cargo_finished);
463                // Coalesce many flycheck updates into a single loop turn
464                while loop_start.elapsed() < Duration::from_millis(50)
465                    && let Ok(message) = self.flycheck_receiver.try_recv()
466                {
467                    self.handle_flycheck_msg(message, &mut cargo_finished);
468                }
469                if cargo_finished {
470                    self.send_request::<lsp_types::DiagnosticRefreshRequest>((), |_, _| ());
471                }
472            }
473            Event::TestResult(message) => {
474                let _p = tracing::info_span!("GlobalState::handle_event/test_result").entered();
475                self.handle_cargo_test_msg(message);
476                // Coalesce many test result event into a single loop turn
477                while loop_start.elapsed() < Duration::from_millis(50)
478                    && let Ok(message) = self.test_run_receiver.try_recv()
479                {
480                    self.handle_cargo_test_msg(message);
481                }
482            }
483            Event::DiscoverProject(message) => {
484                self.handle_discover_msg(message);
485                // Coalesce many project discovery events into a single loop turn.
486                while loop_start.elapsed() < Duration::from_millis(50)
487                    && let Ok(message) = self.discover_receiver.try_recv()
488                {
489                    self.handle_discover_msg(message);
490                }
491            }
492            Event::FetchWorkspaces(req) => {
493                self.fetch_workspaces_queue.request_op("project structure change".to_owned(), req)
494            }
495        }
496        let event_handling_duration = loop_start.elapsed();
497        let ((state_changed, changes_cancellation_time), memdocs_added_or_removed) =
498            if self.vfs_done {
499                if let Some(cause) = self.wants_to_switch.take() {
500                    cancellation_time = match (cancellation_time, self.switch_workspaces(cause)) {
501                        (Some(a), Some(b)) => Some(a + b),
502                        (Some(d), None) | (None, Some(d)) => Some(d),
503                        (None, None) => None,
504                    };
505                }
506                (self.process_changes(), self.mem_docs.take_changes())
507            } else {
508                ((false, None), false)
509            };
510        cancellation_time = match (cancellation_time, changes_cancellation_time) {
511            (Some(a), Some(b)) => Some(a + b),
512            (Some(d), None) | (None, Some(d)) => Some(d),
513            (None, None) => None,
514        };
515
516        let mut gc_elapsed = None;
517        if self.is_quiescent() {
518            let became_quiescent = !was_quiescent;
519            if became_quiescent {
520                // delay initial cache priming until proc macros are loaded, or we will load up a bunch of garbage into salsa
521                let proc_macros_loaded = self.config.prefill_caches()
522                    && (!self.config.expand_proc_macros()
523                        || self.fetch_proc_macros_queue.last_op_result().copied().unwrap_or(false));
524                if proc_macros_loaded {
525                    self.prime_caches_queue.request_op("became quiescent".to_owned(), ());
526                }
527                if self.config.check_on_save(None)
528                    && self.config.flycheck_workspace(None)
529                    && !self.fetch_build_data_queue.op_requested()
530                {
531                    if !self.config.prefill_caches() {
532                        self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
533                    } else if proc_macros_loaded
534                        && !self.prime_caches_queue.op_in_progress()
535                        && !self.prime_caches_queue.op_requested()
536                    {
537                        self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
538                    }
539                }
540            }
541
542            let client_refresh = became_quiescent || state_changed;
543            if client_refresh {
544                // Refresh semantic tokens if the client supports it.
545                if self.config.semantic_tokens_refresh() {
546                    self.semantic_tokens_cache.lock().clear();
547                    self.send_request::<lsp_types::SemanticTokensRefreshRequest>((), |_, _| ());
548                }
549
550                // Refresh code lens if the client supports it.
551                if self.config.code_lens_refresh() {
552                    self.send_request::<lsp_types::CodeLensRefreshRequest>((), |_, _| ());
553                }
554
555                // Refresh inlay hints if the client supports it.
556                if self.config.inlay_hints_refresh() {
557                    self.send_request::<lsp_types::InlayHintRefreshRequest>((), |_, _| ());
558                }
559
560                if self.config.diagnostics_refresh() {
561                    self.send_request::<lsp_types::DiagnosticRefreshRequest>((), |_, _| ());
562                }
563            }
564
565            let project_or_mem_docs_changed =
566                became_quiescent || state_changed || memdocs_added_or_removed;
567            if project_or_mem_docs_changed
568                && !self.config.text_document_diagnostic()
569                && self.config.publish_diagnostics(None)
570            {
571                self.update_diagnostics();
572            }
573            if project_or_mem_docs_changed && self.config.test_explorer() {
574                self.update_tests();
575            }
576
577            let current_revision = self.analysis_host.raw_database().nonce_and_revision().1;
578            // no work is currently being done, now we can block a bit and clean up our garbage
579            if self.task_pool.handle.is_empty()
580                && self.fmt_pool.handle.is_empty()
581                && current_revision != self.last_gc_revision
582            {
583                let gc_start = Instant::now();
584                self.analysis_host.trigger_garbage_collection();
585                self.last_gc_revision = self.analysis_host.raw_database().nonce_and_revision().1;
586                gc_elapsed = Some(gc_start.elapsed());
587            }
588        }
589
590        self.cleanup_discover_handles();
591
592        if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
593            for file_id in diagnostic_changes {
594                let uri = file_id_to_url(&self.vfs.read().0, file_id);
595                let version = from_proto::vfs_path(&uri)
596                    .ok()
597                    .and_then(|path| self.mem_docs.get(&path).map(|it| it.version));
598
599                let diagnostics =
600                    self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
601                self.publish_diagnostics(uri, version, diagnostics);
602            }
603        }
604
605        if (self.config.cargo_autoreload_config(None)
606            || self.config.discover_workspace_config().is_some())
607            && let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
608                self.fetch_workspaces_queue.should_start_op()
609        {
610            self.fetch_workspaces(cause, path, force_crate_graph_reload);
611        }
612
613        if !self.fetch_workspaces_queue.op_in_progress() {
614            if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() {
615                self.fetch_build_data(cause);
616            } else if let Some((cause, (change, paths))) =
617                self.fetch_proc_macros_queue.should_start_op()
618            {
619                self.fetch_proc_macros(cause, change, paths);
620            }
621        }
622
623        if let Some((cause, ())) = self.prime_caches_queue.should_start_op() {
624            self.prime_caches(cause);
625        }
626
627        self.update_status_or_notify();
628
629        let loop_duration = loop_start.elapsed();
630        if loop_duration > Duration::from_millis(100) && was_quiescent {
631            tracing::warn!(
632                "overly long loop turn took {loop_duration:?}:\n\
633                (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\
634                (cancellation took {cancellation_time:?})
635                (garbage collection took {gc_elapsed:?})"
636            );
637            self.poke_ra_ap_rust_analyzer_developer(format!(
638                "overly long loop turn took {loop_duration:?}:\n\
639                (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\
640                (cancellation took {cancellation_time:?})
641                (garbage collection took {gc_elapsed:?})"
642            ));
643        }
644    }
645
646    fn prime_caches(&mut self, cause: String) {
647        tracing::debug!(%cause, "will prime caches");
648        let num_worker_threads = self.config.prime_caches_num_threads();
649
650        self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
651            let analysis = AssertUnwindSafe(self.snapshot().analysis);
652            move |sender| {
653                sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
654                let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
655                    let report = PrimeCachesProgress::Report(progress);
656                    sender.send(Task::PrimeCaches(report)).unwrap();
657                });
658                sender
659                    .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() }))
660                    .unwrap();
661            }
662        });
663    }
664
665    fn update_diagnostics(&mut self) {
666        let db = self.analysis_host.raw_database();
667        let generation = self.diagnostics.next_generation();
668        let subscriptions = {
669            let vfs = &self.vfs.read().0;
670            self.mem_docs
671                .iter()
672                .map(|path| vfs.file_id(path).unwrap())
673                .filter_map(|(file_id, excluded)| {
674                    (excluded == vfs::FileExcluded::No).then_some(file_id)
675                })
676                .filter(|&file_id| {
677                    let source_root_id = db.file_source_root(file_id).source_root_id(db);
678                    let source_root = db.source_root(source_root_id).source_root(db);
679                    // Only publish diagnostics for files in the workspace, not from crates.io deps
680                    // or the sysroot.
681                    // While theoretically these should never have errors, we have quite a few false
682                    // positives particularly in the stdlib, and those diagnostics would stay around
683                    // forever if we emitted them here.
684                    !source_root.is_library
685                })
686                .collect::<std::sync::Arc<_>>()
687        };
688        tracing::trace!("updating notifications for {:?}", subscriptions);
689        // Split up the work on multiple threads, but we don't wanna fill the entire task pool with
690        // diagnostic tasks, so we limit the number of tasks to a quarter of the total thread pool.
691        let max_tasks = self.config.main_loop_num_threads().div(4).max(1);
692        let chunk_length = subscriptions.len() / max_tasks;
693        let remainder = subscriptions.len() % max_tasks;
694
695        let mut start = 0;
696        for task_idx in 0..max_tasks {
697            let extra = if task_idx < remainder { 1 } else { 0 };
698            let end = start + chunk_length + extra;
699            let slice = start..end;
700            if slice.is_empty() {
701                break;
702            }
703            // Diagnostics are triggered by the user typing
704            // so we run them on a latency sensitive thread.
705            let snapshot = self.snapshot();
706            self.task_pool.handle.spawn_with_sender(ThreadIntent::LatencySensitive, {
707                let subscriptions = subscriptions.clone();
708                // Do not fetch semantic diagnostics (and populate query results) if we haven't even
709                // loaded the initial workspace yet.
710                //
711                // Only fetch semantic diagnostics when
712                // - we have fully populated the VFS
713                // - have a workspace
714                // - have finished fetching the build data once
715                // - and have finished loading the proc-macros once
716                let fetch_semantic = self.vfs_done
717                    && self.fetch_workspaces_queue.last_op_result().is_some()
718                    && (!self.config.run_build_scripts(None)
719                        || (self.fetch_build_data_queue.last_op_result().is_none()
720                            && !self.fetch_build_data_queue.op_in_progress()))
721                    && (!self.config.expand_proc_macros()
722                        || (self.fetch_proc_macros_queue.last_op_result().is_none()
723                            && !self.fetch_proc_macros_queue.op_in_progress()));
724                move |sender| {
725                    // We aren't observing the semantics token cache here
726                    let snapshot = AssertUnwindSafe(&snapshot);
727                    let diags = std::panic::catch_unwind(|| {
728                        fetch_native_diagnostics(
729                            &snapshot,
730                            subscriptions.clone(),
731                            slice.clone(),
732                            NativeDiagnosticsFetchKind::Syntax,
733                        )
734                    })
735                    .unwrap_or_else(|_| {
736                        subscriptions.iter().map(|&id| (id, Vec::new())).collect::<Vec<_>>()
737                    });
738                    sender
739                        .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags)))
740                        .unwrap();
741
742                    if fetch_semantic {
743                        let diags = std::panic::catch_unwind(|| {
744                            fetch_native_diagnostics(
745                                &snapshot,
746                                subscriptions.clone(),
747                                slice.clone(),
748                                NativeDiagnosticsFetchKind::Semantic,
749                            )
750                        })
751                        .unwrap_or_else(|_| {
752                            subscriptions.iter().map(|&id| (id, Vec::new())).collect::<Vec<_>>()
753                        });
754                        sender
755                            .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic(
756                                generation, diags,
757                            )))
758                            .unwrap();
759                    }
760                }
761            });
762            start = end;
763        }
764    }
765
766    fn update_tests(&mut self) {
767        if !self.vfs_done {
768            return;
769        }
770        let db = self.analysis_host.raw_database();
771        let subscriptions = self
772            .mem_docs
773            .iter()
774            .map(|path| self.vfs.read().0.file_id(path).unwrap())
775            .filter_map(|(file_id, excluded)| {
776                (excluded == vfs::FileExcluded::No).then_some(file_id)
777            })
778            .filter(|&file_id| {
779                let source_root_id = db.file_source_root(file_id).source_root_id(db);
780                let source_root = db.source_root(source_root_id).source_root(db);
781                !source_root.is_library
782            })
783            .collect::<Vec<_>>();
784        tracing::trace!("updating tests for {:?}", subscriptions);
785
786        // Updating tests are triggered by the user typing
787        // so we run them on a latency sensitive thread.
788        self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
789            let snapshot = self.snapshot();
790            move || {
791                let tests = subscriptions
792                    .iter()
793                    .copied()
794                    .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok())
795                    .flatten()
796                    .collect::<Vec<_>>();
797
798                Task::DiscoverTest(lsp_ext::DiscoverTestResults {
799                    tests: tests
800                        .into_iter()
801                        .filter_map(|t| {
802                            let line_index = t.file.and_then(|f| snapshot.file_line_index(f).ok());
803                            to_proto::test_item(&snapshot, t, line_index.as_ref())
804                        })
805                        .collect(),
806                    scope: None,
807                    scope_file: Some(
808                        subscriptions
809                            .into_iter()
810                            .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) })
811                            .collect(),
812                    ),
813                })
814            }
815        });
816    }
817
818    fn update_status_or_notify(&mut self) {
819        let status = self.current_status();
820        if self.last_reported_status != status {
821            self.last_reported_status = status.clone();
822
823            if self.config.server_status_notification() {
824                self.send_notification::<lsp_ext::ServerStatusNotification>(status);
825            } else if let (
826                health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error),
827                Some(message),
828            ) = (status.health, &status.message)
829            {
830                let open_log_button = tracing::enabled!(tracing::Level::ERROR)
831                    && (self.fetch_build_data_error().is_err()
832                        || self.fetch_workspace_error().is_err());
833                self.show_message(
834                    match health {
835                        lsp_ext::Health::Ok => lsp_types::MessageType::Info,
836                        lsp_ext::Health::Warning => lsp_types::MessageType::Warning,
837                        lsp_ext::Health::Error => lsp_types::MessageType::Error,
838                    },
839                    message.clone(),
840                    open_log_button,
841                );
842            }
843        }
844    }
845
846    fn handle_task(
847        &mut self,
848        prime_caches_progress: &mut Vec<PrimeCachesProgress>,
849        task: Task,
850    ) -> Option<Duration> {
851        let mut cancellation_time = None;
852        match task {
853            Task::Response(response) => self.respond(response),
854            // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
855            Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
856            Task::Retry(_) => (),
857            Task::Diagnostics(kind) => {
858                self.diagnostics.set_native_diagnostics(kind);
859            }
860            Task::PrimeCaches(progress) => match progress {
861                PrimeCachesProgress::Begin => prime_caches_progress.push(progress),
862                PrimeCachesProgress::Report(_) => {
863                    match prime_caches_progress.last_mut() {
864                        Some(last @ PrimeCachesProgress::Report(_)) => {
865                            // Coalesce subsequent update events.
866                            *last = progress;
867                        }
868                        _ => prime_caches_progress.push(progress),
869                    }
870                }
871                PrimeCachesProgress::End { .. } => prime_caches_progress.push(progress),
872            },
873            Task::FetchWorkspace(progress) => {
874                let (state, msg) = match progress {
875                    ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
876                    ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
877                    ProjectWorkspaceProgress::End(workspaces, force_crate_graph_reload) => {
878                        let resp = FetchWorkspaceResponse { workspaces, force_crate_graph_reload };
879                        self.fetch_workspaces_queue.op_completed(resp);
880                        if let Err(e) = self.fetch_workspace_error() {
881                            error!("FetchWorkspaceError: {e}");
882                        }
883                        self.wants_to_switch = Some("fetched workspace".to_owned());
884                        self.diagnostics.clear_check_all();
885                        (Progress::End, None)
886                    }
887                };
888
889                self.report_progress("Fetching", state, msg, None, None);
890            }
891            Task::DiscoverLinkedProjects(arg) => {
892                if let Some(cfg) = self.config.discover_workspace_config() {
893                    let command = cfg.command.clone();
894                    let discover = DiscoverCommand::new(self.discover_sender.clone(), command);
895
896                    let discover_path = match &arg {
897                        DiscoverProjectParam::Buildfile(it) => it,
898                        DiscoverProjectParam::Path(it) => it,
899                    };
900                    let current_dir =
901                        self.config.workspace_root_for(discover_path.as_path()).clone();
902
903                    let arg = match arg {
904                        DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it),
905                        DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
906                    };
907
908                    match discover.spawn(arg, current_dir.as_ref()) {
909                        Ok(handle) => {
910                            if self.discover_jobs_active == 0 {
911                                let title = &cfg.progress_label.clone();
912                                self.report_progress(title, Progress::Begin, None, None, None);
913                            }
914                            self.discover_jobs_active += 1;
915                            self.discover_handles.push(handle)
916                        }
917                        Err(e) => self.show_message(
918                            lsp_types::MessageType::Error,
919                            format!("Failed to spawn project discovery command: {e:#}"),
920                            false,
921                        ),
922                    }
923                }
924            }
925            Task::FetchBuildData(progress) => {
926                let (state, msg) = match progress {
927                    BuildDataProgress::Begin => (Some(Progress::Begin), None),
928                    BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
929                    BuildDataProgress::End((workspaces, build_scripts)) => {
930                        let resp = FetchBuildDataResponse { workspaces, build_scripts };
931                        self.fetch_build_data_queue.op_completed(resp);
932
933                        if let Err(e) = self.fetch_build_data_error() {
934                            error!("FetchBuildDataError: {e}");
935                        }
936
937                        if self.wants_to_switch.is_none() {
938                            self.wants_to_switch = Some("fetched build data".to_owned());
939                        }
940                        (Some(Progress::End), None)
941                    }
942                };
943
944                if let Some(state) = state {
945                    self.report_progress("Building compile-time-deps", state, msg, None, None);
946                }
947            }
948            Task::LoadProcMacros(progress) => {
949                let (state, msg) = match progress {
950                    ProcMacroProgress::Begin => (Some(Progress::Begin), None),
951                    ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
952                    ProcMacroProgress::End(change) => {
953                        self.fetch_proc_macros_queue.op_completed(true);
954                        cancellation_time = Some(self.analysis_host.apply_change(change));
955                        // FIXME This feels a bit off, this should go through similar machinery as build scripts?
956                        _ = self.finish_loading_crate_graph();
957                        (Some(Progress::End), None)
958                    }
959                };
960
961                if let Some(state) = state {
962                    self.report_progress("Loading proc-macros", state, msg, None, None);
963                }
964            }
965            Task::BuildDepsHaveChanged => self.build_deps_changed = true,
966            Task::DiscoverTest(tests) => {
967                self.send_notification::<lsp_ext::DiscoveredTestsNotification>(tests);
968            }
969        }
970        cancellation_time
971    }
972
973    fn handle_vfs_msg(
974        &mut self,
975        message: vfs::loader::Message,
976        last_progress_report: &mut Option<(String, f64)>,
977    ) {
978        let _p = tracing::info_span!("GlobalState::handle_vfs_msg").entered();
979        let is_changed = matches!(message, vfs::loader::Message::Changed { .. });
980        match message {
981            vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => {
982                let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered();
983                self.debounce_workspace_fetch();
984                let vfs = &mut self.vfs.write().0;
985                for (path, contents) in files {
986                    if matches!(path.name_and_extension(), Some(("minicore", Some("rs")))) {
987                        // Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that.
988                        self.minicore.minicore_text = contents
989                            .as_ref()
990                            .and_then(|contents| str::from_utf8(contents).ok())
991                            .map(triomphe::Arc::from);
992                    }
993
994                    let path = VfsPath::from(path);
995                    // if the file is in mem docs, it's managed by the client via notifications
996                    // so only set it if its not in there
997                    if !self.mem_docs.contains(&path)
998                        && (is_changed || vfs.file_id(&path).is_none())
999                    {
1000                        vfs.set_file_contents(path, contents);
1001                    }
1002                }
1003            }
1004            vfs::loader::Message::Progress { n_total, n_done, dir, config_version } => {
1005                let _p = span!(Level::INFO, "GlobalState::handle_vfs_msg/progress").entered();
1006                stdx::always!(config_version <= self.vfs_config_version);
1007
1008                let (n_done, state) = match n_done {
1009                    LoadingProgress::Started => {
1010                        self.vfs_span =
1011                            Some(span!(Level::INFO, "vfs_load", total = n_total).entered());
1012                        (0, Progress::Begin)
1013                    }
1014                    LoadingProgress::Progress(n_done) => (n_done.min(n_total), Progress::Report),
1015                    LoadingProgress::Finished => {
1016                        self.vfs_span = None;
1017                        (n_total, Progress::End)
1018                    }
1019                };
1020
1021                self.vfs_progress_config_version = config_version;
1022                self.vfs_done = state == Progress::End;
1023
1024                let mut message = format!("{n_done}/{n_total}");
1025                if let Some(dir) = dir {
1026                    message += &format!(
1027                        ": {}",
1028                        match dir.strip_prefix(self.config.workspace_root_for(&dir)) {
1029                            Some(relative_path) => relative_path.as_utf8_path(),
1030                            None => dir.as_ref(),
1031                        }
1032                    );
1033                }
1034
1035                match state {
1036                    Progress::Begin => self.report_progress(
1037                        "Roots Scanned",
1038                        state,
1039                        Some(message),
1040                        Some(Progress::fraction(n_done, n_total)),
1041                        None,
1042                    ),
1043                    // Don't send too many notifications while batching, sending progress reports
1044                    // serializes notifications on the mainthread at the moment which slows us down
1045                    Progress::Report => {
1046                        if last_progress_report.is_none() {
1047                            self.report_progress(
1048                                "Roots Scanned",
1049                                state,
1050                                Some(message.clone()),
1051                                Some(Progress::fraction(n_done, n_total)),
1052                                None,
1053                            );
1054                        }
1055
1056                        *last_progress_report =
1057                            Some((message, Progress::fraction(n_done, n_total)));
1058                    }
1059                    Progress::End => {
1060                        last_progress_report.take();
1061                        self.report_progress(
1062                            "Roots Scanned",
1063                            state,
1064                            Some(message),
1065                            Some(Progress::fraction(n_done, n_total)),
1066                            None,
1067                        )
1068                    }
1069                }
1070            }
1071        }
1072    }
1073
1074    fn handle_deferred_task(&mut self, task: DeferredTask) {
1075        match task {
1076            DeferredTask::CheckIfIndexed(uri) => {
1077                let snap = self.snapshot();
1078
1079                self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
1080                    let _p = tracing::info_span!("GlobalState::check_if_indexed").entered();
1081                    tracing::debug!(?uri, "handling uri");
1082                    let Some(id) = from_proto::file_id(&snap, &uri).expect("unable to get FileId")
1083                    else {
1084                        return;
1085                    };
1086                    if let Ok(crates) = &snap.analysis.crates_for(id) {
1087                        if crates.is_empty() {
1088                            if snap.config.discover_workspace_config().is_some() {
1089                                let path =
1090                                    from_proto::abs_path(&uri).expect("Unable to get AbsPath");
1091                                let arg = DiscoverProjectParam::Path(path);
1092                                sender.send(Task::DiscoverLinkedProjects(arg)).unwrap();
1093                            }
1094                        } else {
1095                            tracing::debug!(?uri, "is indexed");
1096                        }
1097                    }
1098                });
1099            }
1100            DeferredTask::CheckProcMacroSources(modified_rust_files) => {
1101                let analysis = AssertUnwindSafe(self.snapshot().analysis);
1102                self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
1103                    move |sender| {
1104                        if modified_rust_files.into_iter().any(|file_id| {
1105                            // FIXME: Check whether these files could be build script related
1106                            match analysis.crates_for(file_id) {
1107                                Ok(crates) => crates.iter().any(|&krate| {
1108                                    analysis.is_proc_macro_crate(krate).is_ok_and(|it| it)
1109                                }),
1110                                _ => false,
1111                            }
1112                        }) {
1113                            sender.send(Task::BuildDepsHaveChanged).unwrap();
1114                        }
1115                    }
1116                });
1117            }
1118        }
1119    }
1120
1121    fn handle_discover_msg(&mut self, message: DiscoverProjectMessage) {
1122        let title = self
1123            .config
1124            .discover_workspace_config()
1125            .map(|cfg| cfg.progress_label.clone())
1126            .expect("No title could be found; this is a bug");
1127        match message {
1128            DiscoverProjectMessage::Finished { project, buildfile } => {
1129                self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1);
1130                if self.discover_jobs_active == 0 {
1131                    self.report_progress(&title, Progress::End, None, None, None);
1132                }
1133
1134                let mut config = Config::clone(&*self.config);
1135                config.add_discovered_project_from_command(project, buildfile);
1136                self.update_configuration(config);
1137            }
1138            DiscoverProjectMessage::Progress { message } => {
1139                if self.discover_jobs_active > 0 {
1140                    self.report_progress(&title, Progress::Report, Some(message), None, None)
1141                }
1142            }
1143            DiscoverProjectMessage::Error { error, source } => {
1144                let message = format!("Project discovery failed: {error}");
1145                self.show_and_log_error(message.clone(), source);
1146
1147                self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1);
1148                if self.discover_jobs_active == 0 {
1149                    self.report_progress(&title, Progress::End, Some(message), None, None)
1150                }
1151            }
1152        }
1153    }
1154
1155    /// Drop any discover command processes that have exited, due to
1156    /// finishing or erroring.
1157    fn cleanup_discover_handles(&mut self) {
1158        let mut active_handles = vec![];
1159
1160        for mut discover_handle in self.discover_handles.drain(..) {
1161            if !discover_handle.handle.has_exited() {
1162                active_handles.push(discover_handle);
1163            }
1164        }
1165        self.discover_handles = active_handles;
1166    }
1167
1168    fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) {
1169        match message.output {
1170            CargoTestOutput::Test { name, state } => {
1171                let state = match state {
1172                    TestState::Started => lsp_ext::TestState::Started,
1173                    TestState::Ignored => lsp_ext::TestState::Skipped,
1174                    TestState::Ok => lsp_ext::TestState::Passed,
1175                    TestState::Failed { stdout } => lsp_ext::TestState::Failed { message: stdout },
1176                };
1177
1178                // The notification requires the namespace form (with underscores) of the target
1179                let test_id = format!("{}::{name}", message.target.target.replace('-', "_"));
1180
1181                self.send_notification::<lsp_ext::ChangeTestStateNotification>(
1182                    lsp_ext::ChangeTestStateParams { test_id, state },
1183                );
1184            }
1185            CargoTestOutput::Suite => (),
1186            CargoTestOutput::Finished => {
1187                self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
1188                if self.test_run_remaining_jobs == 0 {
1189                    self.send_notification::<lsp_ext::EndRunTestNotification>(());
1190                    self.test_run_session = None;
1191                }
1192            }
1193            CargoTestOutput::Custom { text } => {
1194                self.send_notification::<lsp_ext::AppendOutputToRunTestNotification>(text);
1195            }
1196        }
1197    }
1198
1199    fn handle_flycheck_msg(&mut self, message: FlycheckMessage, cargo_finished: &mut bool) {
1200        match message {
1201            FlycheckMessage::AddDiagnostic {
1202                id,
1203                generation,
1204                workspace_root,
1205                diagnostic,
1206                package_id,
1207            } => {
1208                let snap = self.snapshot();
1209                let diagnostics = crate::diagnostics::flycheck_to_proto::map_rust_diagnostic_to_lsp(
1210                    &self.config.diagnostics_map(None),
1211                    diagnostic,
1212                    &workspace_root,
1213                    &snap,
1214                );
1215                for diag in diagnostics {
1216                    match url_to_file_id(&self.vfs.read().0, &diag.url) {
1217                        Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic(
1218                            id,
1219                            generation,
1220                            &package_id,
1221                            file_id,
1222                            diag.diagnostic,
1223                            diag.fix,
1224                        ),
1225                        Ok(None) => {}
1226                        Err(err) => {
1227                            error!(
1228                                "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
1229                                err
1230                            );
1231                        }
1232                    };
1233                }
1234            }
1235            FlycheckMessage::ClearDiagnostics {
1236                id,
1237                kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
1238            } => self.diagnostics.clear_check(id),
1239            FlycheckMessage::ClearDiagnostics {
1240                id,
1241                kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)),
1242            } => self.diagnostics.clear_check_for_package(id, package_id),
1243            FlycheckMessage::ClearDiagnostics {
1244                id,
1245                kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Workspace),
1246            } => self.diagnostics.clear_check_older_than(id, generation),
1247            FlycheckMessage::ClearDiagnostics {
1248                id,
1249                kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)),
1250            } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation),
1251            FlycheckMessage::Progress { id, progress } => {
1252                let format_with_id = |user_facing_command: String| {
1253                    // When we're running multiple flychecks, we have to include a disambiguator in
1254                    // the title, or the editor complains. Note that this is a user-facing string.
1255                    if self.flycheck.len() == 1 {
1256                        user_facing_command
1257                    } else {
1258                        format!("{user_facing_command} (#{})", id + 1)
1259                    }
1260                };
1261
1262                self.flycheck_formatted_commands
1263                    .resize_with(self.flycheck.len().max(id + 1), || {
1264                        format_with_id(self.config.flycheck(None).to_string())
1265                    });
1266
1267                let (state, message) = match progress {
1268                    flycheck::Progress::DidStart { user_facing_command } => {
1269                        self.flycheck_formatted_commands[id] = format_with_id(user_facing_command);
1270                        (Progress::Begin, None)
1271                    }
1272                    flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
1273                    flycheck::Progress::DidCancel => {
1274                        self.last_flycheck_error = None;
1275                        *cargo_finished = true;
1276                        (Progress::End, None)
1277                    }
1278                    flycheck::Progress::DidFailToRestart(err) => {
1279                        self.last_flycheck_error =
1280                            Some(format!("cargo check failed to start: {err}"));
1281                        return;
1282                    }
1283                    flycheck::Progress::DidFinish(result) => {
1284                        self.last_flycheck_error =
1285                            result.err().map(|err| format!("cargo check failed to start: {err}"));
1286                        *cargo_finished = true;
1287                        (Progress::End, None)
1288                    }
1289                };
1290
1291                // Clone because we &mut self for report_progress
1292                let title = self.flycheck_formatted_commands[id].clone();
1293                self.report_progress(
1294                    &title,
1295                    state,
1296                    message,
1297                    None,
1298                    Some(format!("rust-analyzer/flycheck/{id}")),
1299                );
1300            }
1301        }
1302    }
1303
1304    /// Registers and handles a request. This should only be called once per incoming request.
1305    fn on_new_request(&mut self, request_received: Instant, req: Request) {
1306        let _p =
1307            span!(Level::INFO, "GlobalState::on_new_request", req.method = ?req.method).entered();
1308        self.register_request(&req, request_received);
1309        self.on_request(req);
1310    }
1311
1312    /// Handles a request.
1313    fn on_request(&mut self, req: Request) {
1314        let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self };
1315        dispatcher.on_sync_mut::<lsp_types::ShutdownRequest>(|s, ()| {
1316            s.shutdown_requested = true;
1317            s.proc_macro_clients =
1318                std::iter::repeat_with(|| None).take(s.proc_macro_clients.len()).collect();
1319            s.flycheck.iter().for_each(|handle| handle.cancel());
1320            s.discover_handles.clear();
1321            Ok(())
1322        });
1323
1324        match &mut dispatcher {
1325            RequestDispatcher { req: Some(req), global_state: this } if this.shutdown_requested => {
1326                this.respond(lsp_server::Response::new_err(
1327                    req.id.clone(),
1328                    lsp_server::ErrorCode::InvalidRequest as i32,
1329                    "Shutdown already requested.".to_owned(),
1330                ));
1331                return;
1332            }
1333            _ => (),
1334        }
1335
1336        use crate::handlers::request as handlers;
1337
1338        const RETRY: bool = true;
1339        const NO_RETRY: bool = false;
1340
1341        #[rustfmt::skip]
1342        dispatcher
1343            // Request handlers that must run on the main thread
1344            // because they mutate GlobalState:
1345            .on_sync_mut::<lsp_ext::ReloadWorkspaceRequest>(handlers::handle_workspace_reload)
1346            .on_sync_mut::<lsp_ext::RebuildProcMacrosRequest>(handlers::handle_proc_macros_rebuild)
1347            .on_sync_mut::<lsp_ext::MemoryUsageRequest>(handlers::handle_memory_usage)
1348            .on_sync_mut::<lsp_ext::RunTestRequest>(handlers::handle_run_test)
1349            // Request handlers which are related to the user typing
1350            // are run on the main thread to reduce latency:
1351            .on_sync::<lsp_ext::JoinLinesRequest>(handlers::handle_join_lines)
1352            .on_sync::<lsp_ext::OnEnterRequest>(handlers::handle_on_enter)
1353            .on_sync::<lsp_types::SelectionRangeRequest>(handlers::handle_selection_range)
1354            .on_sync::<lsp_ext::MatchingBraceRequest>(handlers::handle_matching_brace)
1355            .on_sync::<lsp_ext::DocumentOnTypeFormattingRequest>(handlers::handle_on_type_formatting)
1356            // Formatting should be done immediately as the editor might wait on it, but we can't
1357            // put it on the main thread as we do not want the main thread to block on rustfmt.
1358            // So we have an extra thread just for formatting requests to make sure it gets handled
1359            // as fast as possible.
1360            .on_fmt_thread::<lsp_types::DocumentFormattingRequest>(handlers::handle_formatting)
1361            .on_fmt_thread::<lsp_types::DocumentRangeFormattingRequest>(handlers::handle_range_formatting)
1362            // We can’t run latency-sensitive request handlers which do semantic
1363            // analysis on the main thread because that would block other
1364            // requests. Instead, we run these request handlers on higher priority
1365            // threads in the threadpool.
1366            // FIXME: Retrying can make the result of this stale?
1367            .on_latency_sensitive::<RETRY, lsp_types::CompletionRequest>(handlers::handle_completion)
1368            // FIXME: Retrying can make the result of this stale
1369            .on_latency_sensitive::<RETRY, lsp_types::CompletionResolveRequest>(handlers::handle_completion_resolve)
1370            .on_latency_sensitive::<RETRY, lsp_types::SemanticTokensRequest>(handlers::handle_semantic_tokens_full)
1371            .on_latency_sensitive::<RETRY, lsp_types::SemanticTokensDeltaRequest>(handlers::handle_semantic_tokens_full_delta)
1372            .on_latency_sensitive::<NO_RETRY, lsp_types::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
1373            // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
1374            // All other request handlers
1375            .on_with_vfs_default::<lsp_types::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, empty_diagnostic_report, || lsp_server::ResponseError {
1376                code: lsp_server::ErrorCode::ServerCancelled as i32,
1377                message: "server cancelled the request".to_owned(),
1378                data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
1379                    retrigger_request: true
1380                }).ok(),
1381            })
1382            .on::<RETRY, lsp_types::DocumentSymbolRequest>(handlers::handle_document_symbol)
1383            .on::<RETRY, lsp_types::FoldingRangeRequest>(handlers::handle_folding_range)
1384            .on::<NO_RETRY, lsp_types::SignatureHelpRequest>(handlers::handle_signature_help)
1385            .on::<RETRY, lsp_types::WillRenameFilesRequest>(handlers::handle_will_rename_files)
1386            .on::<NO_RETRY, lsp_types::DefinitionRequest>(handlers::handle_goto_definition)
1387            .on::<NO_RETRY, lsp_types::DeclarationRequest>(handlers::handle_goto_declaration)
1388            .on::<NO_RETRY, lsp_types::ImplementationRequest>(handlers::handle_goto_implementation)
1389            .on::<NO_RETRY, lsp_types::TypeDefinitionRequest>(handlers::handle_goto_type_definition)
1390            .on::<NO_RETRY, lsp_types::InlayHintRequest>(handlers::handle_inlay_hints)
1391            .on_identity::<NO_RETRY, lsp_types::InlayHintResolveRequest, _>(handlers::handle_inlay_hints_resolve)
1392            .on::<NO_RETRY, lsp_types::CodeLensRequest>(handlers::handle_code_lens)
1393            .on_identity::<NO_RETRY, lsp_types::CodeLensResolveRequest, _>(handlers::handle_code_lens_resolve)
1394            .on::<NO_RETRY, lsp_types::PrepareRenameRequest>(handlers::handle_prepare_rename)
1395            .on::<NO_RETRY, lsp_types::RenameRequest>(handlers::handle_rename)
1396            .on::<NO_RETRY, lsp_types::ReferencesRequest>(handlers::handle_references)
1397            .on::<NO_RETRY, lsp_types::DocumentHighlightRequest>(handlers::handle_document_highlight)
1398            .on::<NO_RETRY, lsp_types::CallHierarchyPrepareRequest>(handlers::handle_call_hierarchy_prepare)
1399            .on::<NO_RETRY, lsp_types::CallHierarchyIncomingCallsRequest>(handlers::handle_call_hierarchy_incoming)
1400            .on::<NO_RETRY, lsp_types::CallHierarchyOutgoingCallsRequest>(handlers::handle_call_hierarchy_outgoing)
1401            // All other request handlers (lsp extension)
1402            .on::<RETRY, lsp_ext::FetchDependencyListRequest>(handlers::fetch_dependency_list)
1403            .on::<RETRY, lsp_ext::AnalyzerStatusRequest>(handlers::handle_analyzer_status)
1404            .on::<RETRY, lsp_ext::ViewFileTextRequest>(handlers::handle_view_file_text)
1405            .on::<RETRY, lsp_ext::ViewCrateGraphRequest>(handlers::handle_view_crate_graph)
1406            .on::<RETRY, lsp_ext::ViewItemTreeRequest>(handlers::handle_view_item_tree)
1407            .on::<RETRY, lsp_ext::DiscoverTestRequest>(handlers::handle_discover_test)
1408            .on::<RETRY, lsp_ext::WorkspaceSymbolRequest>(handlers::handle_workspace_symbol)
1409            .on::<NO_RETRY, lsp_ext::SsrRequest>(handlers::handle_ssr)
1410            .on::<NO_RETRY, lsp_ext::ViewRecursiveMemoryLayoutRequest>(handlers::handle_view_recursive_memory_layout)
1411            .on::<NO_RETRY, lsp_ext::ViewSyntaxTreeRequest>(handlers::handle_view_syntax_tree)
1412            .on::<NO_RETRY, lsp_ext::ViewHirRequest>(handlers::handle_view_hir)
1413            .on::<NO_RETRY, lsp_ext::ViewMirRequest>(handlers::handle_view_mir)
1414            .on::<NO_RETRY, lsp_ext::InterpretFunctionRequest>(handlers::handle_interpret_function)
1415            .on::<NO_RETRY, lsp_ext::ExpandMacroRequest>(handlers::handle_expand_macro)
1416            .on::<NO_RETRY, lsp_ext::ParentModuleRequest>(handlers::handle_parent_module)
1417            .on::<NO_RETRY, lsp_ext::ChildModulesRequest>(handlers::handle_child_modules)
1418            .on::<NO_RETRY, lsp_ext::RunnablesRequest>(handlers::handle_runnables)
1419            .on::<NO_RETRY, lsp_ext::RelatedTestsRequest>(handlers::handle_related_tests)
1420            .on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action)
1421            .on_identity::<RETRY, lsp_ext::CodeActionResolveRequest, _>(handlers::handle_code_action_resolve)
1422            .on::<NO_RETRY, lsp_ext::HoverRequest>(handlers::handle_hover)
1423            .on::<NO_RETRY, lsp_ext::ExternalDocsRequest>(handlers::handle_open_docs)
1424            .on::<NO_RETRY, lsp_ext::OpenCargoTomlRequest>(handlers::handle_open_cargo_toml)
1425            .on::<NO_RETRY, lsp_ext::MoveItemRequest>(handlers::handle_move_item)
1426            //
1427            .on::<NO_RETRY, lsp_ext::InternalTestingFetchConfigRequest>(handlers::internal_testing_fetch_config)
1428            .on::<RETRY, lsp_ext::EvaluatePredicateRequest>(handlers::handle_evaluate_predicate)
1429            .on::<RETRY, lsp_ext::GetFailedObligationsRequest>(handlers::get_failed_obligations)
1430            .finish();
1431    }
1432
1433    /// Handles an incoming notification.
1434    fn on_notification(&mut self, not: Notification) {
1435        let _p =
1436            span!(Level::INFO, "GlobalState::on_notification", not.method = ?not.method).entered();
1437        use crate::handlers::notification as handlers;
1438
1439        NotificationDispatcher { not: Some(not), global_state: self }
1440            .on_sync_mut::<lsp_types::CancelNotification>(handlers::handle_cancel)
1441            .on_sync_mut::<lsp_types::WorkDoneProgressCancelNotification>(
1442                handlers::handle_work_done_progress_cancel,
1443            )
1444            .on_sync_mut::<lsp_types::DidOpenTextDocumentNotification>(
1445                handlers::handle_did_open_text_document,
1446            )
1447            .on_sync_mut::<lsp_types::DidChangeTextDocumentNotification>(
1448                handlers::handle_did_change_text_document,
1449            )
1450            .on_sync_mut::<lsp_types::DidCloseTextDocumentNotification>(
1451                handlers::handle_did_close_text_document,
1452            )
1453            .on_sync_mut::<lsp_types::DidSaveTextDocumentNotification>(
1454                handlers::handle_did_save_text_document,
1455            )
1456            .on_sync_mut::<lsp_types::DidChangeConfigurationNotification>(
1457                handlers::handle_did_change_configuration,
1458            )
1459            .on_sync_mut::<lsp_types::DidChangeWorkspaceFoldersNotification>(
1460                handlers::handle_did_change_workspace_folders,
1461            )
1462            .on_sync_mut::<lsp_types::DidChangeWatchedFilesNotification>(
1463                handlers::handle_did_change_watched_files,
1464            )
1465            .on_sync_mut::<lsp_ext::CancelFlycheckNotification>(handlers::handle_cancel_flycheck)
1466            .on_sync_mut::<lsp_ext::ClearFlycheckNotification>(handlers::handle_clear_flycheck)
1467            .on_sync_mut::<lsp_ext::RunFlycheckNotification>(handlers::handle_run_flycheck)
1468            .on_sync_mut::<lsp_ext::AbortRunTestNotification>(handlers::handle_abort_run_test)
1469            .finish();
1470    }
1471}