Index: packages/analyzer/lib/src/plugin/plugin_configuration.dart |
diff --git a/packages/analyzer/lib/src/plugin/plugin_configuration.dart b/packages/analyzer/lib/src/plugin/plugin_configuration.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9365a3c3eefe8a75fa4c31c718874ec12896077f |
--- /dev/null |
+++ b/packages/analyzer/lib/src/plugin/plugin_configuration.dart |
@@ -0,0 +1,198 @@ |
+// 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, YamlNode> options) { |
+ List<PluginInfo> plugins = []; |
+ var analyzerOptions = options[_analyzerOptionScope]; |
+ if (analyzerOptions != null) { |
+ 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, YamlNode> 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}); |
+} |