| // 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: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(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}); |
| } |