about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-08-17 11:18:50 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-08-17 19:14:46 +0200
commitd0ce97f41da59a49104bb252eab0ff5cf9ec613d (patch)
tree13ed1762ca6e55457585b2cd36e1f74c647eac3d /src
parent01245bd4d22fdb150dd4e2aa17ad1e4236854647 (diff)
downloadrust-d0ce97f41da59a49104bb252eab0ff5cf9ec613d.tar.gz
rust-d0ce97f41da59a49104bb252eab0ff5cf9ec613d.zip
feat: Make rust-analyzer work partially when missing an internet connection
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs50
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs23
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs7
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs12
4 files changed, 75 insertions, 17 deletions
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 38eeedec621..db9c20fdc53 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 419fac3f41f..e11f0ee3ae3 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 5620dfade2d..c6be91229fd 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index dee34b1b393..71ed2872688 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/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!(