From b2abbf89c9cc1a35f7765c1305bc3c59d17c79fe Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 17 Aug 2024 11:18:50 +0200 Subject: [PATCH] feat: Make rust-analyzer work partially when missing an internet connection --- crates/project-model/src/cargo_workspace.rs | 35 +++++++++++++++++++-- crates/project-model/src/sysroot.rs | 23 +++++++------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 38eeedec6217..2cf66d244b04 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -259,6 +259,18 @@ impl CargoWorkspace { sysroot: &Sysroot, locked: bool, progress: &dyn Fn(String), + ) -> anyhow::Result { + 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 { let targets = find_list_of_build_targets(config, cargo_toml, sysroot); @@ -314,6 +326,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 +339,22 @@ impl CargoWorkspace { (|| -> Result { 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 +462,8 @@ impl CargoWorkspace { pkg_data.targets.push(tgt); } } - let resolve = meta.resolve.expect("metadata executed with deps"); - for mut node in resolve.nodes { + let nodes = meta.resolve.map_or_else(Vec::new, |it| it.nodes); + for mut node in nodes { let &source = pkg_by_id.get(&node.id).unwrap(); node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); let dependencies = node diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 419fac3f41f7..e11f0ee3ae3f 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);