| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library analysis.server; | 5 library analysis.server; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analyzer/file_system/file_system.dart'; | 10 import 'package:analyzer/file_system/file_system.dart'; |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 new HashMap<AnalysisService, Set<String>>(); | 201 new HashMap<AnalysisService, Set<String>>(); |
| 202 | 202 |
| 203 /** | 203 /** |
| 204 * A table mapping [AnalysisContext]s to the completers that should be | 204 * A table mapping [AnalysisContext]s to the completers that should be |
| 205 * completed when analysis of this context is finished. | 205 * completed when analysis of this context is finished. |
| 206 */ | 206 */ |
| 207 Map<AnalysisContext, Completer<AnalysisDoneReason>> contextAnalysisDoneComplet
ers = | 207 Map<AnalysisContext, Completer<AnalysisDoneReason>> contextAnalysisDoneComplet
ers = |
| 208 new HashMap<AnalysisContext, Completer<AnalysisDoneReason>>(); | 208 new HashMap<AnalysisContext, Completer<AnalysisDoneReason>>(); |
| 209 | 209 |
| 210 /** | 210 /** |
| 211 * The listeners that are listening for lifecycle events from this server. | 211 * The controller that is notified when analysis is started. |
| 212 */ | 212 */ |
| 213 List<AnalysisServerListener> listeners = <AnalysisServerListener>[]; | 213 StreamController<AnalysisContext> _onAnalysisStartedController; |
| 214 |
| 215 /** |
| 216 * The controller that is notified when analysis is complete. |
| 217 */ |
| 218 StreamController _onAnalysisCompleteController; |
| 214 | 219 |
| 215 /** | 220 /** |
| 216 * True if any exceptions thrown by analysis should be propagated up the call | 221 * True if any exceptions thrown by analysis should be propagated up the call |
| 217 * stack. | 222 * stack. |
| 218 */ | 223 */ |
| 219 bool rethrowExceptions; | 224 bool rethrowExceptions; |
| 220 | 225 |
| 221 /** | 226 /** |
| 222 * Initialize a newly created server to receive requests from and send | 227 * Initialize a newly created server to receive requests from and send |
| 223 * responses to the given [channel]. | 228 * responses to the given [channel]. |
| 224 * | 229 * |
| 225 * If [rethrowExceptions] is true, then any exceptions thrown by analysis are | 230 * If [rethrowExceptions] is true, then any exceptions thrown by analysis are |
| 226 * propagated up the call stack. The default is true to allow analysis | 231 * propagated up the call stack. The default is true to allow analysis |
| 227 * exceptions to show up in unit tests, but it should be set to false when | 232 * exceptions to show up in unit tests, but it should be set to false when |
| 228 * running a full analysis server. | 233 * running a full analysis server. |
| 229 */ | 234 */ |
| 230 AnalysisServer(this.channel, this.resourceProvider, | 235 AnalysisServer(this.channel, this.resourceProvider, |
| 231 PackageMapProvider packageMapProvider, this.index, this.defaultSdk, | 236 PackageMapProvider packageMapProvider, this.index, this.defaultSdk, |
| 232 {this.rethrowExceptions: true}) { | 237 {this.rethrowExceptions: true}) { |
| 233 searchEngine = createSearchEngine(index); | 238 searchEngine = createSearchEngine(index); |
| 234 operationQueue = new ServerOperationQueue(this); | 239 operationQueue = new ServerOperationQueue(this); |
| 235 contextDirectoryManager = | 240 contextDirectoryManager = |
| 236 new ServerContextManager(this, resourceProvider, packageMapProvider); | 241 new ServerContextManager(this, resourceProvider, packageMapProvider); |
| 237 AnalysisEngine.instance.logger = new AnalysisLogger(); | 242 AnalysisEngine.instance.logger = new AnalysisLogger(); |
| 243 _onAnalysisStartedController = new StreamController.broadcast(); |
| 244 _onAnalysisCompleteController = new StreamController.broadcast(); |
| 238 running = true; | 245 running = true; |
| 239 Notification notification = new ServerConnectedParams().toNotification(); | 246 Notification notification = new ServerConnectedParams().toNotification(); |
| 240 channel.sendNotification(notification); | 247 channel.sendNotification(notification); |
| 241 channel.listen(handleRequest, onDone: done, onError: error); | 248 channel.listen(handleRequest, onDone: done, onError: error); |
| 242 } | 249 } |
| 243 | 250 |
| 244 /** | 251 /** |
| 245 * Schedules execution of the given [ServerOperation]. | 252 * Schedules execution of the given [ServerOperation]. |
| 246 */ | 253 */ |
| 247 void scheduleOperation(ServerOperation operation) { | 254 void scheduleOperation(ServerOperation operation) { |
| 248 bool wasEmpty = operationQueue.isEmpty; | 255 bool wasEmpty = operationQueue.isEmpty; |
| 249 addOperation(operation); | 256 addOperation(operation); |
| 250 if (wasEmpty) { | 257 if (wasEmpty) { |
| 251 _schedulePerformOperation(); | 258 _schedulePerformOperation(); |
| 252 } | 259 } |
| 253 } | 260 } |
| 254 | 261 |
| 255 /** | 262 /** |
| 256 * Schedules analysis of the given context. | 263 * Schedules analysis of the given context. |
| 257 */ | 264 */ |
| 258 void schedulePerformAnalysisOperation(AnalysisContext context) { | 265 void schedulePerformAnalysisOperation(AnalysisContext context) { |
| 259 _notifyAnalysisStarted(context); | 266 _onAnalysisStartedController.add(context); |
| 260 scheduleOperation(new PerformAnalysisOperation(context, false)); | 267 scheduleOperation(new PerformAnalysisOperation(context, false)); |
| 261 } | 268 } |
| 262 | 269 |
| 263 /** | 270 /** |
| 264 * Send the given [notification] to the client. | 271 * Send the given [notification] to the client. |
| 265 */ | 272 */ |
| 266 void sendNotification(Notification notification) { | 273 void sendNotification(Notification notification) { |
| 267 channel.sendNotification(notification); | 274 channel.sendNotification(notification); |
| 268 } | 275 } |
| 269 | 276 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 // | 335 // |
| 329 // Update the defaults used to create new contexts. | 336 // Update the defaults used to create new contexts. |
| 330 // | 337 // |
| 331 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions; | 338 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions; |
| 332 optionUpdaters.forEach((OptionUpdater optionUpdater) { | 339 optionUpdaters.forEach((OptionUpdater optionUpdater) { |
| 333 optionUpdater(options); | 340 optionUpdater(options); |
| 334 }); | 341 }); |
| 335 } | 342 } |
| 336 | 343 |
| 337 /** | 344 /** |
| 338 * Add the given [listener] to the list of listeners that are listening for | 345 * The stream that is notified when analysis of a context is started. |
| 339 * lifecycle events from this server. | |
| 340 */ | 346 */ |
| 341 void addAnalysisServerListener(AnalysisServerListener listener) { | 347 Stream<AnalysisContext> get onAnalysisStarted { |
| 342 if (!listeners.contains(listener)) { | 348 return _onAnalysisStartedController.stream; |
| 343 listeners.add(listener); | |
| 344 } | |
| 345 } | 349 } |
| 346 | 350 |
| 347 /** | 351 /** |
| 352 * The stream that is notified when analysis is complete. |
| 353 */ |
| 354 Stream get onAnalysisComplete => _onAnalysisCompleteController.stream; |
| 355 |
| 356 /** |
| 348 * Adds the given [ServerOperation] to the queue, but does not schedule | 357 * Adds the given [ServerOperation] to the queue, but does not schedule |
| 349 * operations execution. | 358 * operations execution. |
| 350 */ | 359 */ |
| 351 void addOperation(ServerOperation operation) { | 360 void addOperation(ServerOperation operation) { |
| 352 operationQueue.add(operation); | 361 operationQueue.add(operation); |
| 353 } | 362 } |
| 354 | 363 |
| 355 /** | 364 /** |
| 356 * The socket from which requests are being read has been closed. | 365 * The socket from which requests are being read has been closed. |
| 357 */ | 366 */ |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 'Unexpected exception during analysis', | 485 'Unexpected exception during analysis', |
| 477 new CaughtException(exception, stackTrace)); | 486 new CaughtException(exception, stackTrace)); |
| 478 } | 487 } |
| 479 _sendServerErrorNotification(exception, stackTrace); | 488 _sendServerErrorNotification(exception, stackTrace); |
| 480 shutdown(); | 489 shutdown(); |
| 481 } finally { | 490 } finally { |
| 482 if (!operationQueue.isEmpty) { | 491 if (!operationQueue.isEmpty) { |
| 483 _schedulePerformOperation(); | 492 _schedulePerformOperation(); |
| 484 } else { | 493 } else { |
| 485 sendStatusNotification(null); | 494 sendStatusNotification(null); |
| 486 _notifyAnalysisComplete(); | 495 _onAnalysisCompleteController.add(null); |
| 487 } | 496 } |
| 488 } | 497 } |
| 489 } | 498 } |
| 490 | 499 |
| 491 /** | 500 /** |
| 492 * Trigger reanalysis of all files from disk. | 501 * Trigger reanalysis of all files from disk. |
| 493 */ | 502 */ |
| 494 void reanalyze() { | 503 void reanalyze() { |
| 495 // Clear any operations that are pending. | 504 // Clear any operations that are pending. |
| 496 operationQueue.clear(); | 505 operationQueue.clear(); |
| 497 // Instruct the contextDirectoryManager to rebuild all contexts from | 506 // Instruct the contextDirectoryManager to rebuild all contexts from |
| 498 // scratch. | 507 // scratch. |
| 499 contextDirectoryManager.refresh(); | 508 contextDirectoryManager.refresh(); |
| 500 } | 509 } |
| 501 | 510 |
| 502 /** | 511 /** |
| 503 * Remove the given [listener] from the list of listeners that are listening | |
| 504 * for lifecycle events from this server. | |
| 505 */ | |
| 506 void removeAnalysisServerListener(AnalysisServerListener listener) { | |
| 507 listeners.remove(listener); | |
| 508 } | |
| 509 | |
| 510 /** | |
| 511 * Send status notification to the client. The `operation` is the operation | 512 * Send status notification to the client. The `operation` is the operation |
| 512 * being performed or `null` if analysis is complete. | 513 * being performed or `null` if analysis is complete. |
| 513 */ | 514 */ |
| 514 void sendStatusNotification(ServerOperation operation) { | 515 void sendStatusNotification(ServerOperation operation) { |
| 515 // Only send status when subscribed. | 516 // Only send status when subscribed. |
| 516 if (!serverServices.contains(ServerService.STATUS)) { | 517 if (!serverServices.contains(ServerService.STATUS)) { |
| 517 return; | 518 return; |
| 518 } | 519 } |
| 519 // Only send status when it changes | 520 // Only send status when it changes |
| 520 bool isAnalyzing = operation != null; | 521 bool isAnalyzing = operation != null; |
| (...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 897 } | 898 } |
| 898 | 899 |
| 899 /** | 900 /** |
| 900 * Return `true` if all operations have been performed in this [AnalysisServer
]. | 901 * Return `true` if all operations have been performed in this [AnalysisServer
]. |
| 901 */ | 902 */ |
| 902 bool test_areOperationsFinished() { | 903 bool test_areOperationsFinished() { |
| 903 return operationQueue.isEmpty; | 904 return operationQueue.isEmpty; |
| 904 } | 905 } |
| 905 | 906 |
| 906 /** | 907 /** |
| 907 * Notify all listeners that analysis of [context] is started. | |
| 908 */ | |
| 909 void _notifyAnalysisStarted(AnalysisContext context) { | |
| 910 listeners.forEach((AnalysisServerListener listener) { | |
| 911 listener.analysisStarted(context); | |
| 912 }); | |
| 913 } | |
| 914 | |
| 915 /** | |
| 916 * Notify all listeners that analysis is complete. | |
| 917 */ | |
| 918 void _notifyAnalysisComplete() { | |
| 919 listeners.forEach((AnalysisServerListener listener) { | |
| 920 listener.analysisComplete(); | |
| 921 }); | |
| 922 } | |
| 923 | |
| 924 /** | |
| 925 * Schedules [performOperation] exection. | 908 * Schedules [performOperation] exection. |
| 926 */ | 909 */ |
| 927 void _schedulePerformOperation() { | 910 void _schedulePerformOperation() { |
| 928 new Future(performOperation); | 911 new Future(performOperation); |
| 929 } | 912 } |
| 930 | 913 |
| 931 /** | 914 /** |
| 932 * Sends a fatal `server.error` notification. | 915 * Sends a fatal `server.error` notification. |
| 933 */ | 916 */ |
| 934 void _sendServerErrorNotification(exception, stackTrace) { | 917 void _sendServerErrorNotification(exception, stackTrace) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 948 } | 931 } |
| 949 // send the notification | 932 // send the notification |
| 950 channel.sendNotification( | 933 channel.sendNotification( |
| 951 new ServerErrorParams( | 934 new ServerErrorParams( |
| 952 true, | 935 true, |
| 953 exceptionString, | 936 exceptionString, |
| 954 stackTraceString).toNotification()); | 937 stackTraceString).toNotification()); |
| 955 } | 938 } |
| 956 } | 939 } |
| 957 | 940 |
| 958 /** | |
| 959 * An object that is listening for lifecycle events from an analysis server. | |
| 960 */ | |
| 961 abstract class AnalysisServerListener { | |
| 962 /** | |
| 963 * Analysis is complete. | |
| 964 */ | |
| 965 void analysisComplete(); | |
| 966 | |
| 967 /** | |
| 968 * Analysis is started. | |
| 969 */ | |
| 970 void analysisStarted(AnalysisContext context); | |
| 971 } | |
| 972 | |
| 973 typedef void OptionUpdater(AnalysisOptionsImpl options); | 941 typedef void OptionUpdater(AnalysisOptionsImpl options); |
| OLD | NEW |