@@ -480,6 +480,13 @@ config_data! {
480480 /// tests or binaries. For example, it may be `--release`.
481481 runnables_extraArgs: Vec <String > = "[]" ,
482482
483+ /// Optional path to a rust-analyzer specific target directory.
484+ /// This is useful to prevent rust-analyzer's `cargo check` from blocking builds.
485+ ///
486+ /// Set to `true` to use a subdirectory of the existing target directory or
487+ /// set to a path to use that path.
488+ rust_analyzerTargetDir: Option <TargetDirectory > = "null" ,
489+
483490 /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
484491 /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
485492 /// is installed.
@@ -1192,6 +1199,7 @@ impl Config {
11921199 }
11931200
11941201 pub fn cargo ( & self ) -> CargoConfig {
1202+ let target_directory = self . target_dir_from_config ( ) ;
11951203 let rustc_source = self . data . rustc_source . as_ref ( ) . map ( |rustc_src| {
11961204 if rustc_src == "discover" {
11971205 RustLibSource :: Discover
@@ -1209,6 +1217,10 @@ impl Config {
12091217 let sysroot_src =
12101218 self . data . cargo_sysrootSrc . as_ref ( ) . map ( |sysroot| self . root_path . join ( sysroot) ) ;
12111219
1220+ let mut extra_args = self . data . cargo_extraArgs . clone ( ) ;
1221+
1222+ add_target_dir_to_args ( & mut extra_args, target_directory) ;
1223+
12121224 CargoConfig {
12131225 features : match & self . data . cargo_features {
12141226 CargoFeaturesDef :: All => CargoFeatures :: All ,
@@ -1261,7 +1273,7 @@ impl Config {
12611273 InvocationLocation :: Workspace => project_model:: InvocationLocation :: Workspace ,
12621274 } ,
12631275 run_build_script_command : self . data . cargo_buildScripts_overrideCommand . clone ( ) ,
1264- extra_args : self . data . cargo_extraArgs . clone ( ) ,
1276+ extra_args,
12651277 extra_env : self . data . cargo_extraEnv . clone ( ) ,
12661278 }
12671279 }
@@ -1281,10 +1293,14 @@ impl Config {
12811293 }
12821294
12831295 pub fn flycheck ( & self ) -> FlycheckConfig {
1296+ let target_directory = self . target_dir_from_config ( ) ;
1297+
12841298 match & self . data . check_overrideCommand {
12851299 Some ( args) if !args. is_empty ( ) => {
12861300 let mut args = args. clone ( ) ;
12871301 let command = args. remove ( 0 ) ;
1302+ add_target_dir_to_args ( & mut args, target_directory) ;
1303+
12881304 FlycheckConfig :: CustomCommand {
12891305 command,
12901306 args,
@@ -1303,42 +1319,61 @@ impl Config {
13031319 } ,
13041320 }
13051321 }
1306- Some ( _) | None => FlycheckConfig :: CargoCommand {
1307- command : self . data . check_command . clone ( ) ,
1308- target_triples : self
1309- . data
1310- . check_targets
1311- . clone ( )
1312- . and_then ( |targets| match & targets. 0 [ ..] {
1313- [ ] => None ,
1314- targets => Some ( targets. into ( ) ) ,
1315- } )
1316- . unwrap_or_else ( || self . data . cargo_target . clone ( ) . into_iter ( ) . collect ( ) ) ,
1317- all_targets : self . data . check_allTargets ,
1318- no_default_features : self
1319- . data
1320- . check_noDefaultFeatures
1321- . unwrap_or ( self . data . cargo_noDefaultFeatures ) ,
1322- all_features : matches ! (
1323- self . data. check_features. as_ref( ) . unwrap_or( & self . data. cargo_features) ,
1324- CargoFeaturesDef :: All
1325- ) ,
1326- features : match self
1327- . data
1328- . check_features
1329- . clone ( )
1330- . unwrap_or_else ( || self . data . cargo_features . clone ( ) )
1331- {
1332- CargoFeaturesDef :: All => vec ! [ ] ,
1333- CargoFeaturesDef :: Selected ( it) => it,
1334- } ,
1335- extra_args : self . check_extra_args ( ) ,
1336- extra_env : self . check_extra_env ( ) ,
1337- ansi_color_output : self . color_diagnostic_output ( ) ,
1338- } ,
1322+ Some ( _) | None => {
1323+ let mut extra_args = self . check_extra_args ( ) ;
1324+ add_target_dir_to_args ( & mut extra_args, target_directory) ;
1325+
1326+ FlycheckConfig :: CargoCommand {
1327+ command : self . data . check_command . clone ( ) ,
1328+ target_triples : self
1329+ . data
1330+ . check_targets
1331+ . clone ( )
1332+ . and_then ( |targets| match & targets. 0 [ ..] {
1333+ [ ] => None ,
1334+ targets => Some ( targets. into ( ) ) ,
1335+ } )
1336+ . unwrap_or_else ( || self . data . cargo_target . clone ( ) . into_iter ( ) . collect ( ) ) ,
1337+ all_targets : self . data . check_allTargets ,
1338+ no_default_features : self
1339+ . data
1340+ . check_noDefaultFeatures
1341+ . unwrap_or ( self . data . cargo_noDefaultFeatures ) ,
1342+ all_features : matches ! (
1343+ self . data. check_features. as_ref( ) . unwrap_or( & self . data. cargo_features) ,
1344+ CargoFeaturesDef :: All
1345+ ) ,
1346+ features : match self
1347+ . data
1348+ . check_features
1349+ . clone ( )
1350+ . unwrap_or_else ( || self . data . cargo_features . clone ( ) )
1351+ {
1352+ CargoFeaturesDef :: All => vec ! [ ] ,
1353+ CargoFeaturesDef :: Selected ( it) => it,
1354+ } ,
1355+ extra_args,
1356+ extra_env : self . check_extra_env ( ) ,
1357+ ansi_color_output : self . color_diagnostic_output ( ) ,
1358+ }
1359+ }
13391360 }
13401361 }
13411362
1363+ fn target_dir_from_config ( & self ) -> Option < String > {
1364+ self . data
1365+ . rust_analyzerTargetDir
1366+ . as_ref ( )
1367+ . map ( |target_dir| match target_dir {
1368+ TargetDirectory :: UseSubdirectory ( yes) if * yes => {
1369+ Some ( String :: from ( "target/rust-analyzer" ) )
1370+ }
1371+ TargetDirectory :: UseSubdirectory ( _) => None ,
1372+ TargetDirectory :: Directory ( dir) => Some ( dir. clone ( ) ) ,
1373+ } )
1374+ . flatten ( )
1375+ }
1376+
13421377 pub fn check_on_save ( & self ) -> bool {
13431378 self . data . checkOnSave
13441379 }
@@ -1690,6 +1725,13 @@ impl Config {
16901725 self . is_visual_studio_code
16911726 }
16921727}
1728+
1729+ fn add_target_dir_to_args ( args : & mut Vec < String > , target_dir : Option < String > ) {
1730+ if let Some ( target_dir) = target_dir {
1731+ args. push ( format ! ( "--target-dir={}" , target_dir) ) ;
1732+ }
1733+ }
1734+
16931735// Deserialization definitions
16941736
16951737macro_rules! create_bool_or_string_de {
@@ -2037,6 +2079,14 @@ pub enum MemoryLayoutHoverRenderKindDef {
20372079 Both ,
20382080}
20392081
2082+ #[ derive( Deserialize , Debug , Clone , PartialEq ) ]
2083+ #[ serde( rename_all = "snake_case" ) ]
2084+ #[ serde( untagged) ]
2085+ pub enum TargetDirectory {
2086+ UseSubdirectory ( bool ) ,
2087+ Directory ( String ) ,
2088+ }
2089+
20402090macro_rules! _config_data {
20412091 ( struct $name: ident {
20422092 $(
@@ -2465,6 +2515,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
24652515 } ,
24662516 ] ,
24672517 } ,
2518+ "Option<TargetDirectory>" => set ! {
2519+ "anyOf" : [
2520+ {
2521+ "type" : "null"
2522+ } ,
2523+ {
2524+ "type" : "boolean"
2525+ } ,
2526+ {
2527+ "type" : "string"
2528+ } ,
2529+ ] ,
2530+ } ,
24682531 _ => panic ! ( "missing entry for {ty}: {default}" ) ,
24692532 }
24702533
@@ -2625,4 +2688,73 @@ mod tests {
26252688 Some ( AbsPathBuf :: try_from( project_root( ) . join( "./server" ) ) . unwrap( ) )
26262689 ) ;
26272690 }
2691+
2692+ #[ test]
2693+ fn cargo_target_dir_unset ( ) {
2694+ let mut config = Config :: new (
2695+ AbsPathBuf :: try_from ( project_root ( ) ) . unwrap ( ) ,
2696+ Default :: default ( ) ,
2697+ vec ! [ ] ,
2698+ false ,
2699+ ) ;
2700+ config
2701+ . update ( serde_json:: json!( {
2702+ "rust" : { "analyzerTargetDir" : null }
2703+ } ) )
2704+ . unwrap ( ) ;
2705+ assert_eq ! ( config. data. rust_analyzerTargetDir, None ) ;
2706+ assert_eq ! ( config. cargo( ) . extra_args. len( ) , 0 ) ;
2707+ assert ! (
2708+ matches!( config. flycheck( ) , FlycheckConfig :: CargoCommand { extra_args, .. } if extra_args. is_empty( ) )
2709+ ) ;
2710+ }
2711+
2712+ #[ test]
2713+ fn cargo_target_dir_subdir ( ) {
2714+ let mut config = Config :: new (
2715+ AbsPathBuf :: try_from ( project_root ( ) ) . unwrap ( ) ,
2716+ Default :: default ( ) ,
2717+ vec ! [ ] ,
2718+ false ,
2719+ ) ;
2720+ config
2721+ . update ( serde_json:: json!( {
2722+ "rust" : { "analyzerTargetDir" : true }
2723+ } ) )
2724+ . unwrap ( ) ;
2725+ assert_eq ! (
2726+ config. data. rust_analyzerTargetDir,
2727+ Some ( TargetDirectory :: UseSubdirectory ( true ) )
2728+ ) ;
2729+ assert_eq ! (
2730+ config. cargo( ) . extra_args,
2731+ vec![ "--target-dir=target/rust-analyzer" . to_string( ) ]
2732+ ) ;
2733+ assert ! (
2734+ matches!( config. flycheck( ) , FlycheckConfig :: CargoCommand { extra_args, .. } if extra_args == vec![ "--target-dir=target/rust-analyzer" . to_string( ) ] )
2735+ ) ;
2736+ }
2737+
2738+ #[ test]
2739+ fn cargo_target_dir_relative_dir ( ) {
2740+ let mut config = Config :: new (
2741+ AbsPathBuf :: try_from ( project_root ( ) ) . unwrap ( ) ,
2742+ Default :: default ( ) ,
2743+ vec ! [ ] ,
2744+ false ,
2745+ ) ;
2746+ config
2747+ . update ( serde_json:: json!( {
2748+ "rust" : { "analyzerTargetDir" : "other_folder" }
2749+ } ) )
2750+ . unwrap ( ) ;
2751+ assert_eq ! (
2752+ config. data. rust_analyzerTargetDir,
2753+ Some ( TargetDirectory :: Directory ( "other_folder" . to_string( ) ) )
2754+ ) ;
2755+ assert_eq ! ( config. cargo( ) . extra_args, vec![ "--target-dir=other_folder" . to_string( ) ] ) ;
2756+ assert ! (
2757+ matches!( config. flycheck( ) , FlycheckConfig :: CargoCommand { extra_args, .. } if extra_args == vec![ "--target-dir=other_folder" . to_string( ) ] )
2758+ ) ;
2759+ }
26282760}
0 commit comments