diff options
| author | Lukas Wirth <lukastw97@gmail.com> | 2025-05-19 07:06:10 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-19 07:06:10 +0000 |
| commit | d6859ad411c79ae5586dba1b05998674eae53f40 (patch) | |
| tree | 001b32153738ae616e4fe35ee2dd7a35cbb01f3e | |
| parent | 0e63f085d32350d5563e234938434b46e5539266 (diff) | |
| parent | e493cb1ad2d5aa1c2e21b8c5c948f68aa9f01222 (diff) | |
| download | rust-d6859ad411c79ae5586dba1b05998674eae53f40.tar.gz rust-d6859ad411c79ae5586dba1b05998674eae53f40.zip | |
Merge pull request #19814 from Veykril/lw-sxquntxksovs
Debounce workspace fetching for workspace structure changes
4 files changed, 54 insertions, 9 deletions
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 1a31525b46a..a870232d4a0 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 @@ -3,7 +3,11 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{ops::Not as _, panic::AssertUnwindSafe, time::Instant}; +use std::{ + ops::Not as _, + panic::AssertUnwindSafe, + time::{Duration, Instant}, +}; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; @@ -41,6 +45,7 @@ use crate::{ test_runner::{CargoTestHandle, CargoTestMessage}, }; +#[derive(Debug)] pub(crate) struct FetchWorkspaceRequest { pub(crate) path: Option<AbsPathBuf>, pub(crate) force_crate_graph_reload: bool, @@ -116,6 +121,11 @@ pub(crate) struct GlobalState { pub(crate) discover_sender: Sender<discover::DiscoverProjectMessage>, pub(crate) discover_receiver: Receiver<discover::DiscoverProjectMessage>, + // 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 + // of a VCS operation like `git switch`) + pub(crate) fetch_ws_receiver: Option<(Receiver<Instant>, FetchWorkspaceRequest)>, + // VFS pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, @@ -268,6 +278,8 @@ impl GlobalState { discover_sender, discover_receiver, + fetch_ws_receiver: None, + vfs: Arc::new(RwLock::new((vfs::Vfs::default(), Default::default()))), vfs_config_version: 0, vfs_progress_config_version: 0, @@ -519,11 +531,7 @@ impl GlobalState { if let Some((path, force_crate_graph_reload)) = workspace_structure_change { let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change") .entered(); - - self.fetch_workspaces_queue.request_op( - format!("workspace vfs file change: {path}"), - FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload }, - ); + self.enqueue_workspace_fetch(path, force_crate_graph_reload); } } @@ -671,6 +679,30 @@ impl GlobalState { None }) } + + fn enqueue_workspace_fetch(&mut self, path: AbsPathBuf, force_crate_graph_reload: bool) { + let already_requested = self.fetch_workspaces_queue.op_requested() + && !self.fetch_workspaces_queue.op_in_progress(); + if self.fetch_ws_receiver.is_none() && already_requested { + // Don't queue up a new fetch request if we already have done so + // Otherwise we will re-fetch in quick succession which is unnecessary + // Note though, that if one is already in progress, we *want* to re-queue + // as the in-progress fetch might not have the latest changes in it anymore + // FIXME: We should cancel the in-progress fetch here + return; + } + + self.fetch_ws_receiver = Some(( + crossbeam_channel::after(Duration::from_millis(100)), + FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload }, + )); + } + + pub(crate) fn debounce_workspace_fetch(&mut self) { + if let Some((fetch_receiver, _)) = &mut self.fetch_ws_receiver { + *fetch_receiver = crossbeam_channel::after(Duration::from_millis(100)); + } + } } impl Drop for GlobalState { 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 bd213ffa57a..0c0438c4b8f 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 @@ -8,7 +8,7 @@ use std::{ time::{Duration, Instant}, }; -use crossbeam_channel::{Receiver, select}; +use crossbeam_channel::{Receiver, never, select}; use ide_db::base_db::{SourceDatabase, VfsPath, salsa::Database as _}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{TextDocumentIdentifier, notification::Notification as _}; @@ -71,6 +71,7 @@ enum Event { Flycheck(FlycheckMessage), TestResult(CargoTestMessage), DiscoverProject(DiscoverProjectMessage), + FetchWorkspaces(FetchWorkspaceRequest), } impl fmt::Display for Event { @@ -83,6 +84,7 @@ impl fmt::Display for Event { Event::QueuedTask(_) => write!(f, "Event::QueuedTask"), Event::TestResult(_) => write!(f, "Event::TestResult"), Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"), + Event::FetchWorkspaces(_) => write!(f, "Event::SwitchWorkspaces"), } } } @@ -150,6 +152,7 @@ impl fmt::Debug for Event { } _ => (), } + match self { Event::Lsp(it) => fmt::Debug::fmt(it, f), Event::Task(it) => fmt::Debug::fmt(it, f), @@ -158,6 +161,7 @@ impl fmt::Debug for Event { Event::Flycheck(it) => fmt::Debug::fmt(it, f), Event::TestResult(it) => fmt::Debug::fmt(it, f), Event::DiscoverProject(it) => fmt::Debug::fmt(it, f), + Event::FetchWorkspaces(it) => fmt::Debug::fmt(it, f), } } } @@ -251,7 +255,7 @@ impl GlobalState { } fn next_event( - &self, + &mut self, inbox: &Receiver<lsp_server::Message>, ) -> Result<Option<Event>, crossbeam_channel::RecvError> { // Make sure we reply to formatting requests ASAP so the editor doesn't block @@ -283,6 +287,10 @@ impl GlobalState { recv(self.discover_receiver) -> task => task.map(Event::DiscoverProject), + + recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => { + Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1)) + }, } .map(Some) } @@ -412,6 +420,9 @@ impl GlobalState { self.handle_discover_msg(message); } } + Event::FetchWorkspaces(req) => { + self.fetch_workspaces_queue.request_op("project structure change".to_owned(), req) + } } let event_handling_duration = loop_start.elapsed(); let (state_changed, memdocs_added_or_removed) = if self.vfs_done { @@ -830,6 +841,7 @@ impl GlobalState { match message { vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => { let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered(); + self.debounce_workspace_fetch(); let vfs = &mut self.vfs.write().0; for (path, contents) in files { let path = VfsPath::from(path); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs index 709d99bda75..7af5b48bb7a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs @@ -36,7 +36,7 @@ impl<Args, Output> Default for OpQueue<Args, Output> { } } -impl<Args, Output> OpQueue<Args, Output> { +impl<Args: std::fmt::Debug, Output> OpQueue<Args, Output> { /// Request an operation to start. pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { self.op_requested = Some((reason, args)); 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 d7c6aeccf0b..ae9e3e99874 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -69,6 +69,7 @@ impl GlobalState { /// are ready to do semantic work. pub(crate) fn is_quiescent(&self) -> bool { self.vfs_done + && self.fetch_ws_receiver.is_none() && !self.fetch_workspaces_queue.op_in_progress() && !self.fetch_build_data_queue.op_in_progress() && !self.fetch_proc_macros_queue.op_in_progress() |
