OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:async'; |
| 6 import 'dart:convert'; |
| 7 import 'dart:io'; |
| 8 |
| 9 /// Type of callbacks used to process notification. |
| 10 typedef void NotificationProcessor(String event, Map<String, Object> params); |
| 11 |
| 12 /// Instances of the class [AnalysisServerClient] manage a connection to an |
| 13 /// [AnalysisServer] process, and facilitate communication to and from the |
| 14 /// client/user. |
| 15 class AnalysisServerClient { |
| 16 /// AnalysisServer process object, or null if the server has been shut down. |
| 17 final Process _process; |
| 18 |
| 19 /// Commands that have been sent to the server but not yet acknowledged, |
| 20 /// and the [Completer] objects which should be completed when |
| 21 /// acknowledgement is received. |
| 22 final Map<String, Completer> _pendingCommands = <String, Completer>{}; |
| 23 |
| 24 /// Number which should be used to compute the 'id' to send to the next |
| 25 /// command sent to the server. |
| 26 int _nextId = 0; |
| 27 |
| 28 AnalysisServerClient(this._process); |
| 29 |
| 30 /// Return a future that will complete when all commands that have been |
| 31 /// sent to the server so far have been flushed to the OS buffer. |
| 32 Future<Null> flushCommands() { |
| 33 return _process.stdin.flush(); |
| 34 } |
| 35 |
| 36 void listenToOutput({NotificationProcessor notificationProcessor}) { |
| 37 _process.stdout |
| 38 .transform((new Utf8Codec()).decoder) |
| 39 .transform(new LineSplitter()) |
| 40 .listen((String line) { |
| 41 String trimmedLine = line.trim(); |
| 42 if (trimmedLine.startsWith('Observatory listening on ')) { |
| 43 return; |
| 44 } |
| 45 final result = JSON.decoder.convert(trimmedLine) as Map; |
| 46 if (result.containsKey('id')) { |
| 47 final id = result['id'] as String; |
| 48 final completer = _pendingCommands.remove(id); |
| 49 |
| 50 if (result.containsKey('error')) { |
| 51 completer.completeError(new ServerErrorMessage(result['error'])); |
| 52 } else { |
| 53 completer.complete(result['result']); |
| 54 } |
| 55 } else if (notificationProcessor != null && result.containsKey('event')) { |
| 56 // Message is a notification. It should have an event and possibly |
| 57 // params. |
| 58 notificationProcessor(result['event'], result['params']); |
| 59 } |
| 60 }); |
| 61 } |
| 62 |
| 63 /// Sends a command to the server. An 'id' will be automatically assigned. |
| 64 /// The returned [Future] will be completed when the server acknowledges |
| 65 /// the command with a response. If the server acknowledges the command |
| 66 /// with a normal (non-error) response, the future will be completed |
| 67 /// with the 'result' field from the response. If the server acknowledges |
| 68 /// the command with an error response, the future will be completed with an |
| 69 /// error. |
| 70 Future send(String method, Map<String, dynamic> params) { |
| 71 String id = '${_nextId++}'; |
| 72 Map<String, dynamic> command = <String, dynamic>{ |
| 73 'id': id, |
| 74 'method': method |
| 75 }; |
| 76 if (params != null) { |
| 77 command['params'] = params; |
| 78 } |
| 79 Completer completer = new Completer(); |
| 80 _pendingCommands[id] = completer; |
| 81 String commandAsJson = JSON.encode(command); |
| 82 _process.stdin.add(UTF8.encoder.convert('$commandAsJson\n')); |
| 83 return completer.future; |
| 84 } |
| 85 |
| 86 /// Force kill the server. Returns exit code future. |
| 87 Future<int> kill() { |
| 88 _process.kill(); |
| 89 return _process.exitCode; |
| 90 } |
| 91 } |
| 92 |
| 93 class ServerErrorMessage { |
| 94 final Map errorJson; |
| 95 |
| 96 ServerErrorMessage(this.errorJson); |
| 97 |
| 98 String get code => errorJson['code'].toString(); |
| 99 String get message => errorJson['message']; |
| 100 String get stackTrace => errorJson['stackTrace']; |
| 101 } |
OLD | NEW |