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

Side by Side 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 unified diff | 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 »
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 'dart:collection';
6
7 import 'package:analysis_server/plugin/protocol/protocol.dart' as server;
8 import 'package:analysis_server/src/channel/channel.dart';
9 import 'package:analysis_server/src/plugin/result_collector.dart';
10 import 'package:analysis_server/src/plugin/result_converter.dart';
11 import 'package:analysis_server/src/plugin/result_merger.dart';
12 import 'package:analyzer/file_system/file_system.dart';
13 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
14 import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
15 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
16
17 /**
18 * The object used to coordinate the results of notifications from the analysis
19 * server and multiple plugins.
20 */
21 class NotificationManager {
22 /**
23 * The identifier used to identify results from the server.
24 */
25 static const String serverId = 'server';
26
27 /**
28 * The channel used to send notifications to the client.
29 */
30 final ServerCommunicationChannel channel;
31
32 /**
33 * The resource provider used to get the path context.
34 */
35 final ResourceProvider provider;
36
37 /**
38 * A list of the paths of files and directories that are included for analysis .
39 */
40 List<String> includedPaths = <String>[];
41
42 /**
43 * A list of the paths of files and directories that are excluded from
44 * analysis.
45 */
46 List<String> excludedPaths = <String>[];
47
48 /**
49 * The current set of subscriptions to which the client has subscribed.
50 */
51 Map<server.AnalysisService, List<String>> currentSubscriptions =
52 <server.AnalysisService, List<String>>{};
53
54 /**
55 * The collector being used to collect the analysis errors from the plugins.
56 */
57 ResultCollector<List<server.AnalysisError>> errors;
58
59 /**
60 * The collector being used to collect the folding regions from the plugins.
61 */
62 ResultCollector<List<server.FoldingRegion>> folding;
63
64 /**
65 * The collector being used to collect the highlight regions from the plugins.
66 */
67 ResultCollector<List<server.HighlightRegion>> highlights;
68
69 /**
70 * The collector being used to collect the navigation parameters from the
71 * plugins.
72 */
73 ResultCollector<server.AnalysisNavigationParams> navigation;
74
75 /**
76 * The collector being used to collect the occurrences from the plugins.
77 */
78 ResultCollector<List<server.Occurrences>> occurrences;
79
80 /**
81 * The collector being used to collect the outlines from the plugins.
82 */
83 ResultCollector<List<server.Outline>> outlines;
84
85 /**
86 * The object used to convert results.
87 */
88 ResultConverter converter = new ResultConverter();
89
90 /**
91 * The object used to merge results.
92 */
93 ResultMerger merger = new ResultMerger();
94
95 /**
96 * Initialize a newly created notification manager.
97 */
98 NotificationManager(this.channel, this.provider) {
99 errors = new ResultCollector<List<server.AnalysisError>>(serverId,
100 predicate: _isIncluded);
101 folding = new ResultCollector<List<server.FoldingRegion>>(serverId);
102 highlights = new ResultCollector<List<server.HighlightRegion>>(serverId);
103 navigation = new ResultCollector<server.AnalysisNavigationParams>(serverId);
104 occurrences = new ResultCollector<List<server.Occurrences>>(serverId);
105 outlines = new ResultCollector<List<server.Outline>>(serverId);
106 }
107
108 /**
109 * Handle the given [notification] from the plugin with the given [pluginId].
110 */
111 void handlePluginNotification(
112 String pluginId, plugin.Notification notification) {
113 String event = notification.event;
114 switch (event) {
115 case plugin.ANALYSIS_NOTIFICATION_ERRORS:
116 plugin.AnalysisErrorsParams params =
117 new plugin.AnalysisErrorsParams.fromNotification(notification);
118 recordAnalysisErrors(
119 pluginId,
120 params.file,
121 params.errors
122 .map((plugin.AnalysisError error) =>
123 converter.convertAnalysisError(error))
124 .toList());
125 break;
126 case plugin.ANALYSIS_NOTIFICATION_FOLDING:
127 plugin.AnalysisFoldingParams params =
128 new plugin.AnalysisFoldingParams.fromNotification(notification);
129 recordFoldingRegions(
130 pluginId,
131 params.file,
132 params.regions
133 .map((plugin.FoldingRegion region) =>
134 converter.convertFoldingRegion(region))
135 .toList());
136 break;
137 case plugin.ANALYSIS_NOTIFICATION_HIGHLIGHTS:
138 plugin.AnalysisHighlightsParams params =
139 new plugin.AnalysisHighlightsParams.fromNotification(notification);
140 recordHighlightRegions(
141 pluginId,
142 params.file,
143 params.regions
144 .map((plugin.HighlightRegion region) =>
145 converter.convertHighlightRegion(region))
146 .toList());
147 break;
148 case plugin.ANALYSIS_NOTIFICATION_NAVIGATION:
149 plugin.AnalysisNavigationParams params =
150 new plugin.AnalysisNavigationParams.fromNotification(notification);
151 recordNavigationParams(pluginId, params.file,
152 converter.convertAnalysisNavigationParams(params));
153 break;
154 case plugin.ANALYSIS_NOTIFICATION_OCCURRENCES:
155 plugin.AnalysisOccurrencesParams params =
156 new plugin.AnalysisOccurrencesParams.fromNotification(notification);
157 recordOccurrences(
158 pluginId,
159 params.file,
160 params.occurrences
161 .map((plugin.Occurrences occurrences) =>
162 converter.convertOccurrences(occurrences))
163 .toList());
164 break;
165 case plugin.ANALYSIS_NOTIFICATION_OUTLINE:
166 plugin.AnalysisOutlineParams params =
167 new plugin.AnalysisOutlineParams.fromNotification(notification);
168 recordOutlines(
169 pluginId,
170 params.file,
171 params.outline
172 .map((plugin.Outline outline) =>
173 converter.convertOutline(outline))
174 .toList());
175 break;
176 }
177 }
178
179 /**
180 * Record error information from the plugin with the given [pluginId] for the
181 * file with the given [filePath].
182 */
183 void recordAnalysisErrors(
184 String pluginId, String filePath, List<server.AnalysisError> errorData) {
185 if (errors.isCollectingFor(filePath)) {
186 errors.putResults(filePath, pluginId, errorData);
187 List<List<server.AnalysisError>> unmergedErrors =
188 errors.getResults(filePath);
189 List<server.AnalysisError> mergedErrors =
190 merger.mergeAnalysisErrors(unmergedErrors);
191 channel.sendNotification(
192 new server.AnalysisErrorsParams(filePath, mergedErrors)
193 .toNotification());
194 }
195 }
196
197 /**
198 * Record folding information from the plugin with the given [pluginId] for
199 * the file with the given [filePath].
200 */
201 void recordFoldingRegions(String pluginId, String filePath,
202 List<server.FoldingRegion> foldingData) {
203 if (folding.isCollectingFor(filePath)) {
204 folding.putResults(filePath, pluginId, foldingData);
205 List<List<server.FoldingRegion>> unmergedFolding =
206 folding.getResults(filePath);
207 List<server.FoldingRegion> mergedFolding =
208 merger.mergeFoldingRegions(unmergedFolding);
209 channel.sendNotification(
210 new server.AnalysisFoldingParams(filePath, mergedFolding)
211 .toNotification());
212 }
213 }
214
215 /**
216 * Record highlight information from the plugin with the given [pluginId] for
217 * the file with the given [filePath].
218 */
219 void recordHighlightRegions(String pluginId, String filePath,
220 List<server.HighlightRegion> highlightData) {
221 if (highlights.isCollectingFor(filePath)) {
222 highlights.putResults(filePath, pluginId, highlightData);
223 List<List<server.HighlightRegion>> unmergedHighlights =
224 highlights.getResults(filePath);
225 List<server.HighlightRegion> mergedHighlights =
226 merger.mergeHighlightRegions(unmergedHighlights);
227 channel.sendNotification(
228 new server.AnalysisHighlightsParams(filePath, mergedHighlights)
229 .toNotification());
230 }
231 }
232
233 /**
234 * Record navigation information from the plugin with the given [pluginId] for
235 * the file with the given [filePath].
236 */
237 void recordNavigationParams(String pluginId, String filePath,
238 server.AnalysisNavigationParams navigationData) {
239 if (navigation.isCollectingFor(filePath)) {
240 navigation.putResults(filePath, pluginId, navigationData);
241 List<server.AnalysisNavigationParams> unmergedNavigations =
242 navigation.getResults(filePath);
243 server.AnalysisNavigationParams mergedNavigations =
244 merger.mergeNavigation(unmergedNavigations);
245 channel.sendNotification(mergedNavigations.toNotification());
246 }
247 }
248
249 /**
250 * Record occurrences information from the plugin with the given [pluginId]
251 * for the file with the given [filePath].
252 */
253 void recordOccurrences(String pluginId, String filePath,
254 List<server.Occurrences> occurrencesData) {
255 if (occurrences.isCollectingFor(filePath)) {
256 occurrences.putResults(filePath, pluginId, occurrencesData);
257 List<List<server.Occurrences>> unmergedOccurrences =
258 occurrences.getResults(filePath);
259 List<server.Occurrences> mergedOccurrences =
260 merger.mergeOccurrences(unmergedOccurrences);
261 channel.sendNotification(
262 new server.AnalysisOccurrencesParams(filePath, mergedOccurrences)
263 .toNotification());
264 }
265 }
266
267 /**
268 * Record outline information from the plugin with the given [pluginId] for
269 * the file with the given [filePath].
270 */
271 void recordOutlines(
272 String pluginId, String filePath, List<server.Outline> outlineData) {
273 if (outlines.isCollectingFor(filePath)) {
274 outlines.putResults(filePath, pluginId, outlineData);
275 List<List<server.Outline>> unmergedOutlines =
276 outlines.getResults(filePath);
277 List<server.Outline> mergedOutlines =
278 merger.mergeOutline(unmergedOutlines);
279 channel.sendNotification(new server.AnalysisOutlineParams(
280 filePath, server.FileKind.LIBRARY, mergedOutlines[0])
281 .toNotification());
282 }
283 }
284
285 /**
286 * Set the lists of [included] and [excluded] files.
287 */
288 void setAnalysisRoots(List<String> included, List<String> excluded) {
289 includedPaths = included;
290 excludedPaths = excluded;
291 }
292
293 /**
294 * Set the current subscriptions to the given set of [newSubscriptions].
295 */
296 void setSubscriptions(
297 Map<server.AnalysisService, List<String>> newSubscriptions) {
298 /**
299 * Return the collector associated with the given service, or `null` if the
300 * service is not handled by this manager.
301 */
302 ResultCollector collectorFor(server.AnalysisService service) {
303 switch (service) {
304 case server.AnalysisService.FOLDING:
305 return folding;
306 case server.AnalysisService.HIGHLIGHTS:
307 return highlights;
308 case server.AnalysisService.NAVIGATION:
309 return navigation;
310 case server.AnalysisService.OCCURRENCES:
311 return occurrences;
312 case server.AnalysisService.OUTLINE:
313 return outlines;
314 }
315 return null;
316 }
317
318 Set<server.AnalysisService> services =
319 new HashSet<server.AnalysisService>();
320 services.addAll(currentSubscriptions.keys);
321 services.addAll(newSubscriptions.keys);
322 services.forEach((server.AnalysisService service) {
323 ResultCollector collector = collectorFor(service);
324 if (collector != null) {
325 List<String> currentPaths = currentSubscriptions[service];
326 List<String> newPaths = newSubscriptions[service];
327 if (currentPaths == null) {
328 if (newPaths == null) {
329 // This should not happen.
330 return;
331 }
332 // All of the [newPaths] need to be added.
333 newPaths.forEach((String filePath) {
334 collector.startCollectingFor(filePath);
335 });
336 } else if (newPaths == null) {
337 // All of the [currentPaths] need to be removed.
338 currentPaths.forEach((String filePath) {
339 collector.stopCollectingFor(filePath);
340 });
341 } else {
342 // Compute the difference of the two sets.
343 newPaths.forEach((String filePath) {
344 if (!currentPaths.contains(filePath)) {
345 collector.startCollectingFor(filePath);
346 }
347 });
348 currentPaths.forEach((String filePath) {
349 if (!newPaths.contains(filePath)) {
350 collector.stopCollectingFor(filePath);
351 }
352 });
353 }
354 }
355 });
356 currentSubscriptions = newSubscriptions;
357 }
358
359 /**
360 * Return `true` if errors should be collected for the file with the given
361 * [path] (because it is being analyzed).
362 */
363 bool _isIncluded(String path) {
364 bool isIncluded() {
365 for (String includedPath in includedPaths) {
366 if (provider.pathContext.isWithin(includedPath, path) ||
367 provider.pathContext.equals(includedPath, path)) {
368 return true;
369 }
370 }
371 return false;
372 }
373
374 bool isExcluded() {
375 for (String excludedPath in excludedPaths) {
376 if (provider.pathContext.isWithin(excludedPath, path)) {
377 return true;
378 }
379 }
380 return false;
381 }
382
383 // TODO(brianwilkerson) Return false if error notifications are globally
384 // disabled.
385 return isIncluded() && !isExcluded();
386 }
387 }
OLDNEW
« 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