Chromium Code Reviews| 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 fc93e96ec3b5d302ee074d6c356ed0a982353e09..269f221718aeb5876d325ed51b556593fc9c1c86 100644 |
| --- a/pkg/analysis_server/lib/src/context_manager.dart |
| +++ b/pkg/analysis_server/lib/src/context_manager.dart |
| @@ -6,6 +6,7 @@ library context.directory.manager; |
| import 'dart:async'; |
| import 'dart:collection'; |
| +import 'dart:convert'; |
| import 'dart:core' hide Resource; |
| import 'package:analysis_server/src/analysis_server.dart'; |
| @@ -18,6 +19,9 @@ import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/java_io.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/source_io.dart'; |
| +import 'package:package_config/packages.dart'; |
| +import 'package:package_config/packages_file.dart' as pkgfile show parse; |
| +import 'package:package_config/src/packages_impl.dart' show MapPackages; |
| import 'package:path/path.dart' as pathos; |
| import 'package:watcher/watcher.dart'; |
| @@ -42,6 +46,11 @@ abstract class AbstractContextManager implements ContextManager { |
| static const String PUBSPEC_NAME = 'pubspec.yaml'; |
| /** |
| + * File name of package spec files. |
| + */ |
| + static const String PACKAGE_SPEC_NAME = '.packages'; |
| + |
| + /** |
| * [_ContextInfo] object for each included directory in the most |
| * recent successful call to [setRoots]. |
| */ |
| @@ -106,7 +115,8 @@ abstract class AbstractContextManager implements ContextManager { |
| /** |
| * Create and return a new analysis context. |
| */ |
| - AnalysisContext addContext(Folder folder, UriResolver packageUriResolver); |
| + AnalysisContext addContext( |
| + Folder folder, UriResolver packageUriResolver, Packages packages); |
| /** |
| * Called when the set of files associated with a context have changed (or |
| @@ -298,7 +308,7 @@ abstract class AbstractContextManager implements ContextManager { |
| * Called when the package map for a context has changed. |
| */ |
| void updateContextPackageUriResolver( |
| - Folder contextFolder, UriResolver packageUriResolver); |
| + Folder contextFolder, UriResolver packageUriResolver, Packages packages); |
| /** |
| * Resursively adds all Dart and HTML files to the [changeSet]. |
| @@ -391,6 +401,20 @@ abstract class AbstractContextManager implements ContextManager { |
| info.dependencySubscriptions.clear(); |
| } |
| + void _checkForPackagespecUpdate( |
| + String path, _ContextInfo info, Folder folder) { |
| + // Check to see if this is the .packages file for this context and if so, |
| + // update the context's source factory. |
| + if (pathContext.basename(path) == PACKAGE_SPEC_NAME && |
| + info.isPathToPackageDescription(path)) { |
| + File packagespec = resourceProvider.getFile(path); |
| + if (packagespec.exists) { |
| + Packages packages = _readPackagespec(packagespec); |
| + updateContextPackageUriResolver(folder, null, packages); |
| + } |
| + } |
| + } |
| + |
| /** |
| * Compute the appropriate package URI resolver for [folder], and store |
| * dependency information in [info]. Return `null` if no package map can |
| @@ -479,8 +503,20 @@ abstract class AbstractContextManager implements ContextManager { |
| _handleWatchEvent(folder, info, event); |
| }); |
| try { |
| - UriResolver packageUriResolver = _computePackageUriResolver(folder, info); |
| - info.context = addContext(folder, packageUriResolver); |
| + Packages packages; |
| + UriResolver packageUriResolver; |
| + |
| + // Try .packages first. |
| + if (pathos.basename(pubspecFile.path) == PACKAGE_SPEC_NAME) { |
| + packages = _readPackagespec(pubspecFile); |
| + } |
| + |
| + // Next resort to a package uri resolver. |
| + if (packages == null) { |
| + packageUriResolver = _computePackageUriResolver(folder, info); |
| + } |
| + |
| + info.context = addContext(folder, packageUriResolver, packages); |
| info.context.name = folder.path; |
| } catch (_) { |
| info.changeSubscription.cancel(); |
| @@ -496,13 +532,13 @@ abstract class AbstractContextManager implements ContextManager { |
| * created for them and excluded from the context associated with the |
| * [folder]. |
| * |
| - * If [withPubspecOnly] is `true`, a context will be created only if there |
| - * is a 'pubspec.yaml' file in the [folder]. |
| + * If [withPackageSpecOnly] is `true`, a context will be created only if there |
| + * is a 'pubspec.yaml' or '.packages' file in the [folder]. |
| * |
| - * Returns create pubspec-based contexts. |
| + * Returns created contexts. |
| */ |
| - List<_ContextInfo> _createContexts(Folder folder, bool withPubspecOnly) { |
| - // try to find subfolders with pubspec files |
| + List<_ContextInfo> _createContexts(Folder folder, bool withPackageSpecOnly) { |
| + // Try to find subfolders with pubspecs or .packages files. |
| List<_ContextInfo> children = <_ContextInfo>[]; |
| try { |
| for (Resource child in folder.getChildren()) { |
| @@ -514,20 +550,27 @@ abstract class AbstractContextManager implements ContextManager { |
| // The directory either doesn't exist or cannot be read. Either way, there |
| // are no subfolders that need to be added. |
| } |
| - // check whether there is a pubspec in the folder |
| - File pubspecFile = folder.getChild(PUBSPEC_NAME); |
| - if (pubspecFile.exists) { |
| + |
| + // Start by looking for .packages. |
| + File packageSpec = folder.getChild(PACKAGE_SPEC_NAME); |
| + |
| + // Fall back to looking for a pubspec. |
| + if (!packageSpec.exists) { |
| + packageSpec = folder.getChild(PUBSPEC_NAME); |
| + } |
| + |
| + if (packageSpec.exists) { |
| return <_ContextInfo>[ |
| - _createContextWithSources(folder, pubspecFile, children) |
| + _createContextWithSources(folder, packageSpec, children) |
| ]; |
| } |
| - // no pubspec, done |
| - if (withPubspecOnly) { |
| + // No packagespec? Done. |
| + if (withPackageSpecOnly) { |
| return children; |
| } |
| - // OK, create a context without a pubspec |
| + // OK, create a context without a packagespec. |
| return <_ContextInfo>[ |
| - _createContextWithSources(folder, pubspecFile, children) |
| + _createContextWithSources(folder, packageSpec, children) |
| ]; |
| } |
| @@ -613,11 +656,34 @@ abstract class AbstractContextManager implements ContextManager { |
| return; |
| } |
| Resource resource = resourceProvider.getResource(path); |
| - // pubspec was added in a sub-folder, extract a new context |
| - if (_isPubspec(path) && info.isRoot && !info.isPubspec(path)) { |
| - _extractContext(info, resource); |
| - return; |
| + |
| + String directoryPath = pathContext.dirname(path); |
| + |
| + // Check to see if we need to create a new context. |
| + if (info.isRoot) { |
| + |
| + // Only create a new context if this is not the same directory |
| + // described by our info object. |
| + if (info.folder.path != directoryPath) { |
|
Brian Wilkerson
2015/07/17 18:02:57
If a .packages file is added to the context's root
pquitslund
2015/07/17 19:30:02
Actually, I think this is handled in _createContex
|
| + if (pathContext.basename(path) == PUBSPEC_NAME) { |
| + // Check for a sibling .packages file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, '.packages')).exists) { |
|
Brian Wilkerson
2015/07/17 18:02:58
nit: use PACKAGE_SPEC_NAME (here and below)
pquitslund
2015/07/17 19:30:02
Done.
|
| + _extractContext(info, resource); |
| + return; |
| + } |
| + } |
| + if (pathContext.basename(path) == PACKAGE_SPEC_NAME) { |
| + // Check for a sibling pubspec.yaml file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, 'pubspec.yaml')).exists) { |
|
Brian Wilkerson
2015/07/17 18:02:57
nit: use PUBSPEC_NAME (here and below)
pquitslund
2015/07/17 19:30:02
Done.
|
| + _extractContext(info, resource); |
| + return; |
| + } |
| + } |
| + } |
| } |
| + |
| // If the file went away and was replaced by a folder before we |
| // had a chance to process the event, resource might be a Folder. In |
| // that case don't add it. |
| @@ -633,11 +699,34 @@ abstract class AbstractContextManager implements ContextManager { |
| } |
| break; |
| case ChangeType.REMOVE: |
| - // pubspec was removed, merge the context into its parent |
| - if (info.isPubspec(path) && !info.isRoot) { |
| - _mergeContext(info); |
| - return; |
| + |
| + // If package spec info is removed, check to see if we can merge contexts. |
| + // Note that it's important to verify that there is NEITHER a .packages nor a |
| + // lingering pubspec.yaml before merging. |
| + if (!info.isRoot) { |
| + String directoryPath = pathContext.dirname(path); |
| + |
| + // Only merge if this is the same directory described by our info object. |
| + if (info.folder.path == directoryPath) { |
| + if (pathContext.basename(path) == PUBSPEC_NAME) { |
| + // Check for a sibling .packages file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, '.packages')).exists) { |
| + _mergeContext(info); |
| + return; |
| + } |
| + } |
| + if (pathContext.basename(path) == PACKAGE_SPEC_NAME) { |
| + // Check for a sibling pubspec.yaml file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, 'pubspec.yaml')).exists) { |
| + _mergeContext(info); |
| + return; |
| + } |
| + } |
| + } |
| } |
| + |
| List<Source> sources = info.context.getSourcesWithFullName(path); |
| if (!sources.isEmpty) { |
| ChangeSet changeSet = new ChangeSet(); |
| @@ -660,6 +749,9 @@ abstract class AbstractContextManager implements ContextManager { |
| break; |
| } |
| + //TODO(pquitslund): find the right place for this |
| + _checkForPackagespecUpdate(path, info, folder); |
|
pquitslund
2015/07/17 19:30:02
Here's where we test for .packages changes.
|
| + |
| if (info.packageMapInfo != null && |
| info.packageMapInfo.isChangedDependency(path, resourceProvider)) { |
| _recomputePackageUriResolver(info); |
| @@ -669,9 +761,7 @@ abstract class AbstractContextManager implements ContextManager { |
| /** |
| * Returns `true` if the given [path] is excluded by [excludedPaths]. |
| */ |
| - bool _isExcluded(String path) { |
| - return _isExcludedBy(excludedPaths, path); |
| - } |
| + bool _isExcluded(String path) => _isExcludedBy(excludedPaths, path); |
| /** |
| * Returns `true` if the given [path] is excluded by [excludedPaths]. |
| @@ -696,13 +786,6 @@ abstract class AbstractContextManager implements ContextManager { |
| } |
| /** |
| - * Returns `true` if the given absolute [path] is a pubspec file. |
| - */ |
| - bool _isPubspec(String path) { |
| - return pathContext.basename(path) == PUBSPEC_NAME; |
| - } |
| - |
| - /** |
| * Merges [info] context into its parent. |
| */ |
| void _mergeContext(_ContextInfo info) { |
| @@ -721,6 +804,14 @@ abstract class AbstractContextManager implements ContextManager { |
| } |
| } |
| + Packages _readPackagespec(File specFile) { |
| + //TODO(pquitslund): error-handling? |
|
Brian Wilkerson
2015/07/17 18:02:57
Yes. If we cannot read the file, I think we should
pquitslund
2015/07/17 19:30:02
Cool. Added a try/catch and a TODO to create an e
|
| + String contents = specFile.readAsStringSync(); |
| + Map<String, Uri> map = |
| + pkgfile.parse(UTF8.encode(contents), new Uri.file(specFile.path)); |
| + return new MapPackages(map); |
| + } |
| + |
| /** |
| * Recompute the package URI resolver for the context described by [info], |
| * and update the client appropriately. |
| @@ -732,7 +823,7 @@ abstract class AbstractContextManager implements ContextManager { |
| // "pub list" is in progress is just going to get thrown away anyhow. |
| UriResolver packageUriResolver = |
| _computePackageUriResolver(info.folder, info); |
| - updateContextPackageUriResolver(info.folder, packageUriResolver); |
| + updateContextPackageUriResolver(info.folder, packageUriResolver, null); |
| } |
| /** |
| @@ -875,9 +966,9 @@ class _ContextInfo { |
| _ContextInfo parent; |
| /** |
| - * The `pubspec.yaml` file path for this context. |
| + * The package description file path for this context. |
| */ |
| - String pubspecPath; |
| + String packageDescriptionPath; |
| /** |
| * Stream subscription we are using to watch the context's directory for |
| @@ -911,7 +1002,7 @@ class _ContextInfo { |
| OptimizingPubPackageMapInfo packageMapInfo; |
| _ContextInfo(this.folder, File pubspecFile, this.children, this.packageRoot) { |
| - pubspecPath = pubspecFile.path; |
| + packageDescriptionPath = pubspecFile.path; |
| for (_ContextInfo child in children) { |
| child.parent = this; |
| } |
| @@ -934,14 +1025,12 @@ class _ContextInfo { |
| /** |
| * Returns `true` if [resource] is excluded, as it is in one of the children. |
| */ |
| - bool excludesResource(Resource resource) { |
| - return excludes(resource.path); |
| - } |
| + bool excludesResource(Resource resource) => excludes(resource.path); |
| /** |
| - * Returns `true` if [path] is the pubspec file of this context. |
| + * Returns `true` if [path] is the package description file for this context |
| + * (pubspec.yaml or .packages). |
| */ |
| - bool isPubspec(String path) { |
| - return path == pubspecPath; |
| - } |
| + bool isPathToPackageDescription(String path) => |
| + path == packageDescriptionPath; |
| } |