| Index: pkg/analysis_server/lib/src/single_context_manager.dart
|
| diff --git a/pkg/analysis_server/lib/src/single_context_manager.dart b/pkg/analysis_server/lib/src/single_context_manager.dart
|
| deleted file mode 100644
|
| index b0bf0334a741711492b30e5ed8bcaea8039e3a6a..0000000000000000000000000000000000000000
|
| --- a/pkg/analysis_server/lib/src/single_context_manager.dart
|
| +++ /dev/null
|
| @@ -1,533 +0,0 @@
|
| -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -import 'dart:async';
|
| -import 'dart:core';
|
| -import 'dart:math' as math;
|
| -
|
| -import 'package:analysis_server/src/context_manager.dart';
|
| -import 'package:analyzer/file_system/file_system.dart';
|
| -import 'package:analyzer/plugin/resolver_provider.dart';
|
| -import 'package:analyzer/src/dart/analysis/driver.dart';
|
| -import 'package:analyzer/src/generated/engine.dart';
|
| -import 'package:analyzer/src/generated/sdk.dart';
|
| -import 'package:analyzer/src/generated/source.dart';
|
| -import 'package:analyzer/src/util/glob.dart';
|
| -import 'package:path/path.dart' as path;
|
| -import 'package:watcher/watcher.dart';
|
| -
|
| -/**
|
| - * Implementation of [ContextManager] that supports only one [AnalysisContext].
|
| - * So, sources from all analysis roots are added to this single context. All
|
| - * features that could otherwise cause creating additional contexts, such as
|
| - * presence of `pubspec.yaml` or `.packages` files, or analysis options files
|
| - * are ignored.
|
| - */
|
| -class SingleContextManager implements ContextManager {
|
| - /**
|
| - * The [ResourceProvider] using which paths are converted into [Resource]s.
|
| - */
|
| - final ResourceProvider resourceProvider;
|
| -
|
| - /**
|
| - * The context used to work with file system paths.
|
| - */
|
| - path.Context pathContext;
|
| -
|
| - /**
|
| - * The manager used to access the SDK that should be associated with a
|
| - * particular context.
|
| - */
|
| - final DartSdkManager sdkManager;
|
| -
|
| - /**
|
| - * A function that will return a [UriResolver] that can be used to resolve
|
| - * `package:` URIs.
|
| - */
|
| - final ResolverProvider packageResolverProvider;
|
| -
|
| - /**
|
| - * A list of the globs used to determine which files should be analyzed.
|
| - */
|
| - final List<Glob> analyzedFilesGlobs;
|
| -
|
| - /**
|
| - * The default options used to create new analysis contexts.
|
| - */
|
| - final AnalysisOptionsImpl defaultContextOptions;
|
| -
|
| - /**
|
| - * The list of included paths (folders and files) most recently passed to
|
| - * [setRoots].
|
| - */
|
| - List<String> includedPaths = <String>[];
|
| -
|
| - /**
|
| - * The list of excluded paths (folders and files) most recently passed to
|
| - * [setRoots].
|
| - */
|
| - List<String> excludedPaths = <String>[];
|
| -
|
| - /**
|
| - * The map of package roots most recently passed to [setRoots].
|
| - */
|
| - Map<String, String> packageRoots = <String, String>{};
|
| -
|
| - /**
|
| - * Same as [packageRoots], except that source folders have been normalized
|
| - * and non-folders have been removed.
|
| - */
|
| - Map<String, String> normalizedPackageRoots = <String, String>{};
|
| -
|
| - @override
|
| - ContextManagerCallbacks callbacks;
|
| -
|
| - /**
|
| - * The analysis driver which analyses everything.
|
| - */
|
| - AnalysisDriver analysisDriver;
|
| -
|
| - /**
|
| - * The context in which everything is being analyzed.
|
| - */
|
| - AnalysisContext context;
|
| -
|
| - /**
|
| - * The folder associated with the context.
|
| - */
|
| - Folder contextFolder;
|
| -
|
| - /**
|
| - * The current watch subscriptions.
|
| - */
|
| - Map<String, StreamSubscription<WatchEvent>> watchSubscriptions =
|
| - new Map<String, StreamSubscription<WatchEvent>>();
|
| -
|
| - /**
|
| - * The [packageResolverProvider] must not be `null`.
|
| - */
|
| - SingleContextManager(
|
| - this.resourceProvider,
|
| - this.sdkManager,
|
| - this.packageResolverProvider,
|
| - this.analyzedFilesGlobs,
|
| - this.defaultContextOptions) {
|
| - pathContext = resourceProvider.pathContext;
|
| - }
|
| -
|
| - @override
|
| - Iterable<AnalysisContext> get analysisContexts =>
|
| - context == null ? <AnalysisContext>[] : <AnalysisContext>[context];
|
| -
|
| - @override
|
| - Map<Folder, AnalysisDriver> get driverMap => {contextFolder: analysisDriver};
|
| -
|
| - @override
|
| - Map<Folder, AnalysisContext> get folderMap => {contextFolder: context};
|
| -
|
| - @override
|
| - List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) {
|
| - if (context == null || !includedPaths.contains(analysisRoot.path)) {
|
| - return <AnalysisContext>[];
|
| - }
|
| - return <AnalysisContext>[context];
|
| - }
|
| -
|
| - @override
|
| - Folder getContextFolderFor(String path) {
|
| - if (isInAnalysisRoot(path)) {
|
| - return contextFolder;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - @override
|
| - AnalysisContext getContextFor(String path) {
|
| - if (context == null) {
|
| - return null;
|
| - } else if (_isContainedIn(includedPaths, path)) {
|
| - return context;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - @override
|
| - AnalysisDriver getDriverFor(String path) {
|
| - throw new UnimplementedError(
|
| - 'Unexpected invocation of getDriverFor in SingleContextManager');
|
| - }
|
| -
|
| - @override
|
| - List<AnalysisDriver> getDriversInAnalysisRoot(Folder analysisRoot) {
|
| - throw new UnimplementedError(
|
| - 'Unexpected invocation of getDriversInAnalysisRoot in SingleContextManager');
|
| - }
|
| -
|
| - @override
|
| - bool isIgnored(String path) {
|
| - return !_isContainedIn(includedPaths, path) || _isExcludedPath(path);
|
| - }
|
| -
|
| - @override
|
| - bool isInAnalysisRoot(String path) {
|
| - return _isContainedIn(includedPaths, path) &&
|
| - !_isContainedIn(excludedPaths, path);
|
| - }
|
| -
|
| - @override
|
| - void refresh(List<Resource> roots) {
|
| - if (context != null) {
|
| - callbacks.removeContext(contextFolder, null);
|
| - context.dispose();
|
| - context = null;
|
| - contextFolder = null;
|
| - _cancelCurrentWatchSubscriptions();
|
| - setRoots(includedPaths, excludedPaths, packageRoots);
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void setRoots(List<String> includedPaths, List<String> excludedPaths,
|
| - Map<String, String> packageRoots) {
|
| - includedPaths = _nonOverlappingPaths(includedPaths);
|
| - excludedPaths = _nonOverlappingPaths(excludedPaths);
|
| - this.packageRoots = packageRoots;
|
| - _updateNormalizedPackageRoots();
|
| - // Update context path.
|
| - {
|
| - String contextPath = _commonPrefix(includedPaths);
|
| - Folder contextFolder = resourceProvider.getFolder(contextPath);
|
| - if (contextFolder != this.contextFolder) {
|
| - if (context != null) {
|
| - callbacks.moveContext(this.contextFolder, contextFolder);
|
| - }
|
| - this.contextFolder = contextFolder;
|
| - }
|
| - }
|
| - // Start new watchers and cancel old ones.
|
| - {
|
| - Map<String, StreamSubscription<WatchEvent>> newSubscriptions =
|
| - new Map<String, StreamSubscription<WatchEvent>>();
|
| - for (String includedPath in includedPaths) {
|
| - Resource resource = resourceProvider.getResource(includedPath);
|
| - if (resource is Folder) {
|
| - // Extract the existing subscription or create a new one.
|
| - StreamSubscription<WatchEvent> subscription =
|
| - watchSubscriptions.remove(includedPath);
|
| - if (subscription == null) {
|
| - subscription = resource.changes.listen(_handleWatchEvent);
|
| - }
|
| - // Remember the subscription.
|
| - newSubscriptions[includedPath] = subscription;
|
| - }
|
| - _cancelCurrentWatchSubscriptions();
|
| - this.watchSubscriptions = newSubscriptions;
|
| - }
|
| - }
|
| - // Create or update the analysis context.
|
| - if (context == null) {
|
| - context = callbacks.addContext(contextFolder, defaultContextOptions);
|
| - ChangeSet changeSet =
|
| - _buildChangeSet(added: _includedFiles(includedPaths, excludedPaths));
|
| - callbacks.applyChangesToContext(contextFolder, changeSet);
|
| - } else {
|
| - // TODO(brianwilkerson) Optimize this.
|
| - List<File> oldFiles =
|
| - _includedFiles(this.includedPaths, this.excludedPaths);
|
| - List<File> newFiles = _includedFiles(includedPaths, excludedPaths);
|
| - ChangeSet changeSet = _buildChangeSet(
|
| - added: _diff(newFiles, oldFiles), removed: _diff(oldFiles, newFiles));
|
| - callbacks.applyChangesToContext(contextFolder, changeSet);
|
| - }
|
| - this.includedPaths = includedPaths;
|
| - this.excludedPaths = excludedPaths;
|
| - }
|
| -
|
| - /**
|
| - * Recursively add the given [resource] (if it's a file) or its children (if
|
| - * it's a folder) to the [addedFiles].
|
| - */
|
| - void _addFilesInResource(
|
| - List<File> addedFiles, Resource resource, List<String> excludedPaths) {
|
| - if (_isImplicitlyExcludedResource(resource)) {
|
| - return;
|
| - }
|
| - String path = resource.path;
|
| - if (_isEqualOrWithinAny(excludedPaths, path)) {
|
| - return;
|
| - }
|
| - if (resource is File) {
|
| - if (_matchesAnyAnalyzedFilesGlob(path) && resource.exists) {
|
| - addedFiles.add(resource);
|
| - }
|
| - } else if (resource is Folder) {
|
| - for (Resource child in _getChildrenSafe(resource)) {
|
| - _addFilesInResource(addedFiles, child, excludedPaths);
|
| - }
|
| - }
|
| - }
|
| -
|
| - ChangeSet _buildChangeSet({List<File> added, List<File> removed}) {
|
| - ChangeSet changeSet = new ChangeSet();
|
| - if (added != null) {
|
| - for (File file in added) {
|
| - Source source = createSourceInContext(context, file);
|
| - changeSet.addedSource(source);
|
| - }
|
| - }
|
| - if (removed != null) {
|
| - for (File file in removed) {
|
| - Source source = createSourceInContext(context, file);
|
| - changeSet.removedSource(source);
|
| - }
|
| - }
|
| - return changeSet;
|
| - }
|
| -
|
| - void _cancelCurrentWatchSubscriptions() {
|
| - for (StreamSubscription<WatchEvent> subscription
|
| - in watchSubscriptions.values) {
|
| - subscription.cancel();
|
| - }
|
| - watchSubscriptions.clear();
|
| - }
|
| -
|
| - String _commonPrefix(List<String> paths) {
|
| - if (paths.isEmpty) {
|
| - return '';
|
| - }
|
| - List<String> left = pathContext.split(paths[0]);
|
| - int count = left.length;
|
| - for (int i = 1; i < paths.length; i++) {
|
| - List<String> right = pathContext.split(paths[i]);
|
| - count = _commonComponents(left, count, right);
|
| - }
|
| - return pathContext.joinAll(left.sublist(0, count));
|
| - }
|
| -
|
| - List<Resource> _existingResources(List<String> pathList) {
|
| - List<Resource> resources = <Resource>[];
|
| - for (String path in pathList) {
|
| - Resource resource = resourceProvider.getResource(path);
|
| - if (resource is Folder) {
|
| - resources.add(resource);
|
| - } else if (!resource.exists) {
|
| - // Non-existent resources are ignored. TODO(paulberry): we should set
|
| - // up a watcher to ensure that if the resource appears later, we will
|
| - // begin analyzing it.
|
| - } else if (resource is File) {
|
| - resources.add(resource);
|
| - } else {
|
| - throw new UnimplementedError('$path is not a folder. '
|
| - 'Only support for file and folder analysis is implemented.');
|
| - }
|
| - }
|
| - return resources;
|
| - }
|
| -
|
| - void _handleWatchEvent(WatchEvent event) {
|
| - String path = event.path;
|
| - // Ignore if excluded.
|
| - if (_isExcludedPath(path)) {
|
| - return;
|
| - }
|
| - // Ignore if not in a root.
|
| - if (!_isContainedIn(includedPaths, path)) {
|
| - return;
|
| - }
|
| - // Handle the change.
|
| - switch (event.type) {
|
| - case ChangeType.ADD:
|
| - Resource resource = resourceProvider.getResource(path);
|
| - if (resource is File) {
|
| - if (_matchesAnyAnalyzedFilesGlob(path)) {
|
| - callbacks.applyChangesToContext(
|
| - contextFolder, _buildChangeSet(added: <File>[resource]));
|
| - }
|
| - }
|
| - break;
|
| - case ChangeType.REMOVE:
|
| - List<Source> sources = context.getSourcesWithFullName(path);
|
| - if (!sources.isEmpty) {
|
| - ChangeSet changeSet = new ChangeSet();
|
| - sources.forEach(changeSet.removedSource);
|
| - callbacks.applyChangesToContext(contextFolder, changeSet);
|
| - }
|
| - break;
|
| - case ChangeType.MODIFY:
|
| - List<Source> sources = context.getSourcesWithFullName(path);
|
| - if (!sources.isEmpty) {
|
| - ChangeSet changeSet = new ChangeSet();
|
| - sources.forEach(changeSet.changedSource);
|
| - callbacks.applyChangesToContext(contextFolder, changeSet);
|
| - }
|
| - break;
|
| - }
|
| - }
|
| -
|
| - List<File> _includedFiles(
|
| - List<String> includedPaths, List<String> excludedPaths) {
|
| - List<Resource> includedResources = _existingResources(includedPaths);
|
| - List<File> includedFiles = <File>[];
|
| - for (Resource resource in includedResources) {
|
| - _addFilesInResource(includedFiles, resource, excludedPaths);
|
| - }
|
| - return includedFiles;
|
| - }
|
| -
|
| - bool _isContainedIn(List<String> pathList, String path) {
|
| - for (String pathInList in pathList) {
|
| - if (_isEqualOrWithin(pathInList, path)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool _isEqualOrWithin(String parent, String child) {
|
| - return child == parent || pathContext.isWithin(parent, child);
|
| - }
|
| -
|
| - bool _isEqualOrWithinAny(List<String> parents, String child) {
|
| - for (String parent in parents) {
|
| - if (_isEqualOrWithin(parent, child)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` if the given [path] should be excluded, using explicit
|
| - * or implicit rules.
|
| - */
|
| - bool _isExcludedPath(String path) {
|
| - List<String> parts = resourceProvider.pathContext.split(path);
|
| - // Implicit rules.
|
| - for (String part in parts) {
|
| - if (part.startsWith('.')) {
|
| - return true;
|
| - }
|
| - }
|
| - // Explicitly excluded paths.
|
| - if (_isEqualOrWithinAny(excludedPaths, path)) {
|
| - return true;
|
| - }
|
| - // OK
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` if the given [resource] and children should be excluded
|
| - * because of some implicit exclusion rules, e.g. `.name`.
|
| - */
|
| - bool _isImplicitlyExcludedResource(Resource resource) {
|
| - String shortName = resource.shortName;
|
| - if (shortName.startsWith('.')) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` if the given [path] matches one of the [analyzedFilesGlobs].
|
| - */
|
| - bool _matchesAnyAnalyzedFilesGlob(String path) {
|
| - for (Glob glob in analyzedFilesGlobs) {
|
| - if (glob.matches(path)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return a list consisting of the elements from [pathList] that describe the
|
| - * minimal set of directories that include everything in the original list of
|
| - * paths and nothing more. In particular:
|
| - *
|
| - * * if a path is in the input list multiple times it will appear at most
|
| - * once in the output list, and
|
| - * * if a directory D and a subdirectory of it are both in the input list
|
| - * then only the directory D will be in the output list.
|
| - *
|
| - * The original list is not modified.
|
| - */
|
| - List<String> _nonOverlappingPaths(List<String> pathList) {
|
| - List<String> sortedPaths = new List<String>.from(pathList);
|
| - sortedPaths.sort((a, b) => a.length - b.length);
|
| - int pathCount = sortedPaths.length;
|
| - for (int i = pathCount - 1; i > 0; i--) {
|
| - String path = sortedPaths[i];
|
| - for (int j = 0; j < i; j++) {
|
| - if (_isEqualOrWithin(path, sortedPaths[j])) {
|
| - sortedPaths.removeAt(i);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - return sortedPaths;
|
| - }
|
| -
|
| - /**
|
| - * Normalize all package root sources by mapping them to folders on the
|
| - * filesystem. Ignore any package root sources that aren't folders.
|
| - */
|
| - void _updateNormalizedPackageRoots() {
|
| - normalizedPackageRoots = <String, String>{};
|
| - packageRoots.forEach((String sourcePath, String targetPath) {
|
| - Resource resource = resourceProvider.getResource(sourcePath);
|
| - if (resource is Folder) {
|
| - normalizedPackageRoots[resource.path] = targetPath;
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Create and return a source representing the given [file] within the given
|
| - * [context].
|
| - */
|
| - static Source createSourceInContext(AnalysisContext context, File file) {
|
| - // TODO(brianwilkerson) Optimize this, by allowing support for source
|
| - // factories to restore URI's from a file path rather than a source.
|
| - Source source = file.createSource();
|
| - if (context == null) {
|
| - return source;
|
| - }
|
| - Uri uri = context.sourceFactory.restoreUri(source);
|
| - return file.createSource(uri);
|
| - }
|
| -
|
| - static int _commonComponents(
|
| - List<String> left, int count, List<String> right) {
|
| - int max = math.min(count, right.length);
|
| - for (int i = 0; i < max; i++) {
|
| - if (left[i] != right[i]) {
|
| - return i;
|
| - }
|
| - }
|
| - return max;
|
| - }
|
| -
|
| - /**
|
| - * Return a list of all the files in the [left] that are not in the [right].
|
| - */
|
| - static List<File> _diff(List<File> left, List<File> right) {
|
| - List<File> diff = new List.from(left);
|
| - for (File file in right) {
|
| - diff.remove(file);
|
| - }
|
| - return diff;
|
| - }
|
| -
|
| - static List<Resource> _getChildrenSafe(Folder folder) {
|
| - try {
|
| - return folder.getChildren();
|
| - } on FileSystemException {
|
| - // The folder either doesn't exist or cannot be read.
|
| - // Either way, there are no children.
|
| - return const <Resource>[];
|
| - }
|
| - }
|
| -}
|
|
|