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::{TextDocumentIdentifier, notification::Notification as _};
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::Url),
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::notification::DidOpenTextDocument>(not)
149                    || notification_is::<lsp_types::notification::DidChangeTextDocument>(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::notification::Exit::METHOD
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| lsp_types::DocumentFilter {
222            language: None,
223            scheme: None,
224            pattern: (Some(pattern)),
225        });
226
227        let mut selectors = vec![
228            lsp_types::DocumentFilter {
229                language: None,
230                scheme: None,
231                pattern: Some("**/*.rs".into()),
232            },
233            lsp_types::DocumentFilter {
234                language: None,
235                scheme: None,
236                pattern: Some("**/Cargo.toml".into()),
237            },
238            lsp_types::DocumentFilter {
239                language: None,
240                scheme: None,
241                pattern: Some("**/Cargo.lock".into()),
242            },
243        ];
244        selectors.extend(additional_filters);
245
246        let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
247            include_text: Some(false),
248            text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
249                document_selector: Some(selectors),
250            },
251        };
252
253        let registration = lsp_types::Registration {
254            id: "textDocument/didSave".to_owned(),
255            method: "textDocument/didSave".to_owned(),
256            register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
257        };
258        self.send_request::<lsp_types::request::RegisterCapability>(
259            lsp_types::RegistrationParams { registrations: vec![registration] },
260            |_, _| (),
261        );
262    }
263
264    fn next_event(
265        &mut self,
266        inbox: &Receiver<lsp_server::Message>,
267    ) -> Result<Option<Event>, crossbeam_channel::RecvError> {
268        // Make sure we reply to formatting requests ASAP so the editor doesn't block
269        if let Ok(task) = self.fmt_pool.receiver.try_recv() {
270            return Ok(Some(Event::Task(task)));
271        }
272
273        select! {
274            recv(inbox) -> msg =>
275                return Ok(msg.ok().map(Event::Lsp)),
276
277            recv(self.task_pool.receiver) -> task =>
278                task.map(Event::Task),
279
280            recv(self.deferred_task_queue.receiver) -> task =>
281                task.map(Event::DeferredTask),
282
283            recv(self.fmt_pool.receiver) -> task =>
284                task.map(Event::Task),
285
286            recv(self.loader.receiver) -> task =>
287                task.map(Event::Vfs),
288
289            recv(self.flycheck_receiver) -> task =>
290                task.map(Event::Flycheck),
291
292            recv(self.test_run_receiver) -> task =>
293                task.map(Event::TestResult),
294
295            recv(self.discover_receiver) -> task =>
296                task.map(Event::DiscoverProject),
297
298            recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => {
299                Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1))
300            },
301        }
302        .map(Some)
303    }
304
305    fn handle_event(&mut self, event: Event) {
306        let loop_start = Instant::now();
307        let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered();
308
309        let event_dbg_msg = format!("{event:?}");
310        tracing::debug!(?loop_start, ?event, "handle_event");
311        if tracing::enabled!(tracing::Level::TRACE) {
312            let task_queue_len = self.task_pool.handle.len();
313            if task_queue_len > 0 {
314                tracing::trace!("task queue len: {}", task_queue_len);
315            }
316        }
317
318        let was_quiescent = self.is_quiescent();
319        match event {
320            Event::Lsp(msg) => match msg {
321                lsp_server::Message::Request(req) => self.on_new_request(loop_start, req),
322                lsp_server::Message::Notification(not) => self.on_notification(not),
323                lsp_server::Message::Response(resp) => self.complete_request(resp),
324            },
325            Event::DeferredTask(task) => {
326                let _p = tracing::info_span!("GlobalState::handle_event/queued_task").entered();
327                self.handle_deferred_task(task);
328                // Coalesce multiple deferred task events into one loop turn
329                while let Ok(task) = self.deferred_task_queue.receiver.try_recv() {
330                    self.handle_deferred_task(task);
331                }
332            }
333            Event::Task(task) => {
334                let _p = tracing::info_span!("GlobalState::handle_event/task").entered();
335                let mut prime_caches_progress = Vec::new();
336
337                self.handle_task(&mut prime_caches_progress, task);
338                // Coalesce multiple task events into one loop turn
339                while let Ok(task) = self.task_pool.receiver.try_recv() {
340                    self.handle_task(&mut prime_caches_progress, task);
341                }
342
343                let title = "Indexing";
344                let cancel_token = Some("rustAnalyzer/cachePriming".to_owned());
345
346                let mut last_report = None;
347                for progress in prime_caches_progress {
348                    match progress {
349                        PrimeCachesProgress::Begin => {
350                            self.report_progress(
351                                title,
352                                Progress::Begin,
353                                None,
354                                Some(0.0),
355                                cancel_token.clone(),
356                            );
357                        }
358                        PrimeCachesProgress::Report(report) => {
359                            let message = match &*report.crates_currently_indexing {
360                                [crate_name] => Some(format!(
361                                    "{}/{} ({})",
362                                    report.crates_done,
363                                    report.crates_total,
364                                    crate_name.as_str(),
365                                )),
366                                [crate_name, rest @ ..] => Some(format!(
367                                    "{}/{} ({} + {} more)",
368                                    report.crates_done,
369                                    report.crates_total,
370                                    crate_name.as_str(),
371                                    rest.len()
372                                )),
373                                _ => None,
374                            };
375
376                            // Don't send too many notifications while batching, sending progress reports
377                            // serializes notifications on the mainthread at the moment which slows us down
378                            last_report = Some((
379                                message,
380                                Progress::fraction(report.crates_done, report.crates_total),
381                                report.work_type,
382                            ));
383                        }
384                        PrimeCachesProgress::End { cancelled } => {
385                            self.analysis_host.trigger_garbage_collection();
386                            self.prime_caches_queue.op_completed(());
387                            if cancelled {
388                                self.prime_caches_queue
389                                    .request_op("restart after cancellation".to_owned(), ());
390                            }
391                            if let Some((message, fraction, title)) = last_report.take() {
392                                self.report_progress(
393                                    title,
394                                    Progress::Report,
395                                    message,
396                                    Some(fraction),
397                                    cancel_token.clone(),
398                                );
399                            }
400                            self.report_progress(
401                                title,
402                                Progress::End,
403                                None,
404                                Some(1.0),
405                                cancel_token.clone(),
406                            );
407                        }
408                    };
409                }
410                if let Some((message, fraction, title)) = last_report.take() {
411                    self.report_progress(
412                        title,
413                        Progress::Report,
414                        message,
415                        Some(fraction),
416                        cancel_token.clone(),
417                    );
418                }
419            }
420            Event::Vfs(message) => {
421                let _p = tracing::info_span!("GlobalState::handle_event/vfs").entered();
422                let mut last_progress_report = None;
423                self.handle_vfs_msg(message, &mut last_progress_report);
424                // Coalesce many VFS event into a single loop turn
425                while let Ok(message) = self.loader.receiver.try_recv() {
426                    self.handle_vfs_msg(message, &mut last_progress_report);
427                }
428                if let Some((message, fraction)) = last_progress_report {
429                    self.report_progress(
430                        "Roots Scanned",
431                        Progress::Report,
432                        Some(message),
433                        Some(fraction),
434                        None,
435                    );
436                }
437            }
438            Event::Flycheck(message) => {
439                let mut cargo_finished = false;
440                self.handle_flycheck_msg(message, &mut cargo_finished);
441                // Coalesce many flycheck updates into a single loop turn
442                while let Ok(message) = self.flycheck_receiver.try_recv() {
443                    self.handle_flycheck_msg(message, &mut cargo_finished);
444                }
445                if cargo_finished {
446                    self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
447                        (),
448                        |_, _| (),
449                    );
450                }
451            }
452            Event::TestResult(message) => {
453                let _p = tracing::info_span!("GlobalState::handle_event/test_result").entered();
454                self.handle_cargo_test_msg(message);
455                // Coalesce many test result event into a single loop turn
456                while let Ok(message) = self.test_run_receiver.try_recv() {
457                    self.handle_cargo_test_msg(message);
458                }
459            }
460            Event::DiscoverProject(message) => {
461                self.handle_discover_msg(message);
462                // Coalesce many project discovery events into a single loop turn.
463                while let Ok(message) = self.discover_receiver.try_recv() {
464                    self.handle_discover_msg(message);
465                }
466            }
467            Event::FetchWorkspaces(req) => {
468                self.fetch_workspaces_queue.request_op("project structure change".to_owned(), req)
469            }
470        }
471        let event_handling_duration = loop_start.elapsed();
472        let (state_changed, memdocs_added_or_removed) = if self.vfs_done {
473            if let Some(cause) = self.wants_to_switch.take() {
474                self.switch_workspaces(cause);
475            }
476            (self.process_changes(), self.mem_docs.take_changes())
477        } else {
478            (false, false)
479        };
480
481        if self.is_quiescent() {
482            let became_quiescent = !was_quiescent;
483            if became_quiescent {
484                if self.config.check_on_save(None)
485                    && self.config.flycheck_workspace(None)
486                    && !self.fetch_build_data_queue.op_requested()
487                {
488                    // Project has loaded properly, kick off initial flycheck
489                    self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
490                }
491                // delay initial cache priming until proc macros are loaded, or we will load up a bunch of garbage into salsa
492                let proc_macros_loaded = self.config.prefill_caches()
493                    && (!self.config.expand_proc_macros()
494                        || self.fetch_proc_macros_queue.last_op_result().copied().unwrap_or(false));
495                if proc_macros_loaded {
496                    self.prime_caches_queue.request_op("became quiescent".to_owned(), ());
497                }
498            }
499
500            let client_refresh = became_quiescent || state_changed;
501            if client_refresh {
502                // Refresh semantic tokens if the client supports it.
503                if self.config.semantic_tokens_refresh() {
504                    self.semantic_tokens_cache.lock().clear();
505                    self.send_request::<lsp_types::request::SemanticTokensRefresh>((), |_, _| ());
506                }
507
508                // Refresh code lens if the client supports it.
509                if self.config.code_lens_refresh() {
510                    self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
511                }
512
513                // Refresh inlay hints if the client supports it.
514                if self.config.inlay_hints_refresh() {
515                    self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
516                }
517
518                if self.config.diagnostics_refresh() {
519                    self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
520                        (),
521                        |_, _| (),
522                    );
523                }
524            }
525
526            let project_or_mem_docs_changed =
527                became_quiescent || state_changed || memdocs_added_or_removed;
528            if project_or_mem_docs_changed
529                && !self.config.text_document_diagnostic()
530                && self.config.publish_diagnostics(None)
531            {
532                self.update_diagnostics();
533            }
534            if project_or_mem_docs_changed && self.config.test_explorer() {
535                self.update_tests();
536            }
537
538            let current_revision = self.analysis_host.raw_database().nonce_and_revision().1;
539            // no work is currently being done, now we can block a bit and clean up our garbage
540            if self.task_pool.handle.is_empty()
541                && self.fmt_pool.handle.is_empty()
542                && current_revision != self.last_gc_revision
543            {
544                self.analysis_host.trigger_garbage_collection();
545                self.last_gc_revision = current_revision;
546            }
547        }
548
549        self.cleanup_discover_handles();
550
551        if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
552            for file_id in diagnostic_changes {
553                let uri = file_id_to_url(&self.vfs.read().0, file_id);
554                let version = from_proto::vfs_path(&uri)
555                    .ok()
556                    .and_then(|path| self.mem_docs.get(&path).map(|it| it.version));
557
558                let diagnostics =
559                    self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
560                self.publish_diagnostics(uri, version, diagnostics);
561            }
562        }
563
564        if (self.config.cargo_autoreload_config(None)
565            || self.config.discover_workspace_config().is_some())
566            && let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
567                self.fetch_workspaces_queue.should_start_op()
568        {
569            self.fetch_workspaces(cause, path, force_crate_graph_reload);
570        }
571
572        if !self.fetch_workspaces_queue.op_in_progress() {
573            if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() {
574                self.fetch_build_data(cause);
575            } else if let Some((cause, (change, paths))) =
576                self.fetch_proc_macros_queue.should_start_op()
577            {
578                self.fetch_proc_macros(cause, change, paths);
579            }
580        }
581
582        if let Some((cause, ())) = self.prime_caches_queue.should_start_op() {
583            self.prime_caches(cause);
584        }
585
586        self.update_status_or_notify();
587
588        let loop_duration = loop_start.elapsed();
589        if loop_duration > Duration::from_millis(100) && was_quiescent {
590            tracing::warn!(
591                "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}"
592            );
593            self.poke_ra_ap_rust_analyzer_developer(format!(
594                "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}"
595            ));
596        }
597    }
598
599    fn prime_caches(&mut self, cause: String) {
600        tracing::debug!(%cause, "will prime caches");
601        let num_worker_threads = self.config.prime_caches_num_threads();
602
603        self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
604            let analysis = AssertUnwindSafe(self.snapshot().analysis);
605            move |sender| {
606                sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
607                let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
608                    let report = PrimeCachesProgress::Report(progress);
609                    sender.send(Task::PrimeCaches(report)).unwrap();
610                });
611                sender
612                    .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() }))
613                    .unwrap();
614            }
615        });
616    }
617
618    fn update_diagnostics(&mut self) {
619        let db = self.analysis_host.raw_database();
620        let generation = self.diagnostics.next_generation();
621        let subscriptions = {
622            let vfs = &self.vfs.read().0;
623            self.mem_docs
624                .iter()
625                .map(|path| vfs.file_id(path).unwrap())
626                .filter_map(|(file_id, excluded)| {
627                    (excluded == vfs::FileExcluded::No).then_some(file_id)
628                })
629                .filter(|&file_id| {
630                    let source_root_id = db.file_source_root(file_id).source_root_id(db);
631                    let source_root = db.source_root(source_root_id).source_root(db);
632                    // Only publish diagnostics for files in the workspace, not from crates.io deps
633                    // or the sysroot.
634                    // While theoretically these should never have errors, we have quite a few false
635                    // positives particularly in the stdlib, and those diagnostics would stay around
636                    // forever if we emitted them here.
637                    !source_root.is_library
638                })
639                .collect::<std::sync::Arc<_>>()
640        };
641        tracing::trace!("updating notifications for {:?}", subscriptions);
642        // Split up the work on multiple threads, but we don't wanna fill the entire task pool with
643        // diagnostic tasks, so we limit the number of tasks to a quarter of the total thread pool.
644        let max_tasks = self.config.main_loop_num_threads().div(4).max(1);
645        let chunk_length = subscriptions.len() / max_tasks;
646        let remainder = subscriptions.len() % max_tasks;
647
648        let mut start = 0;
649        for task_idx in 0..max_tasks {
650            let extra = if task_idx < remainder { 1 } else { 0 };
651            let end = start + chunk_length + extra;
652            let slice = start..end;
653            if slice.is_empty() {
654                break;
655            }
656            // Diagnostics are triggered by the user typing
657            // so we run them on a latency sensitive thread.
658            let snapshot = self.snapshot();
659            self.task_pool.handle.spawn_with_sender(ThreadIntent::LatencySensitive, {
660                let subscriptions = subscriptions.clone();
661                // Do not fetch semantic diagnostics (and populate query results) if we haven't even
662                // loaded the initial workspace yet.
663                let fetch_semantic =
664                    self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some();
665                move |sender| {
666                    // We aren't observing the semantics token cache here
667                    let snapshot = AssertUnwindSafe(&snapshot);
668                    let diags = std::panic::catch_unwind(|| {
669                        fetch_native_diagnostics(
670                            &snapshot,
671                            subscriptions.clone(),
672                            slice.clone(),
673                            NativeDiagnosticsFetchKind::Syntax,
674                        )
675                    })
676                    .unwrap_or_else(|_| {
677                        subscriptions.iter().map(|&id| (id, Vec::new())).collect::<Vec<_>>()
678                    });
679                    sender
680                        .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags)))
681                        .unwrap();
682
683                    if fetch_semantic {
684                        let diags = std::panic::catch_unwind(|| {
685                            fetch_native_diagnostics(
686                                &snapshot,
687                                subscriptions.clone(),
688                                slice.clone(),
689                                NativeDiagnosticsFetchKind::Semantic,
690                            )
691                        })
692                        .unwrap_or_else(|_| {
693                            subscriptions.iter().map(|&id| (id, Vec::new())).collect::<Vec<_>>()
694                        });
695                        sender
696                            .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic(
697                                generation, diags,
698                            )))
699                            .unwrap();
700                    }
701                }
702            });
703            start = end;
704        }
705    }
706
707    fn update_tests(&mut self) {
708        if !self.vfs_done {
709            return;
710        }
711        let db = self.analysis_host.raw_database();
712        let subscriptions = self
713            .mem_docs
714            .iter()
715            .map(|path| self.vfs.read().0.file_id(path).unwrap())
716            .filter_map(|(file_id, excluded)| {
717                (excluded == vfs::FileExcluded::No).then_some(file_id)
718            })
719            .filter(|&file_id| {
720                let source_root_id = db.file_source_root(file_id).source_root_id(db);
721                let source_root = db.source_root(source_root_id).source_root(db);
722                !source_root.is_library
723            })
724            .collect::<Vec<_>>();
725        tracing::trace!("updating tests for {:?}", subscriptions);
726
727        // Updating tests are triggered by the user typing
728        // so we run them on a latency sensitive thread.
729        self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
730            let snapshot = self.snapshot();
731            move || {
732                let tests = subscriptions
733                    .iter()
734                    .copied()
735                    .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok())
736                    .flatten()
737                    .collect::<Vec<_>>();
738
739                Task::DiscoverTest(lsp_ext::DiscoverTestResults {
740                    tests: tests
741                        .into_iter()
742                        .filter_map(|t| {
743                            let line_index = t.file.and_then(|f| snapshot.file_line_index(f).ok());
744                            to_proto::test_item(&snapshot, t, line_index.as_ref())
745                        })
746                        .collect(),
747                    scope: None,
748                    scope_file: Some(
749                        subscriptions
750                            .into_iter()
751                            .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) })
752                            .collect(),
753                    ),
754                })
755            }
756        });
757    }
758
759    fn update_status_or_notify(&mut self) {
760        let status = self.current_status();
761        if self.last_reported_status != status {
762            self.last_reported_status = status.clone();
763
764            if self.config.server_status_notification() {
765                self.send_notification::<lsp_ext::ServerStatusNotification>(status);
766            } else if let (
767                health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error),
768                Some(message),
769            ) = (status.health, &status.message)
770            {
771                let open_log_button = tracing::enabled!(tracing::Level::ERROR)
772                    && (self.fetch_build_data_error().is_err()
773                        || self.fetch_workspace_error().is_err());
774                self.show_message(
775                    match health {
776                        lsp_ext::Health::Ok => lsp_types::MessageType::INFO,
777                        lsp_ext::Health::Warning => lsp_types::MessageType::WARNING,
778                        lsp_ext::Health::Error => lsp_types::MessageType::ERROR,
779                    },
780                    message.clone(),
781                    open_log_button,
782                );
783            }
784        }
785    }
786
787    fn handle_task(&mut self, prime_caches_progress: &mut Vec<PrimeCachesProgress>, task: Task) {
788        match task {
789            Task::Response(response) => self.respond(response),
790            // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
791            Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
792            Task::Retry(_) => (),
793            Task::Diagnostics(kind) => {
794                self.diagnostics.set_native_diagnostics(kind);
795            }
796            Task::PrimeCaches(progress) => match progress {
797                PrimeCachesProgress::Begin => prime_caches_progress.push(progress),
798                PrimeCachesProgress::Report(_) => {
799                    match prime_caches_progress.last_mut() {
800                        Some(last @ PrimeCachesProgress::Report(_)) => {
801                            // Coalesce subsequent update events.
802                            *last = progress;
803                        }
804                        _ => prime_caches_progress.push(progress),
805                    }
806                }
807                PrimeCachesProgress::End { .. } => prime_caches_progress.push(progress),
808            },
809            Task::FetchWorkspace(progress) => {
810                let (state, msg) = match progress {
811                    ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
812                    ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
813                    ProjectWorkspaceProgress::End(workspaces, force_crate_graph_reload) => {
814                        let resp = FetchWorkspaceResponse { workspaces, force_crate_graph_reload };
815                        self.fetch_workspaces_queue.op_completed(resp);
816                        if let Err(e) = self.fetch_workspace_error() {
817                            error!("FetchWorkspaceError: {e}");
818                        }
819                        self.wants_to_switch = Some("fetched workspace".to_owned());
820                        self.diagnostics.clear_check_all();
821                        (Progress::End, None)
822                    }
823                };
824
825                self.report_progress("Fetching", state, msg, None, None);
826            }
827            Task::DiscoverLinkedProjects(arg) => {
828                if let Some(cfg) = self.config.discover_workspace_config() {
829                    let command = cfg.command.clone();
830                    let discover = DiscoverCommand::new(self.discover_sender.clone(), command);
831
832                    let discover_path = match &arg {
833                        DiscoverProjectParam::Buildfile(it) => it,
834                        DiscoverProjectParam::Path(it) => it,
835                    };
836                    let current_dir =
837                        self.config.workspace_root_for(discover_path.as_path()).clone();
838
839                    let arg = match arg {
840                        DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it),
841                        DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
842                    };
843
844                    match discover.spawn(arg, current_dir.as_ref()) {
845                        Ok(handle) => {
846                            if self.discover_jobs_active == 0 {
847                                let title = &cfg.progress_label.clone();
848                                self.report_progress(title, Progress::Begin, None, None, None);
849                            }
850                            self.discover_jobs_active += 1;
851                            self.discover_handles.push(handle)
852                        }
853                        Err(e) => self.show_message(
854                            lsp_types::MessageType::ERROR,
855                            format!("Failed to spawn project discovery command: {e:#}"),
856                            false,
857                        ),
858                    }
859                }
860            }
861            Task::FetchBuildData(progress) => {
862                let (state, msg) = match progress {
863                    BuildDataProgress::Begin => (Some(Progress::Begin), None),
864                    BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
865                    BuildDataProgress::End((workspaces, build_scripts)) => {
866                        let resp = FetchBuildDataResponse { workspaces, build_scripts };
867                        self.fetch_build_data_queue.op_completed(resp);
868
869                        if let Err(e) = self.fetch_build_data_error() {
870                            error!("FetchBuildDataError: {e}");
871                        }
872
873                        if self.wants_to_switch.is_none() {
874                            self.wants_to_switch = Some("fetched build data".to_owned());
875                        }
876                        (Some(Progress::End), None)
877                    }
878                };
879
880                if let Some(state) = state {
881                    self.report_progress("Building compile-time-deps", state, msg, None, None);
882                }
883            }
884            Task::LoadProcMacros(progress) => {
885                let (state, msg) = match progress {
886                    ProcMacroProgress::Begin => (Some(Progress::Begin), None),
887                    ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
888                    ProcMacroProgress::End(change) => {
889                        self.fetch_proc_macros_queue.op_completed(true);
890                        self.analysis_host.apply_change(change);
891                        self.finish_loading_crate_graph();
892                        (Some(Progress::End), None)
893                    }
894                };
895
896                if let Some(state) = state {
897                    self.report_progress("Loading proc-macros", state, msg, None, None);
898                }
899            }
900            Task::BuildDepsHaveChanged => self.build_deps_changed = true,
901            Task::DiscoverTest(tests) => {
902                self.send_notification::<lsp_ext::DiscoveredTests>(tests);
903            }
904        }
905    }
906
907    fn handle_vfs_msg(
908        &mut self,
909        message: vfs::loader::Message,
910        last_progress_report: &mut Option<(String, f64)>,
911    ) {
912        let _p = tracing::info_span!("GlobalState::handle_vfs_msg").entered();
913        let is_changed = matches!(message, vfs::loader::Message::Changed { .. });
914        match message {
915            vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => {
916                let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered();
917                self.debounce_workspace_fetch();
918                let vfs = &mut self.vfs.write().0;
919                for (path, contents) in files {
920                    if matches!(path.name_and_extension(), Some(("minicore", Some("rs")))) {
921                        // Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that.
922                        self.minicore.minicore_text = contents
923                            .as_ref()
924                            .and_then(|contents| str::from_utf8(contents).ok())
925                            .map(triomphe::Arc::from);
926                    }
927
928                    let path = VfsPath::from(path);
929                    // if the file is in mem docs, it's managed by the client via notifications
930                    // so only set it if its not in there
931                    if !self.mem_docs.contains(&path)
932                        && (is_changed || vfs.file_id(&path).is_none())
933                    {
934                        vfs.set_file_contents(path, contents);
935                    }
936                }
937            }
938            vfs::loader::Message::Progress { n_total, n_done, dir, config_version } => {
939                let _p = span!(Level::INFO, "GlobalState::handle_vfs_msg/progress").entered();
940                stdx::always!(config_version <= self.vfs_config_version);
941
942                let (n_done, state) = match n_done {
943                    LoadingProgress::Started => {
944                        self.vfs_span =
945                            Some(span!(Level::INFO, "vfs_load", total = n_total).entered());
946                        (0, Progress::Begin)
947                    }
948                    LoadingProgress::Progress(n_done) => (n_done.min(n_total), Progress::Report),
949                    LoadingProgress::Finished => {
950                        self.vfs_span = None;
951                        (n_total, Progress::End)
952                    }
953                };
954
955                self.vfs_progress_config_version = config_version;
956                self.vfs_done = state == Progress::End;
957
958                let mut message = format!("{n_done}/{n_total}");
959                if let Some(dir) = dir {
960                    message += &format!(
961                        ": {}",
962                        match dir.strip_prefix(self.config.workspace_root_for(&dir)) {
963                            Some(relative_path) => relative_path.as_utf8_path(),
964                            None => dir.as_ref(),
965                        }
966                    );
967                }
968
969                match state {
970                    Progress::Begin => self.report_progress(
971                        "Roots Scanned",
972                        state,
973                        Some(message),
974                        Some(Progress::fraction(n_done, n_total)),
975                        None,
976                    ),
977                    // Don't send too many notifications while batching, sending progress reports
978                    // serializes notifications on the mainthread at the moment which slows us down
979                    Progress::Report => {
980                        if last_progress_report.is_none() {
981                            self.report_progress(
982                                "Roots Scanned",
983                                state,
984                                Some(message.clone()),
985                                Some(Progress::fraction(n_done, n_total)),
986                                None,
987                            );
988                        }
989
990                        *last_progress_report =
991                            Some((message, Progress::fraction(n_done, n_total)));
992                    }
993                    Progress::End => {
994                        last_progress_report.take();
995                        self.report_progress(
996                            "Roots Scanned",
997                            state,
998                            Some(message),
999                            Some(Progress::fraction(n_done, n_total)),
1000                            None,
1001                        )
1002                    }
1003                }
1004            }
1005        }
1006    }
1007
1008    fn handle_deferred_task(&mut self, task: DeferredTask) {
1009        match task {
1010            DeferredTask::CheckIfIndexed(uri) => {
1011                let snap = self.snapshot();
1012
1013                self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
1014                    let _p = tracing::info_span!("GlobalState::check_if_indexed").entered();
1015                    tracing::debug!(?uri, "handling uri");
1016                    let Some(id) = from_proto::file_id(&snap, &uri).expect("unable to get FileId")
1017                    else {
1018                        return;
1019                    };
1020                    if let Ok(crates) = &snap.analysis.crates_for(id) {
1021                        if crates.is_empty() {
1022                            if snap.config.discover_workspace_config().is_some() {
1023                                let path =
1024                                    from_proto::abs_path(&uri).expect("Unable to get AbsPath");
1025                                let arg = DiscoverProjectParam::Path(path);
1026                                sender.send(Task::DiscoverLinkedProjects(arg)).unwrap();
1027                            }
1028                        } else {
1029                            tracing::debug!(?uri, "is indexed");
1030                        }
1031                    }
1032                });
1033            }
1034            DeferredTask::CheckProcMacroSources(modified_rust_files) => {
1035                let analysis = AssertUnwindSafe(self.snapshot().analysis);
1036                self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, {
1037                    move |sender| {
1038                        if modified_rust_files.into_iter().any(|file_id| {
1039                            // FIXME: Check whether these files could be build script related
1040                            match analysis.crates_for(file_id) {
1041                                Ok(crates) => crates.iter().any(|&krate| {
1042                                    analysis.is_proc_macro_crate(krate).is_ok_and(|it| it)
1043                                }),
1044                                _ => false,
1045                            }
1046                        }) {
1047                            sender.send(Task::BuildDepsHaveChanged).unwrap();
1048                        }
1049                    }
1050                });
1051            }
1052        }
1053    }
1054
1055    fn handle_discover_msg(&mut self, message: DiscoverProjectMessage) {
1056        let title = self
1057            .config
1058            .discover_workspace_config()
1059            .map(|cfg| cfg.progress_label.clone())
1060            .expect("No title could be found; this is a bug");
1061        match message {
1062            DiscoverProjectMessage::Finished { project, buildfile } => {
1063                self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1);
1064                if self.discover_jobs_active == 0 {
1065                    self.report_progress(&title, Progress::End, None, None, None);
1066                }
1067
1068                let mut config = Config::clone(&*self.config);
1069                config.add_discovered_project_from_command(project, buildfile);
1070                self.update_configuration(config);
1071            }
1072            DiscoverProjectMessage::Progress { message } => {
1073                if self.discover_jobs_active > 0 {
1074                    self.report_progress(&title, Progress::Report, Some(message), None, None)
1075                }
1076            }
1077            DiscoverProjectMessage::Error { error, source } => {
1078                let message = format!("Project discovery failed: {error}");
1079                self.show_and_log_error(message.clone(), source);
1080
1081                self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1);
1082                if self.discover_jobs_active == 0 {
1083                    self.report_progress(&title, Progress::End, Some(message), None, None)
1084                }
1085            }
1086        }
1087    }
1088
1089    /// Drop any discover command processes that have exited, due to
1090    /// finishing or erroring.
1091    fn cleanup_discover_handles(&mut self) {
1092        let mut active_handles = vec![];
1093
1094        for mut discover_handle in self.discover_handles.drain(..) {
1095            if !discover_handle.handle.has_exited() {
1096                active_handles.push(discover_handle);
1097            }
1098        }
1099        self.discover_handles = active_handles;
1100    }
1101
1102    fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) {
1103        match message.output {
1104            CargoTestOutput::Test { name, state } => {
1105                let state = match state {
1106                    TestState::Started => lsp_ext::TestState::Started,
1107                    TestState::Ignored => lsp_ext::TestState::Skipped,
1108                    TestState::Ok => lsp_ext::TestState::Passed,
1109                    TestState::Failed { stdout } => lsp_ext::TestState::Failed { message: stdout },
1110                };
1111
1112                // The notification requires the namespace form (with underscores) of the target
1113                let test_id = format!("{}::{name}", message.target.target.replace('-', "_"));
1114
1115                self.send_notification::<lsp_ext::ChangeTestState>(
1116                    lsp_ext::ChangeTestStateParams { test_id, state },
1117                );
1118            }
1119            CargoTestOutput::Suite => (),
1120            CargoTestOutput::Finished => {
1121                self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
1122                if self.test_run_remaining_jobs == 0 {
1123                    self.send_notification::<lsp_ext::EndRunTest>(());
1124                    self.test_run_session = None;
1125                }
1126            }
1127            CargoTestOutput::Custom { text } => {
1128                self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
1129            }
1130        }
1131    }
1132
1133    fn handle_flycheck_msg(&mut self, message: FlycheckMessage, cargo_finished: &mut bool) {
1134        match message {
1135            FlycheckMessage::AddDiagnostic {
1136                id,
1137                generation,
1138                workspace_root,
1139                diagnostic,
1140                package_id,
1141            } => {
1142                let snap = self.snapshot();
1143                let diagnostics = crate::diagnostics::flycheck_to_proto::map_rust_diagnostic_to_lsp(
1144                    &self.config.diagnostics_map(None),
1145                    diagnostic,
1146                    &workspace_root,
1147                    &snap,
1148                );
1149                for diag in diagnostics {
1150                    match url_to_file_id(&self.vfs.read().0, &diag.url) {
1151                        Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic(
1152                            id,
1153                            generation,
1154                            &package_id,
1155                            file_id,
1156                            diag.diagnostic,
1157                            diag.fix,
1158                        ),
1159                        Ok(None) => {}
1160                        Err(err) => {
1161                            error!(
1162                                "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
1163                                err
1164                            );
1165                        }
1166                    };
1167                }
1168            }
1169            FlycheckMessage::ClearDiagnostics {
1170                id,
1171                kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
1172            } => self.diagnostics.clear_check(id),
1173            FlycheckMessage::ClearDiagnostics {
1174                id,
1175                kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)),
1176            } => self.diagnostics.clear_check_for_package(id, package_id),
1177            FlycheckMessage::ClearDiagnostics {
1178                id,
1179                kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Workspace),
1180            } => self.diagnostics.clear_check_older_than(id, generation),
1181            FlycheckMessage::ClearDiagnostics {
1182                id,
1183                kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)),
1184            } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation),
1185            FlycheckMessage::Progress { id, progress } => {
1186                let format_with_id = |user_facing_command: String| {
1187                    // When we're running multiple flychecks, we have to include a disambiguator in
1188                    // the title, or the editor complains. Note that this is a user-facing string.
1189                    if self.flycheck.len() == 1 {
1190                        user_facing_command
1191                    } else {
1192                        format!("{user_facing_command} (#{})", id + 1)
1193                    }
1194                };
1195
1196                self.flycheck_formatted_commands
1197                    .resize_with(self.flycheck.len().max(id + 1), || {
1198                        format_with_id(self.config.flycheck(None).to_string())
1199                    });
1200
1201                let (state, message) = match progress {
1202                    flycheck::Progress::DidStart { user_facing_command } => {
1203                        self.flycheck_formatted_commands[id] = format_with_id(user_facing_command);
1204                        (Progress::Begin, None)
1205                    }
1206                    flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
1207                    flycheck::Progress::DidCancel => {
1208                        self.last_flycheck_error = None;
1209                        *cargo_finished = true;
1210                        (Progress::End, None)
1211                    }
1212                    flycheck::Progress::DidFailToRestart(err) => {
1213                        self.last_flycheck_error =
1214                            Some(format!("cargo check failed to start: {err}"));
1215                        return;
1216                    }
1217                    flycheck::Progress::DidFinish(result) => {
1218                        self.last_flycheck_error =
1219                            result.err().map(|err| format!("cargo check failed to start: {err}"));
1220                        *cargo_finished = true;
1221                        (Progress::End, None)
1222                    }
1223                };
1224
1225                // Clone because we &mut self for report_progress
1226                let title = self.flycheck_formatted_commands[id].clone();
1227                self.report_progress(
1228                    &title,
1229                    state,
1230                    message,
1231                    None,
1232                    Some(format!("rust-analyzer/flycheck/{id}")),
1233                );
1234            }
1235        }
1236    }
1237
1238    /// Registers and handles a request. This should only be called once per incoming request.
1239    fn on_new_request(&mut self, request_received: Instant, req: Request) {
1240        let _p =
1241            span!(Level::INFO, "GlobalState::on_new_request", req.method = ?req.method).entered();
1242        self.register_request(&req, request_received);
1243        self.on_request(req);
1244    }
1245
1246    /// Handles a request.
1247    fn on_request(&mut self, req: Request) {
1248        let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self };
1249        dispatcher.on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
1250            s.shutdown_requested = true;
1251            Ok(())
1252        });
1253
1254        match &mut dispatcher {
1255            RequestDispatcher { req: Some(req), global_state: this } if this.shutdown_requested => {
1256                this.respond(lsp_server::Response::new_err(
1257                    req.id.clone(),
1258                    lsp_server::ErrorCode::InvalidRequest as i32,
1259                    "Shutdown already requested.".to_owned(),
1260                ));
1261                return;
1262            }
1263            _ => (),
1264        }
1265
1266        use crate::handlers::request as handlers;
1267        use lsp_types::request as lsp_request;
1268
1269        const RETRY: bool = true;
1270        const NO_RETRY: bool = false;
1271
1272        #[rustfmt::skip]
1273        dispatcher
1274            // Request handlers that must run on the main thread
1275            // because they mutate GlobalState:
1276            .on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
1277            .on_sync_mut::<lsp_ext::RebuildProcMacros>(handlers::handle_proc_macros_rebuild)
1278            .on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
1279            .on_sync_mut::<lsp_ext::RunTest>(handlers::handle_run_test)
1280            // Request handlers which are related to the user typing
1281            // are run on the main thread to reduce latency:
1282            .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
1283            .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
1284            .on_sync::<lsp_request::SelectionRangeRequest>(handlers::handle_selection_range)
1285            .on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
1286            .on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
1287            // Formatting should be done immediately as the editor might wait on it, but we can't
1288            // put it on the main thread as we do not want the main thread to block on rustfmt.
1289            // So we have an extra thread just for formatting requests to make sure it gets handled
1290            // as fast as possible.
1291            .on_fmt_thread::<lsp_request::Formatting>(handlers::handle_formatting)
1292            .on_fmt_thread::<lsp_request::RangeFormatting>(handlers::handle_range_formatting)
1293            // We can’t run latency-sensitive request handlers which do semantic
1294            // analysis on the main thread because that would block other
1295            // requests. Instead, we run these request handlers on higher priority
1296            // threads in the threadpool.
1297            // FIXME: Retrying can make the result of this stale?
1298            .on_latency_sensitive::<RETRY, lsp_request::Completion>(handlers::handle_completion)
1299            // FIXME: Retrying can make the result of this stale
1300            .on_latency_sensitive::<RETRY, lsp_request::ResolveCompletionItem>(handlers::handle_completion_resolve)
1301            .on_latency_sensitive::<RETRY, lsp_request::SemanticTokensFullRequest>(handlers::handle_semantic_tokens_full)
1302            .on_latency_sensitive::<RETRY, lsp_request::SemanticTokensFullDeltaRequest>(handlers::handle_semantic_tokens_full_delta)
1303            .on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
1304            // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
1305            // All other request handlers
1306            .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, empty_diagnostic_report, || lsp_server::ResponseError {
1307                code: lsp_server::ErrorCode::ServerCancelled as i32,
1308                message: "server cancelled the request".to_owned(),
1309                data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
1310                    retrigger_request: true
1311                }).ok(),
1312            })
1313            .on::<RETRY, lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
1314            .on::<RETRY, lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
1315            .on::<NO_RETRY, lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
1316            .on::<RETRY, lsp_request::WillRenameFiles>(handlers::handle_will_rename_files)
1317            .on::<NO_RETRY, lsp_request::GotoDefinition>(handlers::handle_goto_definition)
1318            .on::<NO_RETRY, lsp_request::GotoDeclaration>(handlers::handle_goto_declaration)
1319            .on::<NO_RETRY, lsp_request::GotoImplementation>(handlers::handle_goto_implementation)
1320            .on::<NO_RETRY, lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
1321            .on::<NO_RETRY, lsp_request::InlayHintRequest>(handlers::handle_inlay_hints)
1322            .on_identity::<NO_RETRY, lsp_request::InlayHintResolveRequest, _>(handlers::handle_inlay_hints_resolve)
1323            .on::<NO_RETRY, lsp_request::CodeLensRequest>(handlers::handle_code_lens)
1324            .on_identity::<NO_RETRY, lsp_request::CodeLensResolve, _>(handlers::handle_code_lens_resolve)
1325            .on::<NO_RETRY, lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename)
1326            .on::<NO_RETRY, lsp_request::Rename>(handlers::handle_rename)
1327            .on::<NO_RETRY, lsp_request::References>(handlers::handle_references)
1328            .on::<NO_RETRY, lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight)
1329            .on::<NO_RETRY, lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
1330            .on::<NO_RETRY, lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)
1331            .on::<NO_RETRY, lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)
1332            // All other request handlers (lsp extension)
1333            .on::<RETRY, lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list)
1334            .on::<RETRY, lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
1335            .on::<RETRY, lsp_ext::ViewFileText>(handlers::handle_view_file_text)
1336            .on::<RETRY, lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
1337            .on::<RETRY, lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
1338            .on::<RETRY, lsp_ext::DiscoverTest>(handlers::handle_discover_test)
1339            .on::<RETRY, lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
1340            .on::<NO_RETRY, lsp_ext::Ssr>(handlers::handle_ssr)
1341            .on::<NO_RETRY, lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
1342            .on::<NO_RETRY, lsp_ext::ViewSyntaxTree>(handlers::handle_view_syntax_tree)
1343            .on::<NO_RETRY, lsp_ext::ViewHir>(handlers::handle_view_hir)
1344            .on::<NO_RETRY, lsp_ext::ViewMir>(handlers::handle_view_mir)
1345            .on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
1346            .on::<NO_RETRY, lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
1347            .on::<NO_RETRY, lsp_ext::ParentModule>(handlers::handle_parent_module)
1348            .on::<NO_RETRY, lsp_ext::ChildModules>(handlers::handle_child_modules)
1349            .on::<NO_RETRY, lsp_ext::Runnables>(handlers::handle_runnables)
1350            .on::<NO_RETRY, lsp_ext::RelatedTests>(handlers::handle_related_tests)
1351            .on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action)
1352            .on_identity::<RETRY, lsp_ext::CodeActionResolveRequest, _>(handlers::handle_code_action_resolve)
1353            .on::<NO_RETRY, lsp_ext::HoverRequest>(handlers::handle_hover)
1354            .on::<NO_RETRY, lsp_ext::ExternalDocs>(handlers::handle_open_docs)
1355            .on::<NO_RETRY, lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
1356            .on::<NO_RETRY, lsp_ext::MoveItem>(handlers::handle_move_item)
1357            //
1358            .on::<NO_RETRY, lsp_ext::InternalTestingFetchConfig>(handlers::internal_testing_fetch_config)
1359            .on::<RETRY, lsp_ext::GetFailedObligations>(handlers::get_failed_obligations)
1360            .finish();
1361    }
1362
1363    /// Handles an incoming notification.
1364    fn on_notification(&mut self, not: Notification) {
1365        let _p =
1366            span!(Level::INFO, "GlobalState::on_notification", not.method = ?not.method).entered();
1367        use crate::handlers::notification as handlers;
1368        use lsp_types::notification as notifs;
1369
1370        NotificationDispatcher { not: Some(not), global_state: self }
1371            .on_sync_mut::<notifs::Cancel>(handlers::handle_cancel)
1372            .on_sync_mut::<notifs::WorkDoneProgressCancel>(
1373                handlers::handle_work_done_progress_cancel,
1374            )
1375            .on_sync_mut::<notifs::DidOpenTextDocument>(handlers::handle_did_open_text_document)
1376            .on_sync_mut::<notifs::DidChangeTextDocument>(handlers::handle_did_change_text_document)
1377            .on_sync_mut::<notifs::DidCloseTextDocument>(handlers::handle_did_close_text_document)
1378            .on_sync_mut::<notifs::DidSaveTextDocument>(handlers::handle_did_save_text_document)
1379            .on_sync_mut::<notifs::DidChangeConfiguration>(
1380                handlers::handle_did_change_configuration,
1381            )
1382            .on_sync_mut::<notifs::DidChangeWorkspaceFolders>(
1383                handlers::handle_did_change_workspace_folders,
1384            )
1385            .on_sync_mut::<notifs::DidChangeWatchedFiles>(handlers::handle_did_change_watched_files)
1386            .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)
1387            .on_sync_mut::<lsp_ext::ClearFlycheck>(handlers::handle_clear_flycheck)
1388            .on_sync_mut::<lsp_ext::RunFlycheck>(handlers::handle_run_flycheck)
1389            .on_sync_mut::<lsp_ext::AbortRunTest>(handlers::handle_abort_run_test)
1390            .finish();
1391    }
1392}