Index: pkg/analysis_server/lib/src/analysis_server.dart |
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7fd84f4f019d64a765b37e10b6b646ccf7f0b38f |
--- /dev/null |
+++ b/pkg/analysis_server/lib/src/analysis_server.dart |
@@ -0,0 +1,178 @@ |
+// Copyright (c) 2014, 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. |
+ |
+library analysis.server; |
+ |
+import 'dart:async'; |
+ |
+import 'package:analyzer/src/generated/engine.dart'; |
+ |
+import 'analysis_logger.dart'; |
+import 'channel.dart'; |
+import 'protocol.dart'; |
+ |
+/** |
+ * Instances of the class [AnalysisServer] implement a server that listens on a |
+ * [CommunicationChannel] for analysis requests and process them. |
+ */ |
+class AnalysisServer { |
+ /** |
+ * The name of the notification of new errors associated with a source. |
+ */ |
+ static const String ERROR_NOTIFICATION_NAME = 'context.errors'; |
+ |
+ /** |
+ * The name of the parameter whose value is a list of errors. |
+ */ |
+ static const String ERRORS_PARAM = 'errors'; |
+ |
+ /** |
+ * The name of the parameter whose value is a source. |
+ */ |
+ static const String SOURCE_PARAM = 'source'; |
+ |
+ /** |
+ * The channel from which requests are received and to which responses should |
+ * be sent. |
+ */ |
+ CommunicationChannel channel; |
+ |
+ /** |
+ * A flag indicating whether the server is running. |
+ */ |
+ bool running; |
+ |
+ /** |
+ * A list of the request handlers used to handle the requests sent to this |
+ * server. |
+ */ |
+ List<RequestHandler> handlers; |
+ |
+ /** |
+ * A table mapping context id's to the analysis contexts associated with them. |
+ */ |
+ final Map<String, AnalysisContext> contextMap = new Map<String, AnalysisContext>(); |
+ |
+ /** |
+ * A list of the analysis contexts for which analysis work needs to be |
+ * performed. |
+ */ |
+ final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); |
+ |
+ /** |
+ * Initialize a newly created server to receive requests from and send |
+ * responses to the given [channel]. |
+ */ |
+ AnalysisServer(CommunicationChannel channel) { |
+ AnalysisEngine.instance.logger = new AnalysisLogger(); |
+ running = true; |
+ this.channel = channel; |
+ this.channel.listen(handleRequest, onError: error, onDone: done); |
+ } |
+ |
+ /** |
+ * Add the given [context] to the list of analysis contexts for which analysis |
+ * work needs to be performed. Ensure that the work will be performed. |
+ */ |
+ void addContextToWorkQueue(AnalysisContext context) { |
+ if (!contextWorkQueue.contains(context)) { |
+ contextWorkQueue.add(context); |
+ run(); |
+ } |
+ } |
+ |
+ /** |
+ * The socket from which requests are being read has been closed. |
+ */ |
+ void done() { |
+ running = false; |
+ } |
+ |
+ /** |
+ * There was an error related to the socket from which requests are being |
+ * read. |
+ */ |
+ void error() { |
+ running = false; |
+ } |
+ |
+ /** |
+ * Handle a [request] that was read from the communication channel. |
+ */ |
+ void handleRequest(Request request) { |
+ int count = handlers.length; |
+ for (int i = 0; i < count; i++) { |
+ Response response = handlers[i].handleRequest(request); |
+ if (response != null) { |
+ channel.sendResponse(response); |
+ return; |
+ } |
+ } |
+ channel.sendResponse(new Response.unknownRequest(request)); |
+ } |
+ |
+ /** |
+ * Perform the next available task. If a request was received that has not yet |
+ * been performed, perform it next. Otherwise, look for some analysis that |
+ * needs to be done and do that. Otherwise, do nothing. |
+ */ |
+ void performTask() { |
+ // |
+ // Look for a context that has work to be done and then perform one task. |
+ // |
+ if (!contextWorkQueue.isEmpty) { |
+ AnalysisContext context = contextWorkQueue[0]; |
+ AnalysisResult result = context.performAnalysisTask(); |
+ List<ChangeNotice> notices = result.changeNotices; |
+ if (notices == null) { |
+ contextWorkQueue.removeAt(0); |
+ } else { //if (context.analysisOptions.provideErrors) { |
+ sendNotices(notices); |
+ } |
+ } |
+ // |
+ // Schedule this method to be run again if there is any more work to be done. |
+ // |
+ if (contextWorkQueue.isEmpty) { |
+ running = false; |
+ } else { |
+ Timer.run(() { |
+ performTask(); |
+ }); |
+ } |
+ } |
+ |
+ /** |
+ * Send the information in the given list of notices back to the client. |
+ */ |
+ void sendNotices(List<ChangeNotice> notices) { |
+ for (int i = 0; i < notices.length; i++) { |
+ ChangeNotice notice = notices[i]; |
+ Notification notification = new Notification(ERROR_NOTIFICATION_NAME); |
+ notification.setParameter(SOURCE_PARAM, notice.source.encoding); |
+ notification.setParameter(ERRORS_PARAM, notice.errors); |
+ sendNotification(notification); |
+ } |
+ } |
+ |
+ /** |
+ * Perform the tasks that are waiting for execution until the server is shut |
+ * down. |
+ */ |
+ void run() { |
+ if (!running) { |
+ running = true; |
+ Timer.run(() { |
+ performTask(); |
+ }); |
+ } |
+ } |
+ |
+ /** |
+ * Send the given [notification] to the client. |
+ */ |
+ void sendNotification(Notification notification) { |
+ channel.sendNotification(notification); |
+ } |
+} |