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

Side by Side Diff: pkg/analysis_server/lib/src/plugin/plugin_manager.dart

Issue 2842013003: Add support for watch events and error notifications (Closed)
Patch Set: fix comment Created 3 years, 7 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
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:collection'; 6 import 'dart:collection';
7 import 'dart:io' show Platform; 7 import 'dart:io' show Platform;
8 8
9 import 'package:analysis_server/src/plugin/notification_manager.dart'; 9 import 'package:analysis_server/src/plugin/notification_manager.dart';
10 import 'package:analyzer/context/context_root.dart' as analyzer; 10 import 'package:analyzer/context/context_root.dart' as analyzer;
11 import 'package:analyzer/file_system/file_system.dart'; 11 import 'package:analyzer/file_system/file_system.dart';
12 import 'package:analyzer/instrumentation/instrumentation.dart'; 12 import 'package:analyzer/instrumentation/instrumentation.dart';
13 import 'package:analyzer/src/generated/bazel.dart'; 13 import 'package:analyzer/src/generated/bazel.dart';
14 import 'package:analyzer/src/generated/gn.dart'; 14 import 'package:analyzer/src/generated/gn.dart';
15 import 'package:analyzer/src/util/glob.dart';
15 import 'package:analyzer_plugin/channel/channel.dart'; 16 import 'package:analyzer_plugin/channel/channel.dart';
16 import 'package:analyzer_plugin/protocol/protocol.dart'; 17 import 'package:analyzer_plugin/protocol/protocol.dart';
18 import 'package:analyzer_plugin/protocol/protocol_constants.dart';
17 import 'package:analyzer_plugin/protocol/protocol_generated.dart'; 19 import 'package:analyzer_plugin/protocol/protocol_generated.dart';
18 import 'package:analyzer_plugin/src/channel/isolate_channel.dart'; 20 import 'package:analyzer_plugin/src/channel/isolate_channel.dart';
19 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart'; 21 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';
20 import 'package:convert/convert.dart'; 22 import 'package:convert/convert.dart';
21 import 'package:crypto/crypto.dart'; 23 import 'package:crypto/crypto.dart';
22 import 'package:meta/meta.dart'; 24 import 'package:meta/meta.dart';
23 import 'package:path/path.dart' as path; 25 import 'package:path/path.dart' as path;
26 import 'package:watcher/watcher.dart' as watcher;
24 27
25 /** 28 /**
26 * Information about a single plugin. 29 * Information about a single plugin.
27 */ 30 */
28 class PluginInfo { 31 class PluginInfo {
29 /** 32 /**
30 * The path to the root directory of the definition of the plugin on disk (the 33 * The path to the root directory of the definition of the plugin on disk (the
31 * directory containing the 'pubspec.yaml' file and the 'bin' directory). 34 * directory containing the 'pubspec.yaml' file and the 'bin' directory).
32 */ 35 */
33 final String path; 36 final String path;
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 } 244 }
242 } 245 }
243 } 246 }
244 247
245 /** 248 /**
246 * Broadcast a request built from the given [params] to all of the plugins 249 * Broadcast a request built from the given [params] to all of the plugins
247 * that are currently associated with the given [contextRoot]. Return a list 250 * that are currently associated with the given [contextRoot]. Return a list
248 * containing futures that will complete when each of the plugins have sent a 251 * containing futures that will complete when each of the plugins have sent a
249 * response. 252 * response.
250 */ 253 */
251 Map<PluginInfo, Future<Response>> broadcast( 254 Map<PluginInfo, Future<Response>> broadcastRequest(
252 analyzer.ContextRoot contextRoot, RequestParams params) { 255 analyzer.ContextRoot contextRoot, RequestParams params) {
253 List<PluginInfo> plugins = pluginsForContextRoot(contextRoot); 256 List<PluginInfo> plugins = pluginsForContextRoot(contextRoot);
254 Map<PluginInfo, Future<Response>> responseMap = 257 Map<PluginInfo, Future<Response>> responseMap =
255 <PluginInfo, Future<Response>>{}; 258 <PluginInfo, Future<Response>>{};
256 for (PluginInfo plugin in plugins) { 259 for (PluginInfo plugin in plugins) {
257 responseMap[plugin] = plugin.currentSession?.sendRequest(params); 260 responseMap[plugin] = plugin.currentSession?.sendRequest(params);
258 } 261 }
259 return responseMap; 262 return responseMap;
260 } 263 }
261 264
262 /** 265 /**
266 * Broadcast the given [watchEvent] to all of the plugins that are analyzing
267 * in contexts containing the file associated with the event. Return a list
268 * containing futures that will complete when each of the plugins have sent a
269 * response.
270 */
271 Future<List<Future<Response>>> broadcastWatchEvent(
272 watcher.WatchEvent watchEvent) async {
scheglov 2017/04/26 15:55:18 Do we await it anywhere? Or do we want to return t
Brian Wilkerson 2017/04/26 16:35:55 It's useful in test code to allow us to know wheth
273 String filePath = watchEvent.path;
274
275 /**
276 * Return `true` if the given glob [pattern] matches the file being watched.
277 */
278 bool matches(String pattern) =>
279 new Glob(path.separator, pattern).matches(filePath);
280
281 WatchEvent event = null;
282 List<Future<Response>> responses = <Future<Response>>[];
283 for (PluginInfo plugin in _pluginMap.values) {
284 PluginSession session = plugin.currentSession;
285 if (session != null &&
286 path.isWithin(plugin.path, filePath) &&
287 session.interestingFiles.any(matches)) {
288 event ??= _convertWatchEvent(watchEvent);
289 AnalysisHandleWatchEventsParams params =
290 new AnalysisHandleWatchEventsParams([event]);
291 responses.add(session.sendRequest(params));
292 }
293 }
294 return responses;
295 }
296
297 /**
263 * Return a list of all of the plugins that are currently associated with the 298 * Return a list of all of the plugins that are currently associated with the
264 * given [contextRoot]. 299 * given [contextRoot].
265 */ 300 */
266 @visibleForTesting 301 @visibleForTesting
267 List<PluginInfo> pluginsForContextRoot(analyzer.ContextRoot contextRoot) { 302 List<PluginInfo> pluginsForContextRoot(analyzer.ContextRoot contextRoot) {
268 List<PluginInfo> plugins = <PluginInfo>[]; 303 List<PluginInfo> plugins = <PluginInfo>[];
269 for (PluginInfo plugin in _pluginMap.values) { 304 for (PluginInfo plugin in _pluginMap.values) {
270 if (plugin.contextRoots.contains(contextRoot)) { 305 if (plugin.contextRoots.contains(contextRoot)) {
271 plugins.add(plugin); 306 plugins.add(plugin);
272 } 307 }
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 } 378 }
344 } 379 }
345 380
346 /** 381 /**
347 * Stop all of the plugins that are currently running. 382 * Stop all of the plugins that are currently running.
348 */ 383 */
349 Future<List<Null>> stopAll() { 384 Future<List<Null>> stopAll() {
350 return Future.wait(_pluginMap.values.map((PluginInfo info) => info.stop())); 385 return Future.wait(_pluginMap.values.map((PluginInfo info) => info.stop()));
351 } 386 }
352 387
388 WatchEventType _convertChangeType(watcher.ChangeType type) {
389 switch (type) {
390 case watcher.ChangeType.ADD:
391 return WatchEventType.ADD;
392 case watcher.ChangeType.MODIFY:
393 return WatchEventType.MODIFY;
394 case watcher.ChangeType.REMOVE:
395 return WatchEventType.REMOVE;
396 default:
397 throw new StateError('Unknown change type: $type');
398 }
399 }
400
401 WatchEvent _convertWatchEvent(watcher.WatchEvent watchEvent) {
402 return new WatchEvent(_convertChangeType(watchEvent.type), watchEvent.path);
403 }
404
353 /** 405 /**
354 * Return the execution path and .packages path associated with the plugin at 406 * Return the execution path and .packages path associated with the plugin at
355 * the given [path], or `null` if there is a problem that prevents us from 407 * the given [path], or `null` if there is a problem that prevents us from
356 * executing the plugin. 408 * executing the plugin.
357 */ 409 */
358 List<String> _pathsFor(String pluginPath) { 410 List<String> _pathsFor(String pluginPath) {
359 /** 411 /**
360 * Return `true` if the plugin in the give [folder] needs to be copied to a 412 * Return `true` if the plugin in the give [folder] needs to be copied to a
361 * temporary location so that 'pub' can be run to resolve dependencies. We 413 * temporary location so that 'pub' can be run to resolve dependencies. We
362 * need to run `pub` if the plugin contains a `pubspec.yaml` file and is not 414 * need to run `pub` if the plugin contains a `pubspec.yaml` file and is not
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 552
501 /** 553 /**
502 * Return a future that will complete when the plugin has stopped. 554 * Return a future that will complete when the plugin has stopped.
503 */ 555 */
504 Future<Null> get onDone => pluginStoppedCompleter.future; 556 Future<Null> get onDone => pluginStoppedCompleter.future;
505 557
506 /** 558 /**
507 * Handle the given [notification]. 559 * Handle the given [notification].
508 */ 560 */
509 void handleNotification(Notification notification) { 561 void handleNotification(Notification notification) {
562 if (notification.event == PLUGIN_NOTIFICATION_ERROR) {
563 PluginErrorParams params =
564 new PluginErrorParams.fromNotification(notification);
565 if (params.isFatal) {
566 info.stop();
567 stop();
568 }
569 }
510 info.notificationManager.handlePluginNotification(info.path, notification); 570 info.notificationManager.handlePluginNotification(info.path, notification);
511 } 571 }
512 572
513 /** 573 /**
514 * Handle the fact that the plugin has stopped. 574 * Handle the fact that the plugin has stopped.
515 */ 575 */
516 void handleOnDone() { 576 void handleOnDone() {
517 channel.close(); 577 channel.close();
518 channel = null; 578 channel = null;
519 pluginStoppedCompleter.complete(null); 579 pluginStoppedCompleter.complete(null);
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 Future<Null> stop() { 663 Future<Null> stop() {
604 if (channel == null) { 664 if (channel == null) {
605 throw new StateError('Cannot stop a plugin that is not running.'); 665 throw new StateError('Cannot stop a plugin that is not running.');
606 } 666 }
607 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not 667 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not
608 // terminate normally. 668 // terminate normally.
609 sendRequest(new PluginShutdownParams()); 669 sendRequest(new PluginShutdownParams());
610 return pluginStoppedCompleter.future; 670 return pluginStoppedCompleter.future;
611 } 671 }
612 } 672 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698