Cargo-subspace: Make rust-analyzer work better with large cargo workspaces

2 hours ago 2

A tool that forces rust-analyzer to lazily index crates in the workspace as you open new files. It is useful if you have a very large cargo workspace (think hundreds of crates) and you find that rust-analyzer can be slow or laggy as a result.

I want to preface all of this by saying that rust-analyzer is an amazing project, and I am eternally grateful to the many people who contribute to it! It makes developing rust code a breeze, and it has surely significantly contributed to Rust's widespread adoption.

This tool exists to improve the rust-analyzer experience for very large cargo workspaces. Cargo workspaces can contain a (theoretically) unbounded number of crates. Many organizations prefer to maintain a single cargo workspace (e.g. in a monorepo) to keep dependency versions consistent across different services or libraries and simplify tooling. However, rust-analyzer indexes the crates in the cargo workspace pretty eagerly, which can take quite a long time. check.workspace = false and cachePriming.enable = false can help make things a bit lazier, but in my experience, they don't solve the problem entirely. Even after indexing is finished, certain actions, like autocomplete and and finding references to symbols, can be laggy due to the very large dependency graph.

Rather than allowing rust-analyzer to discover all the crates in the workspace at startup, this tool tells rust-analyzer about the crates in your workspace selectively as you open new files. It invokes cargo metadata to get the dependency graph for the workspace and then prunes the graph such that only 1) the crate that owns the current file and 2) that crate's dependencies remain in the graph. This is supported by rust-analyzer's "rust-project.json" feature, which allows rust-analyzer to use third party build tools (e.g. bazel or buck) to discover crates in your project. (This project still uses cargo under the hood, but it integrates into rust-analyzer through this path.)

Note that, because crates are indexed lazily as you open source code files, you will not be able to perform any actions that require knowledge of the dependents of the current crate (that is, the crates in your workspace that depend on the current crate). Some examples include:

  • Searching for a symbol in a crate for which you've not yet opened any source code files
  • Finding references to symbols (e.g. functions, types, etc.) defined in the current crate in crates that depend on the crate (unless you've already opened a file from a dependent crate)

First, make sure that the rust-src component is installed for your rust toolchain. This downloads the source code for the crates built-in to rust (e.g. std and core) so rust-analyzer can properly index them.

rustup component add rust-src

Then:

cargo install --locked cargo-subspace

This tool is designed to be invoked directly by rust-analyzer. I've tested it with both VSCode and neovim, but theoretically, it should work with any editor that has LSP support.

Add the following to your settings.json:

{ "rust-analyzer.workspace.discoverConfig": { "command": [ "cargo-subspace", "discover", "{arg}" ], "progressLabel": "cargo-subspace", "filesToWatch": [ "Cargo.toml" ] }, "rust-analyzer.check.overrideCommand": [ "cargo-subspace", "check", // You can also use "clippy" here "$saved_file", ], }

These settings should be set wherever you configure your LSP servers in your neovim config. I use the great rustaceanvim plugin, but these settings can also be set via lspconfig.

["rust-analyzer"] = { check = { overrideCommand = { "cargo-subspace", "clippy", "$saved_file", }, }, workspace = { discoverConfig = { command = { "cargo-subspace", "discover", "{arg}", }, progressLabel = "cargo-subspace", filesToWatch = { "Cargo.toml", }, }, }, }

Troubleshooting/Debugging

If you run into trouble, please feel free to open an issue with the following:

  • A detailed description of the problem, including steps to reproduce
  • Your rust toolchain version
  • Your cargo-subspace version
  • Verbose logs from the errant invocation of this tool (you can collect verbose logs by running cargo-subspace with the --verbose flag). By default, logs are stored in $HOME/.local/state/cargo-subspace/cargo-subspace.log

You may also feel free to open an issue if you have a feature request. Provided the feature makes sense and is not too involved, I would be happy to consider it. I'll also accept pull requests if you're feeling inspired to implement it yourself.

NOTE: This project is currently untested on Windows. Please feel free to test it and tell me about your experience!

Read Entire Article