blob: 7275e2048b232bf7ef3273e0061336c8b31b61a9 [file] [log] [blame]
// Copyright (c) 2017, 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 'package:analysis_server/src/plugin/plugin_locator.dart';
import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/package_map_resolver.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/util/absolute_path.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
/**
* An object that watches the results produced by analysis drivers to identify
* references to previously unseen packages and, if those packages have plugins
* associated with them, causes the plugin to be associated with the driver's
* context root (which in turn might cause the plugin to be started).
*/
class PluginWatcher {
/**
* The resource provider used to access the file system.
*/
final ResourceProvider resourceProvider;
/**
* The object managing the execution of plugins.
*/
final PluginManager manager;
/**
* The object used to locate plugins within packages.
*/
final PluginLocator _locator;
/**
* A table mapping analysis drivers to information related to the driver.
*/
Map<AnalysisDriver, _DriverInfo> _driverInfo =
<AnalysisDriver, _DriverInfo>{};
/**
* Initialize a newly created plugin watcher.
*/
PluginWatcher(this.resourceProvider, this.manager)
: _locator = new PluginLocator(resourceProvider);
/**
* The context manager has just added the given analysis [driver]. This method
* must be called before the driver has been allowed to perform any analysis.
*/
void addedDriver(AnalysisDriver driver, ContextRoot contextRoot) {
_driverInfo[driver] = new _DriverInfo(
contextRoot, <String>[contextRoot.root, _getSdkPath(driver)]);
driver.results.listen((AnalysisResult result) {
List<String> addedPluginPaths = _checkPluginsFor(driver);
for (String pluginPath in addedPluginPaths) {
manager.addPluginToContextRoot(contextRoot, pluginPath);
}
});
}
/**
* The context manager has just removed the given analysis [driver].
*/
void removedDriver(AnalysisDriver driver) {
_DriverInfo info = _driverInfo[driver];
if (info == null) {
throw new StateError('Cannot remove a driver that was not added');
}
manager.removedContextRoot(info.contextRoot);
_driverInfo.remove(driver);
}
/**
* Check all of the files that have been analyzed so far by the given [driver]
* to see whether any of them are in a package that had not previously been
* seen that defines a plugin. Return a list of the roots of all such plugins
* that are found.
*/
List<String> _checkPluginsFor(AnalysisDriver driver) {
AbsolutePathContext context = resourceProvider.absolutePathContext;
List<String> packageRoots = _driverInfo[driver].packageRoots;
bool isInRoot(String path) {
for (String root in packageRoots) {
if (context.isWithin(root, path)) {
return true;
}
}
return false;
}
String getPackageRoot(String path, Uri uri) {
List<String> segments = uri.pathSegments.toList();
segments[0] = 'lib';
String suffix = resourceProvider.pathContext.joinAll(segments);
return path.substring(0, path.length - suffix.length - 1);
}
List<String> addedPluginPaths = <String>[];
for (FileState state in driver.fsState.knownFiles) {
String path = state.path;
if (!isInRoot(path)) {
// Found a file not in a previously known package.
Uri uri = state.uri;
if (PackageMapUriResolver.isPackageUri(uri)) {
String packageRoot = getPackageRoot(path, uri);
packageRoots.add(packageRoot);
String pluginPath = _locator.findPlugin(packageRoot);
if (pluginPath != null) {
addedPluginPaths.add(pluginPath);
}
}
}
}
return addedPluginPaths;
}
/**
* Return the path to the root of the SDK being used by the given analysis
* [driver].
*/
String _getSdkPath(AnalysisDriver driver) {
AbsolutePathContext context = resourceProvider.absolutePathContext;
String sdkRoot = driver.sourceFactory.forUri('dart:core').fullName;
while (sdkRoot.isNotEmpty && context.basename(sdkRoot) != 'lib') {
sdkRoot = context.dirname(sdkRoot);
}
return sdkRoot;
}
}
/**
* Information related to an analysis driver.
*/
class _DriverInfo {
/**
* The context root representing the context being analyzed by the driver.
*/
final ContextRoot contextRoot;
/**
* A list of the absolute paths of directories inside of which we have already
* searched for a plugin.
*/
final List<String> packageRoots;
/**
* Initialize a newly created information holder.
*/
_DriverInfo(this.contextRoot, this.packageRoots);
}