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 241b9f16b9716373d7952893ceb73eff5f24a697..f4abb48744adcc5dd5f489e5f8f7ce7ac6b790ca 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'; |
| @@ -19,6 +20,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'; |
| @@ -27,6 +31,12 @@ import 'package:watcher/watcher.dart'; |
| * folders that should correspond to analysis contexts. |
| */ |
| abstract class AbstractContextManager implements ContextManager { |
| + |
| + /** |
| + * Temporary flag to hide WIP .packages support. |
|
Paul Berry
2015/07/17 20:49:13
It would be nice to mention the DEP number (DEP 5)
pquitslund
2015/07/20 20:50:22
Done.
|
| + */ |
| + static bool ENABLE_PACKAGESPEC_SUPPORT = false; |
| + |
| /** |
| * The name of the `lib` directory. |
| */ |
| @@ -43,6 +53,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]. |
| */ |
| @@ -107,7 +122,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 |
| @@ -164,16 +180,6 @@ abstract class AbstractContextManager implements ContextManager { |
| // Do nothing. |
| } |
| - // Sets the [ignorePatterns] for the context [folder]. |
| - void setIgnorePatternsForContext(Folder folder, List<String> ignorePatterns) { |
| - _ContextInfo info = _contexts[folder]; |
| - if (info == null) { |
| - return; |
| - } |
| - var pathFilter = info.pathFilter; |
| - pathFilter.setIgnorePatterns(ignorePatterns); |
| - } |
| - |
| @override |
| bool isInAnalysisRoot(String path) { |
| // check if excluded |
| @@ -215,6 +221,16 @@ abstract class AbstractContextManager implements ContextManager { |
| */ |
| void removeContext(Folder folder); |
| + /// Sets the [ignorePatterns] for the context [folder]. |
| + void setIgnorePatternsForContext(Folder folder, List<String> ignorePatterns) { |
| + _ContextInfo info = _contexts[folder]; |
| + if (info == null) { |
| + return; |
| + } |
| + var pathFilter = info.pathFilter; |
| + pathFilter.setIgnorePatterns(ignorePatterns); |
| + } |
| + |
| @override |
| void setRoots(List<String> includedPaths, List<String> excludedPaths, |
| Map<String, String> packageRoots) { |
| @@ -309,7 +325,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]. |
| @@ -406,6 +422,22 @@ 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); |
| + if (packages != null) { |
| + 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 |
| @@ -486,16 +518,30 @@ abstract class AbstractContextManager implements ContextManager { |
| * Create a new empty context associated with [folder]. |
| */ |
| _ContextInfo _createContext( |
| - Folder folder, File pubspecFile, List<_ContextInfo> children) { |
| + Folder folder, File packagespecFile, List<_ContextInfo> children) { |
| _ContextInfo info = new _ContextInfo( |
| - folder, pubspecFile, children, normalizedPackageRoots[folder.path]); |
| + folder, packagespecFile, children, normalizedPackageRoots[folder.path]); |
| _contexts[folder] = info; |
| info.changeSubscription = folder.changes.listen((WatchEvent event) { |
| _handleWatchEvent(folder, info, event); |
| }); |
| try { |
| - UriResolver packageUriResolver = _computePackageUriResolver(folder, info); |
| - info.context = addContext(folder, packageUriResolver); |
| + Packages packages; |
| + UriResolver packageUriResolver; |
| + |
| + if (ENABLE_PACKAGESPEC_SUPPORT) { |
| + // Try .packages first. |
| + if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) { |
| + packages = _readPackagespec(packagespecFile); |
| + } |
| + } |
| + |
| + // 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(); |
| @@ -511,13 +557,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()) { |
| @@ -529,20 +575,31 @@ 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) { |
| + |
| + File packageSpec; |
| + |
| + if (ENABLE_PACKAGESPEC_SUPPORT) { |
| + // Start by looking for .packages. |
| + packageSpec = folder.getChild(PACKAGE_SPEC_NAME); |
| + } |
| + |
| + // Fall back to looking for a pubspec. |
| + if (packageSpec == null || !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) |
| ]; |
| } |
| @@ -572,11 +629,11 @@ abstract class AbstractContextManager implements ContextManager { |
| } |
| /** |
| - * Extract a new [pubspecFile]-based context from [oldInfo]. |
| + * Extract a new [packagespecFile]-based context from [oldInfo]. |
| */ |
| - void _extractContext(_ContextInfo oldInfo, File pubspecFile) { |
| - Folder newFolder = pubspecFile.parent; |
| - _ContextInfo newInfo = _createContext(newFolder, pubspecFile, []); |
| + void _extractContext(_ContextInfo oldInfo, File packagespecFile) { |
| + Folder newFolder = packagespecFile.parent; |
| + _ContextInfo newInfo = _createContext(newFolder, packagespecFile, []); |
| newInfo.parent = oldInfo; |
| // prepare sources to extract |
| Map<String, Source> extractedSources = new HashMap<String, Source>(); |
| @@ -630,12 +687,46 @@ abstract class AbstractContextManager implements ContextManager { |
| if (_isInPackagesDir(path, folder)) { |
| 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; |
| + |
| + if (ENABLE_PACKAGESPEC_SUPPORT) { |
| + 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) { |
| + if (_isPubspec(path)) { |
| + // Check for a sibling .packages file. |
| + if (!resourceProvider.getFile( |
| + pathos.join(directoryPath, PACKAGE_SPEC_NAME)).exists) { |
| + _extractContext(info, resource); |
| + return; |
| + } |
| + } |
| + if (_isPackagespec(path)) { |
| + // Check for a sibling pubspec.yaml file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, PUBSPEC_NAME)).exists) { |
| + _extractContext(info, resource); |
| + return; |
| + } |
| + } |
| + } |
| + } |
| + } else { |
| + // pubspec was added in a sub-folder, extract a new context |
| + if (_isPubspec(path) && |
| + info.isRoot && |
| + !info.isPathToPackageDescription(path)) { |
| + _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. |
| @@ -651,11 +742,41 @@ 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) { |
| + if (ENABLE_PACKAGESPEC_SUPPORT) { |
| + String directoryPath = pathContext.dirname(path); |
| + |
| + // Only merge if this is the same directory described by our info object. |
| + if (info.folder.path == directoryPath) { |
| + if (_isPubspec(path)) { |
| + // Check for a sibling .packages file. |
| + if (!resourceProvider.getFile( |
| + pathos.join(directoryPath, PACKAGE_SPEC_NAME)).exists) { |
| + _mergeContext(info); |
| + return; |
| + } |
| + } |
| + if (_isPackagespec(path)) { |
| + // Check for a sibling pubspec.yaml file. |
| + if (!resourceProvider |
| + .getFile(pathos.join(directoryPath, PUBSPEC_NAME)).exists) { |
| + _mergeContext(info); |
| + return; |
| + } |
| + } |
| + } |
| + } else { |
| + if (info.isPathToPackageDescription(path)) { |
| + _mergeContext(info); |
| + return; |
| + } |
| + } |
| } |
| + |
| List<Source> sources = info.context.getSourcesWithFullName(path); |
| if (!sources.isEmpty) { |
| ChangeSet changeSet = new ChangeSet(); |
| @@ -678,6 +799,9 @@ abstract class AbstractContextManager implements ContextManager { |
| break; |
| } |
| + //TODO(pquitslund): find the right place for this |
| + _checkForPackagespecUpdate(path, info, folder); |
| + |
| if (info.packageMapInfo != null && |
| info.packageMapInfo.isChangedDependency(path, resourceProvider)) { |
| _recomputePackageUriResolver(info); |
| @@ -687,9 +811,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]. |
| @@ -713,12 +835,10 @@ abstract class AbstractContextManager implements ContextManager { |
| return pathParts.contains(PACKAGES_NAME); |
| } |
| - /** |
| - * Returns `true` if the given absolute [path] is a pubspec file. |
| - */ |
| - bool _isPubspec(String path) { |
| - return pathContext.basename(path) == PUBSPEC_NAME; |
| - } |
| + bool _isPackagespec(String path) => |
| + pathContext.basename(path) == PACKAGE_SPEC_NAME; |
| + |
| + bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME; |
| /** |
| * Merges [info] context into its parent. |
| @@ -739,6 +859,18 @@ abstract class AbstractContextManager implements ContextManager { |
| } |
| } |
| + Packages _readPackagespec(File specFile) { |
| + try { |
| + String contents = specFile.readAsStringSync(); |
| + Map<String, Uri> map = |
| + pkgfile.parse(UTF8.encode(contents), new Uri.file(specFile.path)); |
| + return new MapPackages(map); |
| + } catch (_) { |
| + //TODO(pquitslund): consider creating an error for the spec file. |
| + return null; |
| + } |
| + } |
| + |
| /** |
| * Recompute the package URI resolver for the context described by [info], |
| * and update the client appropriately. |
| @@ -750,7 +882,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); |
| } |
| /** |
| @@ -896,9 +1028,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 |
| @@ -934,7 +1066,7 @@ class _ContextInfo { |
| _ContextInfo(Folder folder, File pubspecFile, this.children, this.packageRoot) |
|
Paul Berry
2015/07/17 20:49:13
Rename pubspecFile to packageDescriptionFile to be
pquitslund
2015/07/20 20:50:22
Done.
|
| : folder = folder, |
| pathFilter = new PathFilter(folder.path, null) { |
| - pubspecPath = pubspecFile.path; |
| + packageDescriptionPath = pubspecFile.path; |
| for (_ContextInfo child in children) { |
| child.parent = this; |
| } |
| @@ -954,20 +1086,18 @@ class _ContextInfo { |
| }); |
| } |
| - /// Returns `true` if [path] should be ignored. |
| - bool ignored(String path) => pathFilter.ignored(path); |
| - |
| /** |
| * 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] should be ignored. |
| + bool ignored(String path) => pathFilter.ignored(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; |
| } |