Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #17915 - Veykril:offline-no-deps, r=Veykril
feat: Make rust-analyzer work partially when offline Helps out with https://github.com/rust-lang/rust-analyzer/issues/12499 a bit
bors 2024-08-18
parent 469b062 · parent 1013bf3 · commit fa00326
-rw-r--r--crates/project-model/src/cargo_workspace.rs50
-rw-r--r--crates/project-model/src/sysroot.rs23
-rw-r--r--crates/project-model/src/workspace.rs7
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs8
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs8
-rw-r--r--crates/rust-analyzer/src/flycheck.rs1
-rw-r--r--crates/rust-analyzer/src/reload.rs12
7 files changed, 85 insertions, 24 deletions
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 38eeedec62..db9c20fdc5 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -33,6 +33,8 @@ pub struct CargoWorkspace {
workspace_root: AbsPathBuf,
target_directory: AbsPathBuf,
manifest_path: ManifestPath,
+ // Whether this workspace was queried with `--no-deps`.
+ no_deps: bool,
}
impl ops::Index<Package> for CargoWorkspace {
@@ -260,6 +262,18 @@ impl CargoWorkspace {
locked: bool,
progress: &dyn Fn(String),
) -> anyhow::Result<cargo_metadata::Metadata> {
+ Self::fetch_metadata_(cargo_toml, current_dir, config, sysroot, locked, false, progress)
+ }
+
+ fn fetch_metadata_(
+ cargo_toml: &ManifestPath,
+ current_dir: &AbsPath,
+ config: &CargoConfig,
+ sysroot: &Sysroot,
+ locked: bool,
+ no_deps: bool,
+ progress: &dyn Fn(String),
+ ) -> anyhow::Result<cargo_metadata::Metadata> {
let targets = find_list_of_build_targets(config, cargo_toml, sysroot);
let cargo = sysroot.tool(Tool::Cargo);
@@ -314,6 +328,9 @@ impl CargoWorkspace {
if locked {
other_options.push("--locked".to_owned());
}
+ if no_deps {
+ other_options.push("--no-deps".to_owned());
+ }
meta.other_options(other_options);
// FIXME: Fetching metadata is a slow process, as it might require
@@ -324,6 +341,22 @@ impl CargoWorkspace {
(|| -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
let output = meta.cargo_command().output()?;
if !output.status.success() {
+ if !no_deps {
+ // If we failed to fetch metadata with deps, try again without them.
+ // This makes r-a still work partially when offline.
+ if let Ok(metadata) = Self::fetch_metadata_(
+ cargo_toml,
+ current_dir,
+ config,
+ sysroot,
+ locked,
+ true,
+ progress,
+ ) {
+ return Ok(metadata);
+ }
+ }
+
return Err(cargo_metadata::Error::CargoMetadata {
stderr: String::from_utf8(output.stderr)?,
});
@@ -431,8 +464,8 @@ impl CargoWorkspace {
pkg_data.targets.push(tgt);
}
}
- let resolve = meta.resolve.expect("metadata executed with deps");
- for mut node in resolve.nodes {
+ let no_deps = meta.resolve.is_none();
+ for mut node in meta.resolve.map_or_else(Vec::new, |it| it.nodes) {
let &source = pkg_by_id.get(&node.id).unwrap();
node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
let dependencies = node
@@ -451,7 +484,14 @@ impl CargoWorkspace {
let target_directory = AbsPathBuf::assert(meta.target_directory);
- CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path }
+ CargoWorkspace {
+ packages,
+ targets,
+ workspace_root,
+ target_directory,
+ manifest_path,
+ no_deps,
+ }
}
pub fn packages(&self) -> impl ExactSizeIterator<Item = Package> + '_ {
@@ -533,6 +573,10 @@ impl CargoWorkspace {
fn is_unique(&self, name: &str) -> bool {
self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
}
+
+ pub fn no_deps(&self) -> bool {
+ self.no_deps
+ }
}
fn find_list_of_build_targets(
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index 419fac3f41..e11f0ee3ae 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -372,18 +372,19 @@ impl Sysroot {
.flatten()
};
- let resolve = res.resolve.as_mut().expect("metadata executed with deps");
- resolve.nodes.retain_mut(|node| {
- // Replace `rustc-std-workspace` crate with the actual one in the dependency list
- node.deps.iter_mut().for_each(|dep| {
- let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg);
- if let Some((_, real)) = real_pkg {
- dep.pkg = real;
- }
+ if let Some(resolve) = res.resolve.as_mut() {
+ resolve.nodes.retain_mut(|node| {
+ // Replace `rustc-std-workspace` crate with the actual one in the dependency list
+ node.deps.iter_mut().for_each(|dep| {
+ let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg);
+ if let Some((_, real)) = real_pkg {
+ dep.pkg = real;
+ }
+ });
+ // Remove this node if it's a fake one
+ !patches.clone().any(|((_, fake), _)| fake == node.id)
});
- // Remove this node if it's a fake one
- !patches.clone().any(|((_, fake), _)| fake == node.id)
- });
+ }
// Remove the fake ones from the package list
patches.map(|((idx, _), _)| idx).sorted().rev().for_each(|idx| {
res.packages.remove(idx);
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 5620dfade2..c6be91229f 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -441,14 +441,15 @@ impl ProjectWorkspace {
) -> anyhow::Result<WorkspaceBuildScripts> {
match &self.kind {
ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. }
- | ProjectWorkspaceKind::Cargo { cargo, .. } => {
+ | ProjectWorkspaceKind::Cargo { cargo, .. }
+ if !cargo.no_deps() =>
+ {
WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, &self.sysroot)
.with_context(|| {
format!("Failed to run build scripts for {}", cargo.workspace_root())
})
}
- ProjectWorkspaceKind::DetachedFile { cargo: None, .. }
- | ProjectWorkspaceKind::Json { .. } => Ok(WorkspaceBuildScripts::default()),
+ _ => Ok(WorkspaceBuildScripts::default()),
}
}
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 034c49c3d5..5f2871ac99 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -75,7 +75,7 @@ impl DiagnosticCollection {
flycheck_id: usize,
file_id: FileId,
diagnostic: lsp_types::Diagnostic,
- fix: Option<Fix>,
+ fix: Option<Box<Fix>>,
) {
let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default();
for existing_diagnostic in diagnostics.iter() {
@@ -84,8 +84,10 @@ impl DiagnosticCollection {
}
}
- let check_fixes = Arc::make_mut(&mut self.check_fixes);
- check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().extend(fix);
+ if let Some(fix) = fix {
+ let check_fixes = Arc::make_mut(&mut self.check_fixes);
+ check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().push(*fix);
+ }
diagnostics.push(diagnostic);
self.changes.insert(file_id);
}
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 208a70bc02..e330cfc3cf 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -170,7 +170,7 @@ fn resolve_path(
struct SubDiagnostic {
related: lsp_types::DiagnosticRelatedInformation,
- suggested_fix: Option<Fix>,
+ suggested_fix: Option<Box<Fix>>,
}
enum MappedRustChildDiagnostic {
@@ -241,7 +241,7 @@ fn map_rust_child_diagnostic(
location: location(config, workspace_root, spans[0], snap),
message: message.clone(),
},
- suggested_fix: Some(Fix {
+ suggested_fix: Some(Box::new(Fix {
ranges: spans
.iter()
.map(|&span| location(config, workspace_root, span, snap).range)
@@ -260,7 +260,7 @@ fn map_rust_child_diagnostic(
data: None,
command: None,
},
- }),
+ })),
})
}
}
@@ -269,7 +269,7 @@ fn map_rust_child_diagnostic(
pub(crate) struct MappedRustDiagnostic {
pub(crate) url: lsp_types::Url,
pub(crate) diagnostic: lsp_types::Diagnostic,
- pub(crate) fix: Option<Fix>,
+ pub(crate) fix: Option<Box<Fix>>,
}
/// Converts a Rust root diagnostic to LSP form
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 8f2e7d1ca2..443f52c6dd 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -218,6 +218,7 @@ struct FlycheckActor {
status: FlycheckStatus,
}
+#[allow(clippy::large_enum_variant)]
enum Event {
RequestStateChange(StateChange),
CheckEvent(Option<CargoCheckMessage>),
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index dee34b1b39..71ed287268 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -165,6 +165,18 @@ impl GlobalState {
self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) {
+ if matches!(
+ &ws.kind,
+ ProjectWorkspaceKind::Cargo { cargo, .. } | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. }
+ if cargo.no_deps()
+ ) {
+ status.health |= lsp_ext::Health::Warning;
+ format_to!(
+ message,
+ "Workspace `{}` has been queried without dependencies, connecting to crates.io might have failed.\n\n",
+ ws.manifest_or_root()
+ );
+ }
if let Some(err) = ws.sysroot.error() {
status.health |= lsp_ext::Health::Warning;
format_to!(