| 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 51c42d29c85938cbfd68b7131c3ee0b8235bf761..fb1c3b4f5976cff18bd9f68d078ed152c813840c 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';
|
| @@ -20,6 +21,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';
|
| import 'package:yaml/yaml.dart';
|
| @@ -29,6 +33,12 @@ import 'package:yaml/yaml.dart';
|
| * folders that should correspond to analysis contexts.
|
| */
|
| abstract class AbstractContextManager implements ContextManager {
|
| +
|
| + /**
|
| + * Temporary flag to hide WIP .packages support (DEP 5).
|
| + */
|
| + static bool ENABLE_PACKAGESPEC_SUPPORT = false;
|
| +
|
| /**
|
| * The name of the `lib` directory.
|
| */
|
| @@ -45,6 +55,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].
|
| */
|
| @@ -113,7 +128,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
|
| @@ -170,14 +186,20 @@ 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;
|
| + @override
|
| + bool isInAnalysisRoot(String path) {
|
| + // check if excluded
|
| + if (_isExcluded(path)) {
|
| + return false;
|
| }
|
| - var pathFilter = info.pathFilter;
|
| - pathFilter.setIgnorePatterns(ignorePatterns);
|
| + // check if in of the roots
|
| + for (Folder root in _contexts.keys) {
|
| + if (root.contains(path)) {
|
| + return true;
|
| + }
|
| + }
|
| + // no
|
| + return false;
|
| }
|
|
|
| /// Process [options] for the context [folder].
|
| @@ -200,22 +222,6 @@ abstract class AbstractContextManager implements ContextManager {
|
| }
|
|
|
| @override
|
| - bool isInAnalysisRoot(String path) {
|
| - // check if excluded
|
| - if (_isExcluded(path)) {
|
| - return false;
|
| - }
|
| - // check if in of the roots
|
| - for (Folder root in _contexts.keys) {
|
| - if (root.contains(path)) {
|
| - return true;
|
| - }
|
| - }
|
| - // no
|
| - return false;
|
| - }
|
| -
|
| - @override
|
| void refresh(List<Resource> roots) {
|
| // Destroy old contexts
|
| List<Folder> contextFolders = _contexts.keys.toList();
|
| @@ -240,6 +246,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) {
|
| @@ -334,7 +350,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].
|
| @@ -431,6 +447,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
|
| @@ -511,9 +543,9 @@ 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;
|
| var options = analysisOptionsProvider.getOptions(folder);
|
| processOptionsForContext(folder, options);
|
| @@ -521,8 +553,22 @@ abstract class AbstractContextManager implements ContextManager {
|
| _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();
|
| @@ -538,13 +584,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()) {
|
| @@ -556,20 +602,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)
|
| ];
|
| }
|
|
|
| @@ -599,11 +656,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>();
|
| @@ -657,12 +714,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.
|
| @@ -678,11 +769,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();
|
| @@ -705,6 +826,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);
|
| @@ -714,9 +838,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].
|
| @@ -740,12 +862,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.
|
| @@ -766,6 +886,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.
|
| @@ -777,7 +909,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);
|
| }
|
|
|
| /**
|
| @@ -923,9 +1055,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
|
| @@ -958,10 +1090,11 @@ class _ContextInfo {
|
| */
|
| OptimizingPubPackageMapInfo packageMapInfo;
|
|
|
| - _ContextInfo(Folder folder, File pubspecFile, this.children, this.packageRoot)
|
| + _ContextInfo(
|
| + Folder folder, File packagespecFile, this.children, this.packageRoot)
|
| : folder = folder,
|
| pathFilter = new PathFilter(folder.path, null) {
|
| - pubspecPath = pubspecFile.path;
|
| + packageDescriptionPath = packagespecFile.path;
|
| for (_ContextInfo child in children) {
|
| child.parent = this;
|
| }
|
| @@ -981,20 +1114,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;
|
| }
|
|
|