Index: pkg/analysis_server/lib/src/context_manager.dart |
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart |
index c12453c131fb36493585150d86432f388be227da..d4cb5cee9a35be98423b956748714cb7a1592296 100644 |
--- a/pkg/analysis_server/lib/src/context_manager.dart |
+++ b/pkg/analysis_server/lib/src/context_manager.dart |
@@ -244,10 +244,10 @@ abstract class ContextManager { |
*/ |
abstract class ContextManagerCallbacks { |
/** |
- * Create and return a new analysis context. |
+ * Create and return a new analysis context, allowing [disposition] to govern |
+ * details of how the context is to be created. |
*/ |
- AnalysisContext addContext( |
- Folder folder, UriResolver packageUriResolver, Packages packages); |
+ AnalysisContext addContext(Folder folder, FolderDisposition disposition); |
/** |
* Called when the set of files associated with a context have changed (or |
@@ -284,10 +284,10 @@ abstract class ContextManagerCallbacks { |
bool shouldFileBeAnalyzed(File file); |
/** |
- * Called when the package map for a context has changed. |
+ * Called when the disposition for a context has changed. |
*/ |
void updateContextPackageUriResolver( |
- Folder contextFolder, UriResolver packageUriResolver, Packages packages); |
+ Folder contextFolder, FolderDisposition disposition); |
} |
/** |
@@ -533,7 +533,7 @@ class ContextManagerImpl implements ContextManager { |
String newPackageRoot = normalizedPackageRoots[info.folder.path]; |
if (info.packageRoot != newPackageRoot) { |
info.packageRoot = newPackageRoot; |
- _recomputePackageUriResolver(info); |
+ _recomputeFolderDisposition(info); |
} |
} |
// create new contexts |
@@ -677,7 +677,8 @@ class ContextManagerImpl implements ContextManager { |
if (packagespec.exists) { |
Packages packages = _readPackagespec(packagespec); |
if (packages != null) { |
- callbacks.updateContextPackageUriResolver(folder, null, packages); |
+ callbacks.updateContextPackageUriResolver( |
+ folder, new PackagesFileDisposition(packages)); |
} |
} |
} |
@@ -707,11 +708,10 @@ class ContextManagerImpl implements ContextManager { |
} |
/** |
- * Compute the appropriate package URI resolver for [folder], and store |
- * dependency information in [info]. Return `null` if no package map can |
- * be computed. |
+ * Compute the appropriate [FolderDisposition] for [folder], and store |
+ * dependency information in [info]. |
*/ |
- UriResolver _computePackageUriResolver(Folder folder, ContextInfo info) { |
+ FolderDisposition _computeFolderDisposition(Folder folder, ContextInfo info) { |
_cancelDependencySubscriptions(info); |
if (info.packageRoot != null) { |
info.packageMapInfo = null; |
@@ -736,20 +736,20 @@ class ContextManagerImpl implements ContextManager { |
packageMap[file.getName()] = <Folder>[res]; |
} |
} |
- return new PackageMapUriResolver(resourceProvider, packageMap); |
+ return new PackageMapDisposition(packageMap); |
} |
// The package root does not exist (or is not a folder). Since |
// [setRoots] ignores any package roots that don't exist (or aren't |
// folders), the only way we should be able to get here is due to a race |
// condition. In any case, the package root folder is gone, so we can't |
// resolve packages. |
- return null; |
+ return new NoPackageFolderDisposition(); |
} else { |
callbacks.beginComputePackageMap(); |
if (packageResolverProvider != null) { |
UriResolver resolver = packageResolverProvider(folder); |
if (resolver != null) { |
- return resolver; |
+ return new CustomPackageResolverDisposition(resolver); |
} |
} |
OptimizingPubPackageMapInfo packageMapInfo; |
@@ -766,7 +766,7 @@ class ContextManagerImpl implements ContextManager { |
if (info.packageMapInfo != null && |
info.packageMapInfo.isChangedDependency( |
dependencyPath, resourceProvider)) { |
- _recomputePackageUriResolver(info); |
+ _recomputeFolderDisposition(info); |
} |
}, onError: (error, StackTrace stackTrace) { |
// Gracefully degrade if file is or becomes unwatchable |
@@ -779,12 +779,9 @@ class ContextManagerImpl implements ContextManager { |
} |
info.packageMapInfo = packageMapInfo; |
if (packageMapInfo.packageMap == null) { |
- return null; |
+ return new NoPackageFolderDisposition(); |
} |
- return new PackageMapUriResolver( |
- resourceProvider, packageMapInfo.packageMap); |
- // TODO(paulberry): if any of the dependencies is outside of [folder], |
- // we'll need to watch their parent folders as well. |
+ return new PackageMapDisposition(packageMapInfo.packageMap); |
} |
} |
@@ -802,22 +799,22 @@ class ContextManagerImpl implements ContextManager { |
_handleWatchEvent(folder, info, event); |
}); |
try { |
- Packages packages; |
- UriResolver packageUriResolver; |
+ FolderDisposition disposition; |
if (ENABLE_PACKAGESPEC_SUPPORT) { |
// Try .packages first. |
if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) { |
- packages = _readPackagespec(packagespecFile); |
+ Packages packages = _readPackagespec(packagespecFile); |
+ disposition = new PackagesFileDisposition(packages); |
} |
} |
// Next resort to a package uri resolver. |
- if (packages == null) { |
- packageUriResolver = _computePackageUriResolver(folder, info); |
+ if (disposition == null) { |
+ disposition = _computeFolderDisposition(folder, info); |
} |
- info.context = callbacks.addContext(folder, packageUriResolver, packages); |
+ info.context = callbacks.addContext(folder, disposition); |
info.context.name = folder.path; |
} catch (_) { |
info.changeSubscription.cancel(); |
@@ -1093,7 +1090,7 @@ class ContextManagerImpl implements ContextManager { |
if (info.packageMapInfo != null && |
info.packageMapInfo.isChangedDependency(path, resourceProvider)) { |
- _recomputePackageUriResolver(info); |
+ _recomputeFolderDisposition(info); |
} |
} |
@@ -1161,18 +1158,17 @@ class ContextManagerImpl implements ContextManager { |
} |
/** |
- * Recompute the package URI resolver for the context described by [info], |
+ * Recompute the [FolderDisposition] for the context described by [info], |
* and update the client appropriately. |
*/ |
- void _recomputePackageUriResolver(ContextInfo info) { |
+ void _recomputeFolderDisposition(ContextInfo info) { |
// TODO(paulberry): when computePackageMap is changed into an |
// asynchronous API call, we'll want to suspend analysis for this context |
// while we're rerunning "pub list", since any analysis we complete while |
// "pub list" is in progress is just going to get thrown away anyhow. |
- UriResolver packageUriResolver = |
- _computePackageUriResolver(info.folder, info); |
- callbacks.updateContextPackageUriResolver( |
- info.folder, packageUriResolver, null); |
+ FolderDisposition disposition = |
+ _computeFolderDisposition(info.folder, info); |
+ callbacks.updateContextPackageUriResolver(info.folder, disposition); |
} |
/** |
@@ -1222,3 +1218,106 @@ class ContextsChangedEvent { |
this.changed: AnalysisContext.EMPTY_LIST, |
this.removed: AnalysisContext.EMPTY_LIST}); |
} |
+ |
+/** |
+ * Concrete [FolderDisposition] object indicating that the context for a given |
+ * folder should resolve package URIs using a custom URI resolver. |
+ */ |
+class CustomPackageResolverDisposition extends FolderDisposition { |
+ /** |
+ * The [UriResolver] that should be used to resolve package URIs. |
+ */ |
+ UriResolver resolver; |
+ |
+ CustomPackageResolverDisposition(this.resolver); |
+ |
+ @override |
+ Packages get packages => null; |
+ |
+ @override |
+ Iterable<UriResolver> createPackageUriResolvers( |
+ ResourceProvider resourceProvider) => <UriResolver>[resolver]; |
+} |
+ |
+/** |
+ * An instance of the class [FolderDisposition] represents the information |
+ * gathered by the [ContextManagerImpl] to determine how to create an |
+ * [AnalysisContext] for a given folder. |
+ * |
+ * Note: [ContextManagerImpl] may use equality testing and hash codes to |
+ * determine when two folders should share the same context, so derived classes |
+ * may need to override operator== and hashCode() if object identity is |
+ * insufficient. |
+ * |
+ * TODO(paulberry): consider adding a flag to indicate that it is not necessary |
+ * to recurse into the given folder looking for additional contexts to create |
+ * or files to analyze (this could help avoid unnecessarily weighing down the |
+ * system with file watchers). |
+ */ |
+abstract class FolderDisposition { |
+ /** |
+ * If contexts governed by this [FolderDisposition] should resolve packages |
+ * using the ".packages" file mechanism (DEP 5), retrieve the [Packages] |
+ * object that resulted from parsing the ".packages" file. |
+ */ |
+ Packages get packages; |
+ |
+ /** |
+ * Create all the [UriResolver]s which should be used to resolve packages in |
+ * contexts governed by this [FolderDisposition]. |
+ * |
+ * [resourceProvider] is provided since it is needed to construct most |
+ * [UriResolver]s. |
+ */ |
+ Iterable<UriResolver> createPackageUriResolvers( |
+ ResourceProvider resourceProvider); |
+} |
+ |
+/** |
+ * Concrete [FolderDisposition] object indicating that the context for a given |
+ * folder should not resolve "package:" URIs at all. |
+ * |
+ * TODO(paulberry): consider making this a singleton object (which would cause |
+ * all folders that don't resolve "package:" URIs to share the same context). |
+ */ |
+class NoPackageFolderDisposition extends FolderDisposition { |
+ @override |
+ Packages get packages => null; |
+ |
+ @override |
+ Iterable<UriResolver> createPackageUriResolvers( |
+ ResourceProvider resourceProvider) => const <UriResolver>[]; |
+} |
+ |
+/** |
+ * Concrete [FolderDisposition] object indicating that the context for a given |
+ * folder should resolve packages using a package map. |
+ */ |
+class PackageMapDisposition extends FolderDisposition { |
+ final Map<String, List<Folder>> packageMap; |
+ |
+ PackageMapDisposition(this.packageMap); |
+ |
+ @override |
+ Packages get packages => null; |
+ |
+ @override |
+ Iterable<UriResolver> createPackageUriResolvers( |
+ ResourceProvider resourceProvider) => |
+ <UriResolver>[new PackageMapUriResolver(resourceProvider, packageMap)]; |
+} |
+ |
+/** |
+ * Concrete [FolderDisposition] object indicating that the context for a given |
+ * folder should resolve packages using a ".packages" file. |
+ */ |
+class PackagesFileDisposition extends FolderDisposition { |
+ @override |
+ final Packages packages; |
+ |
+ PackagesFileDisposition(this.packages) {} |
+ |
+ @override |
+ Iterable<UriResolver> createPackageUriResolvers( |
+ ResourceProvider resourceProvider) => const <UriResolver>[]; |
+} |