Unnamed repository; edit this file 'description' to name the repository.
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.
| -rw-r--r-- | crates/rust-analyzer/src/command.rs | 18 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/discover.rs | 4 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 8 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 65 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/reload.rs | 4 |
5 files changed, 69 insertions, 30 deletions
diff --git a/crates/rust-analyzer/src/command.rs b/crates/rust-analyzer/src/command.rs index 41055272b1..2f052618cd 100644 --- a/crates/rust-analyzer/src/command.rs +++ b/crates/rust-analyzer/src/command.rs @@ -197,4 +197,22 @@ impl<T: Sized + Send + 'static> CommandHandle<T> { ))) } } + + 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/crates/rust-analyzer/src/discover.rs b/crates/rust-analyzer/src/discover.rs index 0e96eff278..4aef5b0b7f 100644 --- a/crates/rust-analyzer/src/discover.rs +++ b/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<DiscoverProjectMessage>, + pub(crate) handle: CommandHandle<DiscoverProjectMessage>, #[allow(dead_code)] // not accessed, but used to log on drop. span: EnteredSpan, } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 91f7db7854..0f7115b305 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/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<discover::DiscoverHandle>, + pub(crate) discover_handles: Vec<discover::DiscoverHandle>, pub(crate) discover_sender: Sender<discover::DiscoverProjectMessage>, pub(crate) discover_receiver: Receiver<discover::DiscoverProjectMessage>, + 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<ProcMacroPaths>), 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/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 8b4748ddb3..97af02279f 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/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,25 +1039,43 @@ 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) { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 8876b850be..317c112365 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/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); |