blob: 13027ddf6371370ab1ab14e6f8cde66f47cf9001 [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/context/context_root.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/util/absolute_path.dart';
import 'package:front_end/src/base/source.dart';
import 'package:path/src/context.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 implements DriverWatcher {
/**
* 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)]);
List<String> enabledPlugins = driver.analysisOptions.enabledPluginNames;
for (String package in enabledPlugins) {
//
// Determine whether the package exists and defines a plugin.
//
Source source =
driver.sourceFactory.forUri('package:$package/$package.dart');
if (source != null) {
Context context = resourceProvider.pathContext;
String packageRoot = context.dirname(context.dirname(source.fullName));
String pluginPath = _locator.findPlugin(packageRoot);
if (pluginPath != null) {
//
// Add the plugin to the context root.
//
// TODO(brianwilkerson) Do we need to wait for the plugin to be added?
// If we don't, then tests don't have any way to know when to expect
// that the list of plugins has been updated.
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);
}
/**
* 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 (context.basename(sdkRoot) != 'lib') {
String parent = context.dirname(sdkRoot);
if (parent == sdkRoot) {
break;
}
sdkRoot = parent;
}
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);
}