Index: pkg/analysis_server_client/lib/analysis_server_client.dart |
diff --git a/pkg/analysis_server_client/lib/analysis_server_client.dart b/pkg/analysis_server_client/lib/analysis_server_client.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..96286e40dd94c92e8e016ec7b244d241db1d5c2a |
--- /dev/null |
+++ b/pkg/analysis_server_client/lib/analysis_server_client.dart |
@@ -0,0 +1,101 @@ |
+// Copyright (c) 2017, 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. |
+ |
+import 'dart:async'; |
+import 'dart:convert'; |
+import 'dart:io'; |
+ |
+/// Type of callbacks used to process notification. |
+typedef void NotificationProcessor(String event, Map<String, Object> params); |
+ |
+/// Instances of the class [AnalysisServerClient] manage a connection to an |
+/// [AnalysisServer] process, and facilitate communication to and from the |
+/// client/user. |
+class AnalysisServerClient { |
+ /// AnalysisServer process object, or null if the server has been shut down. |
+ final Process _process; |
+ |
+ /// Commands that have been sent to the server but not yet acknowledged, |
+ /// and the [Completer] objects which should be completed when |
+ /// acknowledgement is received. |
+ final Map<String, Completer> _pendingCommands = <String, Completer>{}; |
+ |
+ /// Number which should be used to compute the 'id' to send to the next |
+ /// command sent to the server. |
+ int _nextId = 0; |
+ |
+ AnalysisServerClient(this._process); |
+ |
+ /// Return a future that will complete when all commands that have been |
+ /// sent to the server so far have been flushed to the OS buffer. |
+ Future<Null> flushCommands() { |
+ return _process.stdin.flush(); |
+ } |
+ |
+ void listenToOutput({NotificationProcessor notificationProcessor}) { |
+ _process.stdout |
+ .transform((new Utf8Codec()).decoder) |
+ .transform(new LineSplitter()) |
+ .listen((String line) { |
+ String trimmedLine = line.trim(); |
+ if (trimmedLine.startsWith('Observatory listening on ')) { |
+ return; |
+ } |
+ final result = JSON.decoder.convert(trimmedLine) as Map; |
+ if (result.containsKey('id')) { |
+ final id = result['id'] as String; |
+ final completer = _pendingCommands.remove(id); |
+ |
+ if (result.containsKey('error')) { |
+ completer.completeError(new ServerErrorMessage(result['error'])); |
+ } else { |
+ completer.complete(result['result']); |
+ } |
+ } else if (notificationProcessor != null && result.containsKey('event')) { |
+ // Message is a notification. It should have an event and possibly |
+ // params. |
+ notificationProcessor(result['event'], result['params']); |
+ } |
+ }); |
+ } |
+ |
+ /// Sends a command to the server. An 'id' will be automatically assigned. |
+ /// The returned [Future] will be completed when the server acknowledges |
+ /// the command with a response. If the server acknowledges the command |
+ /// with a normal (non-error) response, the future will be completed |
+ /// with the 'result' field from the response. If the server acknowledges |
+ /// the command with an error response, the future will be completed with an |
+ /// error. |
+ Future send(String method, Map<String, dynamic> params) { |
+ String id = '${_nextId++}'; |
+ Map<String, dynamic> command = <String, dynamic>{ |
+ 'id': id, |
+ 'method': method |
+ }; |
+ if (params != null) { |
+ command['params'] = params; |
+ } |
+ Completer completer = new Completer(); |
+ _pendingCommands[id] = completer; |
+ String commandAsJson = JSON.encode(command); |
+ _process.stdin.add(UTF8.encoder.convert('$commandAsJson\n')); |
+ return completer.future; |
+ } |
+ |
+ /// Force kill the server. Returns exit code future. |
+ Future<int> kill() { |
+ _process.kill(); |
+ return _process.exitCode; |
+ } |
+} |
+ |
+class ServerErrorMessage { |
+ final Map errorJson; |
+ |
+ ServerErrorMessage(this.errorJson); |
+ |
+ String get code => errorJson['code'].toString(); |
+ String get message => errorJson['message']; |
+ String get stackTrace => errorJson['stackTrace']; |
+} |