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