OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library analyzer.src.plugin.plugin_configuration; |
| 6 |
| 7 import 'package:analyzer/plugin/options.dart'; |
| 8 import 'package:analyzer/src/generated/engine.dart'; |
| 9 import 'package:yaml/yaml.dart'; |
| 10 |
| 11 const _analyzerOptionScope = 'analyzer'; |
| 12 |
| 13 const _pluginOptionScope = 'plugins'; |
| 14 |
| 15 /// Parse the given string into a plugin manifest. |
| 16 PluginManifest parsePluginManifestString(String manifestSource) { |
| 17 var yaml = loadYaml(manifestSource); |
| 18 if (yaml == null) { |
| 19 return null; |
| 20 } |
| 21 _verifyMap(yaml, 'plugin manifest'); |
| 22 Iterable<String> pluginHost = _parseHosts(yaml['contributes_to']); |
| 23 PluginInfo plugin = _parsePlugin(yaml); |
| 24 return new PluginManifest(contributesTo: pluginHost, plugin: plugin); |
| 25 } |
| 26 |
| 27 String _asString(dynamic yaml) { |
| 28 if (yaml != null && yaml is! String) { |
| 29 throw new PluginConfigFormatException( |
| 30 'Unable to parse pugin manifest, ' |
| 31 'expected `String`, got `${yaml.runtimeType}`', |
| 32 yaml); |
| 33 } |
| 34 return yaml; |
| 35 } |
| 36 |
| 37 Iterable<String> _parseHosts(dynamic yaml) { |
| 38 List<String> hosts = <String>[]; |
| 39 if (yaml is String) { |
| 40 hosts.add(yaml); |
| 41 } else if (yaml is YamlList) { |
| 42 yaml.forEach((h) => hosts.add(_asString(h))); |
| 43 } |
| 44 return hosts; |
| 45 } |
| 46 |
| 47 PluginInfo _parsePlugin(dynamic yaml) { |
| 48 if (yaml != null) { |
| 49 _verifyMap(yaml, 'plugin manifest'); |
| 50 return new PluginInfo._fromYaml(details: yaml); |
| 51 } |
| 52 return null; |
| 53 } |
| 54 |
| 55 PluginInfo _processPluginMapping(dynamic name, dynamic details) { |
| 56 if (name is String) { |
| 57 if (details is String) { |
| 58 return new PluginInfo(name: name, version: details); |
| 59 } |
| 60 if (details is YamlMap) { |
| 61 return new PluginInfo._fromYaml(name: name, details: details); |
| 62 } |
| 63 } |
| 64 |
| 65 return null; |
| 66 } |
| 67 |
| 68 _verifyMap(dynamic yaml, String context) { |
| 69 if (yaml is! YamlMap) { |
| 70 throw new PluginConfigFormatException( |
| 71 'Unable to parse $context, ' |
| 72 'expected `YamlMap`, got `${yaml.runtimeType}`', |
| 73 yaml); |
| 74 } |
| 75 } |
| 76 |
| 77 /// A callback for error handling. |
| 78 typedef ErrorHandler(Exception e); |
| 79 |
| 80 /// Describes plugin configuration information as extracted from an |
| 81 /// analysis options map or plugin manifest. |
| 82 class PluginConfig { |
| 83 final Iterable<PluginInfo> plugins; |
| 84 PluginConfig(this.plugins); |
| 85 |
| 86 /// Create a plugin configuration from an options map. |
| 87 factory PluginConfig.fromOptions(Map<String, YamlNode> options) { |
| 88 List<PluginInfo> plugins = []; |
| 89 var analyzerOptions = options[_analyzerOptionScope]; |
| 90 if (analyzerOptions != null) { |
| 91 if (analyzerOptions is YamlMap) { |
| 92 var pluginConfig = analyzerOptions[_pluginOptionScope]; |
| 93 if (pluginConfig is YamlMap) { |
| 94 pluginConfig.forEach((name, details) { |
| 95 var plugin = _processPluginMapping(name, details); |
| 96 if (plugin != null) { |
| 97 plugins.add(plugin); |
| 98 } |
| 99 }); |
| 100 } else { |
| 101 // Anything but an empty list of plugins is treated as a format error. |
| 102 if (pluginConfig != null) { |
| 103 throw new PluginConfigFormatException( |
| 104 'Unrecognized plugin config format, expected `YamlMap`, ' |
| 105 'got `${pluginConfig.runtimeType}`', |
| 106 pluginConfig); |
| 107 } |
| 108 } |
| 109 } |
| 110 } |
| 111 |
| 112 return new PluginConfig(plugins); |
| 113 } |
| 114 } |
| 115 |
| 116 /// Thrown on bad plugin config format. |
| 117 class PluginConfigFormatException implements Exception { |
| 118 /// Descriptive message. |
| 119 final message; |
| 120 |
| 121 /// The `plugin:` yaml node for generating detailed error feedback. |
| 122 final yamlNode; |
| 123 PluginConfigFormatException(this.message, this.yamlNode); |
| 124 } |
| 125 |
| 126 /// Extracts plugin config details from analysis options. |
| 127 class PluginConfigOptionsProcessor extends OptionsProcessor { |
| 128 final ErrorHandler _errorHandler; |
| 129 |
| 130 PluginConfig _config; |
| 131 |
| 132 PluginConfigOptionsProcessor([this._errorHandler]); |
| 133 |
| 134 /// The processed plugin config. |
| 135 PluginConfig get config => _config; |
| 136 |
| 137 @override |
| 138 void onError(Exception exception) { |
| 139 if (_errorHandler != null) { |
| 140 _errorHandler(exception); |
| 141 } |
| 142 } |
| 143 |
| 144 @override |
| 145 void optionsProcessed( |
| 146 AnalysisContext context, Map<String, YamlNode> options) { |
| 147 _config = new PluginConfig.fromOptions(options); |
| 148 } |
| 149 } |
| 150 |
| 151 /// Describes plugin information. |
| 152 class PluginInfo { |
| 153 final String name; |
| 154 final String className; |
| 155 final String version; |
| 156 final String libraryUri; |
| 157 final String packageName; |
| 158 final String path; |
| 159 PluginInfo( |
| 160 {this.name, |
| 161 this.version, |
| 162 this.className, |
| 163 this.libraryUri, |
| 164 this.packageName, |
| 165 this.path}); |
| 166 |
| 167 factory PluginInfo._fromYaml({String name, YamlMap details}) => |
| 168 new PluginInfo( |
| 169 name: name, |
| 170 version: _asString(details['version']), |
| 171 className: _asString(details['class_name']), |
| 172 libraryUri: _asString(details['library_uri']), |
| 173 packageName: _asString(details['package_name']), |
| 174 path: _asString(details['path'])); |
| 175 } |
| 176 |
| 177 /// Plugin manifests accompany plugin packages, providing |
| 178 /// configuration information for published plugins. |
| 179 /// |
| 180 /// Provisionally, plugin manifests live in a file `plugin.yaml` |
| 181 /// at the root of the plugin package. |
| 182 /// |
| 183 /// my_plugin/ |
| 184 /// bin/ |
| 185 /// lib/ |
| 186 /// plugin.yaml |
| 187 /// pubspec.yaml |
| 188 /// |
| 189 /// Provisional manifest file format: |
| 190 /// |
| 191 /// class_name: MyAnalyzerPlugin |
| 192 /// library_uri: 'my_plugin/my_analyzer_plugin.dart' |
| 193 /// contributes_to: analyzer |
| 194 class PluginManifest { |
| 195 PluginInfo plugin; |
| 196 Iterable<String> contributesTo; |
| 197 PluginManifest({this.plugin, this.contributesTo}); |
| 198 } |
OLD | NEW |