mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:31:20 +00:00
Remove the AnalysisOptionsProcessor
R=scheglov@google.com Review URL: https://codereview.chromium.org/2559523005 .
This commit is contained in:
parent
70581541e6
commit
0ea654a068
2
DEPS
2
DEPS
|
@ -73,7 +73,7 @@ vars = {
|
|||
"isolate_tag": "@0.2.3",
|
||||
"jinja2_rev": "@2222b31554f03e62600cd7e383376a7c187967a1",
|
||||
"json_rpc_2_tag": "@2.0.2",
|
||||
"linter_rev": "@17870d56361a95831e1ab32c82a0851ffe34c82b",
|
||||
"linter_rev": "@592e0f66957df2e548f9f9c788ee384f596fea4e",
|
||||
"logging_tag": "@0.11.3+1",
|
||||
"markdown_tag": "@0.11.0",
|
||||
"matcher_tag": "@0.12.0+2",
|
||||
|
|
|
@ -10,10 +10,8 @@ import 'dart:convert';
|
|||
import 'dart:core';
|
||||
|
||||
import 'package:analysis_server/src/analysis_server.dart';
|
||||
import 'package:analyzer/exception/exception.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/instrumentation/instrumentation.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/plugin/resolver_provider.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/source/package_map_provider.dart';
|
||||
|
@ -30,9 +28,6 @@ import 'package:analyzer/src/generated/java_io.dart';
|
|||
import 'package:analyzer/src/generated/sdk.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
import 'package:analyzer/src/lint/config.dart';
|
||||
import 'package:analyzer/src/lint/linter.dart';
|
||||
import 'package:analyzer/src/lint/registry.dart';
|
||||
import 'package:analyzer/src/task/options.dart';
|
||||
import 'package:analyzer/src/util/absolute_path.dart';
|
||||
import 'package:analyzer/src/util/glob.dart';
|
||||
|
@ -345,11 +340,6 @@ abstract class ContextManagerCallbacks {
|
|||
*/
|
||||
AnalysisContext addContext(Folder folder, AnalysisOptions options);
|
||||
|
||||
/**
|
||||
* The given [file] was removed from the folder analyzed in the [driver].
|
||||
*/
|
||||
void applyFileRemoved(AnalysisDriver driver, String file);
|
||||
|
||||
/**
|
||||
* Called when the set of files associated with a context have changed (or
|
||||
* some of those files have been modified). [changeSet] is the set of
|
||||
|
@ -357,6 +347,11 @@ abstract class ContextManagerCallbacks {
|
|||
*/
|
||||
void applyChangesToContext(Folder contextFolder, ChangeSet changeSet);
|
||||
|
||||
/**
|
||||
* The given [file] was removed from the folder analyzed in the [driver].
|
||||
*/
|
||||
void applyFileRemoved(AnalysisDriver driver, String file);
|
||||
|
||||
/**
|
||||
* Signals that the context manager has started to compute a package map (if
|
||||
* [computing] is `true`) or has finished (if [computing] is `false`).
|
||||
|
@ -635,37 +630,25 @@ class ContextManagerImpl implements ContextManager {
|
|||
return;
|
||||
}
|
||||
|
||||
// In case options files are removed, revert to defaults.
|
||||
AnalysisOptionsImpl analysisOptions;
|
||||
if (optionsRemoved) {
|
||||
// Start with defaults.
|
||||
info.context.analysisOptions = new AnalysisOptionsImpl();
|
||||
|
||||
// In case options files are removed, revert to defaults.
|
||||
analysisOptions = new AnalysisOptionsImpl.from(defaultContextOptions);
|
||||
// Apply inherited options.
|
||||
options = _toStringMap(_getEmbeddedOptions(info));
|
||||
if (options != null) {
|
||||
configureContextOptions(info.context, options);
|
||||
}
|
||||
} else {
|
||||
analysisOptions =
|
||||
new AnalysisOptionsImpl.from(info.context.analysisOptions);
|
||||
// Check for embedded options.
|
||||
Map embeddedOptions = _getEmbeddedOptions(info);
|
||||
if (embeddedOptions != null) {
|
||||
options = _toStringMap(new Merger().merge(embeddedOptions, options));
|
||||
}
|
||||
}
|
||||
|
||||
// Notify options processors.
|
||||
AnalysisEngine.instance.optionsPlugin.optionsProcessors
|
||||
.forEach((OptionsProcessor p) {
|
||||
try {
|
||||
p.optionsProcessed(info.context, options);
|
||||
} catch (e, stacktrace) {
|
||||
AnalysisEngine.instance.logger.logError(
|
||||
'Error processing analysis options',
|
||||
new CaughtException(e, stacktrace));
|
||||
}
|
||||
});
|
||||
|
||||
configureContextOptions(info.context, options);
|
||||
if (options != null) {
|
||||
applyToAnalysisOptions(analysisOptions, options);
|
||||
}
|
||||
info.context.analysisOptions = analysisOptions;
|
||||
|
||||
// Nothing more to do.
|
||||
if (options == null) {
|
||||
|
@ -711,16 +694,6 @@ class ContextManagerImpl implements ContextManager {
|
|||
}
|
||||
}
|
||||
|
||||
var lintOptions = options['linter'];
|
||||
if (lintOptions != null) {
|
||||
LintConfig config = new LintConfig.parseMap(lintOptions);
|
||||
Iterable<LintRule> lintRules = Registry.ruleRegistry.enabled(config);
|
||||
if (lintRules.isNotEmpty) {
|
||||
analysisOptions.lint = true;
|
||||
analysisOptions.lintRules = lintRules.toList();
|
||||
}
|
||||
}
|
||||
|
||||
applyToAnalysisOptions(analysisOptions, options);
|
||||
|
||||
// Nothing more to do.
|
||||
|
@ -1004,8 +977,7 @@ class ContextManagerImpl implements ContextManager {
|
|||
String contextRoot = info.folder.path;
|
||||
ContextBuilder builder =
|
||||
callbacks.createContextBuilder(info.folder, defaultContextOptions);
|
||||
AnalysisOptions options =
|
||||
builder.getAnalysisOptions(info.context, contextRoot);
|
||||
AnalysisOptions options = builder.getAnalysisOptions(contextRoot);
|
||||
SourceFactory factory = builder.createSourceFactory(contextRoot, options);
|
||||
info.context.analysisOptions = options;
|
||||
info.context.sourceFactory = factory;
|
||||
|
|
|
@ -7,18 +7,10 @@
|
|||
library analyzer.plugin.options;
|
||||
|
||||
import 'package:analyzer/error/listener.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/plugin/options_plugin.dart';
|
||||
import 'package:plugin/plugin.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
/// The identifier of the extension point that allows plugins to access
|
||||
/// options defined in the analysis options file. The object used as an
|
||||
/// extension must be an [OptionsProcessor].
|
||||
final String OPTIONS_PROCESSOR_EXTENSION_POINT_ID = Plugin.join(
|
||||
OptionsPlugin.UNIQUE_IDENTIFIER,
|
||||
OptionsPlugin.OPTIONS_PROCESSOR_EXTENSION_POINT);
|
||||
|
||||
/// The identifier of the extension point that allows plugins to validate
|
||||
/// options defined in the analysis options file. The object used as an
|
||||
/// extension must be an [OptionsValidator].
|
||||
|
@ -26,9 +18,7 @@ final String OPTIONS_VALIDATOR_EXTENSION_POINT_ID = Plugin.join(
|
|||
OptionsPlugin.UNIQUE_IDENTIFIER,
|
||||
OptionsPlugin.OPTIONS_VALIDATOR_EXTENSION_POINT);
|
||||
|
||||
/// Processes options defined in the analysis options file.
|
||||
///
|
||||
/// Clients may implement this class when implementing plugins.
|
||||
/// Validates options as defined in an analysis options file.
|
||||
///
|
||||
/// The options file format is intentionally very open-ended, giving clients
|
||||
/// utmost flexibility in defining their own options. The only hardfast
|
||||
|
@ -55,29 +45,9 @@ final String OPTIONS_VALIDATOR_EXTENSION_POINT_ID = Plugin.join(
|
|||
///
|
||||
/// bool useMultiPackage =
|
||||
/// options['compiler']['resolver']['useMultiPackage'];
|
||||
abstract class OptionsProcessor {
|
||||
/// Called when an error occurs in processing options.
|
||||
void onError(Exception exception);
|
||||
|
||||
/// Called when an options file is processed.
|
||||
///
|
||||
/// The options file is processed on analyzer initialization and
|
||||
/// subsequently when the file is changed on disk. In the event of a
|
||||
/// change notification, note that the notification simply indicates
|
||||
/// a change on disk. Content in specific option scopes may or may not
|
||||
/// be different. It is up to the implementer to check whether specific
|
||||
/// options have changed and to handle those changes appropriately. In
|
||||
/// addition to the [options] map, the associated analysis [context] is
|
||||
/// provided as well to allow for context-specific configuration.
|
||||
void optionsProcessed(AnalysisContext context, Map<String, Object> options);
|
||||
}
|
||||
|
||||
/// Validates options as defined in an analysis options file.
|
||||
///
|
||||
/// Clients may implement this class when implementing plugins.
|
||||
///
|
||||
/// See [OptionsProcessor] for a description of the options file format.
|
||||
///
|
||||
abstract class OptionsValidator {
|
||||
/// Validate [options], reporting any errors to the given [reporter].
|
||||
void validate(ErrorReporter reporter, Map<String, YamlNode> options);
|
||||
|
|
|
@ -9,7 +9,6 @@ import 'dart:core';
|
|||
|
||||
import 'package:analyzer/context/declared_variables.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/plugin/resolver_provider.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/source/package_map_resolver.dart';
|
||||
|
@ -102,7 +101,7 @@ class ContextBuilder {
|
|||
AnalysisContext buildContext(String path) {
|
||||
InternalAnalysisContext context =
|
||||
AnalysisEngine.instance.createAnalysisContext();
|
||||
AnalysisOptions options = getAnalysisOptions(context, path);
|
||||
AnalysisOptions options = getAnalysisOptions(path);
|
||||
context.contentCache = contentCache;
|
||||
context.sourceFactory = createSourceFactory(path, options);
|
||||
context.analysisOptions = options;
|
||||
|
@ -325,15 +324,13 @@ class ContextBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the analysis options that should be used when the given [context] is
|
||||
* used to analyze code in the directory with the given [path].
|
||||
* Return the analysis options that should be used to analyze code in the
|
||||
* directory with the given [path].
|
||||
*/
|
||||
AnalysisOptions getAnalysisOptions(AnalysisContext context, String path) {
|
||||
AnalysisOptions getAnalysisOptions(String path) {
|
||||
AnalysisOptionsImpl options = createDefaultOptions();
|
||||
File optionsFile = getOptionsFile(path);
|
||||
if (optionsFile != null) {
|
||||
List<OptionsProcessor> optionsProcessors =
|
||||
AnalysisEngine.instance.optionsPlugin.optionsProcessors;
|
||||
// TODO(danrubel) restructure so that we don't recalculate the package map
|
||||
// more than once per path.
|
||||
Packages packages = createPackageMap(path);
|
||||
|
@ -348,11 +345,9 @@ class ContextBuilder {
|
|||
Map<String, YamlNode> optionMap =
|
||||
new AnalysisOptionsProvider(sourceFactory)
|
||||
.getOptionsFromFile(optionsFile);
|
||||
optionsProcessors.forEach(
|
||||
(OptionsProcessor p) => p.optionsProcessed(context, optionMap));
|
||||
applyToAnalysisOptions(options, optionMap);
|
||||
} on Exception catch (exception) {
|
||||
optionsProcessors.forEach((OptionsProcessor p) => p.onError(exception));
|
||||
} catch (_) {
|
||||
// Ignore exceptions thrown while trying to load the options file.
|
||||
}
|
||||
}
|
||||
return options;
|
||||
|
|
|
@ -13,10 +13,6 @@ import 'package:plugin/plugin.dart';
|
|||
/// by applications that want to consume options defined in the analysis
|
||||
/// options file.
|
||||
class OptionsPlugin implements Plugin {
|
||||
/// The simple identifier of the extension point that allows plugins to
|
||||
/// register new options processors.
|
||||
static const String OPTIONS_PROCESSOR_EXTENSION_POINT = 'optionsProcessor';
|
||||
|
||||
/// The simple identifier of the extension point that allows plugins to
|
||||
/// register new options validators.
|
||||
static const String OPTIONS_VALIDATOR_EXTENSION_POINT = 'optionsValidator';
|
||||
|
@ -24,18 +20,10 @@ class OptionsPlugin implements Plugin {
|
|||
/// The unique identifier of this plugin.
|
||||
static const String UNIQUE_IDENTIFIER = 'options.core';
|
||||
|
||||
/// The extension point that allows plugins to register new options
|
||||
/// processors.
|
||||
ExtensionPoint<OptionsProcessor> optionsProcessorExtensionPoint;
|
||||
|
||||
/// The extension point that allows plugins to register new options
|
||||
/// validators.
|
||||
ExtensionPoint<OptionsValidator> optionsValidatorExtensionPoint;
|
||||
|
||||
/// All contributed options processors.
|
||||
List<OptionsProcessor> get optionsProcessors =>
|
||||
optionsProcessorExtensionPoint?.extensions ?? const <OptionsProcessor>[];
|
||||
|
||||
/// All contributed options validators.
|
||||
List<OptionsValidator> get optionsValidators =>
|
||||
optionsValidatorExtensionPoint?.extensions ?? const <OptionsValidator>[];
|
||||
|
@ -45,9 +33,6 @@ class OptionsPlugin implements Plugin {
|
|||
|
||||
@override
|
||||
void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {
|
||||
optionsProcessorExtensionPoint = new ExtensionPoint<OptionsProcessor>(
|
||||
this, OPTIONS_PROCESSOR_EXTENSION_POINT, null);
|
||||
registerExtensionPoint(optionsProcessorExtensionPoint);
|
||||
optionsValidatorExtensionPoint = new ExtensionPoint<OptionsValidator>(
|
||||
this, OPTIONS_VALIDATOR_EXTENSION_POINT, null);
|
||||
registerExtensionPoint(optionsValidatorExtensionPoint);
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library analyzer.src.plugin.plugin_configuration;
|
||||
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
const _analyzerOptionScope = 'analyzer';
|
||||
|
||||
const _pluginOptionScope = 'plugins';
|
||||
|
||||
/// Parse the given string into a plugin manifest.
|
||||
PluginManifest parsePluginManifestString(String manifestSource) {
|
||||
var yaml = loadYaml(manifestSource);
|
||||
if (yaml == null) {
|
||||
return null;
|
||||
}
|
||||
_verifyMap(yaml, 'plugin manifest');
|
||||
Iterable<String> pluginHost = _parseHosts(yaml['contributes_to']);
|
||||
PluginInfo plugin = _parsePlugin(yaml);
|
||||
return new PluginManifest(contributesTo: pluginHost, plugin: plugin);
|
||||
}
|
||||
|
||||
String _asString(dynamic yaml) {
|
||||
if (yaml != null && yaml is! String) {
|
||||
throw new PluginConfigFormatException(
|
||||
'Unable to parse pugin manifest, '
|
||||
'expected `String`, got `${yaml.runtimeType}`',
|
||||
yaml);
|
||||
}
|
||||
return yaml;
|
||||
}
|
||||
|
||||
Iterable<String> _parseHosts(dynamic yaml) {
|
||||
List<String> hosts = <String>[];
|
||||
if (yaml is String) {
|
||||
hosts.add(yaml);
|
||||
} else if (yaml is YamlList) {
|
||||
yaml.forEach((h) => hosts.add(_asString(h)));
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
PluginInfo _parsePlugin(dynamic yaml) {
|
||||
if (yaml != null) {
|
||||
_verifyMap(yaml, 'plugin manifest');
|
||||
return new PluginInfo._fromYaml(details: yaml);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PluginInfo _processPluginMapping(dynamic name, dynamic details) {
|
||||
if (name is String) {
|
||||
if (details is String) {
|
||||
return new PluginInfo(name: name, version: details);
|
||||
}
|
||||
if (details is YamlMap) {
|
||||
return new PluginInfo._fromYaml(name: name, details: details);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_verifyMap(dynamic yaml, String context) {
|
||||
if (yaml is! YamlMap) {
|
||||
throw new PluginConfigFormatException(
|
||||
'Unable to parse $context, '
|
||||
'expected `YamlMap`, got `${yaml.runtimeType}`',
|
||||
yaml);
|
||||
}
|
||||
}
|
||||
|
||||
/// A callback for error handling.
|
||||
typedef ErrorHandler(Exception e);
|
||||
|
||||
/// Describes plugin configuration information as extracted from an
|
||||
/// analysis options map or plugin manifest.
|
||||
class PluginConfig {
|
||||
final Iterable<PluginInfo> plugins;
|
||||
PluginConfig(this.plugins);
|
||||
|
||||
/// Create a plugin configuration from an options map.
|
||||
factory PluginConfig.fromOptions(Map<String, Object> options) {
|
||||
List<PluginInfo> plugins = [];
|
||||
var analyzerOptions = options[_analyzerOptionScope];
|
||||
if (analyzerOptions != null) {
|
||||
//TODO(pq): handle "raw" maps (https://github.com/dart-lang/sdk/issues/25126)
|
||||
if (analyzerOptions is YamlMap) {
|
||||
var pluginConfig = analyzerOptions[_pluginOptionScope];
|
||||
if (pluginConfig is YamlMap) {
|
||||
pluginConfig.forEach((name, details) {
|
||||
var plugin = _processPluginMapping(name, details);
|
||||
if (plugin != null) {
|
||||
plugins.add(plugin);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Anything but an empty list of plugins is treated as a format error.
|
||||
if (pluginConfig != null) {
|
||||
throw new PluginConfigFormatException(
|
||||
'Unrecognized plugin config format, expected `YamlMap`, '
|
||||
'got `${pluginConfig.runtimeType}`',
|
||||
pluginConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new PluginConfig(plugins);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thrown on bad plugin config format.
|
||||
class PluginConfigFormatException implements Exception {
|
||||
/// Descriptive message.
|
||||
final message;
|
||||
|
||||
/// The `plugin:` yaml node for generating detailed error feedback.
|
||||
final yamlNode;
|
||||
PluginConfigFormatException(this.message, this.yamlNode);
|
||||
}
|
||||
|
||||
/// Extracts plugin config details from analysis options.
|
||||
class PluginConfigOptionsProcessor extends OptionsProcessor {
|
||||
final ErrorHandler _errorHandler;
|
||||
|
||||
PluginConfig _config;
|
||||
|
||||
PluginConfigOptionsProcessor([this._errorHandler]);
|
||||
|
||||
/// The processed plugin config.
|
||||
PluginConfig get config => _config;
|
||||
|
||||
@override
|
||||
void onError(Exception exception) {
|
||||
if (_errorHandler != null) {
|
||||
_errorHandler(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void optionsProcessed(AnalysisContext context, Map<String, Object> options) {
|
||||
_config = new PluginConfig.fromOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes plugin information.
|
||||
class PluginInfo {
|
||||
final String name;
|
||||
final String className;
|
||||
final String version;
|
||||
final String libraryUri;
|
||||
final String packageName;
|
||||
final String path;
|
||||
PluginInfo(
|
||||
{this.name,
|
||||
this.version,
|
||||
this.className,
|
||||
this.libraryUri,
|
||||
this.packageName,
|
||||
this.path});
|
||||
|
||||
factory PluginInfo._fromYaml({String name, YamlMap details}) =>
|
||||
new PluginInfo(
|
||||
name: name,
|
||||
version: _asString(details['version']),
|
||||
className: _asString(details['class_name']),
|
||||
libraryUri: _asString(details['library_uri']),
|
||||
packageName: _asString(details['package_name']),
|
||||
path: _asString(details['path']));
|
||||
}
|
||||
|
||||
/// Plugin manifests accompany plugin packages, providing
|
||||
/// configuration information for published plugins.
|
||||
///
|
||||
/// Provisionally, plugin manifests live in a file `plugin.yaml`
|
||||
/// at the root of the plugin package.
|
||||
///
|
||||
/// my_plugin/
|
||||
/// bin/
|
||||
/// lib/
|
||||
/// plugin.yaml
|
||||
/// pubspec.yaml
|
||||
///
|
||||
/// Provisional manifest file format:
|
||||
///
|
||||
/// class_name: MyAnalyzerPlugin
|
||||
/// library_uri: 'my_plugin/my_analyzer_plugin.dart'
|
||||
/// contributes_to: analyzer
|
||||
class PluginManifest {
|
||||
PluginInfo plugin;
|
||||
Iterable<String> contributesTo;
|
||||
PluginManifest({this.plugin, this.contributesTo});
|
||||
}
|
|
@ -14,6 +14,9 @@ import 'package:analyzer/src/generated/engine.dart';
|
|||
import 'package:analyzer/src/generated/java_engine.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/utilities_general.dart';
|
||||
import 'package:analyzer/src/lint/config.dart';
|
||||
import 'package:analyzer/src/lint/linter.dart';
|
||||
import 'package:analyzer/src/lint/registry.dart';
|
||||
import 'package:analyzer/src/task/general.dart';
|
||||
import 'package:analyzer/src/util/yaml.dart';
|
||||
import 'package:analyzer/task/general.dart';
|
||||
|
@ -35,12 +38,6 @@ void applyToAnalysisOptions(
|
|||
_processor.applyToAnalysisOptions(options, optionMap);
|
||||
}
|
||||
|
||||
/// Configure this [context] based on configuration details specified in
|
||||
/// the given [options]. If [options] is `null`, default values are applied.
|
||||
void configureContextOptions(
|
||||
AnalysisContext context, Map<String, Object> options) =>
|
||||
_processor.configure(context, options);
|
||||
|
||||
/// `analyzer` analysis options constants.
|
||||
class AnalyzerOptions {
|
||||
static const String analyzer = 'analyzer';
|
||||
|
@ -513,109 +510,13 @@ class _OptionsProcessor {
|
|||
var excludes = analyzer[AnalyzerOptions.exclude];
|
||||
_applyExcludes(options, excludes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure [context] based on the given [options] (which can be `null`
|
||||
/// to restore [defaults]).
|
||||
void configure(AnalysisContext context, Map<String, Object> options) {
|
||||
if (options == null) {
|
||||
options = defaults;
|
||||
}
|
||||
|
||||
var analyzer = options[AnalyzerOptions.analyzer];
|
||||
if (analyzer is Map) {
|
||||
// Set strong mode (default is false).
|
||||
var strongMode = analyzer[AnalyzerOptions.strong_mode];
|
||||
setStrongMode(context, strongMode);
|
||||
|
||||
// Set filters.
|
||||
var filters = analyzer[AnalyzerOptions.errors];
|
||||
setProcessors(context, filters);
|
||||
|
||||
// Process language options.
|
||||
var language = analyzer[AnalyzerOptions.language];
|
||||
setLanguageOptions(context, language);
|
||||
|
||||
// Process excludes.
|
||||
var excludes = analyzer[AnalyzerOptions.exclude];
|
||||
setExcludes(context, excludes);
|
||||
}
|
||||
}
|
||||
|
||||
void setExcludes(AnalysisContext context, Object excludes) {
|
||||
if (excludes is YamlList) {
|
||||
List<String> excludeList = toStringList(excludes);
|
||||
if (excludeList != null) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.excludePatterns = excludeList;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLanguageOption(
|
||||
AnalysisContext context, Object feature, Object value) {
|
||||
if (feature == AnalyzerOptions.enableAssertInitializer) {
|
||||
if (isTrue(value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableAssertInitializer = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
if (feature == AnalyzerOptions.enableStrictCallChecks) {
|
||||
if (isTrue(value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableStrictCallChecks = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
if (feature == AnalyzerOptions.enableSuperMixins) {
|
||||
if (isTrue(value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableSuperMixins = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLanguageOptions(AnalysisContext context, Object configs) {
|
||||
if (configs is YamlMap) {
|
||||
configs.nodes.forEach((k, v) {
|
||||
if (k is YamlScalar && v is YamlScalar) {
|
||||
String feature = k.value?.toString();
|
||||
setLanguageOption(context, feature, v.value);
|
||||
}
|
||||
});
|
||||
} else if (configs is Map) {
|
||||
configs.forEach((k, v) => setLanguageOption(context, k, v));
|
||||
}
|
||||
}
|
||||
|
||||
void setProcessors(AnalysisContext context, Object codes) {
|
||||
ErrorConfig config = new ErrorConfig(codes);
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.errorProcessors = config.processors;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
|
||||
void setStrongMode(AnalysisContext context, Object strongMode) {
|
||||
if (strongMode is Map) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
_applyStrongOptions(options, strongMode);
|
||||
context.analysisOptions = options;
|
||||
} else {
|
||||
strongMode = strongMode is bool ? strongMode : false;
|
||||
if (context.analysisOptions.strongMode != strongMode) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.strongMode = strongMode;
|
||||
context.analysisOptions = options;
|
||||
LintConfig config = parseConfig(optionMap);
|
||||
if (config != null) {
|
||||
Iterable<LintRule> lintRules = Registry.ruleRegistry.enabled(config);
|
||||
if (lintRules.isNotEmpty) {
|
||||
options.lint = true;
|
||||
options.lintRules = lintRules.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -635,6 +536,8 @@ class _OptionsProcessor {
|
|||
if (boolValue != null) {
|
||||
if (feature == AnalyzerOptions.enableAssertInitializer) {
|
||||
options.enableAssertInitializer = boolValue;
|
||||
} else if (feature == AnalyzerOptions.enableStrictCallChecks) {
|
||||
options.enableStrictCallChecks = true;
|
||||
} else if (feature == AnalyzerOptions.enableSuperMixins) {
|
||||
options.enableSuperMixins = boolValue;
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ ErrorProcessor processor;
|
|||
void configureOptions(String options) {
|
||||
Map<String, YamlNode> optionMap =
|
||||
optionsProvider.getOptionsFromString(options);
|
||||
configureContextOptions(context, optionMap);
|
||||
applyToAnalysisOptions(context.analysisOptions, optionMap);
|
||||
}
|
||||
|
||||
ErrorProcessor getProcessor(AnalysisError error) =>
|
||||
|
|
|
@ -6,18 +6,15 @@ library analyzer.test.src.context.context_builder_test;
|
|||
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/memory_file_system.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/src/context/builder.dart';
|
||||
import 'package:analyzer/src/context/source.dart';
|
||||
import 'package:analyzer/src/generated/bazel.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/plugin/options_plugin.dart';
|
||||
import 'package:package_config/packages.dart';
|
||||
import 'package:package_config/src/packages_impl.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:plugin/src/plugin_impl.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
|
@ -513,8 +510,7 @@ linter:
|
|||
- empty_constructor_bodies
|
||||
''');
|
||||
|
||||
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
_expectEqualOptions(options, expected);
|
||||
}
|
||||
|
||||
|
@ -536,24 +532,8 @@ analyzer:
|
|||
enableSuperMixins : true
|
||||
''');
|
||||
|
||||
AnalysisEngine engine = AnalysisEngine.instance;
|
||||
OptionsPlugin plugin = engine.optionsPlugin;
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
try {
|
||||
_TestOptionsProcessor processor = new _TestOptionsProcessor();
|
||||
processor.expectedOptions = <String, Object>{
|
||||
'analyzer': {
|
||||
'language': {'enableSuperMixins': true}
|
||||
}
|
||||
};
|
||||
(plugin.optionsProcessorExtensionPoint as ExtensionPointImpl)
|
||||
.add(processor);
|
||||
AnalysisContext context = engine.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
_expectEqualOptions(options, expected);
|
||||
} finally {
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
}
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
_expectEqualOptions(options, expected);
|
||||
}
|
||||
|
||||
void test_getAnalysisOptions_includes() {
|
||||
|
@ -589,26 +569,8 @@ analyzer:
|
|||
enableSuperMixins : true
|
||||
''');
|
||||
|
||||
AnalysisEngine engine = AnalysisEngine.instance;
|
||||
OptionsPlugin plugin = engine.optionsPlugin;
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
try {
|
||||
_TestOptionsProcessor processor = new _TestOptionsProcessor();
|
||||
processor.expectedOptions = <String, Object>{
|
||||
'analyzer': {
|
||||
'language': {'enableSuperMixins': true}
|
||||
},
|
||||
'foo': {'bar': 'baz'},
|
||||
'two': {'boo': 'newt'},
|
||||
};
|
||||
(plugin.optionsProcessorExtensionPoint as ExtensionPointImpl)
|
||||
.add(processor);
|
||||
AnalysisContext context = engine.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
_expectEqualOptions(options, expected);
|
||||
} finally {
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
}
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
_expectEqualOptions(options, expected);
|
||||
}
|
||||
|
||||
void test_getAnalysisOptions_invalid() {
|
||||
|
@ -617,20 +579,8 @@ analyzer:
|
|||
pathContext.join(path, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
|
||||
resourceProvider.newFile(filePath, ';');
|
||||
|
||||
AnalysisEngine engine = AnalysisEngine.instance;
|
||||
OptionsPlugin plugin = engine.optionsPlugin;
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
try {
|
||||
_TestOptionsProcessor processor = new _TestOptionsProcessor();
|
||||
(plugin.optionsProcessorExtensionPoint as ExtensionPointImpl)
|
||||
.add(processor);
|
||||
AnalysisContext context = engine.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
expect(options, isNotNull);
|
||||
expect(processor.errorCount, 1);
|
||||
} finally {
|
||||
plugin.registerExtensionPoints((_) {});
|
||||
}
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
expect(options, isNotNull);
|
||||
}
|
||||
|
||||
void test_getAnalysisOptions_noDefault_noOverrides() {
|
||||
|
@ -645,8 +595,7 @@ linter:
|
|||
- empty_constructor_bodies
|
||||
''');
|
||||
|
||||
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
_expectEqualOptions(options, new AnalysisOptionsImpl());
|
||||
}
|
||||
|
||||
|
@ -664,8 +613,7 @@ analyzer:
|
|||
enableSuperMixins : true
|
||||
''');
|
||||
|
||||
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
|
||||
AnalysisOptions options = builder.getAnalysisOptions(context, path);
|
||||
AnalysisOptions options = builder.getAnalysisOptions(path);
|
||||
_expectEqualOptions(options, expected);
|
||||
}
|
||||
|
||||
|
@ -783,27 +731,3 @@ class EmbedderYamlLocatorTest extends EmbedderRelatedTest {
|
|||
expect(locator.embedderYamls, hasLength(1));
|
||||
}
|
||||
}
|
||||
|
||||
class _TestOptionsProcessor implements OptionsProcessor {
|
||||
Map<String, Object> expectedOptions = null;
|
||||
|
||||
int errorCount = 0;
|
||||
|
||||
@override
|
||||
void onError(Exception exception) {
|
||||
errorCount++;
|
||||
}
|
||||
|
||||
@override
|
||||
void optionsProcessed(AnalysisContext context, Map<String, Object> options) {
|
||||
if (expectedOptions == null) {
|
||||
fail('Unexpected invocation of optionsProcessed');
|
||||
}
|
||||
expect(options, hasLength(expectedOptions.length));
|
||||
for (String key in expectedOptions.keys) {
|
||||
expect(options.containsKey(key), isTrue, reason: 'missing key $key');
|
||||
expect(options[key], expectedOptions[key],
|
||||
reason: 'values for key $key do not match');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library analyzer.test.src.plugin.plugin_config_test;
|
||||
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
main() {
|
||||
group('plugin config tests', () {
|
||||
group('parsing', () {
|
||||
test('plugin map', () {
|
||||
const optionsSrc = '''
|
||||
analyzer:
|
||||
plugins:
|
||||
my_plugin1: ^0.1.0 #shorthand
|
||||
my_plugin2:
|
||||
version: ^0.2.0
|
||||
my_plugin3:
|
||||
class_name: MyPlugin
|
||||
library_uri: myplugin/myplugin.dart
|
||||
path: '/u/disk/src/'
|
||||
''';
|
||||
var config = parseConfig(optionsSrc);
|
||||
var plugins = pluginsSortedByName(config.plugins);
|
||||
expect(plugins, hasLength(3));
|
||||
expect(plugins[0].name, equals('my_plugin1'));
|
||||
expect(plugins[0].version, equals('^0.1.0'));
|
||||
expect(plugins[1].name, equals('my_plugin2'));
|
||||
expect(plugins[1].version, equals('^0.2.0'));
|
||||
expect(plugins[2].name, equals('my_plugin3'));
|
||||
expect(plugins[2].version, isNull);
|
||||
expect(plugins[2].path, equals('/u/disk/src/'));
|
||||
expect(plugins[2].libraryUri, equals('myplugin/myplugin.dart'));
|
||||
expect(plugins[2].className, equals('MyPlugin'));
|
||||
});
|
||||
|
||||
test('plugin map (empty)', () {
|
||||
const optionsSrc = '''
|
||||
analyzer:
|
||||
plugins:
|
||||
# my_plugin1: ^0.1.0 #shorthand
|
||||
''';
|
||||
var config = parseConfig(optionsSrc);
|
||||
// Commented out plugins shouldn't cause a parse failure.
|
||||
expect(config.plugins, hasLength(0));
|
||||
});
|
||||
|
||||
test('plugin manifest', () {
|
||||
const manifestSrc = '''
|
||||
class_name: AnalyzerPlugin
|
||||
library_uri: myplugin/analyzer_plugin.dart
|
||||
contributes_to: analyzer
|
||||
''';
|
||||
var manifest = parsePluginManifestString(manifestSrc);
|
||||
var plugin = manifest.plugin;
|
||||
expect(plugin.libraryUri, equals('myplugin/analyzer_plugin.dart'));
|
||||
expect(plugin.className, equals('AnalyzerPlugin'));
|
||||
expect(manifest.contributesTo, unorderedEquals(['analyzer']));
|
||||
});
|
||||
|
||||
test('plugin manifest (contributes_to list)', () {
|
||||
const manifestSrc = '''
|
||||
class_name: AnalyzerPlugin
|
||||
library_uri: myplugin/analyzer_plugin.dart
|
||||
contributes_to:
|
||||
- analyzer
|
||||
- analysis_server
|
||||
''';
|
||||
var manifest = parsePluginManifestString(manifestSrc);
|
||||
var plugin = manifest.plugin;
|
||||
expect(plugin.libraryUri, equals('myplugin/analyzer_plugin.dart'));
|
||||
expect(plugin.className, equals('AnalyzerPlugin'));
|
||||
expect(manifest.contributesTo,
|
||||
unorderedEquals(['analyzer', 'analysis_server']));
|
||||
});
|
||||
|
||||
group('errors', () {
|
||||
test('bad config format', () {
|
||||
const optionsSrc = '''
|
||||
analyzer:
|
||||
plugins:
|
||||
- my_plugin1
|
||||
- my_plugin2
|
||||
''';
|
||||
try {
|
||||
parseConfig(optionsSrc);
|
||||
fail('expected PluginConfigFormatException');
|
||||
} on PluginConfigFormatException catch (e) {
|
||||
expect(
|
||||
e.message,
|
||||
equals(
|
||||
'Unrecognized plugin config format, expected `YamlMap`, got `YamlList`'));
|
||||
expect(e.yamlNode, new isInstanceOf<YamlList>());
|
||||
}
|
||||
});
|
||||
test('bad manifest format', () {
|
||||
const manifestSource = '''
|
||||
library_uri:
|
||||
- should be a scalar uri
|
||||
''';
|
||||
try {
|
||||
parsePluginManifestString(manifestSource);
|
||||
fail('expected PluginConfigFormatException');
|
||||
} on PluginConfigFormatException catch (e) {
|
||||
expect(
|
||||
e.message,
|
||||
equals(
|
||||
'Unable to parse pugin manifest, expected `String`, got `YamlList`'));
|
||||
expect(e.yamlNode, new isInstanceOf<YamlList>());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
PluginConfig parseConfig(String optionsSrc) {
|
||||
var options = new AnalysisOptionsProvider().getOptionsFromString(optionsSrc);
|
||||
return new PluginConfig.fromOptions(options);
|
||||
}
|
||||
|
||||
List<PluginInfo> pluginsSortedByName(Iterable<PluginInfo> plugins) =>
|
||||
plugins.toList()..sort((p1, p2) => p1.name.compareTo(p2.name));
|
|
@ -38,7 +38,7 @@ class ContextConfigurationTest extends AbstractContextTest {
|
|||
AnalysisOptions get analysisOptions => context.analysisOptions;
|
||||
|
||||
configureContext(String optionsSource) =>
|
||||
configureContextOptions(context, parseOptions(optionsSource));
|
||||
applyToAnalysisOptions(analysisOptions, parseOptions(optionsSource));
|
||||
|
||||
Map<String, YamlNode> parseOptions(String source) =>
|
||||
optionsProvider.getOptionsFromString(source);
|
||||
|
@ -87,7 +87,7 @@ analyzer:
|
|||
unused_local_variable: error
|
||||
''');
|
||||
|
||||
List<ErrorProcessor> processors = context.analysisOptions.errorProcessors;
|
||||
List<ErrorProcessor> processors = analysisOptions.errorProcessors;
|
||||
expect(processors, hasLength(2));
|
||||
|
||||
var unused_local = new AnalysisError(
|
||||
|
@ -118,7 +118,7 @@ analyzer:
|
|||
- 'test/**'
|
||||
''');
|
||||
|
||||
List<String> excludes = context.analysisOptions.excludePatterns;
|
||||
List<String> excludes = analysisOptions.excludePatterns;
|
||||
expect(excludes, unorderedEquals(['foo/bar.dart', 'test/**']));
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|||
import 'command_line/test_all.dart' as command_line;
|
||||
import 'context/test_all.dart' as context;
|
||||
import 'dart/test_all.dart' as dart;
|
||||
import 'plugin/plugin_config_test.dart' as plugin;
|
||||
import 'source/test_all.dart' as source;
|
||||
import 'summary/test_all.dart' as summary;
|
||||
import 'task/test_all.dart' as task;
|
||||
|
@ -21,7 +20,6 @@ main() {
|
|||
command_line.main();
|
||||
context.main();
|
||||
dart.main();
|
||||
plugin.main();
|
||||
source.main();
|
||||
summary.main();
|
||||
task.main();
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library analyzer_cli.src.boot_loader;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/src/context/context.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' as engine;
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:yaml/src/yaml_node.dart';
|
||||
|
||||
const _analyzerPackageName = 'analyzer';
|
||||
|
||||
/// Return non-null if there is a validation issue with this plugin.
|
||||
String validate(PluginInfo plugin) {
|
||||
var missing = <String>[];
|
||||
if (plugin.className == null) {
|
||||
missing.add('class name');
|
||||
}
|
||||
if (plugin.libraryUri == null) {
|
||||
missing.add('library uri');
|
||||
}
|
||||
if (missing.isEmpty) {
|
||||
// All good.
|
||||
return null;
|
||||
}
|
||||
return 'Plugin ${plugin.name} skipped, config missing: ${missing.join(", ")}';
|
||||
}
|
||||
|
||||
List<PluginInfo> _validate(Iterable<PluginInfo> plugins) {
|
||||
List<PluginInfo> validated = <PluginInfo>[];
|
||||
plugins.forEach((PluginInfo plugin) {
|
||||
String validation = validate(plugin);
|
||||
if (validation != null) {
|
||||
errorSink.writeln(validation);
|
||||
} else {
|
||||
validated.add(plugin);
|
||||
}
|
||||
});
|
||||
return validated;
|
||||
}
|
||||
|
||||
/// Source code assembler.
|
||||
class Assembler {
|
||||
/// Plugins to configure.
|
||||
final Iterable<PluginInfo> plugins;
|
||||
|
||||
/// Create an assembler for the given plugin [config].
|
||||
Assembler(this.plugins);
|
||||
|
||||
/// A string enumerating required package `import`s.
|
||||
String get enumerateImports =>
|
||||
plugins.map((PluginInfo p) => "import '${p.libraryUri}';").join('\n');
|
||||
|
||||
/// A string listing initialized plugin instances.
|
||||
String get pluginList =>
|
||||
plugins.map((PluginInfo p) => 'new ${p.className}()').join(', ');
|
||||
|
||||
/// Create a file containing a `main()` suitable for loading in spawned
|
||||
/// isolate.
|
||||
String createMain() => _generateMain();
|
||||
|
||||
String _generateMain() => """
|
||||
// Copyright (c) 2015, 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.
|
||||
|
||||
// This code was auto-generated, is not intended to be edited, and is subject to
|
||||
// significant change. Please see the README file for more information.
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
|
||||
$enumerateImports
|
||||
|
||||
void main(List<String> args) {
|
||||
var starter = new Driver();
|
||||
starter.userDefinedPlugins = [$pluginList];
|
||||
starter.start(args);
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
/// Given environment information extracted from command-line `args`, creates a
|
||||
/// a loadable analyzer "image".
|
||||
class BootLoader {
|
||||
/// Emits an error message to [errorSink] if plugin config can't be read.
|
||||
static final ErrorHandler _pluginConfigErrorHandler = (Exception e) {
|
||||
String details;
|
||||
if (e is PluginConfigFormatException) {
|
||||
details = e.message;
|
||||
var node = e.yamlNode;
|
||||
if (node is YamlNode) {
|
||||
SourceLocation location = node.span.start;
|
||||
details += ' (line ${location.line}, column ${location.column})';
|
||||
}
|
||||
} else {
|
||||
details = e.toString();
|
||||
}
|
||||
|
||||
errorSink.writeln('Plugin configuration skipped: $details');
|
||||
};
|
||||
|
||||
/// Reads plugin config info from the analysis options file.
|
||||
PluginConfigOptionsProcessor _pluginOptionsProcessor =
|
||||
new PluginConfigOptionsProcessor(_pluginConfigErrorHandler);
|
||||
|
||||
/// Create a loadable analyzer image configured with plugins derived from
|
||||
/// the given analyzer command-line `args`.
|
||||
Image createImage(List<String> args) {
|
||||
// Parse commandline options.
|
||||
CommandLineOptions options = CommandLineOptions.parse(args);
|
||||
|
||||
// Process analysis options file (and notify all interested parties).
|
||||
_processAnalysisOptions(options);
|
||||
|
||||
// TODO(pquitslund): Pass in .packages info
|
||||
return new Image(_pluginOptionsProcessor.config,
|
||||
args: args, packageRootPath: options.packageRootPath);
|
||||
}
|
||||
|
||||
File _getOptionsFile(
|
||||
CommandLineOptions options, ResourceProvider resourceProvider) {
|
||||
String analysisOptionsFile = options.analysisOptionsFile;
|
||||
if (analysisOptionsFile != null) {
|
||||
return resourceProvider.getFile(analysisOptionsFile);
|
||||
}
|
||||
File file =
|
||||
resourceProvider.getFile(engine.AnalysisEngine.ANALYSIS_OPTIONS_FILE);
|
||||
if (!file.exists) {
|
||||
file = resourceProvider
|
||||
.getFile(engine.AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
void _processAnalysisOptions(CommandLineOptions commandLineOptions) {
|
||||
// Determine options file path.
|
||||
try {
|
||||
File file = _getOptionsFile(
|
||||
commandLineOptions, PhysicalResourceProvider.INSTANCE);
|
||||
AnalysisOptionsProvider analysisOptionsProvider =
|
||||
new AnalysisOptionsProvider();
|
||||
Map<String, YamlNode> options =
|
||||
analysisOptionsProvider.getOptionsFromFile(file);
|
||||
//TODO(pq): thread in proper context.
|
||||
var temporaryContext = new AnalysisContextImpl();
|
||||
_pluginOptionsProcessor.optionsProcessed(temporaryContext, options);
|
||||
} on Exception catch (e) {
|
||||
_pluginOptionsProcessor.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A loadable "image" of a a configured analyzer instance.
|
||||
class Image {
|
||||
/// (Optional) package root path.
|
||||
final String packageRootPath;
|
||||
|
||||
/// (Optional) package map.
|
||||
final Map<String, Uri> packages;
|
||||
|
||||
/// (Optional) args to be passed on to the loaded main.
|
||||
final List<String> args;
|
||||
|
||||
/// Plugin configuration.
|
||||
final PluginConfig config;
|
||||
|
||||
/// Create an image with the given [config] and optionally [packages],
|
||||
/// [packageRootPath], and command line [args].
|
||||
Image(this.config, {this.packages, this.packageRootPath, this.args});
|
||||
|
||||
/// Load this image.
|
||||
///
|
||||
/// Loading an image consists in assembling an analyzer `main()`, configured
|
||||
/// to include the appropriate analyzer plugins as specified in
|
||||
/// `.analyzer_options` which is then run in a spawned isolate.
|
||||
Future load() {
|
||||
List<PluginInfo> plugins = _validate(config.plugins);
|
||||
String mainSource = new Assembler(plugins).createMain();
|
||||
|
||||
Completer completer = new Completer();
|
||||
ReceivePort exitListener = new ReceivePort();
|
||||
exitListener.listen((data) {
|
||||
completer.complete();
|
||||
exitListener.close();
|
||||
});
|
||||
|
||||
Uri uri =
|
||||
Uri.parse('data:application/dart;charset=utf-8,${Uri.encodeComponent(
|
||||
mainSource)}');
|
||||
|
||||
// TODO(pquitslund): update once .packages are supported.
|
||||
String packageRoot =
|
||||
packageRootPath != null ? packageRootPath : './packages';
|
||||
Uri packageUri = new Uri.file(packageRoot);
|
||||
|
||||
Isolate.spawnUri(uri, args, null /* msg */,
|
||||
packageRoot: packageUri, onExit: exitListener.sendPort);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ import 'package:analyzer/error/error.dart';
|
|||
import 'package:analyzer/file_system/file_system.dart' as file_system;
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/plugin/resolver_provider.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/source/package_map_provider.dart';
|
||||
|
@ -770,29 +769,21 @@ class Driver implements CommandLineStarter {
|
|||
AnalysisContext context,
|
||||
CommandLineOptions options) {
|
||||
file_system.File file = _getOptionsFile(resourceProvider, options);
|
||||
List<OptionsProcessor> optionsProcessors =
|
||||
AnalysisEngine.instance.optionsPlugin.optionsProcessors;
|
||||
|
||||
try {
|
||||
AnalysisOptionsProvider analysisOptionsProvider =
|
||||
new AnalysisOptionsProvider(sourceFactory);
|
||||
Map<String, YamlNode> optionMap =
|
||||
analysisOptionsProvider.getOptionsFromFile(file);
|
||||
optionsProcessors.forEach(
|
||||
(OptionsProcessor p) => p.optionsProcessed(context, optionMap));
|
||||
AnalysisOptionsProvider analysisOptionsProvider =
|
||||
new AnalysisOptionsProvider(sourceFactory);
|
||||
Map<String, YamlNode> optionMap =
|
||||
analysisOptionsProvider.getOptionsFromFile(file);
|
||||
|
||||
// Fill in lint rule defaults in case lints are enabled and rules are
|
||||
// not specified in an options file.
|
||||
if (options.lints && !containsLintRuleEntry(optionMap)) {
|
||||
setLints(context, linterPlugin.contributedRules);
|
||||
}
|
||||
// Fill in lint rule defaults in case lints are enabled and rules are
|
||||
// not specified in an options file.
|
||||
if (options.lints && !containsLintRuleEntry(optionMap)) {
|
||||
setLints(context, linterPlugin.contributedRules);
|
||||
}
|
||||
|
||||
// Ask engine to further process options.
|
||||
if (optionMap != null) {
|
||||
configureContextOptions(context, optionMap);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
optionsProcessors.forEach((OptionsProcessor p) => p.onError(e));
|
||||
// Ask engine to further process options.
|
||||
if (optionMap != null) {
|
||||
applyToAnalysisOptions(context.analysisOptions, optionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library analyzer_cli.src.plugin.plugin_manager;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
const _manifestFileName = 'plugins.yaml';
|
||||
|
||||
/// Given a local configuration (as defined in an analysis options file) and
|
||||
/// information from a plugin manifest, return plugin info appropriate for
|
||||
/// configuring this plugin.
|
||||
PluginInfo combine(PluginInfo localConfig, PluginInfo manifestInfo) {
|
||||
return new PluginInfo(
|
||||
name: localConfig.name,
|
||||
version: manifestInfo.version,
|
||||
className: manifestInfo.className,
|
||||
libraryUri: manifestInfo.libraryUri);
|
||||
}
|
||||
|
||||
/// Call-back to allow for the injection of manifest readers that do not need
|
||||
/// to go to disk (for testing purposes).
|
||||
typedef String ManifestReader(Uri uri);
|
||||
|
||||
/// Wraps a [plugin] info object elaborated with any configuration information
|
||||
/// extracted from an associated manifest and [status].
|
||||
class PluginDetails {
|
||||
/// Plugin status.
|
||||
final PluginStatus status;
|
||||
|
||||
/// Plugin info.
|
||||
final PluginInfo plugin;
|
||||
|
||||
/// Wrap a [plugin] with [status] info.
|
||||
PluginDetails(this.plugin) : status = PluginStatus.Applicable;
|
||||
PluginDetails.notApplicable(this.plugin)
|
||||
: status = PluginStatus.NotApplicable;
|
||||
PluginDetails.notFound(this.plugin) : status = PluginStatus.NotFound;
|
||||
}
|
||||
|
||||
/// Manages plugin information derived from plugin manifests.
|
||||
class PluginManager {
|
||||
/// Mapping from package name to package location.
|
||||
final Map<String, Uri> _packageMap;
|
||||
|
||||
/// The package naming the app to host plugins.
|
||||
final String hostPackage;
|
||||
|
||||
/// Function to perform the reading of manifest URIs. (For testing.)
|
||||
ManifestReader _manifestReader;
|
||||
|
||||
/// Create a plugin manager with backing package map information.
|
||||
PluginManager(this._packageMap, this.hostPackage,
|
||||
[ManifestReader manifestReader]) {
|
||||
_manifestReader =
|
||||
manifestReader != null ? manifestReader : _findAndReadManifestAtUri;
|
||||
}
|
||||
|
||||
/// Find a plugin manifest describing the given [pluginPackage].
|
||||
PluginManifest findManifest(String pluginPackage) {
|
||||
Uri uri = _packageMap[pluginPackage];
|
||||
String contents = _manifestReader(uri);
|
||||
if (contents == null) {
|
||||
return null;
|
||||
}
|
||||
return parsePluginManifestString(contents);
|
||||
}
|
||||
|
||||
/// Return [PluginDetails] derived from associated plugin manifests
|
||||
/// corresponding to plugins specified in the given [config].
|
||||
Iterable<PluginDetails> getPluginDetails(PluginConfig config) =>
|
||||
config.plugins.map((PluginInfo localConfig) {
|
||||
PluginManifest manifest = findManifest(localConfig.name);
|
||||
return _getDetails(localConfig, manifest);
|
||||
});
|
||||
|
||||
String _findAndReadManifestAtUri(Uri uri) {
|
||||
File manifestFile = _findManifest(uri);
|
||||
return manifestFile?.readAsStringSync();
|
||||
}
|
||||
|
||||
File _findManifest(Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Directory directory = new Directory.fromUri(uri);
|
||||
File file = new File(path.join(directory.path, _manifestFileName));
|
||||
|
||||
return file.existsSync() ? file : null;
|
||||
}
|
||||
|
||||
PluginDetails _getDetails(PluginInfo localConfig, PluginManifest manifest) {
|
||||
if (manifest == null) {
|
||||
return new PluginDetails.notFound(localConfig);
|
||||
}
|
||||
if (!manifest.contributesTo.contains(hostPackage)) {
|
||||
return new PluginDetails.notApplicable(localConfig);
|
||||
}
|
||||
|
||||
return new PluginDetails(combine(localConfig, manifest.plugin));
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes plugin status.
|
||||
enum PluginStatus { Applicable, NotApplicable, NotFound }
|
|
@ -2,7 +2,6 @@
|
|||
// 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 'boot_loader_test.dart' as boot_loader;
|
||||
import 'build_mode_test.dart' as build_mode_test;
|
||||
import 'driver_test.dart' as driver;
|
||||
import 'embedder_test.dart' as embedder;
|
||||
|
@ -10,14 +9,12 @@ import 'error_test.dart' as error;
|
|||
import 'options_test.dart' as options;
|
||||
import 'package_prefix_test.dart' as package_prefix;
|
||||
import 'perf_report_test.dart' as perf;
|
||||
import 'plugin_manager_test.dart' as plugin_manager;
|
||||
import 'reporter_test.dart' as reporter;
|
||||
import 'sdk_ext_test.dart' as sdk_ext;
|
||||
import 'super_mixin_test.dart' as super_mixin;
|
||||
//import 'strong_mode_test.dart' as strong_mode;
|
||||
|
||||
main() {
|
||||
boot_loader.main();
|
||||
build_mode_test.main();
|
||||
driver.main();
|
||||
embedder.main();
|
||||
|
@ -28,7 +25,6 @@ main() {
|
|||
error.main();
|
||||
options.main();
|
||||
perf.main();
|
||||
plugin_manager.main();
|
||||
reporter.main();
|
||||
super_mixin.main();
|
||||
package_prefix.main();
|
||||
|
|
|
@ -1,96 +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.
|
||||
|
||||
library analyzer_cli.test.boot_loader_test;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer_cli/src/boot_loader.dart';
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
main() {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
ExitHandler savedExitHandler;
|
||||
|
||||
/// Base setup.
|
||||
_setUp() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitHandler = exitHandler;
|
||||
savedExitCode = exitCode;
|
||||
exitHandler = (code) => exitCode = code;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
}
|
||||
|
||||
/// Base teardown.
|
||||
_tearDown() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
exitHandler = savedExitHandler;
|
||||
}
|
||||
|
||||
setUp(() => _setUp());
|
||||
|
||||
tearDown(() => _tearDown());
|
||||
|
||||
group('Bootloader', () {
|
||||
group('plugin processing', () {
|
||||
test('bad format', () {
|
||||
BootLoader loader = new BootLoader();
|
||||
loader.createImage([
|
||||
'--options',
|
||||
path.join(testDirectory, 'data/bad_plugin_options.yaml'),
|
||||
path.join(testDirectory, 'data/test_file.dart')
|
||||
]);
|
||||
expect(
|
||||
errorSink.toString(),
|
||||
'Plugin configuration skipped: Unrecognized plugin config '
|
||||
'format, expected `YamlMap`, got `YamlList` '
|
||||
'(line 2, column 4)\n');
|
||||
});
|
||||
test('plugin config', () {
|
||||
BootLoader loader = new BootLoader();
|
||||
Image image = loader.createImage([
|
||||
'--options',
|
||||
path.join(testDirectory, 'data/plugin_options.yaml'),
|
||||
path.join(testDirectory, 'data/test_file.dart')
|
||||
]);
|
||||
var plugins = image.config.plugins;
|
||||
expect(plugins, hasLength(1));
|
||||
expect(plugins.first.name, 'my_plugin1');
|
||||
});
|
||||
group('plugin validation', () {
|
||||
test('requires class name', () {
|
||||
expect(
|
||||
validate(new PluginInfo(
|
||||
name: 'test_plugin', libraryUri: 'my_package/foo.dart')),
|
||||
isNotNull);
|
||||
});
|
||||
test('requires library URI', () {
|
||||
expect(
|
||||
validate(
|
||||
new PluginInfo(name: 'test_plugin', className: 'MyPlugin')),
|
||||
isNotNull);
|
||||
});
|
||||
test('check', () {
|
||||
expect(
|
||||
validate(new PluginInfo(
|
||||
name: 'test_plugin',
|
||||
className: 'MyPlugin',
|
||||
libraryUri: 'my_package/foo.dart')),
|
||||
isNull);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -7,7 +7,6 @@ library analyzer_cli.test.driver;
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/source/error_processor.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
|
@ -17,7 +16,6 @@ import 'package:analyzer/src/services/lint.dart';
|
|||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:plugin/plugin.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:yaml/src/yaml_node.dart';
|
||||
|
||||
|
@ -53,18 +51,6 @@ main() {
|
|||
|
||||
group('Driver', () {
|
||||
group('options', () {
|
||||
test('custom processor', () {
|
||||
Driver driver = new Driver();
|
||||
TestProcessor processor = new TestProcessor();
|
||||
driver.userDefinedPlugins = [new TestPlugin(processor)];
|
||||
driver.start([
|
||||
'--options',
|
||||
path.join(testDirectory, 'data/test_options.yaml'),
|
||||
path.join(testDirectory, 'data/test_file.dart')
|
||||
]);
|
||||
expect(processor.options['test_plugin'], isNotNull);
|
||||
expect(processor.exception, isNull);
|
||||
});
|
||||
test('todos', () {
|
||||
drive('data/file_with_todo.dart');
|
||||
expect(outSink.toString().contains('[info]'), isFalse);
|
||||
|
@ -521,39 +507,6 @@ Map<String, YamlNode> parseOptions(String src) =>
|
|||
ErrorProcessor processorFor(AnalysisError error) =>
|
||||
processors.firstWhere((p) => p.appliesTo(error));
|
||||
|
||||
class TestPlugin extends Plugin {
|
||||
TestProcessor processor;
|
||||
TestPlugin(this.processor);
|
||||
|
||||
@override
|
||||
String get uniqueIdentifier => 'test_plugin.core';
|
||||
|
||||
@override
|
||||
void registerExtensionPoints(RegisterExtensionPoint register) {
|
||||
// None
|
||||
}
|
||||
|
||||
@override
|
||||
void registerExtensions(RegisterExtension register) {
|
||||
register(OPTIONS_PROCESSOR_EXTENSION_POINT_ID, processor);
|
||||
}
|
||||
}
|
||||
|
||||
class TestProcessor extends OptionsProcessor {
|
||||
Map<String, Object> options;
|
||||
Exception exception;
|
||||
|
||||
@override
|
||||
void onError(Exception exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@override
|
||||
void optionsProcessed(AnalysisContext context, Map<String, Object> options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
class TestSource implements Source {
|
||||
TestSource();
|
||||
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library analyzer_cli.test.plugin_manager_test;
|
||||
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer_cli/src/plugin/plugin_manager.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
group('plugin manager tests', () {
|
||||
test('combine plugin info', () {
|
||||
PluginInfo localInfo = new PluginInfo(name: 'my_plugin');
|
||||
PluginInfo manifestInfo = new PluginInfo(
|
||||
className: 'MyPlugin', libraryUri: 'my_plugin/my_plugin.dart');
|
||||
|
||||
PluginInfo merged = combine(localInfo, manifestInfo);
|
||||
expect(merged.name, equals('my_plugin'));
|
||||
expect(merged.className, equals('MyPlugin'));
|
||||
expect(merged.libraryUri, equals('my_plugin/my_plugin.dart'));
|
||||
});
|
||||
|
||||
test('find manifest', () {
|
||||
const manifestSrc = '''
|
||||
library_uri: 'my_plugin/my_plugin.dart'
|
||||
''';
|
||||
var packageMap = {'my_plugin': new Uri.file('my_plugin')};
|
||||
|
||||
PluginManager pm =
|
||||
new PluginManager(packageMap, 'analyzer', (Uri uri) => manifestSrc);
|
||||
|
||||
PluginManifest manifest = pm.findManifest('my_plugin');
|
||||
expect(manifest, isNotNull);
|
||||
expect(manifest.plugin.libraryUri, equals('my_plugin/my_plugin.dart'));
|
||||
});
|
||||
|
||||
final plugin1Uri = new Uri.file('my_plugin1');
|
||||
final plugin2Uri = new Uri.file('my_plugin2');
|
||||
final plugin3Uri = new Uri.file('my_plugin3');
|
||||
|
||||
const serverPluginManifest = '''
|
||||
library_uri: 'my_plugin2/my_plugin2.dart'
|
||||
contributes_to: analysis_server
|
||||
''';
|
||||
const analyzerPluginManifest = '''
|
||||
library_uri: 'my_plugin3/my_plugin3.dart'
|
||||
contributes_to: analyzer
|
||||
''';
|
||||
|
||||
var packageMap = {
|
||||
'my_plugin': plugin1Uri,
|
||||
'my_plugin2': plugin2Uri,
|
||||
'my_plugin3': plugin3Uri
|
||||
};
|
||||
|
||||
var manifestReader = (Uri uri) {
|
||||
if (uri == plugin2Uri) return serverPluginManifest;
|
||||
if (uri == plugin3Uri) return analyzerPluginManifest;
|
||||
return null;
|
||||
};
|
||||
|
||||
test('get plugin details', () {
|
||||
PluginManager pm =
|
||||
new PluginManager(packageMap, 'analysis_server', manifestReader);
|
||||
|
||||
PluginInfo notFound = new PluginInfo(name: 'my_plugin1');
|
||||
PluginInfo applicable = new PluginInfo(name: 'my_plugin2');
|
||||
PluginInfo notApplicable = new PluginInfo(name: 'my_plugin3');
|
||||
|
||||
PluginConfig localConfig =
|
||||
new PluginConfig([notFound, applicable, notApplicable]);
|
||||
|
||||
Iterable<PluginDetails> details = pm.getPluginDetails(localConfig);
|
||||
expect(details, hasLength(3));
|
||||
|
||||
List<PluginDetails> plugins = sortByName(details);
|
||||
|
||||
expect(plugins[0].plugin.name, equals('my_plugin1'));
|
||||
expect(plugins[0].status, equals(PluginStatus.NotFound));
|
||||
expect(plugins[1].plugin.name, equals('my_plugin2'));
|
||||
expect(
|
||||
plugins[1].plugin.libraryUri, equals('my_plugin2/my_plugin2.dart'));
|
||||
expect(plugins[1].status, equals(PluginStatus.Applicable));
|
||||
expect(plugins[2].plugin.name, equals('my_plugin3'));
|
||||
expect(plugins[2].status, equals(PluginStatus.NotApplicable));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
List<PluginDetails> sortByName(Iterable<PluginDetails> details) =>
|
||||
details.toList()
|
||||
..sort((p1, p2) => p1.plugin.name.compareTo(p2.plugin.name));
|
Loading…
Reference in a new issue