| 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 145c942d1b3d9639e07153ae405e2544cb43b9b0..78dcc69e79a8959c5684252d727ede6802d8d23d 100644
|
| --- a/pkg/analysis_server/lib/src/context_manager.dart
|
| +++ b/pkg/analysis_server/lib/src/context_manager.dart
|
| @@ -69,21 +69,13 @@ class ContextInfo {
|
| String packageDescriptionPath;
|
|
|
| /**
|
| - * Stream subscription we are using to watch the context's directory for
|
| - * changes.
|
| - */
|
| - StreamSubscription<WatchEvent> changeSubscription;
|
| -
|
| - /**
|
| - * Stream subscriptions we are using to watch the files
|
| - * used to determine the package map. Organized as a map from path to
|
| - * subscription.
|
| + * Paths to files which determine the folder disposition and package map.
|
| *
|
| - * For paths that are inside [folder], the subscription is null, since the
|
| - * entire contents of the folder are automatically being watched.
|
| + * TODO(paulberry): if any of these files are outside of [folder], they won't
|
| + * be watched for changes. I believe the use case for watching these files
|
| + * is no longer relevant.
|
| */
|
| - final Map<String, StreamSubscription<WatchEvent>> _dependencySubscriptions =
|
| - <String, StreamSubscription<WatchEvent>>{};
|
| + Set<String> _dependencies = new Set<String>();
|
|
|
| /**
|
| * The analysis context that was created for the [folder].
|
| @@ -161,7 +153,7 @@ class ContextInfo {
|
| * Determine if the given [path] is one of the dependencies most recently
|
| * passed to [setDependencies].
|
| */
|
| - bool hasDependency(String path) => _dependencySubscriptions.containsKey(path);
|
| + bool hasDependency(String path) => _dependencies.contains(path);
|
|
|
| /// Returns `true` if [path] should be ignored.
|
| bool ignored(String path) => pathFilter.ignored(path);
|
| @@ -174,42 +166,10 @@ class ContextInfo {
|
| path == packageDescriptionPath;
|
|
|
| /**
|
| - * Update the set of dependencies for this context. Watchers are
|
| - * automatically set up for the dependencies (if necessary) to ensure that
|
| - * [ContextManagerImpl._handleWatchEvent] is called when they are modified.
|
| + * Update the set of dependencies for this context.
|
| */
|
| void setDependencies(Iterable<String> newDependencies) {
|
| - for (String oldDependency in _dependencySubscriptions.keys.toList()) {
|
| - if (!newDependencies.contains(oldDependency)) {
|
| - StreamSubscription<WatchEvent> subscription =
|
| - _dependencySubscriptions[oldDependency];
|
| - if (subscription != null) {
|
| - subscription.cancel();
|
| - }
|
| - _dependencySubscriptions.remove(oldDependency);
|
| - }
|
| - }
|
| - for (String newDependency in newDependencies) {
|
| - if (!_dependencySubscriptions.containsKey(newDependency)) {
|
| - StreamSubscription<WatchEvent> subscription;
|
| - if (!folder.contains(newDependency)) {
|
| - Resource resource =
|
| - contextManager.resourceProvider.getResource(newDependency);
|
| - if (resource is File) {
|
| - subscription = resource.changes.listen((WatchEvent event) {
|
| - contextManager._handleWatchEvent(folder, this, event);
|
| - }, onError: (error, StackTrace stackTrace) {
|
| - // Gracefully degrade if file is or becomes unwatchable
|
| - contextManager._instrumentationService.logException(
|
| - error, stackTrace);
|
| - subscription.cancel();
|
| - _dependencySubscriptions[newDependency] = null;
|
| - });
|
| - }
|
| - }
|
| - _dependencySubscriptions[newDependency] = subscription;
|
| - }
|
| - }
|
| + _dependencies = newDependencies.toSet();
|
| }
|
| }
|
|
|
| @@ -433,6 +393,13 @@ class ContextManagerImpl implements ContextManager {
|
| */
|
| final ContextInfo _rootInfo = new ContextInfo._root();
|
|
|
| + /**
|
| + * Stream subscription we are using to watch each analysis root directory for
|
| + * changes.
|
| + */
|
| + final Map<Folder, StreamSubscription<WatchEvent>> _changeSubscriptions =
|
| + <Folder, StreamSubscription<WatchEvent>>{};
|
| +
|
| ContextManagerImpl(this.resourceProvider, this.packageResolverProvider,
|
| this._packageMapProvider, this._instrumentationService) {
|
| pathContext = resourceProvider.pathContext;
|
| @@ -591,6 +558,8 @@ class ContextManagerImpl implements ContextManager {
|
| return info.folder.isOrContains(includedFolder.path);
|
| });
|
| if (!wasIncluded) {
|
| + _changeSubscriptions[includedFolder] =
|
| + includedFolder.changes.listen(_handleWatchEvent);
|
| _createContexts(_rootInfo, includedFolder, false);
|
| }
|
| }
|
| @@ -706,13 +675,6 @@ class ContextManagerImpl implements ContextManager {
|
| }
|
| }
|
|
|
| - /**
|
| - * Cancel all dependency subscriptions for the given context.
|
| - */
|
| - void _cancelDependencySubscriptions(ContextInfo info) {
|
| - info.setDependencies(const <String>[]);
|
| - }
|
| -
|
| void _checkForPackagespecUpdate(
|
| String path, ContextInfo info, Folder folder) {
|
| // Check to see if this is the .packages file for this context and if so,
|
| @@ -754,7 +716,7 @@ class ContextManagerImpl implements ContextManager {
|
| }
|
|
|
| /**
|
| - * Compute the appropriate [FolderDisposition] for [info]. Use
|
| + * Compute the appropriate [FolderDisposition] for [folder]. Use
|
| * [addDependency] to indicate which files needed to be consulted in order to
|
| * figure out the [FolderDisposition]; these dependencies will be watched in
|
| * order to determine when it is necessary to call this function again.
|
| @@ -763,11 +725,12 @@ class ContextManagerImpl implements ContextManager {
|
| * dependencies (currently we only use it to track "pub list" dependencies).
|
| */
|
| FolderDisposition _computeFolderDisposition(
|
| - Folder folder, ContextInfo info, void addDependency(String path)) {
|
| - if (info.packageRoot != null) {
|
| + Folder folder, void addDependency(String path)) {
|
| + String packageRoot = normalizedPackageRoots[folder.path];
|
| + if (packageRoot != null) {
|
| // TODO(paulberry): We shouldn't be using JavaFile here because it
|
| // makes the code untestable (see dartbug.com/23909).
|
| - JavaFile packagesDir = new JavaFile(info.packageRoot);
|
| + JavaFile packagesDir = new JavaFile(packageRoot);
|
| Map<String, List<Folder>> packageMap = new Map<String, List<Folder>>();
|
| if (packagesDir.isDirectory()) {
|
| for (JavaFile file in packagesDir.listFiles()) {
|
| @@ -786,15 +749,14 @@ class ContextManagerImpl implements ContextManager {
|
| packageMap[file.getName()] = <Folder>[res];
|
| }
|
| }
|
| - return new PackageMapDisposition(packageMap,
|
| - packageRoot: info.packageRoot);
|
| + return new PackageMapDisposition(packageMap, packageRoot: packageRoot);
|
| }
|
| // 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 new NoPackageFolderDisposition(packageRoot: info.packageRoot);
|
| + return new NoPackageFolderDisposition(packageRoot: packageRoot);
|
| } else {
|
| callbacks.beginComputePackageMap();
|
| if (packageResolverProvider != null) {
|
| @@ -828,33 +790,25 @@ class ContextManagerImpl implements ContextManager {
|
| normalizedPackageRoots[folder.path]);
|
| Map<String, YamlNode> options = analysisOptionsProvider.getOptions(folder);
|
| processOptionsForContext(info, options);
|
| - info.changeSubscription = folder.changes.listen((WatchEvent event) {
|
| - _handleWatchEvent(folder, info, event);
|
| - });
|
| - try {
|
| - FolderDisposition disposition;
|
| - List<String> dependencies = <String>[];
|
| -
|
| - if (ENABLE_PACKAGESPEC_SUPPORT) {
|
| - // Try .packages first.
|
| - if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
|
| - Packages packages = _readPackagespec(packagespecFile);
|
| - disposition = new PackagesFileDisposition(packages);
|
| - }
|
| - }
|
| + FolderDisposition disposition;
|
| + List<String> dependencies = <String>[];
|
|
|
| - // Next resort to a package uri resolver.
|
| - if (disposition == null) {
|
| - disposition = _computeFolderDisposition(folder, info, dependencies.add);
|
| + if (ENABLE_PACKAGESPEC_SUPPORT) {
|
| + // Try .packages first.
|
| + if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
|
| + Packages packages = _readPackagespec(packagespecFile);
|
| + disposition = new PackagesFileDisposition(packages);
|
| }
|
| + }
|
|
|
| - info.setDependencies(dependencies);
|
| - info.context = callbacks.addContext(folder, disposition);
|
| - info.context.name = folder.path;
|
| - } catch (_) {
|
| - info.changeSubscription.cancel();
|
| - rethrow;
|
| + // Next resort to a package uri resolver.
|
| + if (disposition == null) {
|
| + disposition = _computeFolderDisposition(folder, dependencies.add);
|
| }
|
| +
|
| + info.setDependencies(dependencies);
|
| + info.context = callbacks.addContext(folder, disposition);
|
| + info.context.name = folder.path;
|
| return info;
|
| }
|
|
|
| @@ -917,8 +871,9 @@ class ContextManagerImpl implements ContextManager {
|
| * Clean up and destroy the context associated with the given folder.
|
| */
|
| void _destroyContext(ContextInfo info) {
|
| - info.changeSubscription.cancel();
|
| - _cancelDependencySubscriptions(info);
|
| + if (_changeSubscriptions.containsKey(info.folder)) {
|
| + _changeSubscriptions[info.folder].cancel();
|
| + }
|
| callbacks.removeContext(info.folder, _computeFlushedFiles(info));
|
| bool wasRemoved = info.parent.children.remove(info);
|
| assert(wasRemoved);
|
| @@ -983,13 +938,21 @@ class ContextManagerImpl implements ContextManager {
|
| }
|
| }
|
|
|
| - void _handleWatchEvent(Folder folder, ContextInfo info, WatchEvent event) {
|
| + void _handleWatchEvent(WatchEvent event) {
|
| + // Figure out which context this event applies to.
|
| // TODO(brianwilkerson) If a file is explicitly included in one context
|
| // but implicitly referenced in another context, we will only send a
|
| // changeSet to the context that explicitly includes the file (because
|
| // that's the only context that's watching the file).
|
| + ContextInfo info = _getInnermostContextInfoFor(event.path);
|
| + if (info == null) {
|
| + // This event doesn't apply to any context. This could happen due to a
|
| + // race condition (e.g. a context was removed while one of its events was
|
| + // in the event loop). The event is inapplicable now, so just ignore it.
|
| + return;
|
| + }
|
| _instrumentationService.logWatchEvent(
|
| - folder.path, event.path, event.type.toString());
|
| + info.folder.path, event.path, event.type.toString());
|
| String path = event.path;
|
| // First handle changes that affect folderDisposition (since these need to
|
| // be processed regardless of whether they are part of an excluded/ignored
|
| @@ -1011,7 +974,7 @@ class ContextManagerImpl implements ContextManager {
|
| // handle the change
|
| switch (event.type) {
|
| case ChangeType.ADD:
|
| - if (_isInPackagesDir(path, folder)) {
|
| + if (_isInPackagesDir(path, info.folder)) {
|
| return;
|
| }
|
|
|
| @@ -1063,7 +1026,7 @@ class ContextManagerImpl implements ContextManager {
|
| ChangeSet changeSet = new ChangeSet();
|
| Source source = createSourceInContext(info.context, file);
|
| changeSet.addedSource(source);
|
| - callbacks.applyChangesToContext(folder, changeSet);
|
| + callbacks.applyChangesToContext(info.folder, changeSet);
|
| info.sources[path] = source;
|
| }
|
| }
|
| @@ -1110,7 +1073,7 @@ class ContextManagerImpl implements ContextManager {
|
| sources.forEach((Source source) {
|
| changeSet.removedSource(source);
|
| });
|
| - callbacks.applyChangesToContext(folder, changeSet);
|
| + callbacks.applyChangesToContext(info.folder, changeSet);
|
| info.sources.remove(path);
|
| }
|
| break;
|
| @@ -1121,13 +1084,13 @@ class ContextManagerImpl implements ContextManager {
|
| sources.forEach((Source source) {
|
| changeSet.changedSource(source);
|
| });
|
| - callbacks.applyChangesToContext(folder, changeSet);
|
| + callbacks.applyChangesToContext(info.folder, changeSet);
|
| }
|
| break;
|
| }
|
|
|
| //TODO(pquitslund): find the right place for this
|
| - _checkForPackagespecUpdate(path, info, folder);
|
| + _checkForPackagespecUpdate(path, info, info.folder);
|
| }
|
|
|
| /**
|
| @@ -1204,7 +1167,7 @@ class ContextManagerImpl implements ContextManager {
|
| // "pub list" is in progress is just going to get thrown away anyhow.
|
| List<String> dependencies = <String>[];
|
| FolderDisposition disposition =
|
| - _computeFolderDisposition(info.folder, info, dependencies.add);
|
| + _computeFolderDisposition(info.folder, dependencies.add);
|
| info.setDependencies(dependencies);
|
| callbacks.updateContextPackageUriResolver(info.folder, disposition);
|
| }
|
|
|