Don't switch workspace on vfs file changes from libraries
When r-a starts up, it starts switching the workspace before all vfs events have been processed which causes us to switch workspace multiple times until all vfs changes have been processed. This scales with the size of the project and its dependencies. If workspace files from dependencies as well as the sysroot get loaded, we shouldn't switch the workspace as those have no impact on the project workspace.
This commit is contained in:
parent
cb52271701
commit
6a1737242b
@ -8,7 +8,7 @@
|
|||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use flycheck::FlycheckHandle;
|
use flycheck::FlycheckHandle;
|
||||||
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
|
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
|
||||||
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
|
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase, SourceDatabaseExt};
|
||||||
use lsp_types::{SemanticTokens, Url};
|
use lsp_types::{SemanticTokens, Url};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use proc_macro_api::ProcMacroServer;
|
use proc_macro_api::ProcMacroServer;
|
||||||
@ -176,7 +176,7 @@ pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> Global
|
|||||||
|
|
||||||
pub(crate) fn process_changes(&mut self) -> bool {
|
pub(crate) fn process_changes(&mut self) -> bool {
|
||||||
let _p = profile::span("GlobalState::process_changes");
|
let _p = profile::span("GlobalState::process_changes");
|
||||||
let mut fs_changes = Vec::new();
|
let mut fs_refresh_changes = Vec::new();
|
||||||
// A file was added or deleted
|
// A file was added or deleted
|
||||||
let mut has_structure_changes = false;
|
let mut has_structure_changes = false;
|
||||||
|
|
||||||
@ -192,11 +192,8 @@ pub(crate) fn process_changes(&mut self) -> bool {
|
|||||||
if let Some(path) = vfs.file_path(file.file_id).as_path() {
|
if let Some(path) = vfs.file_path(file.file_id).as_path() {
|
||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
if reload::should_refresh_for_change(&path, file.change_kind) {
|
if reload::should_refresh_for_change(&path, file.change_kind) {
|
||||||
tracing::warn!("fetch-fiel_change");
|
fs_refresh_changes.push((path, file.file_id));
|
||||||
self.fetch_workspaces_queue
|
|
||||||
.request_op(format!("vfs file change: {}", path.display()));
|
|
||||||
}
|
}
|
||||||
fs_changes.push((path, file.change_kind));
|
|
||||||
if file.is_created_or_deleted() {
|
if file.is_created_or_deleted() {
|
||||||
has_structure_changes = true;
|
has_structure_changes = true;
|
||||||
}
|
}
|
||||||
@ -228,14 +225,25 @@ pub(crate) fn process_changes(&mut self) -> bool {
|
|||||||
|
|
||||||
self.analysis_host.apply_change(change);
|
self.analysis_host.apply_change(change);
|
||||||
|
|
||||||
let raw_database = &self.analysis_host.raw_database();
|
{
|
||||||
self.proc_macro_changed =
|
let raw_database = self.analysis_host.raw_database();
|
||||||
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
|
let workspace_structure_change =
|
||||||
let crates = raw_database.relevant_crates(file.file_id);
|
fs_refresh_changes.into_iter().find(|&(_, file_id)| {
|
||||||
let crate_graph = raw_database.crate_graph();
|
!raw_database.source_root(raw_database.file_source_root(file_id)).is_library
|
||||||
|
});
|
||||||
|
if let Some((path, _)) = workspace_structure_change {
|
||||||
|
self.fetch_workspaces_queue
|
||||||
|
.request_op(format!("workspace vfs file change: {}", path.display()));
|
||||||
|
}
|
||||||
|
self.proc_macro_changed =
|
||||||
|
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
|
||||||
|
let crates = raw_database.relevant_crates(file.file_id);
|
||||||
|
let crate_graph = raw_database.crate_graph();
|
||||||
|
|
||||||
|
crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
|
|
||||||
});
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,10 +196,7 @@ pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Err(error) = self.fetch_build_data_error() {
|
if let Err(error) = self.fetch_build_data_error() {
|
||||||
self.show_and_log_error(
|
self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
|
||||||
"rust-analyzer failed to run build scripts".to_string(),
|
|
||||||
Some(error),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspaces = self
|
let workspaces = self
|
||||||
@ -308,6 +305,7 @@ fn eq_ignore_build_data<'a>(
|
|||||||
|
|
||||||
if self.proc_macro_clients.is_empty() {
|
if self.proc_macro_clients.is_empty() {
|
||||||
if let Some((path, args)) = self.config.proc_macro_srv() {
|
if let Some((path, args)) = self.config.proc_macro_srv() {
|
||||||
|
tracing::info!("Spawning proc-macro servers");
|
||||||
self.proc_macro_clients = self
|
self.proc_macro_clients = self
|
||||||
.workspaces
|
.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
@ -316,20 +314,20 @@ fn eq_ignore_build_data<'a>(
|
|||||||
let mut path = path.clone();
|
let mut path = path.clone();
|
||||||
|
|
||||||
if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
|
if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
|
||||||
tracing::info!("Found a cargo workspace...");
|
tracing::debug!("Found a cargo workspace...");
|
||||||
if let Some(sysroot) = sysroot.as_ref() {
|
if let Some(sysroot) = sysroot.as_ref() {
|
||||||
tracing::info!("Found a cargo workspace with a sysroot...");
|
tracing::debug!("Found a cargo workspace with a sysroot...");
|
||||||
let server_path =
|
let server_path =
|
||||||
sysroot.root().join("libexec").join(&standalone_server_name);
|
sysroot.root().join("libexec").join(&standalone_server_name);
|
||||||
if std::fs::metadata(&server_path).is_ok() {
|
if std::fs::metadata(&server_path).is_ok() {
|
||||||
tracing::info!(
|
tracing::debug!(
|
||||||
"And the server exists at {}",
|
"And the server exists at {}",
|
||||||
server_path.display()
|
server_path.display()
|
||||||
);
|
);
|
||||||
path = server_path;
|
path = server_path;
|
||||||
args = vec![];
|
args = vec![];
|
||||||
} else {
|
} else {
|
||||||
tracing::info!(
|
tracing::debug!(
|
||||||
"And the server does not exist at {}",
|
"And the server does not exist at {}",
|
||||||
server_path.display()
|
server_path.display()
|
||||||
);
|
);
|
||||||
@ -337,14 +335,10 @@ fn eq_ignore_build_data<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
|
||||||
"Using proc-macro server at {} with args {:?}",
|
|
||||||
path.display(),
|
|
||||||
args
|
|
||||||
);
|
|
||||||
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
|
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
|
||||||
let error = format!(
|
let error = format!(
|
||||||
"Failed to run proc_macro_srv from path {}, error: {:?}",
|
"Failed to run proc-macro server from path {}, error: {:?}",
|
||||||
path.display(),
|
path.display(),
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
@ -684,22 +678,26 @@ fn expand(
|
|||||||
pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
|
pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
|
||||||
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
||||||
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
||||||
let file_name = path.file_name().unwrap_or_default();
|
|
||||||
|
|
||||||
if file_name == "Cargo.toml" || file_name == "Cargo.lock" {
|
let file_name = match path.file_name().unwrap_or_default().to_str() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let "Cargo.toml" | "Cargo.lock" = file_name {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if change_kind == ChangeKind::Modify {
|
if change_kind == ChangeKind::Modify {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// .cargo/config{.toml}
|
||||||
if path.extension().unwrap_or_default() != "rs" {
|
if path.extension().unwrap_or_default() != "rs" {
|
||||||
if (file_name == "config.toml" || file_name == "config")
|
let is_cargo_config = matches!(file_name, "config.toml" | "config")
|
||||||
&& path.parent().map(|parent| parent.as_ref().ends_with(".cargo")) == Some(true)
|
&& path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false);
|
||||||
{
|
return is_cargo_config;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
|
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -40,12 +40,15 @@ fn spawn(sender: loader::Sender) -> NotifyHandle {
|
|||||||
.expect("failed to spawn thread");
|
.expect("failed to spawn thread");
|
||||||
NotifyHandle { sender, _thread: thread }
|
NotifyHandle { sender, _thread: thread }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, config: loader::Config) {
|
fn set_config(&mut self, config: loader::Config) {
|
||||||
self.sender.send(Message::Config(config)).unwrap();
|
self.sender.send(Message::Config(config)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&mut self, path: AbsPathBuf) {
|
fn invalidate(&mut self, path: AbsPathBuf) {
|
||||||
self.sender.send(Message::Invalidate(path)).unwrap();
|
self.sender.send(Message::Invalidate(path)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
|
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
|
||||||
read(path)
|
read(path)
|
||||||
}
|
}
|
||||||
@ -70,6 +73,7 @@ impl NotifyActor {
|
|||||||
fn new(sender: loader::Sender) -> NotifyActor {
|
fn new(sender: loader::Sender) -> NotifyActor {
|
||||||
NotifyActor { sender, watched_entries: Vec::new(), watcher: None }
|
NotifyActor { sender, watched_entries: Vec::new(), watcher: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
|
fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
|
||||||
let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
|
let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
|
||||||
select! {
|
select! {
|
||||||
@ -77,9 +81,10 @@ fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
|
|||||||
recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
|
recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self, inbox: Receiver<Message>) {
|
fn run(mut self, inbox: Receiver<Message>) {
|
||||||
while let Some(event) = self.next_event(&inbox) {
|
while let Some(event) = self.next_event(&inbox) {
|
||||||
tracing::debug!("vfs-notify event: {:?}", event);
|
tracing::debug!(?event, "vfs-notify event");
|
||||||
match event {
|
match event {
|
||||||
Event::Message(msg) => match msg {
|
Event::Message(msg) => match msg {
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user