Unnamed repository; edit this file 'description' to name the repository.
fix: Fix DidSaveDocument requests blocking the server on startup
Lukas Wirth 2022-10-21
parent f3cce5f · commit de195ff
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/parent_module.rs10
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs6
-rw-r--r--crates/rust-analyzer/src/global_state.rs17
-rw-r--r--crates/rust-analyzer/src/handlers.rs14
-rw-r--r--crates/rust-analyzer/src/main_loop.rs153
-rw-r--r--crates/rust-analyzer/src/reload.rs5
7 files changed, 132 insertions, 83 deletions
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 09a5cb03ec..416817ca0b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -486,6 +486,16 @@ impl Analysis {
self.with_db(|db| parent_module::crates_for(db, file_id))
}
+ /// Returns crates this file belongs too.
+ pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
+ self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
+ }
+
+ /// Returns crates this file *might* belong too.
+ pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
+ self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
+ }
+
/// Returns the edition of the given crate.
pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
self.with_db(|db| db.crate_graph()[crate_id].edition)
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 9d425954e3..506f9452cf 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -1,8 +1,9 @@
-use hir::Semantics;
+use hir::{db::DefDatabase, Semantics};
use ide_db::{
base_db::{CrateId, FileId, FileLoader, FilePosition},
RootDatabase,
};
+use itertools::Itertools;
use syntax::{
algo::find_node_at_offset,
ast::{self, AstNode},
@@ -55,7 +56,12 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
/// Returns `Vec` for the same reason as `parent_module`
pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
- db.relevant_crates(file_id).iter().copied().collect()
+ db.relevant_crates(file_id)
+ .iter()
+ .copied()
+ .filter(|&crate_id| db.crate_def_map(crate_id).modules_for_file(file_id).next().is_some())
+ .sorted()
+ .collect()
}
#[cfg(test)]
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index cbde735476..6ede194bab 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -118,7 +118,11 @@ impl CargoTargetSpec {
global_state_snapshot: &GlobalStateSnapshot,
file_id: FileId,
) -> Result<Option<CargoTargetSpec>> {
- let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_file_id(file_id) {
+ let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? {
+ &[crate_id, ..] => crate_id,
+ _ => return Ok(None),
+ };
+ let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) {
Some(it) => it,
None => return Ok(None),
};
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 4cddb12083..3fb06c31f7 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant};
use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
-use ide_db::base_db::{FileLoader, SourceDatabase};
+use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
@@ -64,7 +64,7 @@ pub(crate) struct GlobalState {
pub(crate) source_root_config: SourceRootConfig,
pub(crate) proc_macro_clients: Vec<Result<ProcMacroServer, String>>,
- pub(crate) flycheck: Vec<FlycheckHandle>,
+ pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
@@ -117,6 +117,7 @@ pub(crate) struct GlobalStateSnapshot {
vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
pub(crate) proc_macros_loaded: bool,
+ pub(crate) flycheck: Arc<[FlycheckHandle]>,
}
impl std::panic::UnwindSafe for GlobalStateSnapshot {}
@@ -155,7 +156,7 @@ impl GlobalState {
source_root_config: SourceRootConfig::default(),
proc_macro_clients: vec![],
- flycheck: Vec::new(),
+ flycheck: Arc::new([]),
flycheck_sender,
flycheck_receiver,
@@ -295,6 +296,7 @@ impl GlobalState {
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(),
+ flycheck: self.flycheck.clone(),
}
}
@@ -398,10 +400,15 @@ impl GlobalStateSnapshot {
url_from_abs_path(path)
}
- pub(crate) fn cargo_target_for_file_id(
+ pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
+ self.vfs.read().0.file_path(file_id)
+ }
+
+ pub(crate) fn cargo_target_for_crate_root(
&self,
- file_id: FileId,
+ crate_id: CrateId,
) -> Option<(&CargoWorkspace, Target)> {
+ let file_id = self.analysis.crate_root(crate_id).ok()?;
let path = self.vfs.read().0.file_path(file_id);
let path = path.as_path()?;
self.workspaces.iter().find_map(|ws| match ws {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 701a009ea8..34795a8eb4 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1782,7 +1782,15 @@ fn run_rustfmt(
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
let file_id = from_proto::file_id(snap, &text_document.uri)?;
let file = snap.analysis.file_text(file_id)?;
- let crate_ids = snap.analysis.crates_for(file_id)?;
+
+ // find the edition of the package the file belongs to
+ // (if it belongs to multiple we'll just pick the first one and pray)
+ let edition = snap
+ .analysis
+ .relevant_crates_for(file_id)?
+ .into_iter()
+ .find_map(|crate_id| snap.cargo_target_for_crate_root(crate_id))
+ .map(|(ws, target)| ws[ws[target].package].edition);
let line_index = snap.file_line_index(file_id)?;
@@ -1808,9 +1816,7 @@ fn run_rustfmt(
);
}
}
- if let Some(&crate_id) = crate_ids.first() {
- // Assume all crates are in the same edition
- let edition = snap.analysis.crate_edition(crate_id)?;
+ if let Some(edition) = edition {
cmd.arg("--edition");
cmd.arg(edition.to_string());
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 89189cef14..319b86c58b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -10,7 +10,7 @@ use std::{
use always_assert::always;
use crossbeam_channel::{select, Receiver};
use flycheck::FlycheckHandle;
-use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
+use ide_db::base_db::{SourceDatabaseExt, VfsPath};
use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
@@ -191,7 +191,7 @@ impl GlobalState {
// NOTE: don't count blocking select! call as a loop-turn time
let _p = profile::span("GlobalState::handle_event");
- tracing::debug!("handle_event({:?})", event);
+ tracing::debug!("{:?} handle_event({:?})", loop_start, event);
let task_queue_len = self.task_pool.handle.len();
if task_queue_len > 0 {
tracing::info!("task queue len: {}", task_queue_len);
@@ -727,7 +727,7 @@ impl GlobalState {
.insert(path.clone(), DocumentData::new(params.text_document.version))
.is_err();
if already_exists {
- tracing::error!("duplicate DidOpenTextDocument: {}", path)
+ tracing::error!("duplicate DidOpenTextDocument: {}", path);
}
this.vfs
.write()
@@ -774,69 +774,7 @@ impl GlobalState {
Ok(())
})?
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
- let mut updated = false;
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
- let (vfs, _) = &*this.vfs.read();
-
- // Trigger flychecks for all workspaces that depend on the saved file
- if let Some(file_id) = vfs.file_id(&vfs_path) {
- let analysis = this.analysis_host.analysis();
- // Crates containing or depending on the saved file
- let crate_ids: Vec<_> = analysis
- .crates_for(file_id)?
- .into_iter()
- .flat_map(|id| {
- this.analysis_host
- .raw_database()
- .crate_graph()
- .transitive_rev_deps(id)
- })
- .sorted()
- .unique()
- .collect();
-
- let crate_root_paths: Vec<_> = crate_ids
- .iter()
- .filter_map(|&crate_id| {
- analysis
- .crate_root(crate_id)
- .map(|file_id| {
- vfs.file_path(file_id).as_path().map(ToOwned::to_owned)
- })
- .transpose()
- })
- .collect::<ide::Cancellable<_>>()?;
- let crate_root_paths: Vec<_> =
- crate_root_paths.iter().map(Deref::deref).collect();
-
- // Find all workspaces that have at least one target containing the saved file
- let workspace_ids =
- this.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
- project_model::ProjectWorkspace::Cargo { cargo, .. } => {
- cargo.packages().any(|pkg| {
- cargo[pkg].targets.iter().any(|&it| {
- crate_root_paths.contains(&cargo[it].root.as_path())
- })
- })
- }
- project_model::ProjectWorkspace::Json { project, .. } => project
- .crates()
- .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
- project_model::ProjectWorkspace::DetachedFiles { .. } => false,
- });
-
- // Find and trigger corresponding flychecks
- for flycheck in &this.flycheck {
- for (id, _) in workspace_ids.clone() {
- if id == flycheck.id() {
- updated = true;
- flycheck.restart();
- continue;
- }
- }
- }
- }
-
// Re-fetch workspaces if a workspace related file has changed
if let Some(abs_path) = vfs_path.as_path() {
if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
@@ -844,13 +782,90 @@ impl GlobalState {
.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
}
}
+
+ let file_id = this.vfs.read().0.file_id(&vfs_path);
+ if let Some(file_id) = file_id {
+ let world = this.snapshot();
+ let mut updated = false;
+ let task = move || -> std::result::Result<(), ide::Cancelled> {
+ // Trigger flychecks for all workspaces that depend on the saved file
+ // Crates containing or depending on the saved file
+ let crate_ids: Vec<_> = world
+ .analysis
+ .crates_for(file_id)?
+ .into_iter()
+ .flat_map(|id| world.analysis.transitive_rev_deps(id))
+ .flatten()
+ .sorted()
+ .unique()
+ .collect();
+
+ let crate_root_paths: Vec<_> = crate_ids
+ .iter()
+ .filter_map(|&crate_id| {
+ world
+ .analysis
+ .crate_root(crate_id)
+ .map(|file_id| {
+ world
+ .file_id_to_file_path(file_id)
+ .as_path()
+ .map(ToOwned::to_owned)
+ })
+ .transpose()
+ })
+ .collect::<ide::Cancellable<_>>()?;
+ let crate_root_paths: Vec<_> =
+ crate_root_paths.iter().map(Deref::deref).collect();
+
+ // Find all workspaces that have at least one target containing the saved file
+ let workspace_ids =
+ world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
+ project_model::ProjectWorkspace::Cargo { cargo, .. } => {
+ cargo.packages().any(|pkg| {
+ cargo[pkg].targets.iter().any(|&it| {
+ crate_root_paths.contains(&cargo[it].root.as_path())
+ })
+ })
+ }
+ project_model::ProjectWorkspace::Json { project, .. } => {
+ project.crates().any(|(c, _)| {
+ crate_ids.iter().any(|&crate_id| crate_id == c)
+ })
+ }
+ project_model::ProjectWorkspace::DetachedFiles { .. } => false,
+ });
+
+ // Find and trigger corresponding flychecks
+ for flycheck in world.flycheck.iter() {
+ for (id, _) in workspace_ids.clone() {
+ if id == flycheck.id() {
+ updated = true;
+ flycheck.restart();
+ continue;
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ if !updated {
+ for flycheck in world.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+ };
+ this.task_pool.handle.spawn_with_sender(move |_| {
+ if let Err(e) = std::panic::catch_unwind(task) {
+ tracing::error!("DidSaveTextDocument flycheck task panicked: {e:?}")
+ }
+ });
+ return Ok(());
+ }
}
// No specific flycheck was triggered, so let's trigger all of them.
- if !updated {
- for flycheck in &this.flycheck {
- flycheck.restart();
- }
+ for flycheck in this.flycheck.iter() {
+ flycheck.restart();
}
Ok(())
})?
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index f7db62baf2..cc7600a2fa 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -466,7 +466,7 @@ impl GlobalState {
let config = match self.config.flycheck() {
Some(it) => it,
None => {
- self.flycheck = Vec::new();
+ self.flycheck = Arc::new([]);
self.diagnostics.clear_check_all();
return;
}
@@ -510,7 +510,8 @@ impl GlobalState {
})
.collect()
}
- };
+ }
+ .into();
}
}