From 39cd4a1da10eb4d6a4782a68d3ed6f66d3c2bb8a Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 28 Nov 2025 15:18:15 +0000 Subject: [PATCH] fix: Allow multiple discover operations Previously, rust-analyzer would drop discover requests that arrived before we'd finished processing the previous request. Fix this by allowing multiple discover requests to be active. Keep track of the number of discover operations for the quiescence check, and keep the process handles until they terminate. --- .../crates/rust-analyzer/src/command.rs | 18 +++++ .../crates/rust-analyzer/src/discover.rs | 4 +- .../crates/rust-analyzer/src/global_state.rs | 8 +-- .../crates/rust-analyzer/src/main_loop.rs | 65 ++++++++++++------- .../crates/rust-analyzer/src/reload.rs | 4 +- 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index 41055272b1ae..2f052618cdfa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -197,4 +197,22 @@ impl CommandHandle { ))) } } + + pub(crate) fn has_exited(&mut self) -> bool { + match self.child.0.try_wait() { + Ok(Some(_exit_code)) => { + // We have an exit code. + true + } + Ok(None) => { + // Hasn't exited yet. + false + } + Err(_) => { + // Couldn't get an exit code. Assume that we've + // exited. + true + } + } + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 0e96eff27832..4aef5b0b7f3d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -67,7 +67,7 @@ impl DiscoverCommand { cmd.args(args); Ok(DiscoverHandle { - _handle: CommandHandle::spawn(cmd, DiscoverProjectParser, self.sender.clone(), None)?, + handle: CommandHandle::spawn(cmd, DiscoverProjectParser, self.sender.clone(), None)?, span: info_span!("discover_command").entered(), }) } @@ -76,7 +76,7 @@ impl DiscoverCommand { /// A handle to a spawned [Discover]. #[derive(Debug)] pub(crate) struct DiscoverHandle { - _handle: CommandHandle, + pub(crate) handle: CommandHandle, #[allow(dead_code)] // not accessed, but used to log on drop. span: EnteredSpan, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 91f7db785440..0f7115b305bb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -121,9 +121,10 @@ pub(crate) struct GlobalState { pub(crate) test_run_remaining_jobs: usize, // Project loading - pub(crate) discover_handle: Option, + pub(crate) discover_handles: Vec, pub(crate) discover_sender: Sender, pub(crate) discover_receiver: Receiver, + pub(crate) discover_jobs_active: u32, // Debouncing channel for fetching the workspace // we want to delay it until the VFS looks stable-ish (and thus is not currently in the middle @@ -175,7 +176,6 @@ pub(crate) struct GlobalState { pub(crate) fetch_build_data_queue: OpQueue<(), FetchBuildDataResponse>, pub(crate) fetch_proc_macros_queue: OpQueue<(ChangeWithProcMacros, Vec), bool>, pub(crate) prime_caches_queue: OpQueue, - pub(crate) discover_workspace_queue: OpQueue, /// A deferred task queue. /// @@ -291,9 +291,10 @@ impl GlobalState { test_run_receiver, test_run_remaining_jobs: 0, - discover_handle: None, + discover_handles: vec![], discover_sender, discover_receiver, + discover_jobs_active: 0, fetch_ws_receiver: None, @@ -312,7 +313,6 @@ impl GlobalState { fetch_proc_macros_queue: OpQueue::default(), prime_caches_queue: OpQueue::default(), - discover_workspace_queue: OpQueue::default(), deferred_task_queue: task_queue, incomplete_crate_graph: false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 8b4748ddb3d7..97af02279f45 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -531,6 +531,8 @@ impl GlobalState { } } + self.cleanup_discover_handles(); + if let Some(diagnostic_changes) = self.diagnostics.take_changes() { for file_id in diagnostic_changes { let uri = file_id_to_url(&self.vfs.read().0, file_id); @@ -806,33 +808,34 @@ impl GlobalState { self.report_progress("Fetching", state, msg, None, None); } Task::DiscoverLinkedProjects(arg) => { - if let Some(cfg) = self.config.discover_workspace_config() - && !self.discover_workspace_queue.op_in_progress() - { + if let Some(cfg) = self.config.discover_workspace_config() { // the clone is unfortunately necessary to avoid a borrowck error when // `self.report_progress` is called later let title = &cfg.progress_label.clone(); let command = cfg.command.clone(); let discover = DiscoverCommand::new(self.discover_sender.clone(), command); - self.report_progress(title, Progress::Begin, None, None, None); - self.discover_workspace_queue - .request_op("Discovering workspace".to_owned(), ()); - let _ = self.discover_workspace_queue.should_start_op(); + if self.discover_jobs_active == 0 { + self.report_progress(title, Progress::Begin, None, None, None); + } + self.discover_jobs_active += 1; let arg = match arg { DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = discover.spawn( - arg, - &std::env::current_dir() - .expect("Failed to get cwd during project discovery"), - ); - self.discover_handle = Some(handle.unwrap_or_else(|e| { - panic!("Failed to spawn project discovery command: {e}") - })); + let handle = discover + .spawn( + arg, + &std::env::current_dir() + .expect("Failed to get cwd during project discovery"), + ) + .unwrap_or_else(|e| { + panic!("Failed to spawn project discovery command: {e}") + }); + + self.discover_handles.push(handle); } } Task::FetchBuildData(progress) => { @@ -1036,27 +1039,45 @@ impl GlobalState { .expect("No title could be found; this is a bug"); match message { DiscoverProjectMessage::Finished { project, buildfile } => { - self.discover_handle = None; - self.report_progress(&title, Progress::End, None, None, None); - self.discover_workspace_queue.op_completed(()); + self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1); + if self.discover_jobs_active == 0 { + self.report_progress(&title, Progress::End, None, None, None); + } let mut config = Config::clone(&*self.config); config.add_discovered_project_from_command(project, buildfile); self.update_configuration(config); } DiscoverProjectMessage::Progress { message } => { - self.report_progress(&title, Progress::Report, Some(message), None, None) + if self.discover_jobs_active > 0 { + self.report_progress(&title, Progress::Report, Some(message), None, None) + } } DiscoverProjectMessage::Error { error, source } => { - self.discover_handle = None; let message = format!("Project discovery failed: {error}"); - self.discover_workspace_queue.op_completed(()); self.show_and_log_error(message.clone(), source); - self.report_progress(&title, Progress::End, Some(message), None, None) + + self.discover_jobs_active = self.discover_jobs_active.saturating_sub(1); + if self.discover_jobs_active == 0 { + self.report_progress(&title, Progress::End, Some(message), None, None) + } } } } + /// Drop any discover command processes that have exited, due to + /// finishing or erroring. + fn cleanup_discover_handles(&mut self) { + let mut active_handles = vec![]; + + for mut discover_handle in self.discover_handles.drain(..) { + if !discover_handle.handle.has_exited() { + active_handles.push(discover_handle); + } + } + self.discover_handles = active_handles; + } + fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) { match message.output { CargoTestOutput::Test { name, state } => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 8876b850be15..317c1123659e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -74,7 +74,7 @@ impl GlobalState { && !self.fetch_workspaces_queue.op_in_progress() && !self.fetch_build_data_queue.op_in_progress() && !self.fetch_proc_macros_queue.op_in_progress() - && !self.discover_workspace_queue.op_in_progress() + && self.discover_jobs_active == 0 && self.vfs_progress_config_version >= self.vfs_config_version } @@ -297,7 +297,7 @@ impl GlobalState { .collect(); let cargo_config = self.config.cargo(None); let discover_command = self.config.discover_workspace_config().cloned(); - let is_quiescent = !(self.discover_workspace_queue.op_in_progress() + let is_quiescent = !(self.discover_jobs_active > 0 || self.vfs_progress_config_version < self.vfs_config_version || !self.vfs_done);