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

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

Issue 2874803003: Add support for built-in plugins (Closed)
Patch Set: 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
« no previous file with comments | « no previous file | pkg/analysis_server/test/domain_completion_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
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/src/util/glob.dart';
16 import 'package:analyzer_plugin/channel/channel.dart'; 16 import 'package:analyzer_plugin/channel/channel.dart';
17 import 'package:analyzer_plugin/protocol/protocol.dart'; 17 import 'package:analyzer_plugin/protocol/protocol.dart';
18 import 'package:analyzer_plugin/protocol/protocol_constants.dart'; 18 import 'package:analyzer_plugin/protocol/protocol_constants.dart';
19 import 'package:analyzer_plugin/protocol/protocol_generated.dart'; 19 import 'package:analyzer_plugin/protocol/protocol_generated.dart';
20 import 'package:analyzer_plugin/src/channel/isolate_channel.dart'; 20 import 'package:analyzer_plugin/src/channel/isolate_channel.dart';
21 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart'; 21 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';
22 import 'package:convert/convert.dart'; 22 import 'package:convert/convert.dart';
23 import 'package:crypto/crypto.dart'; 23 import 'package:crypto/crypto.dart';
24 import 'package:meta/meta.dart'; 24 import 'package:meta/meta.dart';
25 import 'package:path/path.dart' as path;
26 import 'package:watcher/watcher.dart' as watcher; 25 import 'package:watcher/watcher.dart' as watcher;
27 26
28 /** 27 /**
29 * Information about a single plugin. 28 * Information about a plugin that is built-in.
30 */ 29 */
31 class PluginInfo { 30 class BuiltInPluginInfo extends PluginInfo {
31 /**
32 * The entry point function that will be executed in the plugin's isolate.
33 */
34 final EntryPoint entryPoint;
35
36 @override
37 final String pluginId;
38
39 /**
40 * Initialize a newly created built-in plugin.
41 */
42 BuiltInPluginInfo(
43 this.entryPoint,
44 this.pluginId,
45 NotificationManager notificationManager,
46 InstrumentationService instrumentationService)
47 : super(notificationManager, instrumentationService);
48
49 @override
50 ServerCommunicationChannel _createChannel() {
51 return new ServerIsolateChannel.builtIn(
52 entryPoint, pluginId, instrumentationService);
53 }
54 }
55
56 /**
57 * Information about a plugin that was discovered.
58 */
59 class DiscoveredPluginInfo extends PluginInfo {
32 /** 60 /**
33 * The path to the root directory of the definition of the plugin on disk (the 61 * The path to the root directory of the definition of the plugin on disk (the
34 * directory containing the 'pubspec.yaml' file and the 'bin' directory). 62 * directory containing the 'pubspec.yaml' file and the 'bin' directory).
35 */ 63 */
36 final String path; 64 final String path;
37 65
38 /** 66 /**
39 * The path to the 'plugin.dart' file that will be executed in an isolate. 67 * The path to the 'plugin.dart' file that will be executed in an isolate.
40 */ 68 */
41 final String executionPath; 69 final String executionPath;
42 70
43 /** 71 /**
44 * The path to the '.packages' file used to control the resolution of 72 * The path to the '.packages' file used to control the resolution of
45 * 'package:' URIs. 73 * 'package:' URIs.
46 */ 74 */
47 final String packagesPath; 75 final String packagesPath;
48 76
49 /** 77 /**
78 * Initialize the newly created information about a plugin.
79 */
80 DiscoveredPluginInfo(
81 this.path,
82 this.executionPath,
83 this.packagesPath,
84 NotificationManager notificationManager,
85 InstrumentationService instrumentationService)
86 : super(notificationManager, instrumentationService);
87
88 @override
89 String get pluginId => path;
90
91 @override
92 ServerCommunicationChannel _createChannel() {
93 return new ServerIsolateChannel.discovered(
94 new Uri.file(executionPath, windows: Platform.isWindows),
95 new Uri.file(packagesPath, windows: Platform.isWindows),
96 instrumentationService);
97 }
98 }
99
100 /**
101 * Information about a single plugin.
102 */
103 abstract class PluginInfo {
104 /**
50 * The object used to manage the receiving and sending of notifications. 105 * The object used to manage the receiving and sending of notifications.
51 */ 106 */
52 final NotificationManager notificationManager; 107 final NotificationManager notificationManager;
53 108
54 /** 109 /**
55 * The instrumentation service that is being used by the analysis server. 110 * The instrumentation service that is being used by the analysis server.
56 */ 111 */
57 final InstrumentationService instrumentationService; 112 final InstrumentationService instrumentationService;
58 113
59 /** 114 /**
60 * The context roots that are currently using the results produced by the 115 * The context roots that are currently using the results produced by the
61 * plugin. 116 * plugin.
62 */ 117 */
63 Set<analyzer.ContextRoot> contextRoots = new HashSet<analyzer.ContextRoot>(); 118 Set<analyzer.ContextRoot> contextRoots = new HashSet<analyzer.ContextRoot>();
64 119
65 /** 120 /**
66 * The current execution of the plugin, or `null` if the plugin is not 121 * The current execution of the plugin, or `null` if the plugin is not
67 * currently being executed. 122 * currently being executed.
68 */ 123 */
69 PluginSession currentSession; 124 PluginSession currentSession;
70 125
71 /** 126 /**
72 * Initialize the newly created information about a plugin. 127 * Initialize the newly created information about a plugin.
73 */ 128 */
74 PluginInfo(this.path, this.executionPath, this.packagesPath, 129 PluginInfo(this.notificationManager, this.instrumentationService);
75 this.notificationManager, this.instrumentationService);
76 130
77 /** 131 /**
78 * Return the data known about this plugin. 132 * Return the data known about this plugin.
79 */ 133 */
80 PluginData get data => 134 PluginData get data =>
81 new PluginData(path, currentSession?.name, currentSession?.version); 135 new PluginData(pluginId, currentSession?.name, currentSession?.version);
136
137 /**
138 * Return the id of this plugin, used to identify the plugin to users.
139 */
140 String get pluginId;
82 141
83 /** 142 /**
84 * Add the given [contextRoot] to the set of context roots being analyzed by 143 * Add the given [contextRoot] to the set of context roots being analyzed by
85 * this plugin. 144 * this plugin.
86 */ 145 */
87 void addContextRoot(analyzer.ContextRoot contextRoot) { 146 void addContextRoot(analyzer.ContextRoot contextRoot) {
88 if (contextRoots.add(contextRoot)) { 147 if (contextRoots.add(contextRoot)) {
89 _updatePluginRoots(); 148 _updatePluginRoots();
90 } 149 }
91 } 150 }
92 151
93 /** 152 /**
153 * Return `true` if at least one of the context roots being analyzed contains
154 * the file with the given [filePath].
155 */
156 bool isAnalyzing(String filePath) {
157 for (var contextRoot in contextRoots) {
158 if (contextRoot.containsFile(filePath)) {
159 return true;
160 }
161 }
162 return false;
163 }
164
165 /**
94 * Remove the given [contextRoot] from the set of context roots being analyzed 166 * Remove the given [contextRoot] from the set of context roots being analyzed
95 * by this plugin. 167 * by this plugin.
96 */ 168 */
97 void removeContextRoot(analyzer.ContextRoot contextRoot) { 169 void removeContextRoot(analyzer.ContextRoot contextRoot) {
98 if (contextRoots.remove(contextRoot)) { 170 if (contextRoots.remove(contextRoot)) {
99 _updatePluginRoots(); 171 _updatePluginRoots();
100 } 172 }
101 } 173 }
102 174
103 /** 175 /**
(...skipping 27 matching lines...) Expand all
131 Future<Null> stop() { 203 Future<Null> stop() {
132 if (currentSession == null) { 204 if (currentSession == null) {
133 throw new StateError('Cannot stop a plugin that is not running.'); 205 throw new StateError('Cannot stop a plugin that is not running.');
134 } 206 }
135 Future<Null> doneFuture = currentSession.stop(); 207 Future<Null> doneFuture = currentSession.stop();
136 currentSession = null; 208 currentSession = null;
137 return doneFuture; 209 return doneFuture;
138 } 210 }
139 211
140 /** 212 /**
213 * Create the channel used to communicate with the server.
214 */
215 ServerCommunicationChannel _createChannel();
216
217 /**
141 * Update the context roots that the plugin should be analyzing. 218 * Update the context roots that the plugin should be analyzing.
142 */ 219 */
143 void _updatePluginRoots() { 220 void _updatePluginRoots() {
144 if (currentSession != null) { 221 if (currentSession != null) {
145 AnalysisSetContextRootsParams params = new AnalysisSetContextRootsParams( 222 AnalysisSetContextRootsParams params = new AnalysisSetContextRootsParams(
146 contextRoots 223 contextRoots
147 .map((analyzer.ContextRoot contextRoot) => 224 .map((analyzer.ContextRoot contextRoot) =>
148 new ContextRoot(contextRoot.root, contextRoot.exclude)) 225 new ContextRoot(contextRoot.root, contextRoot.exclude))
149 .toList()); 226 .toList());
150 currentSession.sendRequest(params); 227 currentSession.sendRequest(params);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 */ 294 */
218 Future<Null> addPluginToContextRoot( 295 Future<Null> addPluginToContextRoot(
219 analyzer.ContextRoot contextRoot, String path) async { 296 analyzer.ContextRoot contextRoot, String path) async {
220 PluginInfo plugin = _pluginMap[path]; 297 PluginInfo plugin = _pluginMap[path];
221 bool isNew = plugin == null; 298 bool isNew = plugin == null;
222 if (isNew) { 299 if (isNew) {
223 List<String> pluginPaths = _pathsFor(path); 300 List<String> pluginPaths = _pathsFor(path);
224 if (pluginPaths == null) { 301 if (pluginPaths == null) {
225 return; 302 return;
226 } 303 }
227 plugin = new PluginInfo(path, pluginPaths[0], pluginPaths[1], 304 plugin = new DiscoveredPluginInfo(path, pluginPaths[0], pluginPaths[1],
228 notificationManager, instrumentationService); 305 notificationManager, instrumentationService);
229 _pluginMap[path] = plugin; 306 _pluginMap[path] = plugin;
230 if (pluginPaths[0] != null) { 307 if (pluginPaths[0] != null) {
231 PluginSession session = await plugin.start(byteStorePath); 308 PluginSession session = await plugin.start(byteStorePath);
232 session?.onDone?.then((_) { 309 session?.onDone?.then((_) {
233 _pluginMap.remove(path); 310 _pluginMap.remove(path);
234 }); 311 });
235 } 312 }
236 } 313 }
237 plugin.addContextRoot(contextRoot); 314 plugin.addContextRoot(contextRoot);
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 * response. 349 * response.
273 */ 350 */
274 Future<List<Future<Response>>> broadcastWatchEvent( 351 Future<List<Future<Response>>> broadcastWatchEvent(
275 watcher.WatchEvent watchEvent) async { 352 watcher.WatchEvent watchEvent) async {
276 String filePath = watchEvent.path; 353 String filePath = watchEvent.path;
277 354
278 /** 355 /**
279 * Return `true` if the given glob [pattern] matches the file being watched. 356 * Return `true` if the given glob [pattern] matches the file being watched.
280 */ 357 */
281 bool matches(String pattern) => 358 bool matches(String pattern) =>
282 new Glob(path.separator, pattern).matches(filePath); 359 new Glob(resourceProvider.pathContext.separator, pattern)
360 .matches(filePath);
283 361
284 WatchEvent event = null; 362 WatchEvent event = null;
285 List<Future<Response>> responses = <Future<Response>>[]; 363 List<Future<Response>> responses = <Future<Response>>[];
286 for (PluginInfo plugin in _pluginMap.values) { 364 for (PluginInfo plugin in _pluginMap.values) {
287 PluginSession session = plugin.currentSession; 365 PluginSession session = plugin.currentSession;
288 if (session != null && 366 if (session != null &&
289 path.isWithin(plugin.path, filePath) && 367 plugin.isAnalyzing(filePath) &&
290 session.interestingFiles.any(matches)) { 368 session.interestingFiles.any(matches)) {
291 event ??= _convertWatchEvent(watchEvent); 369 event ??= _convertWatchEvent(watchEvent);
292 AnalysisHandleWatchEventsParams params = 370 AnalysisHandleWatchEventsParams params =
293 new AnalysisHandleWatchEventsParams([event]); 371 new AnalysisHandleWatchEventsParams([event]);
294 responses.add(session.sendRequest(params)); 372 responses.add(session.sendRequest(params));
295 } 373 }
296 } 374 }
297 return responses; 375 return responses;
298 } 376 }
299 377
(...skipping 15 matching lines...) Expand all
315 return plugins; 393 return plugins;
316 } 394 }
317 395
318 /** 396 /**
319 * The given [contextRoot] is no longer being analyzed. 397 * The given [contextRoot] is no longer being analyzed.
320 */ 398 */
321 void removedContextRoot(analyzer.ContextRoot contextRoot) { 399 void removedContextRoot(analyzer.ContextRoot contextRoot) {
322 List<PluginInfo> plugins = _pluginMap.values.toList(); 400 List<PluginInfo> plugins = _pluginMap.values.toList();
323 for (PluginInfo plugin in plugins) { 401 for (PluginInfo plugin in plugins) {
324 plugin.removeContextRoot(contextRoot); 402 plugin.removeContextRoot(contextRoot);
325 if (plugin.contextRoots.isEmpty) { 403 if (plugin is DiscoveredPluginInfo && plugin.contextRoots.isEmpty) {
326 _pluginMap.remove(plugin.path); 404 _pluginMap.remove(plugin.path);
327 plugin.stop(); 405 plugin.stop();
328 } 406 }
329 } 407 }
330 } 408 }
331 409
332 /** 410 /**
333 * Send a request based on the given [params] to existing plugins to set the 411 * Send a request based on the given [params] to existing plugins to set the
334 * priority files to those specified by the [params]. As a side-effect, record 412 * priority files to those specified by the [params]. As a side-effect, record
335 * the parameters so that they can be sent to any newly started plugins. 413 * the parameters so that they can be sent to any newly started plugins.
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
566 */ 644 */
567 void handleNotification(Notification notification) { 645 void handleNotification(Notification notification) {
568 if (notification.event == PLUGIN_NOTIFICATION_ERROR) { 646 if (notification.event == PLUGIN_NOTIFICATION_ERROR) {
569 PluginErrorParams params = 647 PluginErrorParams params =
570 new PluginErrorParams.fromNotification(notification); 648 new PluginErrorParams.fromNotification(notification);
571 if (params.isFatal) { 649 if (params.isFatal) {
572 info.stop(); 650 info.stop();
573 stop(); 651 stop();
574 } 652 }
575 } 653 }
576 info.notificationManager.handlePluginNotification(info.path, notification); 654 info.notificationManager
655 .handlePluginNotification(info.pluginId, notification);
577 } 656 }
578 657
579 /** 658 /**
580 * Handle the fact that the plugin has stopped. 659 * Handle the fact that the plugin has stopped.
581 */ 660 */
582 void handleOnDone() { 661 void handleOnDone() {
583 channel.close(); 662 channel.close();
584 channel = null; 663 channel = null;
585 pluginStoppedCompleter.complete(null); 664 pluginStoppedCompleter.complete(null);
586 } 665 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
634 Future<bool> start(String byteStorePath) async { 713 Future<bool> start(String byteStorePath) async {
635 if (channel != null) { 714 if (channel != null) {
636 throw new StateError('Cannot start a plugin that is already running.'); 715 throw new StateError('Cannot start a plugin that is already running.');
637 } 716 }
638 if (byteStorePath == null || byteStorePath.isEmpty) { 717 if (byteStorePath == null || byteStorePath.isEmpty) {
639 throw new StateError('Missing byte store path'); 718 throw new StateError('Missing byte store path');
640 } 719 }
641 if (!isCompatible) { 720 if (!isCompatible) {
642 return false; 721 return false;
643 } 722 }
644 channel = new ServerIsolateChannel( 723 channel = info._createChannel();
645 new Uri.file(info.executionPath, windows: Platform.isWindows),
646 new Uri.file(info.packagesPath, windows: Platform.isWindows),
647 info.instrumentationService);
648 await channel.listen(handleResponse, handleNotification, 724 await channel.listen(handleResponse, handleNotification,
649 onDone: handleOnDone, onError: handleOnError); 725 onDone: handleOnDone, onError: handleOnError);
650 if (channel == null) { 726 if (channel == null) {
651 // If there is an error when starting the isolate, the channel will invoke 727 // If there is an error when starting the isolate, the channel will invoke
652 // handleOnDone, which will cause `channel` to be set to `null`. 728 // handleOnDone, which will cause `channel` to be set to `null`.
653 return false; 729 return false;
654 } 730 }
655 Response response = await sendRequest( 731 Response response = await sendRequest(
656 new PluginVersionCheckParams(byteStorePath ?? '', '1.0.0-alpha.0')); 732 new PluginVersionCheckParams(byteStorePath ?? '', '1.0.0-alpha.0'));
657 PluginVersionCheckResult result = 733 PluginVersionCheckResult result =
(...skipping 16 matching lines...) Expand all
674 Future<Null> stop() { 750 Future<Null> stop() {
675 if (channel == null) { 751 if (channel == null) {
676 throw new StateError('Cannot stop a plugin that is not running.'); 752 throw new StateError('Cannot stop a plugin that is not running.');
677 } 753 }
678 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not 754 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not
679 // terminate normally. 755 // terminate normally.
680 sendRequest(new PluginShutdownParams()); 756 sendRequest(new PluginShutdownParams());
681 return pluginStoppedCompleter.future; 757 return pluginStoppedCompleter.future;
682 } 758 }
683 } 759 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/domain_completion_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698