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

Unified Diff: pkg/analysis_server/lib/src/plugin/notification_manager.dart

Issue 2692373002: Add the notification manager (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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analysis_server/test/src/plugin/notification_manager_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/lib/src/plugin/notification_manager.dart
diff --git a/pkg/analysis_server/lib/src/plugin/notification_manager.dart b/pkg/analysis_server/lib/src/plugin/notification_manager.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7f150245b368fbef1d20c7d994b196278c541d6b
--- /dev/null
+++ b/pkg/analysis_server/lib/src/plugin/notification_manager.dart
@@ -0,0 +1,387 @@
+// 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 'dart:collection';
+
+import 'package:analysis_server/plugin/protocol/protocol.dart' as server;
+import 'package:analysis_server/src/channel/channel.dart';
+import 'package:analysis_server/src/plugin/result_collector.dart';
+import 'package:analysis_server/src/plugin/result_converter.dart';
+import 'package:analysis_server/src/plugin/result_merger.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+
+/**
+ * The object used to coordinate the results of notifications from the analysis
+ * server and multiple plugins.
+ */
+class NotificationManager {
+ /**
+ * The identifier used to identify results from the server.
+ */
+ static const String serverId = 'server';
+
+ /**
+ * The channel used to send notifications to the client.
+ */
+ final ServerCommunicationChannel channel;
+
+ /**
+ * The resource provider used to get the path context.
+ */
+ final ResourceProvider provider;
+
+ /**
+ * A list of the paths of files and directories that are included for analysis.
+ */
+ List<String> includedPaths = <String>[];
+
+ /**
+ * A list of the paths of files and directories that are excluded from
+ * analysis.
+ */
+ List<String> excludedPaths = <String>[];
+
+ /**
+ * The current set of subscriptions to which the client has subscribed.
+ */
+ Map<server.AnalysisService, List<String>> currentSubscriptions =
+ <server.AnalysisService, List<String>>{};
+
+ /**
+ * The collector being used to collect the analysis errors from the plugins.
+ */
+ ResultCollector<List<server.AnalysisError>> errors;
+
+ /**
+ * The collector being used to collect the folding regions from the plugins.
+ */
+ ResultCollector<List<server.FoldingRegion>> folding;
+
+ /**
+ * The collector being used to collect the highlight regions from the plugins.
+ */
+ ResultCollector<List<server.HighlightRegion>> highlights;
+
+ /**
+ * The collector being used to collect the navigation parameters from the
+ * plugins.
+ */
+ ResultCollector<server.AnalysisNavigationParams> navigation;
+
+ /**
+ * The collector being used to collect the occurrences from the plugins.
+ */
+ ResultCollector<List<server.Occurrences>> occurrences;
+
+ /**
+ * The collector being used to collect the outlines from the plugins.
+ */
+ ResultCollector<List<server.Outline>> outlines;
+
+ /**
+ * The object used to convert results.
+ */
+ ResultConverter converter = new ResultConverter();
+
+ /**
+ * The object used to merge results.
+ */
+ ResultMerger merger = new ResultMerger();
+
+ /**
+ * Initialize a newly created notification manager.
+ */
+ NotificationManager(this.channel, this.provider) {
+ errors = new ResultCollector<List<server.AnalysisError>>(serverId,
+ predicate: _isIncluded);
+ folding = new ResultCollector<List<server.FoldingRegion>>(serverId);
+ highlights = new ResultCollector<List<server.HighlightRegion>>(serverId);
+ navigation = new ResultCollector<server.AnalysisNavigationParams>(serverId);
+ occurrences = new ResultCollector<List<server.Occurrences>>(serverId);
+ outlines = new ResultCollector<List<server.Outline>>(serverId);
+ }
+
+ /**
+ * Handle the given [notification] from the plugin with the given [pluginId].
+ */
+ void handlePluginNotification(
+ String pluginId, plugin.Notification notification) {
+ String event = notification.event;
+ switch (event) {
+ case plugin.ANALYSIS_NOTIFICATION_ERRORS:
+ plugin.AnalysisErrorsParams params =
+ new plugin.AnalysisErrorsParams.fromNotification(notification);
+ recordAnalysisErrors(
+ pluginId,
+ params.file,
+ params.errors
+ .map((plugin.AnalysisError error) =>
+ converter.convertAnalysisError(error))
+ .toList());
+ break;
+ case plugin.ANALYSIS_NOTIFICATION_FOLDING:
+ plugin.AnalysisFoldingParams params =
+ new plugin.AnalysisFoldingParams.fromNotification(notification);
+ recordFoldingRegions(
+ pluginId,
+ params.file,
+ params.regions
+ .map((plugin.FoldingRegion region) =>
+ converter.convertFoldingRegion(region))
+ .toList());
+ break;
+ case plugin.ANALYSIS_NOTIFICATION_HIGHLIGHTS:
+ plugin.AnalysisHighlightsParams params =
+ new plugin.AnalysisHighlightsParams.fromNotification(notification);
+ recordHighlightRegions(
+ pluginId,
+ params.file,
+ params.regions
+ .map((plugin.HighlightRegion region) =>
+ converter.convertHighlightRegion(region))
+ .toList());
+ break;
+ case plugin.ANALYSIS_NOTIFICATION_NAVIGATION:
+ plugin.AnalysisNavigationParams params =
+ new plugin.AnalysisNavigationParams.fromNotification(notification);
+ recordNavigationParams(pluginId, params.file,
+ converter.convertAnalysisNavigationParams(params));
+ break;
+ case plugin.ANALYSIS_NOTIFICATION_OCCURRENCES:
+ plugin.AnalysisOccurrencesParams params =
+ new plugin.AnalysisOccurrencesParams.fromNotification(notification);
+ recordOccurrences(
+ pluginId,
+ params.file,
+ params.occurrences
+ .map((plugin.Occurrences occurrences) =>
+ converter.convertOccurrences(occurrences))
+ .toList());
+ break;
+ case plugin.ANALYSIS_NOTIFICATION_OUTLINE:
+ plugin.AnalysisOutlineParams params =
+ new plugin.AnalysisOutlineParams.fromNotification(notification);
+ recordOutlines(
+ pluginId,
+ params.file,
+ params.outline
+ .map((plugin.Outline outline) =>
+ converter.convertOutline(outline))
+ .toList());
+ break;
+ }
+ }
+
+ /**
+ * Record error information from the plugin with the given [pluginId] for the
+ * file with the given [filePath].
+ */
+ void recordAnalysisErrors(
+ String pluginId, String filePath, List<server.AnalysisError> errorData) {
+ if (errors.isCollectingFor(filePath)) {
+ errors.putResults(filePath, pluginId, errorData);
+ List<List<server.AnalysisError>> unmergedErrors =
+ errors.getResults(filePath);
+ List<server.AnalysisError> mergedErrors =
+ merger.mergeAnalysisErrors(unmergedErrors);
+ channel.sendNotification(
+ new server.AnalysisErrorsParams(filePath, mergedErrors)
+ .toNotification());
+ }
+ }
+
+ /**
+ * Record folding information from the plugin with the given [pluginId] for
+ * the file with the given [filePath].
+ */
+ void recordFoldingRegions(String pluginId, String filePath,
+ List<server.FoldingRegion> foldingData) {
+ if (folding.isCollectingFor(filePath)) {
+ folding.putResults(filePath, pluginId, foldingData);
+ List<List<server.FoldingRegion>> unmergedFolding =
+ folding.getResults(filePath);
+ List<server.FoldingRegion> mergedFolding =
+ merger.mergeFoldingRegions(unmergedFolding);
+ channel.sendNotification(
+ new server.AnalysisFoldingParams(filePath, mergedFolding)
+ .toNotification());
+ }
+ }
+
+ /**
+ * Record highlight information from the plugin with the given [pluginId] for
+ * the file with the given [filePath].
+ */
+ void recordHighlightRegions(String pluginId, String filePath,
+ List<server.HighlightRegion> highlightData) {
+ if (highlights.isCollectingFor(filePath)) {
+ highlights.putResults(filePath, pluginId, highlightData);
+ List<List<server.HighlightRegion>> unmergedHighlights =
+ highlights.getResults(filePath);
+ List<server.HighlightRegion> mergedHighlights =
+ merger.mergeHighlightRegions(unmergedHighlights);
+ channel.sendNotification(
+ new server.AnalysisHighlightsParams(filePath, mergedHighlights)
+ .toNotification());
+ }
+ }
+
+ /**
+ * Record navigation information from the plugin with the given [pluginId] for
+ * the file with the given [filePath].
+ */
+ void recordNavigationParams(String pluginId, String filePath,
+ server.AnalysisNavigationParams navigationData) {
+ if (navigation.isCollectingFor(filePath)) {
+ navigation.putResults(filePath, pluginId, navigationData);
+ List<server.AnalysisNavigationParams> unmergedNavigations =
+ navigation.getResults(filePath);
+ server.AnalysisNavigationParams mergedNavigations =
+ merger.mergeNavigation(unmergedNavigations);
+ channel.sendNotification(mergedNavigations.toNotification());
+ }
+ }
+
+ /**
+ * Record occurrences information from the plugin with the given [pluginId]
+ * for the file with the given [filePath].
+ */
+ void recordOccurrences(String pluginId, String filePath,
+ List<server.Occurrences> occurrencesData) {
+ if (occurrences.isCollectingFor(filePath)) {
+ occurrences.putResults(filePath, pluginId, occurrencesData);
+ List<List<server.Occurrences>> unmergedOccurrences =
+ occurrences.getResults(filePath);
+ List<server.Occurrences> mergedOccurrences =
+ merger.mergeOccurrences(unmergedOccurrences);
+ channel.sendNotification(
+ new server.AnalysisOccurrencesParams(filePath, mergedOccurrences)
+ .toNotification());
+ }
+ }
+
+ /**
+ * Record outline information from the plugin with the given [pluginId] for
+ * the file with the given [filePath].
+ */
+ void recordOutlines(
+ String pluginId, String filePath, List<server.Outline> outlineData) {
+ if (outlines.isCollectingFor(filePath)) {
+ outlines.putResults(filePath, pluginId, outlineData);
+ List<List<server.Outline>> unmergedOutlines =
+ outlines.getResults(filePath);
+ List<server.Outline> mergedOutlines =
+ merger.mergeOutline(unmergedOutlines);
+ channel.sendNotification(new server.AnalysisOutlineParams(
+ filePath, server.FileKind.LIBRARY, mergedOutlines[0])
+ .toNotification());
+ }
+ }
+
+ /**
+ * Set the lists of [included] and [excluded] files.
+ */
+ void setAnalysisRoots(List<String> included, List<String> excluded) {
+ includedPaths = included;
+ excludedPaths = excluded;
+ }
+
+ /**
+ * Set the current subscriptions to the given set of [newSubscriptions].
+ */
+ void setSubscriptions(
+ Map<server.AnalysisService, List<String>> newSubscriptions) {
+ /**
+ * Return the collector associated with the given service, or `null` if the
+ * service is not handled by this manager.
+ */
+ ResultCollector collectorFor(server.AnalysisService service) {
+ switch (service) {
+ case server.AnalysisService.FOLDING:
+ return folding;
+ case server.AnalysisService.HIGHLIGHTS:
+ return highlights;
+ case server.AnalysisService.NAVIGATION:
+ return navigation;
+ case server.AnalysisService.OCCURRENCES:
+ return occurrences;
+ case server.AnalysisService.OUTLINE:
+ return outlines;
+ }
+ return null;
+ }
+
+ Set<server.AnalysisService> services =
+ new HashSet<server.AnalysisService>();
+ services.addAll(currentSubscriptions.keys);
+ services.addAll(newSubscriptions.keys);
+ services.forEach((server.AnalysisService service) {
+ ResultCollector collector = collectorFor(service);
+ if (collector != null) {
+ List<String> currentPaths = currentSubscriptions[service];
+ List<String> newPaths = newSubscriptions[service];
+ if (currentPaths == null) {
+ if (newPaths == null) {
+ // This should not happen.
+ return;
+ }
+ // All of the [newPaths] need to be added.
+ newPaths.forEach((String filePath) {
+ collector.startCollectingFor(filePath);
+ });
+ } else if (newPaths == null) {
+ // All of the [currentPaths] need to be removed.
+ currentPaths.forEach((String filePath) {
+ collector.stopCollectingFor(filePath);
+ });
+ } else {
+ // Compute the difference of the two sets.
+ newPaths.forEach((String filePath) {
+ if (!currentPaths.contains(filePath)) {
+ collector.startCollectingFor(filePath);
+ }
+ });
+ currentPaths.forEach((String filePath) {
+ if (!newPaths.contains(filePath)) {
+ collector.stopCollectingFor(filePath);
+ }
+ });
+ }
+ }
+ });
+ currentSubscriptions = newSubscriptions;
+ }
+
+ /**
+ * Return `true` if errors should be collected for the file with the given
+ * [path] (because it is being analyzed).
+ */
+ bool _isIncluded(String path) {
+ bool isIncluded() {
+ for (String includedPath in includedPaths) {
+ if (provider.pathContext.isWithin(includedPath, path) ||
+ provider.pathContext.equals(includedPath, path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool isExcluded() {
+ for (String excludedPath in excludedPaths) {
+ if (provider.pathContext.isWithin(excludedPath, path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // TODO(brianwilkerson) Return false if error notifications are globally
+ // disabled.
+ return isIncluded() && !isExcluded();
+ }
+}
« no previous file with comments | « no previous file | pkg/analysis_server/test/src/plugin/notification_manager_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698