Skip to content

Commit f6abc05

Browse files
committed
Debounce workspace fetching for workspace structure changes
1 parent 3866336 commit f6abc05

File tree

4 files changed

+49
-9
lines changed

4 files changed

+49
-9
lines changed

crates/rust-analyzer/src/global_state.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
//!
44
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
55
6-
use std::{ops::Not as _, panic::AssertUnwindSafe, time::Instant};
6+
use std::{
7+
ops::Not as _,
8+
panic::AssertUnwindSafe,
9+
time::{Duration, Instant},
10+
};
711

812
use crossbeam_channel::{Receiver, Sender, unbounded};
913
use hir::ChangeWithProcMacros;
@@ -41,6 +45,7 @@ use crate::{
4145
test_runner::{CargoTestHandle, CargoTestMessage},
4246
};
4347

48+
#[derive(Debug)]
4449
pub(crate) struct FetchWorkspaceRequest {
4550
pub(crate) path: Option<AbsPathBuf>,
4651
pub(crate) force_crate_graph_reload: bool,
@@ -116,6 +121,11 @@ pub(crate) struct GlobalState {
116121
pub(crate) discover_sender: Sender<discover::DiscoverProjectMessage>,
117122
pub(crate) discover_receiver: Receiver<discover::DiscoverProjectMessage>,
118123

124+
// Debouncing channel for fetching the workspace
125+
// we want to delay it until the VFS looks stable-ish (and thus is not currently in the middle
126+
// of a VCS operation like `git switch`)
127+
pub(crate) fetch_ws_receiver: Option<(Receiver<Instant>, FetchWorkspaceRequest)>,
128+
119129
// VFS
120130
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
121131
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
@@ -268,6 +278,8 @@ impl GlobalState {
268278
discover_sender,
269279
discover_receiver,
270280

281+
fetch_ws_receiver: None,
282+
271283
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), Default::default()))),
272284
vfs_config_version: 0,
273285
vfs_progress_config_version: 0,
@@ -519,11 +531,7 @@ impl GlobalState {
519531
if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
520532
let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change")
521533
.entered();
522-
523-
self.fetch_workspaces_queue.request_op(
524-
format!("workspace vfs file change: {path}"),
525-
FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
526-
);
534+
self.enqueue_workspace_fetch(path, force_crate_graph_reload);
527535
}
528536
}
529537

@@ -671,6 +679,25 @@ impl GlobalState {
671679
None
672680
})
673681
}
682+
683+
fn enqueue_workspace_fetch(&mut self, path: AbsPathBuf, force_crate_graph_reload: bool) {
684+
let already_requested = self.fetch_workspaces_queue.op_requested()
685+
|| self.fetch_workspaces_queue.op_in_progress();
686+
if self.fetch_ws_receiver.is_none() && already_requested {
687+
return;
688+
}
689+
690+
self.fetch_ws_receiver = Some((
691+
crossbeam_channel::after(Duration::from_millis(100)),
692+
FetchWorkspaceRequest { path: Some(path), force_crate_graph_reload },
693+
));
694+
}
695+
696+
pub(crate) fn debounce_workspace_fetch(&mut self) {
697+
if let Some((fetch_receiver, _)) = &mut self.fetch_ws_receiver {
698+
*fetch_receiver = crossbeam_channel::after(Duration::from_millis(100));
699+
}
700+
}
674701
}
675702

676703
impl Drop for GlobalState {

crates/rust-analyzer/src/main_loop.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
time::{Duration, Instant},
99
};
1010

11-
use crossbeam_channel::{Receiver, select};
11+
use crossbeam_channel::{Receiver, never, select};
1212
use ide_db::base_db::{SourceDatabase, VfsPath, salsa::Database as _};
1313
use lsp_server::{Connection, Notification, Request};
1414
use lsp_types::{TextDocumentIdentifier, notification::Notification as _};
@@ -71,6 +71,7 @@ enum Event {
7171
Flycheck(FlycheckMessage),
7272
TestResult(CargoTestMessage),
7373
DiscoverProject(DiscoverProjectMessage),
74+
FetchWorkspaces(FetchWorkspaceRequest),
7475
}
7576

7677
impl fmt::Display for Event {
@@ -83,6 +84,7 @@ impl fmt::Display for Event {
8384
Event::QueuedTask(_) => write!(f, "Event::QueuedTask"),
8485
Event::TestResult(_) => write!(f, "Event::TestResult"),
8586
Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"),
87+
Event::FetchWorkspaces(_) => write!(f, "Event::SwitchWorkspaces"),
8688
}
8789
}
8890
}
@@ -150,6 +152,7 @@ impl fmt::Debug for Event {
150152
}
151153
_ => (),
152154
}
155+
153156
match self {
154157
Event::Lsp(it) => fmt::Debug::fmt(it, f),
155158
Event::Task(it) => fmt::Debug::fmt(it, f),
@@ -158,6 +161,7 @@ impl fmt::Debug for Event {
158161
Event::Flycheck(it) => fmt::Debug::fmt(it, f),
159162
Event::TestResult(it) => fmt::Debug::fmt(it, f),
160163
Event::DiscoverProject(it) => fmt::Debug::fmt(it, f),
164+
Event::FetchWorkspaces(_) => Ok(()),
161165
}
162166
}
163167
}
@@ -251,7 +255,7 @@ impl GlobalState {
251255
}
252256

253257
fn next_event(
254-
&self,
258+
&mut self,
255259
inbox: &Receiver<lsp_server::Message>,
256260
) -> Result<Option<Event>, crossbeam_channel::RecvError> {
257261
// Make sure we reply to formatting requests ASAP so the editor doesn't block
@@ -283,6 +287,10 @@ impl GlobalState {
283287

284288
recv(self.discover_receiver) -> task =>
285289
task.map(Event::DiscoverProject),
290+
291+
recv(self.fetch_ws_receiver.as_ref().map_or(&never(), |(chan, _)| chan)) -> _instant => {
292+
Ok(Event::FetchWorkspaces(self.fetch_ws_receiver.take().unwrap().1))
293+
},
286294
}
287295
.map(Some)
288296
}
@@ -412,6 +420,9 @@ impl GlobalState {
412420
self.handle_discover_msg(message);
413421
}
414422
}
423+
Event::FetchWorkspaces(req) => {
424+
self.fetch_workspaces_queue.request_op("vfs change".to_owned(), req)
425+
}
415426
}
416427
let event_handling_duration = loop_start.elapsed();
417428
let (state_changed, memdocs_added_or_removed) = if self.vfs_done {
@@ -830,6 +841,7 @@ impl GlobalState {
830841
match message {
831842
vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => {
832843
let _p = tracing::info_span!("GlobalState::handle_vfs_msg{changed/load}").entered();
844+
self.debounce_workspace_fetch();
833845
let vfs = &mut self.vfs.write().0;
834846
for (path, contents) in files {
835847
let path = VfsPath::from(path);

crates/rust-analyzer/src/op_queue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl<Args, Output> Default for OpQueue<Args, Output> {
3636
}
3737
}
3838

39-
impl<Args, Output> OpQueue<Args, Output> {
39+
impl<Args: std::fmt::Debug, Output> OpQueue<Args, Output> {
4040
/// Request an operation to start.
4141
pub(crate) fn request_op(&mut self, reason: Cause, args: Args) {
4242
self.op_requested = Some((reason, args));

crates/rust-analyzer/src/reload.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl GlobalState {
6969
/// are ready to do semantic work.
7070
pub(crate) fn is_quiescent(&self) -> bool {
7171
self.vfs_done
72+
&& self.fetch_ws_receiver.is_none()
7273
&& !self.fetch_workspaces_queue.op_in_progress()
7374
&& !self.fetch_build_data_queue.op_in_progress()
7475
&& !self.fetch_proc_macros_queue.op_in_progress()

0 commit comments

Comments
 (0)