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, Object> options) { | |
88 List<PluginInfo> plugins = []; | |
89 var analyzerOptions = options[_analyzerOptionScope]; | |
90 if (analyzerOptions != null) { | |
91 //TODO(pq): handle "raw" maps (https://github.com/dart-lang/sdk/issues/251
26) | |
92 if (analyzerOptions is YamlMap) { | |
93 var pluginConfig = analyzerOptions[_pluginOptionScope]; | |
94 if (pluginConfig is YamlMap) { | |
95 pluginConfig.forEach((name, details) { | |
96 var plugin = _processPluginMapping(name, details); | |
97 if (plugin != null) { | |
98 plugins.add(plugin); | |
99 } | |
100 }); | |
101 } else { | |
102 // Anything but an empty list of plugins is treated as a format error. | |
103 if (pluginConfig != null) { | |
104 throw new PluginConfigFormatException( | |
105 'Unrecognized plugin config format, expected `YamlMap`, ' | |
106 'got `${pluginConfig.runtimeType}`', | |
107 pluginConfig); | |
108 } | |
109 } | |
110 } | |
111 } | |
112 | |
113 return new PluginConfig(plugins); | |
114 } | |
115 } | |
116 | |
117 /// Thrown on bad plugin config format. | |
118 class PluginConfigFormatException implements Exception { | |
119 /// Descriptive message. | |
120 final message; | |
121 | |
122 /// The `plugin:` yaml node for generating detailed error feedback. | |
123 final yamlNode; | |
124 PluginConfigFormatException(this.message, this.yamlNode); | |
125 } | |
126 | |
127 /// Extracts plugin config details from analysis options. | |
128 class PluginConfigOptionsProcessor extends OptionsProcessor { | |
129 final ErrorHandler _errorHandler; | |
130 | |
131 PluginConfig _config; | |
132 | |
133 PluginConfigOptionsProcessor([this._errorHandler]); | |
134 | |
135 /// The processed plugin config. | |
136 PluginConfig get config => _config; | |
137 | |
138 @override | |
139 void onError(Exception exception) { | |
140 if (_errorHandler != null) { | |
141 _errorHandler(exception); | |
142 } | |
143 } | |
144 | |
145 @override | |
146 void optionsProcessed(AnalysisContext context, Map<String, Object> 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 |