mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-01 20:31:59 +00:00
Debounce workspace fetching for workspace structure changes
This commit is contained in:
parent
3866336d41
commit
575d43e1ce
4 changed files with 54 additions and 9 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue