@@ -296,11 +296,25 @@ impl GlobalState {
296
296
let workspaces =
297
297
workspaces. iter ( ) . filter_map ( |res| res. as_ref ( ) . ok ( ) . cloned ( ) ) . collect :: < Vec < _ > > ( ) ;
298
298
299
- let same_workspaces = workspaces. len ( ) == self . workspaces . len ( )
300
- && workspaces
301
- . iter ( )
302
- . zip ( self . workspaces . iter ( ) )
303
- . all ( |( l, r) | l. eq_ignore_build_data ( r) ) ;
299
+ // `different_workspaces` is used to spawn a new proc macro server for a newly-added
300
+ // rust workspace (most commonly sourced from a `rust-project.json`). While the algorithm
301
+ // to find the new workspaces is quadratic, we generally expect that the number of total
302
+ // workspaces to remain in the low single digits. the `cloned_workspace` is needed for borrowck
303
+ // reasons.
304
+ let cloned_workspaces = workspaces. clone ( ) ;
305
+ let different_workspaces = cloned_workspaces
306
+ . iter ( )
307
+ . filter ( |ws| {
308
+ !self
309
+ . workspaces
310
+ . iter ( )
311
+ . find ( |existing_ws| ws. eq_ignore_build_data ( & existing_ws) )
312
+ . is_some ( )
313
+ } )
314
+ . collect :: < Vec < _ > > ( ) ;
315
+ let same_workspaces = different_workspaces. is_empty ( ) ;
316
+
317
+ tracing:: debug!( current_workspaces = ?self . workspaces, new_workspaces = ?workspaces, ?same_workspaces, "comparing workspaces" ) ;
304
318
305
319
if same_workspaces {
306
320
let ( workspaces, build_scripts) = self . fetch_build_data_queue . last_op_result ( ) ;
@@ -370,11 +384,10 @@ impl GlobalState {
370
384
let files_config = self . config . files ( ) ;
371
385
let project_folders = ProjectFolders :: new ( & self . workspaces , & files_config. exclude ) ;
372
386
373
- if self . proc_macro_clients . is_empty ( ) {
387
+ if self . proc_macro_clients . is_empty ( ) || !different_workspaces . is_empty ( ) {
374
388
if let Some ( ( path, path_manually_set) ) = self . config . proc_macro_srv ( ) {
375
389
tracing:: info!( "Spawning proc-macro servers" ) ;
376
- self . proc_macro_clients = self
377
- . workspaces
390
+ self . proc_macro_clients = different_workspaces
378
391
. iter ( )
379
392
. map ( |ws| {
380
393
let ( path, args) : ( _ , & [ _ ] ) = if path_manually_set {
@@ -448,7 +461,19 @@ impl GlobalState {
448
461
} ;
449
462
let mut change = Change :: new ( ) ;
450
463
451
- if same_workspaces {
464
+ // `self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths)` is only called in
465
+ // when `switch_workspaces` is called _without_ changing workspaces. This typically occurs
466
+ // when build scripts have finishing building, but when rust-analyzer is used with a
467
+ // rust-project.json, the build scripts have already been built by the external build system
468
+ // that generated the `rust-project.json`.
469
+
470
+ // Therefore, in order to allow _new_ workspaces added via rust-project.json (e.g., after
471
+ // a workspace was already added), we check whether this is the same workspace _or_
472
+ // if any of the new workspaces is a `rust-project.json`.
473
+ //
474
+ // The else branch is used to provide better diagnostics to users while procedural macros
475
+ // are still being built.
476
+ if same_workspaces || different_workspaces. iter ( ) . any ( |ws| ws. is_json ( ) ) {
452
477
if self . config . expand_proc_macros ( ) {
453
478
self . fetch_proc_macros_queue . request_op ( cause, proc_macro_paths) ;
454
479
}
0 commit comments