@@ -283,6 +283,19 @@ impl ProjectFolders {
283283 }
284284 }
285285
286+ // Collect workspace roots not already covered by a local PackageRoot
287+ // (e.g. virtual workspaces where no package lives at the workspace root).
288+ // We need these to load workspace-root rust-analyzer.toml into a local source root.
289+ let uncovered_ws_roots: Vec < AbsPathBuf > = workspaces
290+ . iter ( )
291+ . filter_map ( |ws| {
292+ let ws_root = ws. workspace_root ( ) . to_path_buf ( ) ;
293+ let dominated =
294+ roots. iter ( ) . any ( |root| root. is_local && root. include . contains ( & ws_root) ) ;
295+ ( !dominated) . then_some ( ws_root)
296+ } )
297+ . collect ( ) ;
298+
286299 for root in roots. into_iter ( ) . filter ( |it| !it. include . is_empty ( ) ) {
287300 let file_set_roots: Vec < VfsPath > =
288301 root. include . iter ( ) . cloned ( ) . map ( VfsPath :: from) . collect ( ) ;
@@ -335,6 +348,20 @@ impl ProjectFolders {
335348 }
336349 }
337350
351+ // For virtual workspaces, the workspace root has no local PackageRoot, so
352+ // rust-analyzer.toml there would fall into a library source root and be
353+ // ignored. Load it explicitly via Entry::Files and register the workspace
354+ // root as a local file-set root so the file is classified as local.
355+ for ws_root in & uncovered_ws_roots {
356+ let ratoml_path = ws_root. join ( "rust-analyzer.toml" ) ;
357+ let file_set_roots = vec ! [ VfsPath :: from( ws_root. clone( ) ) ] ;
358+ let entry = vfs:: loader:: Entry :: Files ( vec ! [ ratoml_path] ) ;
359+ res. watch . push ( res. load . len ( ) ) ;
360+ res. load . push ( entry) ;
361+ local_filesets. push ( fsc. len ( ) as u64 ) ;
362+ fsc. add_file_set ( file_set_roots) ;
363+ }
364+
338365 if let Some ( user_config_path) = user_config_dir_path {
339366 let ratoml_path = {
340367 let mut p = user_config_path. to_path_buf ( ) ;
0 commit comments