@@ -12,6 +12,7 @@ import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
1212import 'package:analyzer/src/context/packages.dart' ;
1313import 'package:analyzer/src/dart/analysis/analysis_options.dart' ;
1414import 'package:analyzer/src/dart/analysis/context_root.dart' ;
15+ import 'package:analyzer/src/lint/pub.dart' ;
1516import 'package:analyzer/src/task/options.dart' ;
1617import 'package:analyzer/src/util/file_paths.dart' as file_paths;
1718import 'package:analyzer/src/util/yaml.dart' ;
@@ -111,8 +112,69 @@ class ContextLocatorImpl {
111112 }
112113 }
113114
115+ var workspaceResolutionRootMap = < String , List <Folder >> {};
116+ var nonWorkspaceResolutionFolders = < Folder > [];
117+ _sortIncludedFoldersIntoWorkspaceResolutions (
118+ includedFolders,
119+ defaultOptionsFile,
120+ defaultPackagesFile,
121+ nonWorkspaceResolutionFolders,
122+ workspaceResolutionRootMap,
123+ );
124+
114125 var roots = < ContextRootImpl > [];
115- for (Folder folder in includedFolders) {
126+ for (var workspaceResolution in workspaceResolutionRootMap.entries) {
127+ var workspaceRootFolder = resourceProvider.getFolder (
128+ workspaceResolution.key,
129+ );
130+ var location = _contextRootLocation (
131+ workspaceRootFolder,
132+ defaultOptionsFile: defaultOptionsFile,
133+ defaultPackagesFile: defaultPackagesFile,
134+ defaultRootFolder: () => workspaceRootFolder,
135+ );
136+
137+ ContextRootImpl root = _createContextRoot (
138+ roots,
139+ rootFolder: workspaceRootFolder,
140+ workspace: location.workspace,
141+ optionsFile: location.optionsFile,
142+ packagesFile: location.packagesFile,
143+ );
144+
145+ var rootEnabledLegacyPlugins = _getEnabledLegacyPlugins (
146+ location.workspace,
147+ location.optionsFile,
148+ );
149+
150+ Set <String > visited = {};
151+ bool usedRoot = false ;
152+
153+ for (var folder in workspaceResolution.value) {
154+ if (! root.isAnalyzed (folder.path)) {
155+ root.included.add (folder);
156+ }
157+
158+ usedRoot | = _createContextRoots (
159+ roots,
160+ visited,
161+ folder,
162+ excludedFolders,
163+ root,
164+ rootEnabledLegacyPlugins,
165+ root.excludedGlobs,
166+ defaultOptionsFile,
167+ defaultPackagesFile,
168+ );
169+ }
170+ if (! usedRoot) {
171+ // If all included folders under this workspace resolution ended up
172+ // creating new contexts remove the (not used) root.
173+ roots.remove (root);
174+ }
175+ }
176+
177+ for (Folder folder in nonWorkspaceResolutionFolders) {
116178 var location = _contextRootLocation (
117179 folder,
118180 defaultOptionsFile: defaultOptionsFile,
@@ -315,7 +377,10 @@ class ContextLocatorImpl {
315377 /// For each directory within the given [folder] that is neither in the list
316378 /// of [excludedFolders] nor excluded by the [excludedGlobs] , recursively
317379 /// search for nested context roots.
318- void _createContextRoots (
380+ ///
381+ /// Returns true if the folder was contained in the root and did not create a
382+ /// new root, false if it did create a new root.
383+ bool _createContextRoots (
319384 List <ContextRoot > roots,
320385 Set <String > visited,
321386 Folder folder,
@@ -353,6 +418,8 @@ class ContextLocatorImpl {
353418 localEnabledPlugins,
354419 );
355420
421+ bool usedThisRoot = true ;
422+
356423 // Create a context root for the given [folder] if a packages or build file
357424 // is locally specified, or the set of enabled legacy plugins changed.
358425 if (pluginsDiffer || localPackagesFile != null || buildGnFile != null ) {
@@ -391,6 +458,7 @@ class ContextLocatorImpl {
391458 containingRootEnabledLegacyPlugins = localEnabledPlugins;
392459 excludedGlobs = _getExcludedGlobs (root.optionsFile, workspace);
393460 root.excludedGlobs = excludedGlobs;
461+ usedThisRoot = false ;
394462 }
395463
396464 if (localOptionsFile != null ) {
@@ -414,6 +482,8 @@ class ContextLocatorImpl {
414482 optionsFile,
415483 packagesFile,
416484 );
485+
486+ return usedThisRoot;
417487 }
418488
419489 /// For each directory within the given [folder] that is neither in the list
@@ -647,6 +717,47 @@ class ContextLocatorImpl {
647717 return null ;
648718 }
649719
720+ /// Load the `workspace` paths from the pubspec file in the given [root] .
721+ ///
722+ /// From https://dart.dev/tools/pub/workspaces a root folder pubspec file will
723+ /// look like this:
724+ ///
725+ /// ```
726+ /// name: _
727+ /// publish_to: none
728+ /// environment:
729+ /// sdk: ^3.6.0
730+ /// workspace:
731+ /// - packages/helper
732+ /// - packages/client_package
733+ /// - packages/server_package
734+ /// ```
735+ ///
736+ /// This loads the paths from the `workspace` entry and return them as
737+ /// Folders if they exist as folders in the filesystem.
738+ Set <Folder > _loadWorkspaceDetailsFromPubspec (String root) {
739+ var result = < Folder > {};
740+ var rootFolder = resourceProvider.getFolder (root);
741+ var rootPubspecFile = rootFolder.getChildAssumingFile (
742+ file_paths.pubspecYaml,
743+ );
744+ if (rootPubspecFile.exists) {
745+ var rootPubspec = Pubspec .parse (rootPubspecFile.readAsStringSync ());
746+ var workspace = rootPubspec.workspace;
747+ if (workspace != null ) {
748+ for (var entry in workspace) {
749+ if (entry.text case var relativePath? ) {
750+ var child = rootFolder.getChild (relativePath);
751+ if (child.exists && child is Folder ) {
752+ result.add (child);
753+ }
754+ }
755+ }
756+ }
757+ }
758+ return result;
759+ }
760+
650761 /// Add to the given lists of [folders] and [files] all of the resources in
651762 /// the given list of [paths] that exist and are not contained within one of
652763 /// the folders.
@@ -667,6 +778,65 @@ class ContextLocatorImpl {
667778 }
668779 }
669780
781+ /// Sorts [includedFolders] into either pub workspace resolution or not.
782+ ///
783+ /// For each [Folder] in [includedFolders] sort into either
784+ /// [nonWorkspaceResolutionFolders] or [workspaceResolutionRootMap] depending
785+ /// on `pubspec.yaml` specifications.
786+ ///
787+ /// Folders with `pubspec.yaml` files with a `resolution: workspace` setting
788+ /// that matches a root-folders `pubspec.yaml` files `workspace` list is
789+ /// sorted into the [workspaceResolutionRootMap] map. Other folders end up in
790+ /// [nonWorkspaceResolutionFolders] .
791+ void _sortIncludedFoldersIntoWorkspaceResolutions (
792+ List <Folder > includedFolders,
793+ File ? defaultOptionsFile,
794+ File ? defaultPackagesFile,
795+ List <Folder > nonWorkspaceResolutionFolders,
796+ Map <String , List <Folder >> workspaceResolutionRootMap,
797+ ) {
798+ var rootWorkspaceSpecification = < String , Set <Folder >> {};
799+ for (Folder folder in includedFolders) {
800+ var location = _contextRootLocation (
801+ folder,
802+ defaultOptionsFile: defaultOptionsFile,
803+ defaultPackagesFile: defaultPackagesFile,
804+ defaultRootFolder: () => folder,
805+ );
806+
807+ var addedToWorkspace = false ;
808+
809+ if (folder.path == location.workspace.root) {
810+ // If opening the root don't try to do anything special.
811+ var known = rootWorkspaceSpecification[location.workspace.root] ?? = {};
812+ known.clear ();
813+ nonWorkspaceResolutionFolders.addAll (
814+ workspaceResolutionRootMap[location.workspace.root] ?? [],
815+ );
816+ } else {
817+ var pubspecFile = folder.getChildAssumingFile (file_paths.pubspecYaml);
818+ if (pubspecFile.exists) {
819+ var pubspec = Pubspec .parse (pubspecFile.readAsStringSync ());
820+ var resolution = pubspec.resolution;
821+ if (resolution != null && resolution.value.text == 'workspace' ) {
822+ var known =
823+ rootWorkspaceSpecification[location.workspace.root] ?? =
824+ _loadWorkspaceDetailsFromPubspec (location.workspace.root);
825+ if (known.contains (folder)) {
826+ (workspaceResolutionRootMap[location.workspace.root] ?? = []).add (
827+ folder,
828+ );
829+ addedToWorkspace = true ;
830+ }
831+ }
832+ }
833+ }
834+ if (! addedToWorkspace) {
835+ nonWorkspaceResolutionFolders.add (folder);
836+ }
837+ }
838+ }
839+
670840 /// Return a list of paths that contains all of the unique elements from the
671841 /// given list of [paths] , sorted such that shorter paths are first.
672842 List <String > _uniqueSortedPaths (List <String > paths) {
0 commit comments