Unnamed repository; edit this file 'description' to name the repository.
Add config option to use `rust-analyzer` specific target dir
Adds a Rust Analyzer configuration option to set a custom target directory for builds. This is a workaround for Rust Analyzer blocking debug builds while running `cargo check`. This change should close #6007
Victor Song 2023-10-09
parent 7e9b25b · commit aeef7b6
-rw-r--r--crates/rust-analyzer/src/config.rs200
-rw-r--r--docs/user/generated_config.adoc9
-rw-r--r--editors/code/package.json15
3 files changed, 190 insertions, 34 deletions
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 8e780baa36..3cd6fa49b2 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -480,6 +480,13 @@ config_data! {
/// tests or binaries. For example, it may be `--release`.
runnables_extraArgs: Vec<String> = "[]",
+ /// Optional path to a rust-analyzer specific target directory.
+ /// This is useful to prevent rust-analyzer's `cargo check` from blocking builds.
+ ///
+ /// Set to `true` to use a subdirectory of the existing target directory or
+ /// set to a path to use that path.
+ rust_analyzerTargetDir: Option<TargetDirectory> = "null",
+
/// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
/// projects, or "discover" to try to automatically find it if the `rustc-dev` component
/// is installed.
@@ -1192,6 +1199,7 @@ impl Config {
}
pub fn cargo(&self) -> CargoConfig {
+ let target_directory = self.target_dir_from_config();
let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
if rustc_src == "discover" {
RustLibSource::Discover
@@ -1209,6 +1217,10 @@ impl Config {
let sysroot_src =
self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot));
+ let mut extra_args = self.data.cargo_extraArgs.clone();
+
+ add_target_dir_to_args(&mut extra_args, target_directory);
+
CargoConfig {
features: match &self.data.cargo_features {
CargoFeaturesDef::All => CargoFeatures::All,
@@ -1261,7 +1273,7 @@ impl Config {
InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
},
run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
- extra_args: self.data.cargo_extraArgs.clone(),
+ extra_args,
extra_env: self.data.cargo_extraEnv.clone(),
}
}
@@ -1281,10 +1293,14 @@ impl Config {
}
pub fn flycheck(&self) -> FlycheckConfig {
+ let target_directory = self.target_dir_from_config();
+
match &self.data.check_overrideCommand {
Some(args) if !args.is_empty() => {
let mut args = args.clone();
let command = args.remove(0);
+ add_target_dir_to_args(&mut args, target_directory);
+
FlycheckConfig::CustomCommand {
command,
args,
@@ -1303,42 +1319,61 @@ impl Config {
},
}
}
- Some(_) | None => FlycheckConfig::CargoCommand {
- command: self.data.check_command.clone(),
- target_triples: self
- .data
- .check_targets
- .clone()
- .and_then(|targets| match &targets.0[..] {
- [] => None,
- targets => Some(targets.into()),
- })
- .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
- all_targets: self.data.check_allTargets,
- no_default_features: self
- .data
- .check_noDefaultFeatures
- .unwrap_or(self.data.cargo_noDefaultFeatures),
- all_features: matches!(
- self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
- CargoFeaturesDef::All
- ),
- features: match self
- .data
- .check_features
- .clone()
- .unwrap_or_else(|| self.data.cargo_features.clone())
- {
- CargoFeaturesDef::All => vec![],
- CargoFeaturesDef::Selected(it) => it,
- },
- extra_args: self.check_extra_args(),
- extra_env: self.check_extra_env(),
- ansi_color_output: self.color_diagnostic_output(),
- },
+ Some(_) | None => {
+ let mut extra_args = self.check_extra_args();
+ add_target_dir_to_args(&mut extra_args, target_directory);
+
+ FlycheckConfig::CargoCommand {
+ command: self.data.check_command.clone(),
+ target_triples: self
+ .data
+ .check_targets
+ .clone()
+ .and_then(|targets| match &targets.0[..] {
+ [] => None,
+ targets => Some(targets.into()),
+ })
+ .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()),
+ all_targets: self.data.check_allTargets,
+ no_default_features: self
+ .data
+ .check_noDefaultFeatures
+ .unwrap_or(self.data.cargo_noDefaultFeatures),
+ all_features: matches!(
+ self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features),
+ CargoFeaturesDef::All
+ ),
+ features: match self
+ .data
+ .check_features
+ .clone()
+ .unwrap_or_else(|| self.data.cargo_features.clone())
+ {
+ CargoFeaturesDef::All => vec![],
+ CargoFeaturesDef::Selected(it) => it,
+ },
+ extra_args,
+ extra_env: self.check_extra_env(),
+ ansi_color_output: self.color_diagnostic_output(),
+ }
+ }
}
}
+ fn target_dir_from_config(&self) -> Option<String> {
+ self.data
+ .rust_analyzerTargetDir
+ .as_ref()
+ .map(|target_dir| match target_dir {
+ TargetDirectory::UseSubdirectory(yes) if *yes => {
+ Some(String::from("target/rust-analyzer"))
+ }
+ TargetDirectory::UseSubdirectory(_) => None,
+ TargetDirectory::Directory(dir) => Some(dir.clone()),
+ })
+ .flatten()
+ }
+
pub fn check_on_save(&self) -> bool {
self.data.checkOnSave
}
@@ -1690,6 +1725,13 @@ impl Config {
self.is_visual_studio_code
}
}
+
+fn add_target_dir_to_args(args: &mut Vec<String>, target_dir: Option<String>) {
+ if let Some(target_dir) = target_dir {
+ args.push(format!("--target-dir={}", target_dir));
+ }
+}
+
// Deserialization definitions
macro_rules! create_bool_or_string_de {
@@ -2037,6 +2079,14 @@ pub enum MemoryLayoutHoverRenderKindDef {
Both,
}
+#[derive(Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
+pub enum TargetDirectory {
+ UseSubdirectory(bool),
+ Directory(String),
+}
+
macro_rules! _config_data {
(struct $name:ident {
$(
@@ -2465,6 +2515,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
},
],
},
+ "Option<TargetDirectory>" => set! {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ },
+ ],
+ },
_ => panic!("missing entry for {ty}: {default}"),
}
@@ -2625,4 +2688,73 @@ mod tests {
Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap())
);
}
+
+ #[test]
+ fn cargo_target_dir_unset() {
+ let mut config = Config::new(
+ AbsPathBuf::try_from(project_root()).unwrap(),
+ Default::default(),
+ vec![],
+ false,
+ );
+ config
+ .update(serde_json::json!({
+ "rust": { "analyzerTargetDir": null }
+ }))
+ .unwrap();
+ assert_eq!(config.data.rust_analyzerTargetDir, None);
+ assert_eq!(config.cargo().extra_args.len(), 0);
+ assert!(
+ matches!(config.flycheck(), FlycheckConfig::CargoCommand { extra_args, .. } if extra_args.is_empty())
+ );
+ }
+
+ #[test]
+ fn cargo_target_dir_subdir() {
+ let mut config = Config::new(
+ AbsPathBuf::try_from(project_root()).unwrap(),
+ Default::default(),
+ vec![],
+ false,
+ );
+ config
+ .update(serde_json::json!({
+ "rust": { "analyzerTargetDir": true }
+ }))
+ .unwrap();
+ assert_eq!(
+ config.data.rust_analyzerTargetDir,
+ Some(TargetDirectory::UseSubdirectory(true))
+ );
+ assert_eq!(
+ config.cargo().extra_args,
+ vec!["--target-dir=target/rust-analyzer".to_string()]
+ );
+ assert!(
+ matches!(config.flycheck(), FlycheckConfig::CargoCommand { extra_args, .. } if extra_args == vec!["--target-dir=target/rust-analyzer".to_string()])
+ );
+ }
+
+ #[test]
+ fn cargo_target_dir_relative_dir() {
+ let mut config = Config::new(
+ AbsPathBuf::try_from(project_root()).unwrap(),
+ Default::default(),
+ vec![],
+ false,
+ );
+ config
+ .update(serde_json::json!({
+ "rust": { "analyzerTargetDir": "other_folder" }
+ }))
+ .unwrap();
+ assert_eq!(
+ config.data.rust_analyzerTargetDir,
+ Some(TargetDirectory::Directory("other_folder".to_string()))
+ );
+ assert_eq!(config.cargo().extra_args, vec!["--target-dir=other_folder".to_string()]);
+ assert!(
+ matches!(config.flycheck(), FlycheckConfig::CargoCommand { extra_args, .. } if extra_args == vec!["--target-dir=other_folder".to_string()])
+ );
+ }
}
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index dec7a50757..f7ae6afe38 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -757,6 +757,15 @@ Command to be executed instead of 'cargo' for runnables.
Additional arguments to be passed to cargo for runnables such as
tests or binaries. For example, it may be `--release`.
--
+[[rust-analyzer.rust.analyzerTargetDir]]rust-analyzer.rust.analyzerTargetDir (default: `null`)::
++
+--
+Optional path to a rust-analyzer specific target directory.
+This is useful to prevent rust-analyzer's `cargo check` from blocking builds.
+
+Set to `true` to use a subdirectory of the existing target directory or
+set to a path to use that path.
+--
[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
+
--
diff --git a/editors/code/package.json b/editors/code/package.json
index 554b05c46c..e1402cb0f5 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1488,6 +1488,21 @@
"type": "string"
}
},
+ "rust-analyzer.rust.analyzerTargetDir": {
+ "markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis is useful to prevent rust-analyzer's `cargo check` from blocking builds.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path to use that path.",
+ "default": null,
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
"rust-analyzer.rustc.source": {
"markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
"default": null,