Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: pkg/analyzer_plugin/lib/plugin/plugin.dart

Issue 2667823003: Add top-level driver and abstract plugin superclass (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017, 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 import 'package:analyzer/file_system/file_system.dart';
6 import 'package:analyzer/src/dart/analysis/byte_store.dart';
7 import 'package:analyzer/src/dart/analysis/driver.dart'
8 show AnalysisDriverScheduler, PerformanceLog;
9 import 'package:analyzer/src/dart/analysis/file_state.dart';
10 import 'package:analyzer/src/generated/source.dart';
11 import 'package:analyzer_plugin/channel/channel.dart';
12 import 'package:analyzer_plugin/protocol/generated_protocol.dart';
13 import 'package:analyzer_plugin/protocol/protocol.dart';
14 import 'package:analyzer_plugin/protocol/protocol_constants.dart';
15 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';
16 import 'package:analyzer_plugin/src/utilities/null_string_sink.dart';
17 import 'package:analyzer_plugin/utilities/subscription_manager.dart';
18
19 /**
20 * The abstract superclass of any class implementing a plugin for the analysis
21 * server.
22 *
23 * Clients may not implement or mix-in this class, but are expected to extend
24 * it.
25 */
26 abstract class ServerPlugin {
27 /**
28 * The communication channel being used to communicate with the analysis
29 * server.
30 */
31 PluginCommunicationChannel _channel;
32
33 /**
34 * The resource provider used to access the file system.
35 */
36 final ResourceProvider resourceProvider;
37
38 /**
39 * The object used to manage analysis subscriptions.
40 */
41 final SubscriptionManager subscriptionManager = new SubscriptionManager();
42
43 /**
44 * The scheduler used by any analysis drivers that are created.
45 */
46 AnalysisDriverScheduler analysisDriverScheduler;
47
48 /**
49 * The performance log used by any analysis drivers that are created.
50 */
51 final PerformanceLog performanceLog =
52 new PerformanceLog(new NullStringSink());
53
54 /**
55 * The byte store used by any analysis drivers that are created.
56 */
57 final ByteStore byteStore = new MemoryByteStore();
scheglov 2017/01/31 21:27:02 Will it be injected? I guess we want to share anal
Brian Wilkerson 2017/01/31 21:37:09 I assume you mean that the on-disk data should be
scheglov 2017/01/31 21:41:17 SGTM
58
59 /**
60 * The file content overlay used by any analysis drivers that are created.
61 */
62 final FileContentOverlay fileContentOverlay = new FileContentOverlay();
63
64 /**
65 * Initialize a newly created analysis server plugin. If a resource [provider]
66 * is given, then it will be used to access the file system. Otherwise a
67 * resource provider that accesses the physical file system will be used.
68 */
69 ServerPlugin(this.resourceProvider) {
70 analysisDriverScheduler = new AnalysisDriverScheduler(performanceLog);
71 }
72
73 /**
74 * Return the communication channel being used to communicate with the
75 * analysis server, or `null` if the plugin has not been started.
76 */
77 PluginCommunicationChannel get channel => _channel;
78
79 /**
80 * Handle the fact that the file with the given [path] has been modified.
81 */
82 void contentChanged(String path) {
83 // Ignore changes to files.
84 }
85
86 /**
87 * Handle an 'analysis.handleWatchEvents' request.
88 */
89 AnalysisHandleWatchEventsResult handleAnalysisHandleWatchEvents(
90 Map<String, Object> parameters) =>
91 null;
92
93 /**
94 * Handle an 'analysis.reanalyze' request.
95 */
96 AnalysisReanalyzeResult handleAnalysisReanalyze(
97 Map<String, Object> parameters) =>
98 null;
99
100 /**
101 * Handle an 'analysis.setContextBuilderOptions' request.
102 */
103 AnalysisSetContextBuilderOptionsResult handleAnalysisSetContextBuilderOptions(
104 Map<String, Object> parameters) =>
105 null;
106
107 /**
108 * Handle an 'analysis.setContextRoots' request.
109 */
110 AnalysisSetContextRootsResult handleAnalysisSetContextRoots(
111 Map<String, Object> parameters) {
112 // TODO(brianwilkerson) Implement this so that implementors don't have to
113 // figure out how to manage contexts.
114 return null;
115 }
116
117 /**
118 * Handle an 'analysis.setPriorityFiles' request.
119 */
120 AnalysisSetPriorityFilesResult handleAnalysisSetPriorityFiles(
121 Map<String, Object> parameters) =>
122 new AnalysisSetPriorityFilesResult();
123
124 /**
125 * Handle an 'analysis.setSubscriptions' request. Most subclasses should not
126 * override this method, but should instead use the [subscriptionManager] to
127 * access the list of subscriptions for any given file.
128 */
129 AnalysisSetSubscriptionsResult handleAnalysisSetSubscriptions(
130 Map<String, Object> parameters) {
131 Map<AnalysisService, List<String>> subscriptions = validateParameter(
132 parameters,
133 ANALYSIS_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS,
134 'analysis.setSubscriptions');
135 subscriptionManager.setSubscriptions(subscriptions);
136 // TODO(brianwilkerson) Cause any newly subscribed for notifications to be s ent.
137 return new AnalysisSetSubscriptionsResult();
138 }
139
140 /**
141 * Handle an 'analysis.updateContent' request. Most subclasses should not
142 * override this method, but should instead use the [contentCache] to access
143 * the current content of overlaid files.
144 */
145 AnalysisUpdateContentResult handleAnalysisUpdateContent(
146 Map<String, Object> parameters) {
147 Map<String, Object> files = validateParameter(parameters,
148 ANALYSIS_REQUEST_UPDATE_CONTENT_FILES, 'analysis.updateContent');
149 files.forEach((String filePath, Object overlay) {
150 // We don't need to get the correct URI because only the full path is
151 // used by the contentCache.
152 Source source = resourceProvider.getFile(filePath).createSource();
153 if (overlay is AddContentOverlay) {
154 fileContentOverlay[source.fullName] = overlay.content;
155 } else if (overlay is ChangeContentOverlay) {
156 String fileName = source.fullName;
157 String oldContents = fileContentOverlay[fileName];
158 String newContents;
159 if (oldContents == null) {
160 // The server should only send a ChangeContentOverlay if there is
161 // already an existing overlay for the source.
162 throw new RequestFailure(new RequestError(
163 RequestErrorCode.INVALID_OVERLAY_CHANGE,
164 'Invalid overlay change: no content to change'));
165 }
166 try {
167 newContents = SourceEdit.applySequence(oldContents, overlay.edits);
168 } on RangeError {
169 throw new RequestFailure(new RequestError(
170 RequestErrorCode.INVALID_OVERLAY_CHANGE,
171 'Invalid overlay change: invalid edit'));
172 }
173 fileContentOverlay[fileName] = newContents;
174 } else if (overlay is RemoveContentOverlay) {
175 fileContentOverlay[source.fullName] = null;
176 }
177 contentChanged(filePath);
178 });
179 return new AnalysisUpdateContentResult();
180 }
181
182 /**
183 * Handle a 'completion.getSuggestions' request.
184 */
185 CompletionGetSuggestionsResult handleCompletionGetSuggestions(
186 Map<String, Object> parameters) =>
187 new CompletionGetSuggestionsResult(
188 -1, -1, const <CompletionSuggestion>[]);
189
190 /**
191 * Handle an 'edit.getAssists' request.
192 */
193 EditGetAssistsResult handleEditGetAssists(Map<String, Object> parameters) =>
194 new EditGetAssistsResult(const <SourceChange>[]);
195
196 /**
197 * Handle an 'edit.getAvailableRefactorings' request. Subclasses that override
198 * this method in order to participate in refactorings must also override the
199 * method [handleEditGetRefactoring].
200 */
201 EditGetAvailableRefactoringsResult handleEditGetAvailableRefactorings(
202 Map<String, Object> parameters) =>
203 new EditGetAvailableRefactoringsResult(const <RefactoringKind>[]);
204
205 /**
206 * Handle an 'edit.getFixes' request.
207 */
208 EditGetFixesResult handleEditGetFixes(Map<String, Object> parameters) =>
209 new EditGetFixesResult(const <AnalysisErrorFixes>[]);
210
211 /**
212 * Handle an 'edit.getRefactoring' request.
213 */
214 EditGetRefactoringResult handleEditGetRefactoring(
215 Map<String, Object> parameters) =>
216 null;
217
218 /**
219 * Handle a 'plugin.shutdown' request. Subclasses can override this method to
220 * perform any required clean-up, but cannot prevent the plugin from shutting
221 * down.
222 */
223 PluginShutdownResult handlePluginShutdown(Map<String, Object> parameters) =>
224 new PluginShutdownResult();
225
226 /**
227 * Handle a 'plugin.versionCheck' request.
228 */
229 PluginVersionCheckResult handlePluginVersionCheck(
230 Map<String, Object> parameters);
231
232 /**
233 * The method that is called when the analysis server closes the communication
234 * channel. This method will not be invoked under normal conditions because
235 * the server will send a shutdown request and the plugin will stop listening
236 * to the channel before the server closes the channel.
237 */
238 void onDone() {}
239
240 /**
241 * The method that is called when an error has occurred in the analysis
242 * server. This method will not be invoked under normal conditions.
243 */
244 void onError(Object exception, StackTrace stackTrace) {}
245
246 /**
247 * Start this plugin by listening to the given communication [channel].
248 */
249 void start(PluginCommunicationChannel channel) {
250 this._channel = channel;
251 _channel.listen(_onRequest, onError: onError, onDone: onDone);
252 }
253
254 /**
255 * Validate that the value in the map of [parameters] at the given [key] is of
256 * the type [T]. If it is, return it. Otherwise throw a [RequestFailure] that
257 * will cause an error to be returned to the server.
258 */
259 Object/*=T*/ validateParameter/*<T>*/(
260 Map<String, Object> parameters, String key, String requestName) {
261 Object value = parameters[key];
262 // ignore: type_annotation_generic_function_parameter
263 if (value is Object/*=T*/) {
264 return value;
265 }
266 String message;
267 if (value == null) {
268 message = 'Missing parameter $key in $requestName';
269 } else {
270 message = 'Invalid value for $key in $requestName (${value.runtimeType})';
271 }
272 throw new RequestFailure(
273 new RequestError(RequestErrorCode.INVALID_PARAMETER, message));
274 }
275
276 /**
277 * Compute the response that should be returned for the given [request], or
278 * `null` if the response has already been sent.
279 */
280 Response _getResponse(Request request) {
281 ResponseResult result = null;
282 switch (request.id) {
283 case ANALYSIS_REQUEST_HANDLE_WATCH_EVENTS:
284 result = handleAnalysisHandleWatchEvents(request.params);
285 break;
286 case ANALYSIS_REQUEST_REANALYZE:
287 result = handleAnalysisReanalyze(request.params);
288 break;
289 case ANALYSIS_REQUEST_SET_CONTEXT_BUILDER_OPTIONS:
290 result = handleAnalysisSetContextBuilderOptions(request.params);
291 break;
292 case ANALYSIS_REQUEST_SET_CONTEXT_ROOTS:
293 result = handleAnalysisSetContextRoots(request.params);
294 break;
295 case ANALYSIS_REQUEST_SET_PRIORITY_FILES:
296 result = handleAnalysisSetPriorityFiles(request.params);
297 break;
298 case ANALYSIS_REQUEST_SET_SUBSCRIPTIONS:
299 result = handleAnalysisSetSubscriptions(request.params);
300 break;
301 case ANALYSIS_REQUEST_UPDATE_CONTENT:
302 result = handleAnalysisUpdateContent(request.params);
303 break;
304 case COMPLETION_REQUEST_GET_SUGGESTIONS:
305 result = handleCompletionGetSuggestions(request.params);
306 break;
307 case EDIT_REQUEST_GET_ASSISTS:
308 result = handleEditGetAssists(request.params);
309 break;
310 case EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS:
311 result = handleEditGetAvailableRefactorings(request.params);
312 break;
313 case EDIT_REQUEST_GET_FIXES:
314 result = handleEditGetFixes(request.params);
315 break;
316 case EDIT_REQUEST_GET_REFACTORING:
317 result = handleEditGetRefactoring(request.params);
318 break;
319 case PLUGIN_REQUEST_SHUTDOWN:
320 result = handlePluginShutdown(request.params);
321 _channel.sendResponse(result.toResponse(request.id));
322 _channel.close();
323 return null;
324 case PLUGIN_REQUEST_VERSION_CHECK:
325 result = handlePluginVersionCheck(request.params);
326 break;
327 }
328 if (result == null) {
329 return new Response(request.id,
330 error: RequestErrorFactory.unknownRequest(request));
331 }
332 return result.toResponse(request.id);
333 }
334
335 /**
336 * The method that is called when a [request] is received from the analysis
337 * server.
338 */
339 void _onRequest(Request request) {
340 String id = request.id;
341 Response response;
342 try {
343 response = _getResponse(request);
344 } on RequestFailure catch (exception) {
345 _channel.sendResponse(new Response(id, error: exception.error));
346 } catch (exception, stackTrace) {
347 response = new Response(id,
348 error: new RequestError(
349 RequestErrorCode.PLUGIN_ERROR, exception.toString(),
350 stackTrace: stackTrace.toString()));
351 }
352 if (response != null) {
353 _channel.sendResponse(response);
354 }
355 }
356 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698