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

Unified Diff: pkg/analyzer_plugin/lib/plugin/plugin.dart

Issue 2667823003: Add top-level driver and abstract plugin superclass (Closed)
Patch Set: Created 3 years, 11 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer_plugin/lib/plugin/plugin.dart
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
new file mode 100644
index 0000000000000000000000000000000000000000..195e0ca245aa9a99fd37f4d1aabeb4abb857cb8c
--- /dev/null
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -0,0 +1,356 @@
+// Copyright (c) 2017, 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.
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart'
+ show AnalysisDriverScheduler, PerformanceLog;
+import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/channel/channel.dart';
+import 'package:analyzer_plugin/protocol/generated_protocol.dart';
+import 'package:analyzer_plugin/protocol/protocol.dart';
+import 'package:analyzer_plugin/protocol/protocol_constants.dart';
+import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';
+import 'package:analyzer_plugin/src/utilities/null_string_sink.dart';
+import 'package:analyzer_plugin/utilities/subscription_manager.dart';
+
+/**
+ * The abstract superclass of any class implementing a plugin for the analysis
+ * server.
+ *
+ * Clients may not implement or mix-in this class, but are expected to extend
+ * it.
+ */
+abstract class ServerPlugin {
+ /**
+ * The communication channel being used to communicate with the analysis
+ * server.
+ */
+ PluginCommunicationChannel _channel;
+
+ /**
+ * The resource provider used to access the file system.
+ */
+ final ResourceProvider resourceProvider;
+
+ /**
+ * The object used to manage analysis subscriptions.
+ */
+ final SubscriptionManager subscriptionManager = new SubscriptionManager();
+
+ /**
+ * The scheduler used by any analysis drivers that are created.
+ */
+ AnalysisDriverScheduler analysisDriverScheduler;
+
+ /**
+ * The performance log used by any analysis drivers that are created.
+ */
+ final PerformanceLog performanceLog =
+ new PerformanceLog(new NullStringSink());
+
+ /**
+ * The byte store used by any analysis drivers that are created.
+ */
+ 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
+
+ /**
+ * The file content overlay used by any analysis drivers that are created.
+ */
+ final FileContentOverlay fileContentOverlay = new FileContentOverlay();
+
+ /**
+ * Initialize a newly created analysis server plugin. If a resource [provider]
+ * is given, then it will be used to access the file system. Otherwise a
+ * resource provider that accesses the physical file system will be used.
+ */
+ ServerPlugin(this.resourceProvider) {
+ analysisDriverScheduler = new AnalysisDriverScheduler(performanceLog);
+ }
+
+ /**
+ * Return the communication channel being used to communicate with the
+ * analysis server, or `null` if the plugin has not been started.
+ */
+ PluginCommunicationChannel get channel => _channel;
+
+ /**
+ * Handle the fact that the file with the given [path] has been modified.
+ */
+ void contentChanged(String path) {
+ // Ignore changes to files.
+ }
+
+ /**
+ * Handle an 'analysis.handleWatchEvents' request.
+ */
+ AnalysisHandleWatchEventsResult handleAnalysisHandleWatchEvents(
+ Map<String, Object> parameters) =>
+ null;
+
+ /**
+ * Handle an 'analysis.reanalyze' request.
+ */
+ AnalysisReanalyzeResult handleAnalysisReanalyze(
+ Map<String, Object> parameters) =>
+ null;
+
+ /**
+ * Handle an 'analysis.setContextBuilderOptions' request.
+ */
+ AnalysisSetContextBuilderOptionsResult handleAnalysisSetContextBuilderOptions(
+ Map<String, Object> parameters) =>
+ null;
+
+ /**
+ * Handle an 'analysis.setContextRoots' request.
+ */
+ AnalysisSetContextRootsResult handleAnalysisSetContextRoots(
+ Map<String, Object> parameters) {
+ // TODO(brianwilkerson) Implement this so that implementors don't have to
+ // figure out how to manage contexts.
+ return null;
+ }
+
+ /**
+ * Handle an 'analysis.setPriorityFiles' request.
+ */
+ AnalysisSetPriorityFilesResult handleAnalysisSetPriorityFiles(
+ Map<String, Object> parameters) =>
+ new AnalysisSetPriorityFilesResult();
+
+ /**
+ * Handle an 'analysis.setSubscriptions' request. Most subclasses should not
+ * override this method, but should instead use the [subscriptionManager] to
+ * access the list of subscriptions for any given file.
+ */
+ AnalysisSetSubscriptionsResult handleAnalysisSetSubscriptions(
+ Map<String, Object> parameters) {
+ Map<AnalysisService, List<String>> subscriptions = validateParameter(
+ parameters,
+ ANALYSIS_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS,
+ 'analysis.setSubscriptions');
+ subscriptionManager.setSubscriptions(subscriptions);
+ // TODO(brianwilkerson) Cause any newly subscribed for notifications to be sent.
+ return new AnalysisSetSubscriptionsResult();
+ }
+
+ /**
+ * Handle an 'analysis.updateContent' request. Most subclasses should not
+ * override this method, but should instead use the [contentCache] to access
+ * the current content of overlaid files.
+ */
+ AnalysisUpdateContentResult handleAnalysisUpdateContent(
+ Map<String, Object> parameters) {
+ Map<String, Object> files = validateParameter(parameters,
+ ANALYSIS_REQUEST_UPDATE_CONTENT_FILES, 'analysis.updateContent');
+ files.forEach((String filePath, Object overlay) {
+ // We don't need to get the correct URI because only the full path is
+ // used by the contentCache.
+ Source source = resourceProvider.getFile(filePath).createSource();
+ if (overlay is AddContentOverlay) {
+ fileContentOverlay[source.fullName] = overlay.content;
+ } else if (overlay is ChangeContentOverlay) {
+ String fileName = source.fullName;
+ String oldContents = fileContentOverlay[fileName];
+ String newContents;
+ if (oldContents == null) {
+ // The server should only send a ChangeContentOverlay if there is
+ // already an existing overlay for the source.
+ throw new RequestFailure(new RequestError(
+ RequestErrorCode.INVALID_OVERLAY_CHANGE,
+ 'Invalid overlay change: no content to change'));
+ }
+ try {
+ newContents = SourceEdit.applySequence(oldContents, overlay.edits);
+ } on RangeError {
+ throw new RequestFailure(new RequestError(
+ RequestErrorCode.INVALID_OVERLAY_CHANGE,
+ 'Invalid overlay change: invalid edit'));
+ }
+ fileContentOverlay[fileName] = newContents;
+ } else if (overlay is RemoveContentOverlay) {
+ fileContentOverlay[source.fullName] = null;
+ }
+ contentChanged(filePath);
+ });
+ return new AnalysisUpdateContentResult();
+ }
+
+ /**
+ * Handle a 'completion.getSuggestions' request.
+ */
+ CompletionGetSuggestionsResult handleCompletionGetSuggestions(
+ Map<String, Object> parameters) =>
+ new CompletionGetSuggestionsResult(
+ -1, -1, const <CompletionSuggestion>[]);
+
+ /**
+ * Handle an 'edit.getAssists' request.
+ */
+ EditGetAssistsResult handleEditGetAssists(Map<String, Object> parameters) =>
+ new EditGetAssistsResult(const <SourceChange>[]);
+
+ /**
+ * Handle an 'edit.getAvailableRefactorings' request. Subclasses that override
+ * this method in order to participate in refactorings must also override the
+ * method [handleEditGetRefactoring].
+ */
+ EditGetAvailableRefactoringsResult handleEditGetAvailableRefactorings(
+ Map<String, Object> parameters) =>
+ new EditGetAvailableRefactoringsResult(const <RefactoringKind>[]);
+
+ /**
+ * Handle an 'edit.getFixes' request.
+ */
+ EditGetFixesResult handleEditGetFixes(Map<String, Object> parameters) =>
+ new EditGetFixesResult(const <AnalysisErrorFixes>[]);
+
+ /**
+ * Handle an 'edit.getRefactoring' request.
+ */
+ EditGetRefactoringResult handleEditGetRefactoring(
+ Map<String, Object> parameters) =>
+ null;
+
+ /**
+ * Handle a 'plugin.shutdown' request. Subclasses can override this method to
+ * perform any required clean-up, but cannot prevent the plugin from shutting
+ * down.
+ */
+ PluginShutdownResult handlePluginShutdown(Map<String, Object> parameters) =>
+ new PluginShutdownResult();
+
+ /**
+ * Handle a 'plugin.versionCheck' request.
+ */
+ PluginVersionCheckResult handlePluginVersionCheck(
+ Map<String, Object> parameters);
+
+ /**
+ * The method that is called when the analysis server closes the communication
+ * channel. This method will not be invoked under normal conditions because
+ * the server will send a shutdown request and the plugin will stop listening
+ * to the channel before the server closes the channel.
+ */
+ void onDone() {}
+
+ /**
+ * The method that is called when an error has occurred in the analysis
+ * server. This method will not be invoked under normal conditions.
+ */
+ void onError(Object exception, StackTrace stackTrace) {}
+
+ /**
+ * Start this plugin by listening to the given communication [channel].
+ */
+ void start(PluginCommunicationChannel channel) {
+ this._channel = channel;
+ _channel.listen(_onRequest, onError: onError, onDone: onDone);
+ }
+
+ /**
+ * Validate that the value in the map of [parameters] at the given [key] is of
+ * the type [T]. If it is, return it. Otherwise throw a [RequestFailure] that
+ * will cause an error to be returned to the server.
+ */
+ Object/*=T*/ validateParameter/*<T>*/(
+ Map<String, Object> parameters, String key, String requestName) {
+ Object value = parameters[key];
+ // ignore: type_annotation_generic_function_parameter
+ if (value is Object/*=T*/) {
+ return value;
+ }
+ String message;
+ if (value == null) {
+ message = 'Missing parameter $key in $requestName';
+ } else {
+ message = 'Invalid value for $key in $requestName (${value.runtimeType})';
+ }
+ throw new RequestFailure(
+ new RequestError(RequestErrorCode.INVALID_PARAMETER, message));
+ }
+
+ /**
+ * Compute the response that should be returned for the given [request], or
+ * `null` if the response has already been sent.
+ */
+ Response _getResponse(Request request) {
+ ResponseResult result = null;
+ switch (request.id) {
+ case ANALYSIS_REQUEST_HANDLE_WATCH_EVENTS:
+ result = handleAnalysisHandleWatchEvents(request.params);
+ break;
+ case ANALYSIS_REQUEST_REANALYZE:
+ result = handleAnalysisReanalyze(request.params);
+ break;
+ case ANALYSIS_REQUEST_SET_CONTEXT_BUILDER_OPTIONS:
+ result = handleAnalysisSetContextBuilderOptions(request.params);
+ break;
+ case ANALYSIS_REQUEST_SET_CONTEXT_ROOTS:
+ result = handleAnalysisSetContextRoots(request.params);
+ break;
+ case ANALYSIS_REQUEST_SET_PRIORITY_FILES:
+ result = handleAnalysisSetPriorityFiles(request.params);
+ break;
+ case ANALYSIS_REQUEST_SET_SUBSCRIPTIONS:
+ result = handleAnalysisSetSubscriptions(request.params);
+ break;
+ case ANALYSIS_REQUEST_UPDATE_CONTENT:
+ result = handleAnalysisUpdateContent(request.params);
+ break;
+ case COMPLETION_REQUEST_GET_SUGGESTIONS:
+ result = handleCompletionGetSuggestions(request.params);
+ break;
+ case EDIT_REQUEST_GET_ASSISTS:
+ result = handleEditGetAssists(request.params);
+ break;
+ case EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS:
+ result = handleEditGetAvailableRefactorings(request.params);
+ break;
+ case EDIT_REQUEST_GET_FIXES:
+ result = handleEditGetFixes(request.params);
+ break;
+ case EDIT_REQUEST_GET_REFACTORING:
+ result = handleEditGetRefactoring(request.params);
+ break;
+ case PLUGIN_REQUEST_SHUTDOWN:
+ result = handlePluginShutdown(request.params);
+ _channel.sendResponse(result.toResponse(request.id));
+ _channel.close();
+ return null;
+ case PLUGIN_REQUEST_VERSION_CHECK:
+ result = handlePluginVersionCheck(request.params);
+ break;
+ }
+ if (result == null) {
+ return new Response(request.id,
+ error: RequestErrorFactory.unknownRequest(request));
+ }
+ return result.toResponse(request.id);
+ }
+
+ /**
+ * The method that is called when a [request] is received from the analysis
+ * server.
+ */
+ void _onRequest(Request request) {
+ String id = request.id;
+ Response response;
+ try {
+ response = _getResponse(request);
+ } on RequestFailure catch (exception) {
+ _channel.sendResponse(new Response(id, error: exception.error));
+ } catch (exception, stackTrace) {
+ response = new Response(id,
+ error: new RequestError(
+ RequestErrorCode.PLUGIN_ERROR, exception.toString(),
+ stackTrace: stackTrace.toString()));
+ }
+ if (response != null) {
+ _channel.sendResponse(response);
+ }
+ }
+}
« 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