Skip to content

Commit 0677fca

Browse files
jensjohaCommit Queue
authored andcommitted
[analyze] When opening several folders with "resolution: workspace" try to merge them by workspace root
Use-case: I have 20+ folders from the sdk that I have open (front_end, _fe_analyzer_shared, vm, analyzer, analysis_server etc) and before this CL that meant I had 20+ contexts, despite them having `resolution: workspace` and being specified in the roots pubspec.yaml under `workspace`. Opening, for instance, `pkg` directly would successfully give me only 1 context, but also include lots of stuff I don't want to clutter neither my screen nor my mind. This CL merges such folders as possible meaning I with this CL only have 1 context. * If not using the workspace resolution this should change nothing. * If opening only the root of a workspace this should change nothing. * If opening a sub folder that itself isn't a package (say `pkg` in the sdk) this should change nothing. Change-Id: Ia38fa6636b02fa95ed274ebf72d424cae7d4da66 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/434802 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 20dc417 commit 0677fca

File tree

3 files changed

+402
-2
lines changed

3 files changed

+402
-2
lines changed

pkg/analyzer/lib/src/dart/analysis/context_locator.dart

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
1212
import 'package:analyzer/src/context/packages.dart';
1313
import 'package:analyzer/src/dart/analysis/analysis_options.dart';
1414
import 'package:analyzer/src/dart/analysis/context_root.dart';
15+
import 'package:analyzer/src/lint/pub.dart';
1516
import 'package:analyzer/src/task/options.dart';
1617
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
1718
import '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) {

pkg/analyzer/lib/src/lint/pub.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,12 @@ abstract class Pubspec {
188188

189189
PubspecEntry? get repository;
190190

191+
PubspecEntry? get resolution;
192+
191193
PubspecEntry? get version;
192194

195+
PubspecNodeList? get workspace;
196+
193197
void accept(PubspecVisitor visitor);
194198
}
195199

@@ -226,6 +230,9 @@ class _Pubspec implements Pubspec {
226230
@override
227231
final PubspecNodeList? authors;
228232

233+
@override
234+
final PubspecNodeList? workspace;
235+
229236
@override
230237
final PubspecEntry? description;
231238

@@ -247,6 +254,9 @@ class _Pubspec implements Pubspec {
247254
@override
248255
final PubspecEntry? repository;
249256

257+
@override
258+
final PubspecEntry? resolution;
259+
250260
@override
251261
final PubspecEntry? version;
252262

@@ -266,13 +276,15 @@ class _Pubspec implements Pubspec {
266276

267277
PubspecEntry? author;
268278
PubspecNodeList? authors;
279+
PubspecNodeList? workspace;
269280
PubspecEntry? description;
270281
PubspecEntry? documentation;
271282
PubspecEnvironment? environment;
272283
PubspecEntry? homepage;
273284
PubspecEntry? issueTracker;
274285
PubspecEntry? name;
275286
PubspecEntry? repository;
287+
PubspecEntry? resolution;
276288
PubspecEntry? version;
277289
PubspecDependencyList? dependencies;
278290
PubspecDependencyList? devDependencies;
@@ -310,6 +322,10 @@ class _Pubspec implements Pubspec {
310322
environment = _processEnvironment(key, v, resourceProvider);
311323
case 'version':
312324
version = _processScalar(key, v, resourceProvider);
325+
case 'resolution':
326+
resolution = _processScalar(key, v, resourceProvider);
327+
case 'workspace':
328+
workspace = _processScalarList(key, v, resourceProvider);
313329
}
314330
});
315331

@@ -327,12 +343,15 @@ class _Pubspec implements Pubspec {
327343
dependencies: dependencies,
328344
devDependencies: devDependencies,
329345
dependencyOverrides: dependencyOverrides,
346+
resolution: resolution,
347+
workspace: workspace,
330348
);
331349
}
332350

333351
_Pubspec._({
334352
this.author,
335353
this.authors,
354+
this.workspace,
336355
this.description,
337356
this.documentation,
338357
this.environment,
@@ -344,6 +363,7 @@ class _Pubspec implements Pubspec {
344363
this.dependencies,
345364
this.devDependencies,
346365
this.dependencyOverrides,
366+
this.resolution,
347367
});
348368

349369
@override

0 commit comments

Comments
 (0)