Packages required for the new analysis method.
TO-310
Change-Id: Ifa9bb5a56c2046f37d507f76a29d6650a050d681
diff --git a/pub/analysis_server_lib/.gitignore b/pub/analysis_server_lib/.gitignore
new file mode 100644
index 0000000..335fed2
--- /dev/null
+++ b/pub/analysis_server_lib/.gitignore
@@ -0,0 +1,28 @@
+# See https://www.dartlang.org/tools/private-files.html
+
+# Files and directories created by pub
+.buildlog
+.packages
+.project
+.pub/
+.idea/
+build/
+**/packages/
+
+# Files created by dart2js
+# (Most Dart developers will use pub build to compile Dart, use/modify these
+# rules if you intend to use dart2js directly
+# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
+# differentiate from explicit Javascript files)
+*.dart.js
+*.part.js
+*.js.deps
+*.js.map
+*.info.json
+
+# Directory created by dartdoc
+doc/api/
+
+# Don't commit pubspec lock file
+# (Library packages only! Remove pattern if developing an application package)
+pubspec.lock
diff --git a/pub/analysis_server_lib/.travis.yml b/pub/analysis_server_lib/.travis.yml
new file mode 100644
index 0000000..9e27d1f
--- /dev/null
+++ b/pub/analysis_server_lib/.travis.yml
@@ -0,0 +1,3 @@
+language: dart
+sudo: false
+script: ./tool/travis.sh
diff --git a/pub/analysis_server_lib/BUILD.gn b/pub/analysis_server_lib/BUILD.gn
new file mode 100644
index 0000000..95539ec
--- /dev/null
+++ b/pub/analysis_server_lib/BUILD.gn
@@ -0,0 +1,16 @@
+# This file is generated by importer.py for analysis_server_lib-0.1.2+1
+
+import("//build/dart/dart_package.gni")
+
+dart_package("analysis_server_lib") {
+ package_name = "analysis_server_lib"
+
+ source_dir = "lib"
+
+ disable_analysis = true
+
+ deps = [
+ "//third_party/dart-pkg/pub/path",
+ "//third_party/dart-pkg/pub/logging",
+ ]
+}
diff --git a/pub/analysis_server_lib/LICENSE b/pub/analysis_server_lib/LICENSE
new file mode 100644
index 0000000..3a392c0
--- /dev/null
+++ b/pub/analysis_server_lib/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2017, Devon Carew
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pub/analysis_server_lib/README.md b/pub/analysis_server_lib/README.md
new file mode 100644
index 0000000..f8d8340
--- /dev/null
+++ b/pub/analysis_server_lib/README.md
@@ -0,0 +1,33 @@
+# analysis_server_lib
+
+A library to access Dart's analysis server API.
+
+[![Build Status](https://travis-ci.org/devoncarew/analysis_server_lib.svg?branch=master)](https://travis-ci.org/devoncarew/analysis_server_lib)
+
+## What is the analysis server?
+
+The analysis server is a long-running process that provides analysis results to other tools.
+It is designed to provide on-going analysis of one or more code bases as those code bases are
+changing.
+
+## Using the server
+
+Clients (typically tools, such as an editor) are expected to run the analysis server in a separate
+process and communicate with it over a JSON protocol. The protocol is specified
+[here](https://htmlpreview.github.io/?https://github.com/dart-lang/sdk/blob/master/pkg/analysis_server/doc/api.html).
+
+Here's a simple example of starting and communicating with the server:
+
+```dart
+import 'package:analysis_server_lib/analysis_server_lib.dart';
+
+main() async {
+ AnalysisServer server = await AnalysisServer.create();
+ await server.server.onConnected.first;
+
+ VersionResult version = await server.server.getVersion();
+ print(version.version);
+
+ server.dispose();
+}
+```
diff --git a/pub/analysis_server_lib/lib/analysis_server_lib.dart b/pub/analysis_server_lib/lib/analysis_server_lib.dart
new file mode 100644
index 0000000..4de7ede
--- /dev/null
+++ b/pub/analysis_server_lib/lib/analysis_server_lib.dart
@@ -0,0 +1,3351 @@
+// Copyright (c) 2017, Devon Carew. 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.
+
+// This is a generated file.
+
+/// A library to access the analysis server API.
+///
+/// [AnalysisServer] is the main entry-point to this library.
+library analysis_server_lib;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
+
+/// @optional
+const String optional = 'optional';
+
+/// @experimental
+const String experimental = 'experimental';
+
+final Logger _logger = new Logger('analysis_server');
+
+const String generatedProtocolVersion = '1.18.1';
+
+typedef void MethodSend(String methodName);
+
+/// A class to communicate with an analysis server instance.
+///
+/// Here's a simple example of starting and communicating with the server:
+///
+/// ```dart
+/// import 'package:analysis_server_lib/analysis_server_lib.dart';
+///
+/// main() async {
+/// AnalysisServer server = await AnalysisServer.create();
+/// await server.server.onConnected.first;
+///
+/// VersionResult version = await server.server.getVersion();
+/// print(version.version);
+///
+/// server.dispose();
+/// }
+/// ```
+class AnalysisServer {
+ /// Create and connect to a new analysis server instance.
+ ///
+ /// - [sdkPath] override the default sdk path
+ /// - [scriptPath] override the default entry-point script to use for the
+ /// analysis server
+ /// - [onRead] called every time data is read from the server
+ /// - [onWrite] called every time data is written to the server
+ static Future<AnalysisServer> create(
+ {String sdkPath,
+ String scriptPath,
+ onRead(String),
+ onWrite(String),
+ List<String> vmArgs,
+ List<String> serverArgs,
+ String clientId,
+ String clientVersion}) async {
+ Completer<int> processCompleter = new Completer();
+
+ String vmPath;
+ if (sdkPath != null) {
+ vmPath =
+ path.join(sdkPath, 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
+ } else {
+ sdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
+ vmPath = Platform.resolvedExecutable;
+ }
+ scriptPath ??= '$sdkPath/bin/snapshots/analysis_server.dart.snapshot';
+
+ List<String> args = [scriptPath, '--sdk', sdkPath];
+ if (vmArgs != null) args.insertAll(0, vmArgs);
+ if (serverArgs != null) args.addAll(serverArgs);
+ if (clientId != null) args.add('--client-id=$clientId');
+ if (clientVersion != null) args.add('--client-version=$clientVersion');
+
+ Process process = await Process.start(vmPath, args);
+ process.exitCode.then((code) => processCompleter.complete(code));
+
+ Stream<String> inStream = process.stdout
+ .transform(UTF8.decoder)
+ .transform(const LineSplitter())
+ .map((String message) {
+ if (onRead != null) onRead(message);
+ return message;
+ });
+
+ AnalysisServer server = new AnalysisServer(inStream, (String message) {
+ if (onWrite != null) onWrite(message);
+ process.stdin.writeln(message);
+ }, processCompleter, process.kill);
+
+ return server;
+ }
+
+ final Completer<int> processCompleter;
+ final Function _processKillHandler;
+
+ StreamSubscription _streamSub;
+ Function _writeMessage;
+ int _id = 0;
+ Map<String, Completer> _completers = {};
+ Map<String, String> _methodNames = {};
+ JsonCodec _jsonEncoder = new JsonCodec(toEncodable: _toEncodable);
+ Map<String, Domain> _domains = {};
+ StreamController<String> _onSend = new StreamController.broadcast();
+ StreamController<String> _onReceive = new StreamController.broadcast();
+ MethodSend _willSend;
+
+ ServerDomain _server;
+ AnalysisDomain _analysis;
+ CompletionDomain _completion;
+ SearchDomain _search;
+ EditDomain _edit;
+ ExecutionDomain _execution;
+ DiagnosticDomain _diagnostic;
+ AnalyticsDomain _analytics;
+
+ /// Connect to an existing analysis server instance.
+ AnalysisServer(Stream<String> inStream, void writeMessage(String message),
+ this.processCompleter,
+ [this._processKillHandler]) {
+ configure(inStream, writeMessage);
+
+ _server = new ServerDomain(this);
+ _analysis = new AnalysisDomain(this);
+ _completion = new CompletionDomain(this);
+ _search = new SearchDomain(this);
+ _edit = new EditDomain(this);
+ _execution = new ExecutionDomain(this);
+ _diagnostic = new DiagnosticDomain(this);
+ _analytics = new AnalyticsDomain(this);
+ }
+
+ ServerDomain get server => _server;
+ AnalysisDomain get analysis => _analysis;
+ CompletionDomain get completion => _completion;
+ SearchDomain get search => _search;
+ EditDomain get edit => _edit;
+ ExecutionDomain get execution => _execution;
+ DiagnosticDomain get diagnostic => _diagnostic;
+ AnalyticsDomain get analytics => _analytics;
+
+ Stream<String> get onSend => _onSend.stream;
+ Stream<String> get onReceive => _onReceive.stream;
+
+ set willSend(MethodSend fn) {
+ _willSend = fn;
+ }
+
+ void configure(Stream<String> inStream, void writeMessage(String message)) {
+ _streamSub = inStream.listen(_processMessage);
+ _writeMessage = writeMessage;
+ }
+
+ void dispose() {
+ if (_streamSub != null) _streamSub.cancel();
+ //_completers.values.forEach((c) => c.completeError('disposed'));
+ _completers.clear();
+
+ if (_processKillHandler != null) {
+ _processKillHandler();
+ }
+ }
+
+ void _processMessage(String message) {
+ _onReceive.add(message);
+
+ if (!message.startsWith('{')) {
+ _logger.warning('unknown message: ${message}');
+ return;
+ }
+
+ try {
+ var json = JSON.decode(message);
+
+ if (json['id'] == null) {
+ // Handle a notification.
+ String event = json['event'];
+ if (event == null) {
+ _logger.severe('invalid message: ${message}');
+ } else {
+ String prefix = event.substring(0, event.indexOf('.'));
+ if (_domains[prefix] == null) {
+ _logger.severe('no domain for notification: ${message}');
+ } else {
+ _domains[prefix]._handleEvent(event, json['params']);
+ }
+ }
+ } else {
+ Completer completer = _completers.remove(json['id']);
+ String methodName = _methodNames.remove(json['id']);
+
+ if (completer == null) {
+ _logger.severe('unmatched request response: ${message}');
+ } else if (json['error'] != null) {
+ completer
+ .completeError(RequestError.parse(methodName, json['error']));
+ } else {
+ completer.complete(json['result']);
+ }
+ }
+ } catch (e) {
+ _logger.severe('unable to decode message: ${message}, ${e}');
+ }
+ }
+
+ Future _call(String method, [Map args]) {
+ String id = '${++_id}';
+ _completers[id] = new Completer();
+ _methodNames[id] = method;
+ Map m = {'id': id, 'method': method};
+ if (args != null) m['params'] = args;
+ String message = _jsonEncoder.encode(m);
+ if (_willSend != null) _willSend(method);
+ _onSend.add(message);
+ _writeMessage(message);
+ return _completers[id].future;
+ }
+
+ static dynamic _toEncodable(obj) => obj is Jsonable ? obj.toMap() : obj;
+}
+
+abstract class Domain {
+ final AnalysisServer server;
+ final String name;
+
+ Map<String, StreamController> _controllers = {};
+ Map<String, Stream> _streams = {};
+
+ Domain(this.server, this.name) {
+ server._domains[name] = this;
+ }
+
+ Future _call(String method, [Map args]) => server._call(method, args);
+
+ Stream<dynamic> _listen(String name, Function cvt) {
+ if (_streams[name] == null) {
+ _controllers[name] = new StreamController.broadcast();
+ _streams[name] = _controllers[name].stream.map(cvt);
+ }
+
+ return _streams[name];
+ }
+
+ void _handleEvent(String name, dynamic event) {
+ if (_controllers[name] != null) {
+ _controllers[name].add(event);
+ }
+ }
+
+ String toString() => 'Domain ${name}';
+}
+
+abstract class Jsonable {
+ Map toMap();
+}
+
+abstract class RefactoringOptions implements Jsonable {}
+
+abstract class ContentOverlayType {
+ final String type;
+
+ ContentOverlayType(this.type);
+}
+
+class RequestError {
+ static RequestError parse(String method, Map m) {
+ if (m == null) return null;
+ return new RequestError(method, m['code'], m['message'],
+ stackTrace: m['stackTrace']);
+ }
+
+ final String method;
+ final String code;
+ final String message;
+ @optional
+ final String stackTrace;
+
+ RequestError(this.method, this.code, this.message, {this.stackTrace});
+
+ String toString() =>
+ '[Analyzer RequestError method: ${method}, code: ${code}, message: ${message}]';
+}
+
+Map _stripNullValues(Map m) {
+ Map copy = {};
+
+ for (var key in m.keys) {
+ var value = m[key];
+ if (value != null) copy[key] = value;
+ }
+
+ return copy;
+}
+
+// server domain
+
+/// The server domain contains API’s related to the execution of the server.
+class ServerDomain extends Domain {
+ ServerDomain(AnalysisServer server) : super(server, 'server');
+
+ /// Reports that the server is running. This notification is issued once after
+ /// the server has started running but before any requests are processed to
+ /// let the client know that it started correctly.
+ ///
+ /// It is not possible to subscribe to or unsubscribe from this notification.
+ Stream<ServerConnected> get onConnected {
+ return _listen('server.connected', ServerConnected.parse);
+ }
+
+ /// Reports that an unexpected error has occurred while executing the server.
+ /// This notification is not used for problems with specific requests (which
+ /// are returned as part of the response) but is used for exceptions that
+ /// occur while performing other tasks, such as analysis or preparing
+ /// notifications.
+ ///
+ /// It is not possible to subscribe to or unsubscribe from this notification.
+ Stream<ServerError> get onError {
+ return _listen('server.error', ServerError.parse);
+ }
+
+ /// Reports the current status of the server. Parameters are omitted if there
+ /// has been no change in the status represented by that parameter.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"STATUS"` in the list of services passed in a
+ /// server.setSubscriptions request.
+ Stream<ServerStatus> get onStatus {
+ return _listen('server.status', ServerStatus.parse);
+ }
+
+ /// Return the version number of the analysis server.
+ Future<VersionResult> getVersion() =>
+ _call('server.getVersion').then(VersionResult.parse);
+
+ /// Cleanly shutdown the analysis server. Requests that are received after
+ /// this request will not be processed. Requests that were received before
+ /// this request, but for which a response has not yet been sent, will not be
+ /// responded to. No further responses or notifications will be sent after the
+ /// response to this request has been sent.
+ Future shutdown() => _call('server.shutdown');
+
+ /// Subscribe for services. All previous subscriptions are replaced by the
+ /// given set of services.
+ ///
+ /// It is an error if any of the elements in the list are not valid services.
+ /// If there is an error, then the current subscriptions will remain
+ /// unchanged.
+ Future setSubscriptions(List<String> subscriptions) =>
+ _call('server.setSubscriptions', {'subscriptions': subscriptions});
+}
+
+class ServerConnected {
+ static ServerConnected parse(Map m) =>
+ new ServerConnected(m['version'], m['pid'], sessionId: m['sessionId']);
+
+ /// The version number of the analysis server.
+ final String version;
+
+ /// The process id of the analysis server process.
+ final int pid;
+
+ /// The session id for this session.
+ @optional
+ final String sessionId;
+
+ ServerConnected(this.version, this.pid, {this.sessionId});
+}
+
+class ServerError {
+ static ServerError parse(Map m) =>
+ new ServerError(m['isFatal'], m['message'], m['stackTrace']);
+
+ /// True if the error is a fatal error, meaning that the server will shutdown
+ /// automatically after sending this notification.
+ final bool isFatal;
+
+ /// The error message indicating what kind of error was encountered.
+ final String message;
+
+ /// The stack trace associated with the generation of the error, used for
+ /// debugging the server.
+ final String stackTrace;
+
+ ServerError(this.isFatal, this.message, this.stackTrace);
+}
+
+class ServerStatus {
+ static ServerStatus parse(Map m) => new ServerStatus(
+ analysis: AnalysisStatus.parse(m['analysis']),
+ pub: PubStatus.parse(m['pub']));
+
+ /// The current status of analysis, including whether analysis is being
+ /// performed and if so what is being analyzed.
+ @optional
+ final AnalysisStatus analysis;
+
+ /// The current status of pub execution, indicating whether we are currently
+ /// running pub.
+ @optional
+ final PubStatus pub;
+
+ ServerStatus({this.analysis, this.pub});
+}
+
+class VersionResult {
+ static VersionResult parse(Map m) => new VersionResult(m['version']);
+
+ /// The version number of the analysis server.
+ final String version;
+
+ VersionResult(this.version);
+}
+
+// analysis domain
+
+/// The analysis domain contains API’s related to the analysis of files.
+class AnalysisDomain extends Domain {
+ AnalysisDomain(AnalysisServer server) : super(server, 'analysis');
+
+ /// Reports the paths of the files that are being analyzed.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"ANALYZED_FILES"` in the list of services passed
+ /// in an analysis.setGeneralSubscriptions request.
+ Stream<AnalysisAnalyzedFiles> get onAnalyzedFiles {
+ return _listen('analysis.analyzedFiles', AnalysisAnalyzedFiles.parse);
+ }
+
+ /// Reports the errors associated with a given file. The set of errors
+ /// included in the notification is always a complete list that supersedes any
+ /// previously reported errors.
+ Stream<AnalysisErrors> get onErrors {
+ return _listen('analysis.errors', AnalysisErrors.parse);
+ }
+
+ /// Reports that any analysis results that were previously associated with the
+ /// given files should be considered to be invalid because those files are no
+ /// longer being analyzed, either because the analysis root that contained it
+ /// is no longer being analyzed or because the file no longer exists.
+ ///
+ /// If a file is included in this notification and at some later time a
+ /// notification with results for the file is received, clients should assume
+ /// that the file is once again being analyzed and the information should be
+ /// processed.
+ ///
+ /// It is not possible to subscribe to or unsubscribe from this notification.
+ Stream<AnalysisFlushResults> get onFlushResults {
+ return _listen('analysis.flushResults', AnalysisFlushResults.parse);
+ }
+
+ /// Reports the folding regions associated with a given file. Folding regions
+ /// can be nested, but will not be overlapping. Nesting occurs when a foldable
+ /// element, such as a method, is nested inside another foldable element such
+ /// as a class.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"FOLDING"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisFolding> get onFolding {
+ return _listen('analysis.folding', AnalysisFolding.parse);
+ }
+
+ /// Reports the highlight regions associated with a given file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"HIGHLIGHTS"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisHighlights> get onHighlights {
+ return _listen('analysis.highlights', AnalysisHighlights.parse);
+ }
+
+ /// Reports the classes that are implemented or extended and class members
+ /// that are implemented or overridden in a file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"IMPLEMENTED"` in the list of services passed in
+ /// an analysis.setSubscriptions request.
+ Stream<AnalysisImplemented> get onImplemented {
+ return _listen('analysis.implemented', AnalysisImplemented.parse);
+ }
+
+ /// Reports that the navigation information associated with a region of a
+ /// single file has become invalid and should be re-requested.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"INVALIDATE"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisInvalidate> get onInvalidate {
+ return _listen('analysis.invalidate', AnalysisInvalidate.parse);
+ }
+
+ /// Reports the navigation targets associated with a given file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"NAVIGATION"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisNavigation> get onNavigation {
+ return _listen('analysis.navigation', AnalysisNavigation.parse);
+ }
+
+ /// Reports the occurrences of references to elements within a single file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"OCCURRENCES"` in the list of services passed in
+ /// an analysis.setSubscriptions request.
+ Stream<AnalysisOccurrences> get onOccurrences {
+ return _listen('analysis.occurrences', AnalysisOccurrences.parse);
+ }
+
+ /// Reports the outline associated with a single file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"OUTLINE"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisOutline> get onOutline {
+ return _listen('analysis.outline', AnalysisOutline.parse);
+ }
+
+ /// Reports the overriding members in a file.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value `"OVERRIDES"` in the list of services passed in an
+ /// analysis.setSubscriptions request.
+ Stream<AnalysisOverrides> get onOverrides {
+ return _listen('analysis.overrides', AnalysisOverrides.parse);
+ }
+
+ /// Return the errors associated with the given file. If the errors for the
+ /// given file have not yet been computed, or the most recently computed
+ /// errors for the given file are out of date, then the response for this
+ /// request will be delayed until they have been computed. If some or all of
+ /// the errors for the file cannot be computed, then the subset of the errors
+ /// that can be computed will be returned and the response will contain an
+ /// error to indicate why the errors could not be computed. If the content of
+ /// the file changes after this request was received but before a response
+ /// could be sent, then an error of type `CONTENT_MODIFIED` will be generated.
+ ///
+ /// This request is intended to be used by clients that cannot asynchronously
+ /// apply updated error information. Clients that **can** apply error
+ /// information as it becomes available should use the information provided by
+ /// the 'analysis.errors' notification.
+ ///
+ /// If a request is made for a file which does not exist, or which is not
+ /// currently subject to analysis (e.g. because it is not associated with any
+ /// analysis root specified to analysis.setAnalysisRoots), an error of type
+ /// `GET_ERRORS_INVALID_FILE` will be generated.
+ Future<ErrorsResult> getErrors(String file) {
+ Map m = {'file': file};
+ return _call('analysis.getErrors', m).then(ErrorsResult.parse);
+ }
+
+ /// Return the hover information associate with the given location. If some or
+ /// all of the hover information is not available at the time this request is
+ /// processed the information will be omitted from the response.
+ Future<HoverResult> getHover(String file, int offset) {
+ Map m = {'file': file, 'offset': offset};
+ return _call('analysis.getHover', m).then(HoverResult.parse);
+ }
+
+ /// Return the transitive closure of reachable sources for a given file.
+ ///
+ /// If a request is made for a file which does not exist, or which is not
+ /// currently subject to analysis (e.g. because it is not associated with any
+ /// analysis root specified to analysis.setAnalysisRoots), an error of type
+ /// `GET_REACHABLE_SOURCES_INVALID_FILE` will be generated.
+ Future<ReachableSourcesResult> getReachableSources(String file) {
+ Map m = {'file': file};
+ return _call('analysis.getReachableSources', m)
+ .then(ReachableSourcesResult.parse);
+ }
+
+ /// Return library dependency information for use in client-side indexing and
+ /// package URI resolution.
+ ///
+ /// Clients that are only using the libraries field should consider using the
+ /// analyzedFiles notification instead.
+ Future<LibraryDependenciesResult> getLibraryDependencies() =>
+ _call('analysis.getLibraryDependencies')
+ .then(LibraryDependenciesResult.parse);
+
+ /// Return the navigation information associated with the given region of the
+ /// given file. If the navigation information for the given file has not yet
+ /// been computed, or the most recently computed navigation information for
+ /// the given file is out of date, then the response for this request will be
+ /// delayed until it has been computed. If the content of the file changes
+ /// after this request was received but before a response could be sent, then
+ /// an error of type `CONTENT_MODIFIED` will be generated.
+ ///
+ /// If a navigation region overlaps (but extends either before or after) the
+ /// given region of the file it will be included in the result. This means
+ /// that it is theoretically possible to get the same navigation region in
+ /// response to multiple requests. Clients can avoid this by always choosing a
+ /// region that starts at the beginning of a line and ends at the end of a
+ /// (possibly different) line in the file.
+ ///
+ /// If a request is made for a file which does not exist, or which is not
+ /// currently subject to analysis (e.g. because it is not associated with any
+ /// analysis root specified to analysis.setAnalysisRoots), an error of type
+ /// `GET_NAVIGATION_INVALID_FILE` will be generated.
+ Future<NavigationResult> getNavigation(String file, int offset, int length) {
+ Map m = {'file': file, 'offset': offset, 'length': length};
+ return _call('analysis.getNavigation', m).then(NavigationResult.parse);
+ }
+
+ /// Force the re-analysis of everything contained in the specified analysis
+ /// roots. This will cause all previously computed analysis results to be
+ /// discarded and recomputed, and will cause all subscribed notifications to
+ /// be re-sent.
+ ///
+ /// If no analysis roots are provided, then all current analysis roots will be
+ /// re-analyzed. If an empty list of analysis roots is provided, then nothing
+ /// will be re-analyzed. If the list contains one or more paths that are not
+ /// currently analysis roots, then an error of type `INVALID_ANALYSIS_ROOT`
+ /// will be generated.
+ Future reanalyze({List<String> roots}) {
+ Map m = {};
+ if (roots != null) m['roots'] = roots;
+ return _call('analysis.reanalyze', m);
+ }
+
+ /// Sets the root paths used to determine which files to analyze. The set of
+ /// files to be analyzed are all of the files in one of the root paths that
+ /// are not either explicitly or implicitly excluded. A file is explicitly
+ /// excluded if it is in one of the excluded paths. A file is implicitly
+ /// excluded if it is in a subdirectory of one of the root paths where the
+ /// name of the subdirectory starts with a period (that is, a hidden
+ /// directory).
+ ///
+ /// Note that this request determines the set of requested analysis roots. The
+ /// actual set of analysis roots at any given time is the intersection of this
+ /// set with the set of files and directories actually present on the
+ /// filesystem. When the filesystem changes, the actual set of analysis roots
+ /// is automatically updated, but the set of requested analysis roots is
+ /// unchanged. This means that if the client sets an analysis root before the
+ /// root becomes visible to server in the filesystem, there is no error; once
+ /// the server sees the root in the filesystem it will start analyzing it.
+ /// Similarly, server will stop analyzing files that are removed from the file
+ /// system but they will remain in the set of requested roots.
+ ///
+ /// If an included path represents a file, then server will look in the
+ /// directory containing the file for a pubspec.yaml file. If none is found,
+ /// then the parents of the directory will be searched until such a file is
+ /// found or the root of the file system is reached. If such a file is found,
+ /// it will be used to resolve package: URI’s within the file.
+ Future setAnalysisRoots(List<String> included, List<String> excluded,
+ {Map<String, String> packageRoots}) {
+ Map m = {'included': included, 'excluded': excluded};
+ if (packageRoots != null) m['packageRoots'] = packageRoots;
+ return _call('analysis.setAnalysisRoots', m);
+ }
+
+ /// Subscribe for general services (that is, services that are not specific to
+ /// individual files). All previous subscriptions are replaced by the given
+ /// set of services.
+ ///
+ /// It is an error if any of the elements in the list are not valid services.
+ /// If there is an error, then the current subscriptions will remain
+ /// unchanged.
+ Future setGeneralSubscriptions(List<String> subscriptions) => _call(
+ 'analysis.setGeneralSubscriptions', {'subscriptions': subscriptions});
+
+ /// Set the priority files to the files in the given list. A priority file is
+ /// a file that is given priority when scheduling which analysis work to do
+ /// first. The list typically contains those files that are visible to the
+ /// user and those for which analysis results will have the biggest impact on
+ /// the user experience. The order of the files within the list is
+ /// significant: the first file will be given higher priority than the second,
+ /// the second higher priority than the third, and so on.
+ ///
+ /// Note that this request determines the set of requested priority files. The
+ /// actual set of priority files is the intersection of the requested set of
+ /// priority files with the set of files currently subject to analysis. (See
+ /// analysis.setSubscriptions for a description of files that are subject to
+ /// analysis.)
+ ///
+ /// If a requested priority file is a directory it is ignored, but remains in
+ /// the set of requested priority files so that if it later becomes a file it
+ /// can be included in the set of actual priority files.
+ Future setPriorityFiles(List<String> files) =>
+ _call('analysis.setPriorityFiles', {'files': files});
+
+ /// Subscribe for services that are specific to individual files. All previous
+ /// subscriptions are replaced by the current set of subscriptions. If a given
+ /// service is not included as a key in the map then no files will be
+ /// subscribed to the service, exactly as if the service had been included in
+ /// the map with an explicit empty list of files.
+ ///
+ /// Note that this request determines the set of requested subscriptions. The
+ /// actual set of subscriptions at any given time is the intersection of this
+ /// set with the set of files currently subject to analysis. The files
+ /// currently subject to analysis are the set of files contained within an
+ /// actual analysis root but not excluded, plus all of the files transitively
+ /// reachable from those files via import, export and part directives. (See
+ /// analysis.setAnalysisRoots for an explanation of how the actual analysis
+ /// roots are determined.) When the actual analysis roots change, the actual
+ /// set of subscriptions is automatically updated, but the set of requested
+ /// subscriptions is unchanged.
+ ///
+ /// If a requested subscription is a directory it is ignored, but remains in
+ /// the set of requested subscriptions so that if it later becomes a file it
+ /// can be included in the set of actual subscriptions.
+ ///
+ /// It is an error if any of the keys in the map are not valid services. If
+ /// there is an error, then the existing subscriptions will remain unchanged.
+ Future setSubscriptions(Map<String, List<String>> subscriptions) =>
+ _call('analysis.setSubscriptions', {'subscriptions': subscriptions});
+
+ /// Update the content of one or more files. Files that were previously
+ /// updated but not included in this update remain unchanged. This effectively
+ /// represents an overlay of the filesystem. The files whose content is
+ /// overridden are therefore seen by server as being files with the given
+ /// content, even if the files do not exist on the filesystem or if the file
+ /// path represents the path to a directory on the filesystem.
+ Future updateContent(Map<String, ContentOverlayType> files) =>
+ _call('analysis.updateContent', {'files': files});
+
+ @deprecated
+ Future updateOptions(AnalysisOptions options) =>
+ _call('analysis.updateOptions', {'options': options});
+}
+
+class AnalysisAnalyzedFiles {
+ static AnalysisAnalyzedFiles parse(Map m) => new AnalysisAnalyzedFiles(
+ m['directories'] == null ? null : new List.from(m['directories']));
+
+ /// A list of the paths of the files that are being analyzed.
+ final List<String> directories;
+
+ AnalysisAnalyzedFiles(this.directories);
+}
+
+class AnalysisErrors {
+ static AnalysisErrors parse(Map m) => new AnalysisErrors(
+ m['file'],
+ m['errors'] == null
+ ? null
+ : new List.from(m['errors'].map((obj) => AnalysisError.parse(obj))));
+
+ /// The file containing the errors.
+ final String file;
+
+ /// The errors contained in the file.
+ final List<AnalysisError> errors;
+
+ AnalysisErrors(this.file, this.errors);
+}
+
+class AnalysisFlushResults {
+ static AnalysisFlushResults parse(Map m) => new AnalysisFlushResults(
+ m['files'] == null ? null : new List.from(m['files']));
+
+ /// The files that are no longer being analyzed.
+ final List<String> files;
+
+ AnalysisFlushResults(this.files);
+}
+
+class AnalysisFolding {
+ static AnalysisFolding parse(Map m) => new AnalysisFolding(
+ m['file'],
+ m['regions'] == null
+ ? null
+ : new List.from(m['regions'].map((obj) => FoldingRegion.parse(obj))));
+
+ /// The file containing the folding regions.
+ final String file;
+
+ /// The folding regions contained in the file.
+ final List<FoldingRegion> regions;
+
+ AnalysisFolding(this.file, this.regions);
+}
+
+class AnalysisHighlights {
+ static AnalysisHighlights parse(Map m) => new AnalysisHighlights(
+ m['file'],
+ m['regions'] == null
+ ? null
+ : new List.from(
+ m['regions'].map((obj) => HighlightRegion.parse(obj))));
+
+ /// The file containing the highlight regions.
+ final String file;
+
+ /// The highlight regions contained in the file. Each highlight region
+ /// represents a particular syntactic or semantic meaning associated with some
+ /// range. Note that the highlight regions that are returned can overlap other
+ /// highlight regions if there is more than one meaning associated with a
+ /// particular region.
+ final List<HighlightRegion> regions;
+
+ AnalysisHighlights(this.file, this.regions);
+}
+
+class AnalysisImplemented {
+ static AnalysisImplemented parse(Map m) => new AnalysisImplemented(
+ m['file'],
+ m['classes'] == null
+ ? null
+ : new List.from(
+ m['classes'].map((obj) => ImplementedClass.parse(obj))),
+ m['members'] == null
+ ? null
+ : new List.from(
+ m['members'].map((obj) => ImplementedMember.parse(obj))));
+
+ /// The file with which the implementations are associated.
+ final String file;
+
+ /// The classes defined in the file that are implemented or extended.
+ final List<ImplementedClass> classes;
+
+ /// The member defined in the file that are implemented or overridden.
+ final List<ImplementedMember> members;
+
+ AnalysisImplemented(this.file, this.classes, this.members);
+}
+
+class AnalysisInvalidate {
+ static AnalysisInvalidate parse(Map m) =>
+ new AnalysisInvalidate(m['file'], m['offset'], m['length'], m['delta']);
+
+ /// The file whose information has been invalidated.
+ final String file;
+
+ /// The offset of the invalidated region.
+ final int offset;
+
+ /// The length of the invalidated region.
+ final int length;
+
+ /// The delta to be applied to the offsets in information that follows the
+ /// invalidated region in order to update it so that it doesn't need to be
+ /// re-requested.
+ final int delta;
+
+ AnalysisInvalidate(this.file, this.offset, this.length, this.delta);
+}
+
+class AnalysisNavigation {
+ static AnalysisNavigation parse(Map m) => new AnalysisNavigation(
+ m['file'],
+ m['regions'] == null
+ ? null
+ : new List.from(
+ m['regions'].map((obj) => NavigationRegion.parse(obj))),
+ m['targets'] == null
+ ? null
+ : new List.from(
+ m['targets'].map((obj) => NavigationTarget.parse(obj))),
+ m['files'] == null ? null : new List.from(m['files']));
+
+ /// The file containing the navigation regions.
+ final String file;
+
+ /// The navigation regions contained in the file. The regions are sorted by
+ /// their offsets. Each navigation region represents a list of targets
+ /// associated with some range. The lists will usually contain a single
+ /// target, but can contain more in the case of a part that is included in
+ /// multiple libraries or in Dart code that is compiled against multiple
+ /// versions of a package. Note that the navigation regions that are returned
+ /// do not overlap other navigation regions.
+ final List<NavigationRegion> regions;
+
+ /// The navigation targets referenced in the file. They are referenced by
+ /// `NavigationRegion`s by their index in this array.
+ final List<NavigationTarget> targets;
+
+ /// The files containing navigation targets referenced in the file. They are
+ /// referenced by `NavigationTarget`s by their index in this array.
+ final List<String> files;
+
+ AnalysisNavigation(this.file, this.regions, this.targets, this.files);
+}
+
+class AnalysisOccurrences {
+ static AnalysisOccurrences parse(Map m) => new AnalysisOccurrences(
+ m['file'],
+ m['occurrences'] == null
+ ? null
+ : new List.from(
+ m['occurrences'].map((obj) => Occurrences.parse(obj))));
+
+ /// The file in which the references occur.
+ final String file;
+
+ /// The occurrences of references to elements within the file.
+ final List<Occurrences> occurrences;
+
+ AnalysisOccurrences(this.file, this.occurrences);
+}
+
+class AnalysisOutline {
+ static AnalysisOutline parse(Map m) =>
+ new AnalysisOutline(m['file'], m['kind'], Outline.parse(m['outline']),
+ libraryName: m['libraryName']);
+
+ /// The file with which the outline is associated.
+ final String file;
+
+ /// The kind of the file.
+ final String kind;
+
+ /// The outline associated with the file.
+ final Outline outline;
+
+ /// The name of the library defined by the file using a "library" directive,
+ /// or referenced by a "part of" directive. If both "library" and "part of"
+ /// directives are present, then the "library" directive takes precedence.
+ /// This field will be omitted if the file has neither "library" nor "part of"
+ /// directives.
+ @optional
+ final String libraryName;
+
+ AnalysisOutline(this.file, this.kind, this.outline, {this.libraryName});
+}
+
+class AnalysisOverrides {
+ static AnalysisOverrides parse(Map m) => new AnalysisOverrides(
+ m['file'],
+ m['overrides'] == null
+ ? null
+ : new List.from(m['overrides'].map((obj) => Override.parse(obj))));
+
+ /// The file with which the overrides are associated.
+ final String file;
+
+ /// The overrides associated with the file.
+ final List<Override> overrides;
+
+ AnalysisOverrides(this.file, this.overrides);
+}
+
+class ErrorsResult {
+ static ErrorsResult parse(Map m) => new ErrorsResult(m['errors'] == null
+ ? null
+ : new List.from(m['errors'].map((obj) => AnalysisError.parse(obj))));
+
+ /// The errors associated with the file.
+ final List<AnalysisError> errors;
+
+ ErrorsResult(this.errors);
+}
+
+class HoverResult {
+ static HoverResult parse(Map m) => new HoverResult(m['hovers'] == null
+ ? null
+ : new List.from(m['hovers'].map((obj) => HoverInformation.parse(obj))));
+
+ /// The hover information associated with the location. The list will be empty
+ /// if no information could be determined for the location. The list can
+ /// contain multiple items if the file is being analyzed in multiple contexts
+ /// in conflicting ways (such as a part that is included in multiple
+ /// libraries).
+ final List<HoverInformation> hovers;
+
+ HoverResult(this.hovers);
+}
+
+class ReachableSourcesResult {
+ static ReachableSourcesResult parse(Map m) =>
+ new ReachableSourcesResult(new Map.from(m['sources']));
+
+ /// A mapping from source URIs to directly reachable source URIs. For example,
+ /// a file "foo.dart" that imports "bar.dart" would have the corresponding
+ /// mapping { "file:///foo.dart" : ["file:///bar.dart"] }. If "bar.dart" has
+ /// further imports (or exports) there will be a mapping from the URI
+ /// "file:///bar.dart" to them. To check if a specific URI is reachable from a
+ /// given file, clients can check for its presence in the resulting key set.
+ final Map<String, List<String>> sources;
+
+ ReachableSourcesResult(this.sources);
+}
+
+class LibraryDependenciesResult {
+ static LibraryDependenciesResult parse(Map m) =>
+ new LibraryDependenciesResult(
+ m['libraries'] == null ? null : new List.from(m['libraries']),
+ new Map.from(m['packageMap']));
+
+ /// A list of the paths of library elements referenced by files in existing
+ /// analysis roots.
+ final List<String> libraries;
+
+ /// A mapping from context source roots to package maps which map package
+ /// names to source directories for use in client-side package URI resolution.
+ final Map<String, Map<String, List<String>>> packageMap;
+
+ LibraryDependenciesResult(this.libraries, this.packageMap);
+}
+
+class NavigationResult {
+ static NavigationResult parse(Map m) => new NavigationResult(
+ m['files'] == null ? null : new List.from(m['files']),
+ m['targets'] == null
+ ? null
+ : new List.from(
+ m['targets'].map((obj) => NavigationTarget.parse(obj))),
+ m['regions'] == null
+ ? null
+ : new List.from(
+ m['regions'].map((obj) => NavigationRegion.parse(obj))));
+
+ /// A list of the paths of files that are referenced by the navigation
+ /// targets.
+ final List<String> files;
+
+ /// A list of the navigation targets that are referenced by the navigation
+ /// regions.
+ final List<NavigationTarget> targets;
+
+ /// A list of the navigation regions within the requested region of the file.
+ final List<NavigationRegion> regions;
+
+ NavigationResult(this.files, this.targets, this.regions);
+}
+
+// completion domain
+
+/// The code completion domain contains commands related to getting code
+/// completion suggestions.
+class CompletionDomain extends Domain {
+ CompletionDomain(AnalysisServer server) : super(server, 'completion');
+
+ /// Reports the completion suggestions that should be presented to the user.
+ /// The set of suggestions included in the notification is always a complete
+ /// list that supersedes any previously reported suggestions.
+ Stream<CompletionResults> get onResults {
+ return _listen('completion.results', CompletionResults.parse);
+ }
+
+ /// Request that completion suggestions for the given offset in the given file
+ /// be returned.
+ Future<SuggestionsResult> getSuggestions(String file, int offset) {
+ Map m = {'file': file, 'offset': offset};
+ return _call('completion.getSuggestions', m).then(SuggestionsResult.parse);
+ }
+}
+
+class CompletionResults {
+ static CompletionResults parse(Map m) => new CompletionResults(
+ m['id'],
+ m['replacementOffset'],
+ m['replacementLength'],
+ m['results'] == null
+ ? null
+ : new List.from(
+ m['results'].map((obj) => CompletionSuggestion.parse(obj))),
+ m['isLast']);
+
+ /// The id associated with the completion.
+ final String id;
+
+ /// The offset of the start of the text to be replaced. This will be different
+ /// than the offset used to request the completion suggestions if there was a
+ /// portion of an identifier before the original offset. In particular, the
+ /// replacementOffset will be the offset of the beginning of said identifier.
+ final int replacementOffset;
+
+ /// The length of the text to be replaced if the remainder of the identifier
+ /// containing the cursor is to be replaced when the suggestion is applied
+ /// (that is, the number of characters in the existing identifier).
+ final int replacementLength;
+
+ /// The completion suggestions being reported. The notification contains all
+ /// possible completions at the requested cursor position, even those that do
+ /// not match the characters the user has already typed. This allows the
+ /// client to respond to further keystrokes from the user without having to
+ /// make additional requests.
+ final List<CompletionSuggestion> results;
+
+ /// True if this is that last set of results that will be returned for the
+ /// indicated completion.
+ final bool isLast;
+
+ CompletionResults(this.id, this.replacementOffset, this.replacementLength,
+ this.results, this.isLast);
+}
+
+class SuggestionsResult {
+ static SuggestionsResult parse(Map m) => new SuggestionsResult(m['id']);
+
+ /// The identifier used to associate results with this completion request.
+ final String id;
+
+ SuggestionsResult(this.id);
+}
+
+// search domain
+
+/// The search domain contains commands related to searches that can be
+/// performed against the code base.
+class SearchDomain extends Domain {
+ SearchDomain(AnalysisServer server) : super(server, 'search');
+
+ /// Reports some or all of the results of performing a requested search.
+ /// Unlike other notifications, this notification contains search results that
+ /// should be added to any previously received search results associated with
+ /// the same search id.
+ Stream<SearchResults> get onResults {
+ return _listen('search.results', SearchResults.parse);
+ }
+
+ /// Perform a search for references to the element defined or referenced at
+ /// the given offset in the given file.
+ ///
+ /// An identifier is returned immediately, and individual results will be
+ /// returned via the search.results notification as they become available.
+ Future<FindElementReferencesResult> findElementReferences(
+ String file, int offset, bool includePotential) {
+ Map m = {
+ 'file': file,
+ 'offset': offset,
+ 'includePotential': includePotential
+ };
+ return _call('search.findElementReferences', m)
+ .then(FindElementReferencesResult.parse);
+ }
+
+ /// Perform a search for declarations of members whose name is equal to the
+ /// given name.
+ ///
+ /// An identifier is returned immediately, and individual results will be
+ /// returned via the search.results notification as they become available.
+ Future<FindMemberDeclarationsResult> findMemberDeclarations(String name) {
+ Map m = {'name': name};
+ return _call('search.findMemberDeclarations', m)
+ .then(FindMemberDeclarationsResult.parse);
+ }
+
+ /// Perform a search for references to members whose name is equal to the
+ /// given name. This search does not check to see that there is a member
+ /// defined with the given name, so it is able to find references to undefined
+ /// members as well.
+ ///
+ /// An identifier is returned immediately, and individual results will be
+ /// returned via the search.results notification as they become available.
+ Future<FindMemberReferencesResult> findMemberReferences(String name) {
+ Map m = {'name': name};
+ return _call('search.findMemberReferences', m)
+ .then(FindMemberReferencesResult.parse);
+ }
+
+ /// Perform a search for declarations of top-level elements (classes,
+ /// typedefs, getters, setters, functions and fields) whose name matches the
+ /// given pattern.
+ ///
+ /// An identifier is returned immediately, and individual results will be
+ /// returned via the search.results notification as they become available.
+ Future<FindTopLevelDeclarationsResult> findTopLevelDeclarations(
+ String pattern) {
+ Map m = {'pattern': pattern};
+ return _call('search.findTopLevelDeclarations', m)
+ .then(FindTopLevelDeclarationsResult.parse);
+ }
+
+ /// Return the type hierarchy of the class declared or referenced at the given
+ /// location.
+ Future<TypeHierarchyResult> getTypeHierarchy(String file, int offset,
+ {bool superOnly}) {
+ Map m = {'file': file, 'offset': offset};
+ if (superOnly != null) m['superOnly'] = superOnly;
+ return _call('search.getTypeHierarchy', m).then(TypeHierarchyResult.parse);
+ }
+}
+
+class SearchResults {
+ static SearchResults parse(Map m) => new SearchResults(
+ m['id'],
+ m['results'] == null
+ ? null
+ : new List.from(m['results'].map((obj) => SearchResult.parse(obj))),
+ m['isLast']);
+
+ /// The id associated with the search.
+ final String id;
+
+ /// The search results being reported.
+ final List<SearchResult> results;
+
+ /// True if this is that last set of results that will be returned for the
+ /// indicated search.
+ final bool isLast;
+
+ SearchResults(this.id, this.results, this.isLast);
+}
+
+class FindElementReferencesResult {
+ static FindElementReferencesResult parse(Map m) =>
+ new FindElementReferencesResult(
+ id: m['id'], element: Element.parse(m['element']));
+
+ /// The identifier used to associate results with this search request.
+ ///
+ /// If no element was found at the given location, this field will be absent,
+ /// and no results will be reported via the search.results notification.
+ @optional
+ final String id;
+
+ /// The element referenced or defined at the given offset and whose references
+ /// will be returned in the search results.
+ ///
+ /// If no element was found at the given location, this field will be absent.
+ @optional
+ final Element element;
+
+ FindElementReferencesResult({this.id, this.element});
+}
+
+class FindMemberDeclarationsResult {
+ static FindMemberDeclarationsResult parse(Map m) =>
+ new FindMemberDeclarationsResult(m['id']);
+
+ /// The identifier used to associate results with this search request.
+ final String id;
+
+ FindMemberDeclarationsResult(this.id);
+}
+
+class FindMemberReferencesResult {
+ static FindMemberReferencesResult parse(Map m) =>
+ new FindMemberReferencesResult(m['id']);
+
+ /// The identifier used to associate results with this search request.
+ final String id;
+
+ FindMemberReferencesResult(this.id);
+}
+
+class FindTopLevelDeclarationsResult {
+ static FindTopLevelDeclarationsResult parse(Map m) =>
+ new FindTopLevelDeclarationsResult(m['id']);
+
+ /// The identifier used to associate results with this search request.
+ final String id;
+
+ FindTopLevelDeclarationsResult(this.id);
+}
+
+class TypeHierarchyResult {
+ static TypeHierarchyResult parse(Map m) => new TypeHierarchyResult(
+ hierarchyItems: m['hierarchyItems'] == null
+ ? null
+ : new List.from(
+ m['hierarchyItems'].map((obj) => TypeHierarchyItem.parse(obj))));
+
+ /// A list of the types in the requested hierarchy. The first element of the
+ /// list is the item representing the type for which the hierarchy was
+ /// requested. The index of other elements of the list is unspecified, but
+ /// correspond to the integers used to reference supertype and subtype items
+ /// within the items.
+ ///
+ /// This field will be absent if the code at the given file and offset does
+ /// not represent a type, or if the file has not been sufficiently analyzed to
+ /// allow a type hierarchy to be produced.
+ @optional
+ final List<TypeHierarchyItem> hierarchyItems;
+
+ TypeHierarchyResult({this.hierarchyItems});
+}
+
+// edit domain
+
+/// The edit domain contains commands related to edits that can be applied to
+/// the code.
+class EditDomain extends Domain {
+ EditDomain(AnalysisServer server) : super(server, 'edit');
+
+ /// Format the contents of a single file. The currently selected region of
+ /// text is passed in so that the selection can be preserved across the
+ /// formatting operation. The updated selection will be as close to matching
+ /// the original as possible, but whitespace at the beginning or end of the
+ /// selected region will be ignored. If preserving selection information is
+ /// not required, zero (0) can be specified for both the selection offset and
+ /// selection length.
+ ///
+ /// If a request is made for a file which does not exist, or which is not
+ /// currently subject to analysis (e.g. because it is not associated with any
+ /// analysis root specified to analysis.setAnalysisRoots), an error of type
+ /// `FORMAT_INVALID_FILE` will be generated. If the source contains syntax
+ /// errors, an error of type `FORMAT_WITH_ERRORS` will be generated.
+ Future<FormatResult> format(
+ String file, int selectionOffset, int selectionLength,
+ {int lineLength}) {
+ Map m = {
+ 'file': file,
+ 'selectionOffset': selectionOffset,
+ 'selectionLength': selectionLength
+ };
+ if (lineLength != null) m['lineLength'] = lineLength;
+ return _call('edit.format', m).then(FormatResult.parse);
+ }
+
+ /// Return the set of assists that are available at the given location. An
+ /// assist is distinguished from a refactoring primarily by the fact that it
+ /// affects a single file and does not require user input in order to be
+ /// performed.
+ Future<AssistsResult> getAssists(String file, int offset, int length) {
+ Map m = {'file': file, 'offset': offset, 'length': length};
+ return _call('edit.getAssists', m).then(AssistsResult.parse);
+ }
+
+ /// Get a list of the kinds of refactorings that are valid for the given
+ /// selection in the given file.
+ Future<AvailableRefactoringsResult> getAvailableRefactorings(
+ String file, int offset, int length) {
+ Map m = {'file': file, 'offset': offset, 'length': length};
+ return _call('edit.getAvailableRefactorings', m)
+ .then(AvailableRefactoringsResult.parse);
+ }
+
+ /// Return the set of fixes that are available for the errors at a given
+ /// offset in a given file.
+ Future<FixesResult> getFixes(String file, int offset) {
+ Map m = {'file': file, 'offset': offset};
+ return _call('edit.getFixes', m).then(FixesResult.parse);
+ }
+
+ /// Get the changes required to perform a refactoring.
+ ///
+ /// If another refactoring request is received during the processing of this
+ /// one, an error of type `REFACTORING_REQUEST_CANCELLED` will be generated.
+ Future<RefactoringResult> getRefactoring(
+ String kind, String file, int offset, int length, bool validateOnly,
+ {RefactoringOptions options}) {
+ Map m = {
+ 'kind': kind,
+ 'file': file,
+ 'offset': offset,
+ 'length': length,
+ 'validateOnly': validateOnly
+ };
+ if (options != null) m['options'] = options;
+ return _call('edit.getRefactoring', m)
+ .then((m) => RefactoringResult.parse(kind, m));
+ }
+
+ /// Get the changes required to convert the partial statement at the given
+ /// location into a syntactically valid statement. If the current statement is
+ /// already valid the change will insert a newline plus appropriate
+ /// indentation at the end of the line containing the offset. If a change that
+ /// makes the statement valid cannot be determined (perhaps because it has not
+ /// yet been implemented) the statement will be considered already valid and
+ /// the appropriate change returned.
+ @experimental
+ Future<StatementCompletionResult> getStatementCompletion(
+ String file, int offset) {
+ Map m = {'file': file, 'offset': offset};
+ return _call('edit.getStatementCompletion', m)
+ .then(StatementCompletionResult.parse);
+ }
+
+ /// Sort all of the directives, unit and class members of the given Dart file.
+ ///
+ /// If a request is made for a file that does not exist, does not belong to an
+ /// analysis root or is not a Dart file, `SORT_MEMBERS_INVALID_FILE` will be
+ /// generated.
+ ///
+ /// If the Dart file has scan or parse errors, `SORT_MEMBERS_PARSE_ERRORS`
+ /// will be generated.
+ Future<SortMembersResult> sortMembers(String file) {
+ Map m = {'file': file};
+ return _call('edit.sortMembers', m).then(SortMembersResult.parse);
+ }
+
+ /// Organizes all of the directives - removes unused imports and sorts
+ /// directives of the given Dart file according to the (Dart Style
+ /// Guide)[https://www.dartlang.org/articles/style-guide/].
+ ///
+ /// If a request is made for a file that does not exist, does not belong to an
+ /// analysis root or is not a Dart file, `FILE_NOT_ANALYZED` will be
+ /// generated.
+ ///
+ /// If directives of the Dart file cannot be organized, for example because it
+ /// has scan or parse errors, or by other reasons, `ORGANIZE_DIRECTIVES_ERROR`
+ /// will be generated. The message will provide details about the reason.
+ Future<OrganizeDirectivesResult> organizeDirectives(String file) {
+ Map m = {'file': file};
+ return _call('edit.organizeDirectives', m)
+ .then(OrganizeDirectivesResult.parse);
+ }
+}
+
+class FormatResult {
+ static FormatResult parse(Map m) => new FormatResult(
+ m['edits'] == null
+ ? null
+ : new List.from(m['edits'].map((obj) => SourceEdit.parse(obj))),
+ m['selectionOffset'],
+ m['selectionLength']);
+
+ /// The edit(s) to be applied in order to format the code. The list will be
+ /// empty if the code was already formatted (there are no changes).
+ final List<SourceEdit> edits;
+
+ /// The offset of the selection after formatting the code.
+ final int selectionOffset;
+
+ /// The length of the selection after formatting the code.
+ final int selectionLength;
+
+ FormatResult(this.edits, this.selectionOffset, this.selectionLength);
+}
+
+class AssistsResult {
+ static AssistsResult parse(Map m) => new AssistsResult(m['assists'] == null
+ ? null
+ : new List.from(m['assists'].map((obj) => SourceChange.parse(obj))));
+
+ /// The assists that are available at the given location.
+ final List<SourceChange> assists;
+
+ AssistsResult(this.assists);
+}
+
+class AvailableRefactoringsResult {
+ static AvailableRefactoringsResult parse(Map m) =>
+ new AvailableRefactoringsResult(
+ m['kinds'] == null ? null : new List.from(m['kinds']));
+
+ /// The kinds of refactorings that are valid for the given selection.
+ final List<String> kinds;
+
+ AvailableRefactoringsResult(this.kinds);
+}
+
+class FixesResult {
+ static FixesResult parse(Map m) => new FixesResult(m['fixes'] == null
+ ? null
+ : new List.from(m['fixes'].map((obj) => AnalysisErrorFixes.parse(obj))));
+
+ /// The fixes that are available for the errors at the given offset.
+ final List<AnalysisErrorFixes> fixes;
+
+ FixesResult(this.fixes);
+}
+
+class RefactoringResult {
+ static RefactoringResult parse(String kind, Map m) => new RefactoringResult(
+ m['initialProblems'] == null
+ ? null
+ : new List.from(
+ m['initialProblems'].map((obj) => RefactoringProblem.parse(obj))),
+ m['optionsProblems'] == null
+ ? null
+ : new List.from(
+ m['optionsProblems'].map((obj) => RefactoringProblem.parse(obj))),
+ m['finalProblems'] == null
+ ? null
+ : new List.from(
+ m['finalProblems'].map((obj) => RefactoringProblem.parse(obj))),
+ feedback: RefactoringFeedback.parse(kind, m['feedback']),
+ change: SourceChange.parse(m['change']),
+ potentialEdits: m['potentialEdits'] == null
+ ? null
+ : new List.from(m['potentialEdits']));
+
+ /// The initial status of the refactoring, i.e. problems related to the
+ /// context in which the refactoring is requested. The array will be empty if
+ /// there are no known problems.
+ final List<RefactoringProblem> initialProblems;
+
+ /// The options validation status, i.e. problems in the given options, such as
+ /// light-weight validation of a new name, flags compatibility, etc. The array
+ /// will be empty if there are no known problems.
+ final List<RefactoringProblem> optionsProblems;
+
+ /// The final status of the refactoring, i.e. problems identified in the
+ /// result of a full, potentially expensive validation and / or change
+ /// creation. The array will be empty if there are no known problems.
+ final List<RefactoringProblem> finalProblems;
+
+ /// Data used to provide feedback to the user. The structure of the data is
+ /// dependent on the kind of refactoring being created. The data that is
+ /// returned is documented in the section titled
+ /// (Refactorings)[#refactorings], labeled as "Feedback".
+ @optional
+ final RefactoringFeedback feedback;
+
+ /// The changes that are to be applied to affect the refactoring. This field
+ /// will be omitted if there are problems that prevent a set of changes from
+ /// being computed, such as having no options specified for a refactoring that
+ /// requires them, or if only validation was requested.
+ @optional
+ final SourceChange change;
+
+ /// The ids of source edits that are not known to be valid. An edit is not
+ /// known to be valid if there was insufficient type information for the
+ /// server to be able to determine whether or not the code needs to be
+ /// modified, such as when a member is being renamed and there is a reference
+ /// to a member from an unknown type. This field will be omitted if the change
+ /// field is omitted or if there are no potential edits for the refactoring.
+ @optional
+ final List<String> potentialEdits;
+
+ RefactoringResult(
+ this.initialProblems, this.optionsProblems, this.finalProblems,
+ {this.feedback, this.change, this.potentialEdits});
+}
+
+class StatementCompletionResult {
+ static StatementCompletionResult parse(Map m) =>
+ new StatementCompletionResult(
+ SourceChange.parse(m['change']), m['whitespaceOnly']);
+
+ /// The change to be applied in order to complete the statement.
+ final SourceChange change;
+
+ /// Will be true if the change contains nothing but whitespace characters, or
+ /// is empty.
+ final bool whitespaceOnly;
+
+ StatementCompletionResult(this.change, this.whitespaceOnly);
+}
+
+class SortMembersResult {
+ static SortMembersResult parse(Map m) =>
+ new SortMembersResult(SourceFileEdit.parse(m['edit']));
+
+ /// The file edit that is to be applied to the given file to effect the
+ /// sorting.
+ final SourceFileEdit edit;
+
+ SortMembersResult(this.edit);
+}
+
+class OrganizeDirectivesResult {
+ static OrganizeDirectivesResult parse(Map m) =>
+ new OrganizeDirectivesResult(SourceFileEdit.parse(m['edit']));
+
+ /// The file edit that is to be applied to the given file to effect the
+ /// organizing.
+ final SourceFileEdit edit;
+
+ OrganizeDirectivesResult(this.edit);
+}
+
+// execution domain
+
+/// The execution domain contains commands related to providing an execution or
+/// debugging experience.
+class ExecutionDomain extends Domain {
+ ExecutionDomain(AnalysisServer server) : super(server, 'execution');
+
+ /// Reports information needed to allow a single file to be launched.
+ ///
+ /// This notification is not subscribed to by default. Clients can subscribe
+ /// by including the value "LAUNCH_DATA" in the list of services passed in an
+ /// `execution.setSubscriptions` request.
+ Stream<ExecutionLaunchData> get onLaunchData {
+ return _listen('execution.launchData', ExecutionLaunchData.parse);
+ }
+
+ /// Create an execution context for the executable file with the given path.
+ /// The context that is created will persist until execution.deleteContext is
+ /// used to delete it. Clients, therefore, are responsible for managing the
+ /// lifetime of execution contexts.
+ Future<CreateContextResult> createContext(String contextRoot) {
+ Map m = {'contextRoot': contextRoot};
+ return _call('execution.createContext', m).then(CreateContextResult.parse);
+ }
+
+ /// Delete the execution context with the given identifier. The context id is
+ /// no longer valid after this command. The server is allowed to re-use ids
+ /// when they are no longer valid.
+ Future deleteContext(String id) =>
+ _call('execution.deleteContext', {'id': id});
+
+ /// Map a URI from the execution context to the file that it corresponds to,
+ /// or map a file to the URI that it corresponds to in the execution context.
+ ///
+ /// Exactly one of the file and uri fields must be provided. If both fields
+ /// are provided, then an error of type `INVALID_PARAMETER` will be generated.
+ /// Similarly, if neither field is provided, then an error of type
+ /// `INVALID_PARAMETER` will be generated.
+ ///
+ /// If the file field is provided and the value is not the path of a file
+ /// (either the file does not exist or the path references something other
+ /// than a file), then an error of type `INVALID_PARAMETER` will be generated.
+ ///
+ /// If the uri field is provided and the value is not a valid URI or if the
+ /// URI references something that is not a file (either a file that does not
+ /// exist or something other than a file), then an error of type
+ /// `INVALID_PARAMETER` will be generated.
+ ///
+ /// If the contextRoot used to create the execution context does not exist,
+ /// then an error of type `INVALID_EXECUTION_CONTEXT` will be generated.
+ Future<MapUriResult> mapUri(String id, {String file, String uri}) {
+ Map m = {'id': id};
+ if (file != null) m['file'] = file;
+ if (uri != null) m['uri'] = uri;
+ return _call('execution.mapUri', m).then(MapUriResult.parse);
+ }
+
+ @deprecated
+ Future setSubscriptions(List<String> subscriptions) =>
+ _call('execution.setSubscriptions', {'subscriptions': subscriptions});
+}
+
+class ExecutionLaunchData {
+ static ExecutionLaunchData parse(Map m) => new ExecutionLaunchData(m['file'],
+ kind: m['kind'],
+ referencedFiles: m['referencedFiles'] == null
+ ? null
+ : new List.from(m['referencedFiles']));
+
+ /// The file for which launch data is being provided. This will either be a
+ /// Dart library or an HTML file.
+ final String file;
+
+ /// The kind of the executable file. This field is omitted if the file is not
+ /// a Dart file.
+ @optional
+ final String kind;
+
+ /// A list of the Dart files that are referenced by the file. This field is
+ /// omitted if the file is not an HTML file.
+ @optional
+ final List<String> referencedFiles;
+
+ ExecutionLaunchData(this.file, {this.kind, this.referencedFiles});
+}
+
+class CreateContextResult {
+ static CreateContextResult parse(Map m) => new CreateContextResult(m['id']);
+
+ /// The identifier used to refer to the execution context that was created.
+ final String id;
+
+ CreateContextResult(this.id);
+}
+
+class MapUriResult {
+ static MapUriResult parse(Map m) =>
+ new MapUriResult(file: m['file'], uri: m['uri']);
+
+ /// The file to which the URI was mapped. This field is omitted if the uri
+ /// field was not given in the request.
+ @optional
+ final String file;
+
+ /// The URI to which the file path was mapped. This field is omitted if the
+ /// file field was not given in the request.
+ @optional
+ final String uri;
+
+ MapUriResult({this.file, this.uri});
+}
+
+// diagnostic domain
+
+/// The diagnostic domain contains server diagnostics APIs.
+class DiagnosticDomain extends Domain {
+ DiagnosticDomain(AnalysisServer server) : super(server, 'diagnostic');
+
+ /// Return server diagnostics.
+ Future<DiagnosticsResult> getDiagnostics() =>
+ _call('diagnostic.getDiagnostics').then(DiagnosticsResult.parse);
+
+ /// Return the port of the diagnostic web server. If the server is not running
+ /// this call will start the server. If unable to start the diagnostic web
+ /// server, this call will return an error of
+ /// `DEBUG_PORT_COULD_NOT_BE_OPENED`.
+ Future<ServerPortResult> getServerPort() =>
+ _call('diagnostic.getServerPort').then(ServerPortResult.parse);
+}
+
+class DiagnosticsResult {
+ static DiagnosticsResult parse(Map m) =>
+ new DiagnosticsResult(m['contexts'] == null
+ ? null
+ : new List.from(m['contexts'].map((obj) => ContextData.parse(obj))));
+
+ /// The list of analysis contexts.
+ final List<ContextData> contexts;
+
+ DiagnosticsResult(this.contexts);
+}
+
+class ServerPortResult {
+ static ServerPortResult parse(Map m) => new ServerPortResult(m['port']);
+
+ /// The diagnostic server port.
+ final int port;
+
+ ServerPortResult(this.port);
+}
+
+// analytics domain
+
+/// The analytics domain contains APIs related to reporting analytics.
+///
+/// This API allows clients to expose a UI option to enable and disable the
+/// analysis server's reporting of analytics. This value is shared with other
+/// tools and can change outside of this API; because of this, clients should
+/// use the analysis server's flag as the system of record. Clients can choose
+/// to send in additional analytics (see `sendEvent` and `sendTiming`) if they
+/// so choose. Dart command-line tools provide a disclaimer similar to: ` Dart
+/// SDK tools anonymously report feature usage statistics and basic crash
+/// reports to help improve Dart tools over time. See Google's privacy policy:
+/// https://www.google.com/intl/en/policies/privacy/. `
+///
+/// The analysis server will send it's own analytics data (for example,
+/// operations performed, operating system type, SDK version). No data (from the
+/// analysis server or from clients) will be sent if analytics is disabled.
+@experimental
+class AnalyticsDomain extends Domain {
+ AnalyticsDomain(AnalysisServer server) : super(server, 'analytics');
+
+ /// Query whether analytics is enabled.
+ ///
+ /// This flag controls whether the analysis server sends any analytics data to
+ /// the cloud. If disabled, the analysis server does not send any analytics
+ /// data, and any data sent to it by clients (from `sendEvent` and
+ /// `sendTiming`) will be ignored.
+ ///
+ /// The value of this flag can be changed by other tools outside of the
+ /// analysis server's process. When you query the flag, you get the value of
+ /// the flag at a given moment. Clients should not use the value returned to
+ /// decide whether or not to send the `sendEvent` and `sendTiming` requests.
+ /// Those requests should be used unconditionally and server will determine
+ /// whether or not it is appropriate to forward the information to the cloud
+ /// at the time each request is received.
+ Future<IsEnabledResult> isEnabled() =>
+ _call('analytics.isEnabled').then(IsEnabledResult.parse);
+
+ /// Enable or disable the sending of analytics data. Note that there are other
+ /// ways for users to change this setting, so clients cannot assume that they
+ /// have complete control over this setting. In particular, there is no
+ /// guarantee that the result returned by the `isEnabled` request will match
+ /// the last value set via this request.
+ Future enable(bool value) => _call('analytics.enable', {'value': value});
+
+ /// Send information about client events.
+ ///
+ /// Ask the analysis server to include the fact that an action was performed
+ /// in the client as part of the analytics data being sent. The data will only
+ /// be included if the sending of analytics data is enabled at the time the
+ /// request is processed. The action that was performed is indicated by the
+ /// value of the `action` field.
+ ///
+ /// The value of the action field should not include the identity of the
+ /// client. The analytics data sent by server will include the client id
+ /// passed in using the `--client-id` command-line argument. The request will
+ /// be ignored if the client id was not provided when server was started.
+ Future sendEvent(String action) =>
+ _call('analytics.sendEvent', {'action': action});
+
+ /// Send timing information for client events (e.g. code completions).
+ ///
+ /// Ask the analysis server to include the fact that a timed event occurred as
+ /// part of the analytics data being sent. The data will only be included if
+ /// the sending of analytics data is enabled at the time the request is
+ /// processed.
+ ///
+ /// The value of the event field should not include the identity of the
+ /// client. The analytics data sent by server will include the client id
+ /// passed in using the `--client-id` command-line argument. The request will
+ /// be ignored if the client id was not provided when server was started.
+ Future sendTiming(String event, int millis) {
+ Map m = {'event': event, 'millis': millis};
+ return _call('analytics.sendTiming', m);
+ }
+}
+
+class IsEnabledResult {
+ static IsEnabledResult parse(Map m) => new IsEnabledResult(m['enabled']);
+
+ /// Whether sending analytics is enabled or not.
+ final bool enabled;
+
+ IsEnabledResult(this.enabled);
+}
+
+// type definitions
+
+/// A directive to begin overlaying the contents of a file. The supplied content
+/// will be used for analysis in place of the file contents in the filesystem.
+///
+/// If this directive is used on a file that already has a file content overlay,
+/// the old overlay is discarded and replaced with the new one.
+class AddContentOverlay extends ContentOverlayType implements Jsonable {
+ static AddContentOverlay parse(Map m) {
+ if (m == null) return null;
+ return new AddContentOverlay(m['content']);
+ }
+
+ /// The new content of the file.
+ final String content;
+
+ AddContentOverlay(this.content) : super('add');
+
+ Map toMap() => _stripNullValues({'type': type, 'content': content});
+}
+
+/// An indication of an error, warning, or hint that was produced by the
+/// analysis.
+class AnalysisError {
+ static AnalysisError parse(Map m) {
+ if (m == null) return null;
+ return new AnalysisError(m['severity'], m['type'],
+ Location.parse(m['location']), m['message'], m['code'],
+ correction: m['correction'], hasFix: m['hasFix']);
+ }
+
+ /// The severity of the error.
+ final String severity;
+
+ /// The type of the error.
+ final String type;
+
+ /// The location associated with the error.
+ final Location location;
+
+ /// The message to be displayed for this error. The message should indicate
+ /// what is wrong with the code and why it is wrong.
+ final String message;
+
+ /// The name, as a string, of the error code associated with this error.
+ final String code;
+
+ /// The correction message to be displayed for this error. The correction
+ /// message should indicate how the user can fix the error. The field is
+ /// omitted if there is no correction message associated with the error code.
+ @optional
+ final String correction;
+
+ /// A hint to indicate to interested clients that this error has an associated
+ /// fix (or fixes). The absence of this field implies there are not known to
+ /// be fixes. Note that since the operation to calculate whether fixes apply
+ /// needs to be performant it is possible that complicated tests will be
+ /// skipped and a false negative returned. For this reason, this attribute
+ /// should be treated as a "hint". Despite the possibility of false negatives,
+ /// no false positives should be returned. If a client sees this flag set they
+ /// can proceed with the confidence that there are in fact associated fixes.
+ @optional
+ final bool hasFix;
+
+ AnalysisError(
+ this.severity, this.type, this.location, this.message, this.code,
+ {this.correction, this.hasFix});
+
+ bool operator ==(o) =>
+ o is AnalysisError &&
+ severity == o.severity &&
+ type == o.type &&
+ location == o.location &&
+ message == o.message &&
+ code == o.code &&
+ correction == o.correction &&
+ hasFix == o.hasFix;
+
+ int get hashCode =>
+ severity.hashCode ^
+ type.hashCode ^
+ location.hashCode ^
+ message.hashCode ^
+ code.hashCode;
+
+ String toString() =>
+ '[AnalysisError severity: ${severity}, type: ${type}, location: ${location}, message: ${message}, code: ${code}]';
+}
+
+/// A list of fixes associated with a specific error.
+class AnalysisErrorFixes {
+ static AnalysisErrorFixes parse(Map m) {
+ if (m == null) return null;
+ return new AnalysisErrorFixes(
+ AnalysisError.parse(m['error']),
+ m['fixes'] == null
+ ? null
+ : new List.from(m['fixes'].map((obj) => SourceChange.parse(obj))));
+ }
+
+ /// The error with which the fixes are associated.
+ final AnalysisError error;
+
+ /// The fixes associated with the error.
+ final List<SourceChange> fixes;
+
+ AnalysisErrorFixes(this.error, this.fixes);
+}
+
+@deprecated
+class AnalysisOptions implements Jsonable {
+ static AnalysisOptions parse(Map m) {
+ if (m == null) return null;
+ return new AnalysisOptions(
+ enableAsync: m['enableAsync'],
+ enableDeferredLoading: m['enableDeferredLoading'],
+ enableEnums: m['enableEnums'],
+ enableNullAwareOperators: m['enableNullAwareOperators'],
+ enableSuperMixins: m['enableSuperMixins'],
+ generateDart2jsHints: m['generateDart2jsHints'],
+ generateHints: m['generateHints'],
+ generateLints: m['generateLints']);
+ }
+
+ @deprecated
+ @optional
+ final bool enableAsync;
+ @deprecated
+ @optional
+ final bool enableDeferredLoading;
+ @deprecated
+ @optional
+ final bool enableEnums;
+ @deprecated
+ @optional
+ final bool enableNullAwareOperators;
+
+ /// True if the client wants to enable support for the proposed "less
+ /// restricted mixins" proposal (DEP 34).
+ @optional
+ final bool enableSuperMixins;
+
+ /// True if hints that are specific to dart2js should be generated. This
+ /// option is ignored if generateHints is false.
+ @optional
+ final bool generateDart2jsHints;
+
+ /// True if hints should be generated as part of generating errors and
+ /// warnings.
+ @optional
+ final bool generateHints;
+
+ /// True if lints should be generated as part of generating errors and
+ /// warnings.
+ @optional
+ final bool generateLints;
+
+ AnalysisOptions(
+ {this.enableAsync,
+ this.enableDeferredLoading,
+ this.enableEnums,
+ this.enableNullAwareOperators,
+ this.enableSuperMixins,
+ this.generateDart2jsHints,
+ this.generateHints,
+ this.generateLints});
+
+ Map toMap() => _stripNullValues({
+ 'enableAsync': enableAsync,
+ 'enableDeferredLoading': enableDeferredLoading,
+ 'enableEnums': enableEnums,
+ 'enableNullAwareOperators': enableNullAwareOperators,
+ 'enableSuperMixins': enableSuperMixins,
+ 'generateDart2jsHints': generateDart2jsHints,
+ 'generateHints': generateHints,
+ 'generateLints': generateLints
+ });
+}
+
+/// An indication of the current state of analysis.
+class AnalysisStatus {
+ static AnalysisStatus parse(Map m) {
+ if (m == null) return null;
+ return new AnalysisStatus(m['isAnalyzing'],
+ analysisTarget: m['analysisTarget']);
+ }
+
+ /// True if analysis is currently being performed.
+ final bool isAnalyzing;
+
+ /// The name of the current target of analysis. This field is omitted if
+ /// analyzing is false.
+ @optional
+ final String analysisTarget;
+
+ AnalysisStatus(this.isAnalyzing, {this.analysisTarget});
+
+ String toString() => '[AnalysisStatus isAnalyzing: ${isAnalyzing}]';
+}
+
+/// A directive to modify an existing file content overlay. One or more ranges
+/// of text are deleted from the old file content overlay and replaced with new
+/// text.
+///
+/// The edits are applied in the order in which they occur in the list. This
+/// means that the offset of each edit must be correct under the assumption that
+/// all previous edits have been applied.
+///
+/// It is an error to use this overlay on a file that does not yet have a file
+/// content overlay or that has had its overlay removed via
+/// (RemoveContentOverlay)[#type_RemoveContentOverlay].
+///
+/// If any of the edits cannot be applied due to its offset or length being out
+/// of range, an `INVALID_OVERLAY_CHANGE` error will be reported.
+class ChangeContentOverlay extends ContentOverlayType implements Jsonable {
+ static ChangeContentOverlay parse(Map m) {
+ if (m == null) return null;
+ return new ChangeContentOverlay(m['edits'] == null
+ ? null
+ : new List.from(m['edits'].map((obj) => SourceEdit.parse(obj))));
+ }
+
+ /// The edits to be applied to the file.
+ final List<SourceEdit> edits;
+
+ ChangeContentOverlay(this.edits) : super('change');
+
+ Map toMap() => _stripNullValues({'type': type, 'edits': edits});
+}
+
+/// A suggestion for how to complete partially entered text. Many of the fields
+/// are optional, depending on the kind of element being suggested.
+class CompletionSuggestion implements Jsonable {
+ static CompletionSuggestion parse(Map m) {
+ if (m == null) return null;
+ return new CompletionSuggestion(
+ m['kind'],
+ m['relevance'],
+ m['completion'],
+ m['selectionOffset'],
+ m['selectionLength'],
+ m['isDeprecated'],
+ m['isPotential'],
+ docSummary: m['docSummary'],
+ docComplete: m['docComplete'],
+ declaringType: m['declaringType'],
+ defaultArgumentListString: m['defaultArgumentListString'],
+ defaultArgumentListTextRanges:
+ m['defaultArgumentListTextRanges'] == null
+ ? null
+ : new List.from(m['defaultArgumentListTextRanges']),
+ element: Element.parse(m['element']),
+ returnType: m['returnType'],
+ parameterNames: m['parameterNames'] == null
+ ? null
+ : new List.from(m['parameterNames']),
+ parameterTypes: m['parameterTypes'] == null
+ ? null
+ : new List.from(m['parameterTypes']),
+ requiredParameterCount: m['requiredParameterCount'],
+ hasNamedParameters: m['hasNamedParameters'],
+ parameterName: m['parameterName'],
+ parameterType: m['parameterType'],
+ importUri: m['importUri']);
+ }
+
+ /// The kind of element being suggested.
+ final String kind;
+
+ /// The relevance of this completion suggestion where a higher number
+ /// indicates a higher relevance.
+ final int relevance;
+
+ /// The identifier to be inserted if the suggestion is selected. If the
+ /// suggestion is for a method or function, the client might want to
+ /// additionally insert a template for the parameters. The information
+ /// required in order to do so is contained in other fields.
+ final String completion;
+
+ /// The offset, relative to the beginning of the completion, of where the
+ /// selection should be placed after insertion.
+ final int selectionOffset;
+
+ /// The number of characters that should be selected after insertion.
+ final int selectionLength;
+
+ /// True if the suggested element is deprecated.
+ final bool isDeprecated;
+
+ /// True if the element is not known to be valid for the target. This happens
+ /// if the type of the target is dynamic.
+ final bool isPotential;
+
+ /// An abbreviated version of the Dartdoc associated with the element being
+ /// suggested, This field is omitted if there is no Dartdoc associated with
+ /// the element.
+ @optional
+ final String docSummary;
+
+ /// The Dartdoc associated with the element being suggested. This field is
+ /// omitted if there is no Dartdoc associated with the element.
+ @optional
+ final String docComplete;
+
+ /// The class that declares the element being suggested. This field is omitted
+ /// if the suggested element is not a member of a class.
+ @optional
+ final String declaringType;
+
+ /// A default String for use in generating argument list source contents on
+ /// the client side.
+ @optional
+ final String defaultArgumentListString;
+
+ /// Pairs of offsets and lengths describing 'defaultArgumentListString' text
+ /// ranges suitable for use by clients to set up linked edits of default
+ /// argument source contents. For example, given an argument list string 'x,
+ /// y', the corresponding text range [0, 1, 3, 1], indicates two text ranges
+ /// of length 1, starting at offsets 0 and 3. Clients can use these ranges to
+ /// treat the 'x' and 'y' values specially for linked edits.
+ @optional
+ final List<int> defaultArgumentListTextRanges;
+
+ /// Information about the element reference being suggested.
+ @optional
+ final Element element;
+
+ /// The return type of the getter, function or method or the type of the field
+ /// being suggested. This field is omitted if the suggested element is not a
+ /// getter, function or method.
+ @optional
+ final String returnType;
+
+ /// The names of the parameters of the function or method being suggested.
+ /// This field is omitted if the suggested element is not a setter, function
+ /// or method.
+ @optional
+ final List<String> parameterNames;
+
+ /// The types of the parameters of the function or method being suggested.
+ /// This field is omitted if the parameterNames field is omitted.
+ @optional
+ final List<String> parameterTypes;
+
+ /// The number of required parameters for the function or method being
+ /// suggested. This field is omitted if the parameterNames field is omitted.
+ @optional
+ final int requiredParameterCount;
+
+ /// True if the function or method being suggested has at least one named
+ /// parameter. This field is omitted if the parameterNames field is omitted.
+ @optional
+ final bool hasNamedParameters;
+
+ /// The name of the optional parameter being suggested. This field is omitted
+ /// if the suggestion is not the addition of an optional argument within an
+ /// argument list.
+ @optional
+ final String parameterName;
+
+ /// The type of the options parameter being suggested. This field is omitted
+ /// if the parameterName field is omitted.
+ @optional
+ final String parameterType;
+
+ /// The import to be added if the suggestion is out of scope and needs an
+ /// import to be added to be in scope.
+ @optional
+ final String importUri;
+
+ CompletionSuggestion(
+ this.kind,
+ this.relevance,
+ this.completion,
+ this.selectionOffset,
+ this.selectionLength,
+ this.isDeprecated,
+ this.isPotential,
+ {this.docSummary,
+ this.docComplete,
+ this.declaringType,
+ this.defaultArgumentListString,
+ this.defaultArgumentListTextRanges,
+ this.element,
+ this.returnType,
+ this.parameterNames,
+ this.parameterTypes,
+ this.requiredParameterCount,
+ this.hasNamedParameters,
+ this.parameterName,
+ this.parameterType,
+ this.importUri});
+
+ Map toMap() => _stripNullValues({
+ 'kind': kind,
+ 'relevance': relevance,
+ 'completion': completion,
+ 'selectionOffset': selectionOffset,
+ 'selectionLength': selectionLength,
+ 'isDeprecated': isDeprecated,
+ 'isPotential': isPotential,
+ 'docSummary': docSummary,
+ 'docComplete': docComplete,
+ 'declaringType': declaringType,
+ 'defaultArgumentListString': defaultArgumentListString,
+ 'defaultArgumentListTextRanges': defaultArgumentListTextRanges,
+ 'element': element?.toMap(),
+ 'returnType': returnType,
+ 'parameterNames': parameterNames,
+ 'parameterTypes': parameterTypes,
+ 'requiredParameterCount': requiredParameterCount,
+ 'hasNamedParameters': hasNamedParameters,
+ 'parameterName': parameterName,
+ 'parameterType': parameterType,
+ 'importUri': importUri
+ });
+
+ String toString() =>
+ '[CompletionSuggestion kind: ${kind}, relevance: ${relevance}, completion: ${completion}, selectionOffset: ${selectionOffset}, selectionLength: ${selectionLength}, isDeprecated: ${isDeprecated}, isPotential: ${isPotential}]';
+}
+
+/// Information about an analysis context.
+class ContextData {
+ static ContextData parse(Map m) {
+ if (m == null) return null;
+ return new ContextData(
+ m['name'],
+ m['explicitFileCount'],
+ m['implicitFileCount'],
+ m['workItemQueueLength'],
+ m['cacheEntryExceptions'] == null
+ ? null
+ : new List.from(m['cacheEntryExceptions']));
+ }
+
+ /// The name of the context.
+ final String name;
+
+ /// Explicitly analyzed files.
+ final int explicitFileCount;
+
+ /// Implicitly analyzed files.
+ final int implicitFileCount;
+
+ /// The number of work items in the queue.
+ final int workItemQueueLength;
+
+ /// Exceptions associated with cache entries.
+ final List<String> cacheEntryExceptions;
+
+ ContextData(this.name, this.explicitFileCount, this.implicitFileCount,
+ this.workItemQueueLength, this.cacheEntryExceptions);
+}
+
+/// Information about an element (something that can be declared in code).
+class Element implements Jsonable {
+ static Element parse(Map m) {
+ if (m == null) return null;
+ return new Element(m['kind'], m['name'], m['flags'],
+ location: Location.parse(m['location']),
+ parameters: m['parameters'],
+ returnType: m['returnType'],
+ typeParameters: m['typeParameters']);
+ }
+
+ /// The kind of the element.
+ final String kind;
+
+ /// The name of the element. This is typically used as the label in the
+ /// outline.
+ final String name;
+
+ /// A bit-map containing the following flags:
+ final int flags;
+
+ /// The location of the name in the declaration of the element.
+ @optional
+ final Location location;
+
+ /// The parameter list for the element. If the element is not a method or
+ /// function this field will not be defined. If the element doesn't have
+ /// parameters (e.g. getter), this field will not be defined. If the element
+ /// has zero parameters, this field will have a value of "()".
+ @optional
+ final String parameters;
+
+ /// The return type of the element. If the element is not a method or function
+ /// this field will not be defined. If the element does not have a declared
+ /// return type, this field will contain an empty string.
+ @optional
+ final String returnType;
+
+ /// The type parameter list for the element. If the element doesn't have type
+ /// parameters, this field will not be defined.
+ @optional
+ final String typeParameters;
+
+ Element(this.kind, this.name, this.flags,
+ {this.location, this.parameters, this.returnType, this.typeParameters});
+
+ Map toMap() => _stripNullValues({
+ 'kind': kind,
+ 'name': name,
+ 'flags': flags,
+ 'location': location?.toMap(),
+ 'parameters': parameters,
+ 'returnType': returnType,
+ 'typeParameters': typeParameters
+ });
+
+ String toString() =>
+ '[Element kind: ${kind}, name: ${name}, flags: ${flags}]';
+}
+
+/// A description of an executable file.
+class ExecutableFile {
+ static ExecutableFile parse(Map m) {
+ if (m == null) return null;
+ return new ExecutableFile(m['file'], m['kind']);
+ }
+
+ /// The path of the executable file.
+ final String file;
+
+ /// The kind of the executable file.
+ final String kind;
+
+ ExecutableFile(this.file, this.kind);
+}
+
+/// A description of a region that can be folded.
+class FoldingRegion {
+ static FoldingRegion parse(Map m) {
+ if (m == null) return null;
+ return new FoldingRegion(m['kind'], m['offset'], m['length']);
+ }
+
+ /// The kind of the region.
+ final String kind;
+
+ /// The offset of the region to be folded.
+ final int offset;
+
+ /// The length of the region to be folded.
+ final int length;
+
+ FoldingRegion(this.kind, this.offset, this.length);
+}
+
+/// A description of a region that could have special highlighting associated
+/// with it.
+class HighlightRegion {
+ static HighlightRegion parse(Map m) {
+ if (m == null) return null;
+ return new HighlightRegion(m['type'], m['offset'], m['length']);
+ }
+
+ /// The type of highlight associated with the region.
+ final String type;
+
+ /// The offset of the region to be highlighted.
+ final int offset;
+
+ /// The length of the region to be highlighted.
+ final int length;
+
+ HighlightRegion(this.type, this.offset, this.length);
+}
+
+/// The hover information associated with a specific location.
+class HoverInformation {
+ static HoverInformation parse(Map m) {
+ if (m == null) return null;
+ return new HoverInformation(m['offset'], m['length'],
+ containingLibraryPath: m['containingLibraryPath'],
+ containingLibraryName: m['containingLibraryName'],
+ containingClassDescription: m['containingClassDescription'],
+ dartdoc: m['dartdoc'],
+ elementDescription: m['elementDescription'],
+ elementKind: m['elementKind'],
+ isDeprecated: m['isDeprecated'],
+ parameter: m['parameter'],
+ propagatedType: m['propagatedType'],
+ staticType: m['staticType']);
+ }
+
+ /// The offset of the range of characters that encompasses the cursor position
+ /// and has the same hover information as the cursor position.
+ final int offset;
+
+ /// The length of the range of characters that encompasses the cursor position
+ /// and has the same hover information as the cursor position.
+ final int length;
+
+ /// The path to the defining compilation unit of the library in which the
+ /// referenced element is declared. This data is omitted if there is no
+ /// referenced element, or if the element is declared inside an HTML file.
+ @optional
+ final String containingLibraryPath;
+
+ /// The name of the library in which the referenced element is declared. This
+ /// data is omitted if there is no referenced element, or if the element is
+ /// declared inside an HTML file.
+ @optional
+ final String containingLibraryName;
+
+ /// A human-readable description of the class declaring the element being
+ /// referenced. This data is omitted if there is no referenced element, or if
+ /// the element is not a class member.
+ @optional
+ final String containingClassDescription;
+
+ /// The dartdoc associated with the referenced element. Other than the removal
+ /// of the comment delimiters, including leading asterisks in the case of a
+ /// block comment, the dartdoc is unprocessed markdown. This data is omitted
+ /// if there is no referenced element, or if the element has no dartdoc.
+ @optional
+ final String dartdoc;
+
+ /// A human-readable description of the element being referenced. This data is
+ /// omitted if there is no referenced element.
+ @optional
+ final String elementDescription;
+
+ /// A human-readable description of the kind of element being referenced (such
+ /// as "class" or "function type alias"). This data is omitted if there is no
+ /// referenced element.
+ @optional
+ final String elementKind;
+
+ /// True if the referenced element is deprecated.
+ @optional
+ final bool isDeprecated;
+
+ /// A human-readable description of the parameter corresponding to the
+ /// expression being hovered over. This data is omitted if the location is not
+ /// in an argument to a function.
+ @optional
+ final String parameter;
+
+ /// The name of the propagated type of the expression. This data is omitted if
+ /// the location does not correspond to an expression or if there is no
+ /// propagated type information.
+ @optional
+ final String propagatedType;
+
+ /// The name of the static type of the expression. This data is omitted if the
+ /// location does not correspond to an expression.
+ @optional
+ final String staticType;
+
+ HoverInformation(this.offset, this.length,
+ {this.containingLibraryPath,
+ this.containingLibraryName,
+ this.containingClassDescription,
+ this.dartdoc,
+ this.elementDescription,
+ this.elementKind,
+ this.isDeprecated,
+ this.parameter,
+ this.propagatedType,
+ this.staticType});
+}
+
+/// A description of a class that is implemented or extended.
+class ImplementedClass {
+ static ImplementedClass parse(Map m) {
+ if (m == null) return null;
+ return new ImplementedClass(m['offset'], m['length']);
+ }
+
+ /// The offset of the name of the implemented class.
+ final int offset;
+
+ /// The length of the name of the implemented class.
+ final int length;
+
+ ImplementedClass(this.offset, this.length);
+}
+
+/// A description of a class member that is implemented or overridden.
+class ImplementedMember {
+ static ImplementedMember parse(Map m) {
+ if (m == null) return null;
+ return new ImplementedMember(m['offset'], m['length']);
+ }
+
+ /// The offset of the name of the implemented member.
+ final int offset;
+
+ /// The length of the name of the implemented member.
+ final int length;
+
+ ImplementedMember(this.offset, this.length);
+}
+
+/// A collection of positions that should be linked (edited simultaneously) for
+/// the purposes of updating code after a source change. For example, if a set
+/// of edits introduced a new variable name, the group would contain all of the
+/// positions of the variable name so that if the client wanted to let the user
+/// edit the variable name after the operation, all occurrences of the name
+/// could be edited simultaneously.
+class LinkedEditGroup {
+ static LinkedEditGroup parse(Map m) {
+ if (m == null) return null;
+ return new LinkedEditGroup(
+ m['positions'] == null
+ ? null
+ : new List.from(m['positions'].map((obj) => Position.parse(obj))),
+ m['length'],
+ m['suggestions'] == null
+ ? null
+ : new List.from(m['suggestions']
+ .map((obj) => LinkedEditSuggestion.parse(obj))));
+ }
+
+ /// The positions of the regions that should be edited simultaneously.
+ final List<Position> positions;
+
+ /// The length of the regions that should be edited simultaneously.
+ final int length;
+
+ /// Pre-computed suggestions for what every region might want to be changed
+ /// to.
+ final List<LinkedEditSuggestion> suggestions;
+
+ LinkedEditGroup(this.positions, this.length, this.suggestions);
+
+ String toString() =>
+ '[LinkedEditGroup positions: ${positions}, length: ${length}, suggestions: ${suggestions}]';
+}
+
+/// A suggestion of a value that could be used to replace all of the linked edit
+/// regions in a (LinkedEditGroup)[#type_LinkedEditGroup].
+class LinkedEditSuggestion {
+ static LinkedEditSuggestion parse(Map m) {
+ if (m == null) return null;
+ return new LinkedEditSuggestion(m['value'], m['kind']);
+ }
+
+ /// The value that could be used to replace all of the linked edit regions.
+ final String value;
+
+ /// The kind of value being proposed.
+ final String kind;
+
+ LinkedEditSuggestion(this.value, this.kind);
+}
+
+/// A location (character range) within a file.
+class Location implements Jsonable {
+ static Location parse(Map m) {
+ if (m == null) return null;
+ return new Location(
+ m['file'], m['offset'], m['length'], m['startLine'], m['startColumn']);
+ }
+
+ /// The file containing the range.
+ final String file;
+
+ /// The offset of the range.
+ final int offset;
+
+ /// The length of the range.
+ final int length;
+
+ /// The one-based index of the line containing the first character of the
+ /// range.
+ final int startLine;
+
+ /// The one-based index of the column containing the first character of the
+ /// range.
+ final int startColumn;
+
+ Location(
+ this.file, this.offset, this.length, this.startLine, this.startColumn);
+
+ Map toMap() => _stripNullValues({
+ 'file': file,
+ 'offset': offset,
+ 'length': length,
+ 'startLine': startLine,
+ 'startColumn': startColumn
+ });
+
+ bool operator ==(o) =>
+ o is Location &&
+ file == o.file &&
+ offset == o.offset &&
+ length == o.length &&
+ startLine == o.startLine &&
+ startColumn == o.startColumn;
+
+ int get hashCode =>
+ file.hashCode ^
+ offset.hashCode ^
+ length.hashCode ^
+ startLine.hashCode ^
+ startColumn.hashCode;
+
+ String toString() =>
+ '[Location file: ${file}, offset: ${offset}, length: ${length}, startLine: ${startLine}, startColumn: ${startColumn}]';
+}
+
+/// A description of a region from which the user can navigate to the
+/// declaration of an element.
+class NavigationRegion {
+ static NavigationRegion parse(Map m) {
+ if (m == null) return null;
+ return new NavigationRegion(m['offset'], m['length'],
+ m['targets'] == null ? null : new List.from(m['targets']));
+ }
+
+ /// The offset of the region from which the user can navigate.
+ final int offset;
+
+ /// The length of the region from which the user can navigate.
+ final int length;
+
+ /// The indexes of the targets (in the enclosing navigation response) to which
+ /// the given region is bound. By opening the target, clients can implement
+ /// one form of navigation. This list cannot be empty.
+ final List<int> targets;
+
+ NavigationRegion(this.offset, this.length, this.targets);
+
+ String toString() =>
+ '[NavigationRegion offset: ${offset}, length: ${length}, targets: ${targets}]';
+}
+
+/// A description of a target to which the user can navigate.
+class NavigationTarget {
+ static NavigationTarget parse(Map m) {
+ if (m == null) return null;
+ return new NavigationTarget(m['kind'], m['fileIndex'], m['offset'],
+ m['length'], m['startLine'], m['startColumn']);
+ }
+
+ /// The kind of the element.
+ final String kind;
+
+ /// The index of the file (in the enclosing navigation response) to navigate
+ /// to.
+ final int fileIndex;
+
+ /// The offset of the region to which the user can navigate.
+ final int offset;
+
+ /// The length of the region to which the user can navigate.
+ final int length;
+
+ /// The one-based index of the line containing the first character of the
+ /// region.
+ final int startLine;
+
+ /// The one-based index of the column containing the first character of the
+ /// region.
+ final int startColumn;
+
+ NavigationTarget(this.kind, this.fileIndex, this.offset, this.length,
+ this.startLine, this.startColumn);
+
+ String toString() =>
+ '[NavigationTarget kind: ${kind}, fileIndex: ${fileIndex}, offset: ${offset}, length: ${length}, startLine: ${startLine}, startColumn: ${startColumn}]';
+}
+
+/// A description of the references to a single element within a single file.
+class Occurrences {
+ static Occurrences parse(Map m) {
+ if (m == null) return null;
+ return new Occurrences(Element.parse(m['element']),
+ m['offsets'] == null ? null : new List.from(m['offsets']), m['length']);
+ }
+
+ /// The element that was referenced.
+ final Element element;
+
+ /// The offsets of the name of the referenced element within the file.
+ final List<int> offsets;
+
+ /// The length of the name of the referenced element.
+ final int length;
+
+ Occurrences(this.element, this.offsets, this.length);
+}
+
+/// An node in the outline structure of a file.
+class Outline {
+ static Outline parse(Map m) {
+ if (m == null) return null;
+ return new Outline(Element.parse(m['element']), m['offset'], m['length'],
+ children: m['children'] == null
+ ? null
+ : new List.from(m['children'].map((obj) => Outline.parse(obj))));
+ }
+
+ /// A description of the element represented by this node.
+ final Element element;
+
+ /// The offset of the first character of the element. This is different than
+ /// the offset in the Element, which is the offset of the name of the element.
+ /// It can be used, for example, to map locations in the file back to an
+ /// outline.
+ final int offset;
+
+ /// The length of the element.
+ final int length;
+
+ /// The children of the node. The field will be omitted if the node has no
+ /// children.
+ @optional
+ final List<Outline> children;
+
+ Outline(this.element, this.offset, this.length, {this.children});
+}
+
+/// A description of a member that is being overridden.
+class OverriddenMember {
+ static OverriddenMember parse(Map m) {
+ if (m == null) return null;
+ return new OverriddenMember(Element.parse(m['element']), m['className']);
+ }
+
+ /// The element that is being overridden.
+ final Element element;
+
+ /// The name of the class in which the member is defined.
+ final String className;
+
+ OverriddenMember(this.element, this.className);
+}
+
+/// A description of a member that overrides an inherited member.
+class Override {
+ static Override parse(Map m) {
+ if (m == null) return null;
+ return new Override(m['offset'], m['length'],
+ superclassMember: OverriddenMember.parse(m['superclassMember']),
+ interfaceMembers: m['interfaceMembers'] == null
+ ? null
+ : new List.from(m['interfaceMembers']
+ .map((obj) => OverriddenMember.parse(obj))));
+ }
+
+ /// The offset of the name of the overriding member.
+ final int offset;
+
+ /// The length of the name of the overriding member.
+ final int length;
+
+ /// The member inherited from a superclass that is overridden by the
+ /// overriding member. The field is omitted if there is no superclass member,
+ /// in which case there must be at least one interface member.
+ @optional
+ final OverriddenMember superclassMember;
+
+ /// The members inherited from interfaces that are overridden by the
+ /// overriding member. The field is omitted if there are no interface members,
+ /// in which case there must be a superclass member.
+ @optional
+ final List<OverriddenMember> interfaceMembers;
+
+ Override(this.offset, this.length,
+ {this.superclassMember, this.interfaceMembers});
+}
+
+/// A position within a file.
+class Position {
+ static Position parse(Map m) {
+ if (m == null) return null;
+ return new Position(m['file'], m['offset']);
+ }
+
+ /// The file containing the position.
+ final String file;
+
+ /// The offset of the position.
+ final int offset;
+
+ Position(this.file, this.offset);
+
+ String toString() => '[Position file: ${file}, offset: ${offset}]';
+}
+
+/// An indication of the current state of pub execution.
+class PubStatus {
+ static PubStatus parse(Map m) {
+ if (m == null) return null;
+ return new PubStatus(m['isListingPackageDirs']);
+ }
+
+ /// True if the server is currently running pub to produce a list of package
+ /// directories.
+ final bool isListingPackageDirs;
+
+ PubStatus(this.isListingPackageDirs);
+
+ String toString() =>
+ '[PubStatus isListingPackageDirs: ${isListingPackageDirs}]';
+}
+
+/// A description of a parameter in a method refactoring.
+class RefactoringMethodParameter {
+ static RefactoringMethodParameter parse(Map m) {
+ if (m == null) return null;
+ return new RefactoringMethodParameter(m['kind'], m['type'], m['name'],
+ id: m['id'], parameters: m['parameters']);
+ }
+
+ /// The kind of the parameter.
+ final String kind;
+
+ /// The type that should be given to the parameter, or the return type of the
+ /// parameter's function type.
+ final String type;
+
+ /// The name that should be given to the parameter.
+ final String name;
+
+ /// The unique identifier of the parameter. Clients may omit this field for
+ /// the parameters they want to add.
+ @optional
+ final String id;
+
+ /// The parameter list of the parameter's function type. If the parameter is
+ /// not of a function type, this field will not be defined. If the function
+ /// type has zero parameters, this field will have a value of '()'.
+ @optional
+ final String parameters;
+
+ RefactoringMethodParameter(this.kind, this.type, this.name,
+ {this.id, this.parameters});
+}
+
+/// A description of a problem related to a refactoring.
+class RefactoringProblem {
+ static RefactoringProblem parse(Map m) {
+ if (m == null) return null;
+ return new RefactoringProblem(m['severity'], m['message'],
+ location: Location.parse(m['location']));
+ }
+
+ /// The severity of the problem being represented.
+ final String severity;
+
+ /// A human-readable description of the problem being represented.
+ final String message;
+
+ /// The location of the problem being represented. This field is omitted
+ /// unless there is a specific location associated with the problem (such as a
+ /// location where an element being renamed will be shadowed).
+ @optional
+ final Location location;
+
+ RefactoringProblem(this.severity, this.message, {this.location});
+}
+
+/// A directive to remove an existing file content overlay. After processing
+/// this directive, the file contents will once again be read from the file
+/// system.
+///
+/// If this directive is used on a file that doesn't currently have a content
+/// overlay, it has no effect.
+class RemoveContentOverlay extends ContentOverlayType implements Jsonable {
+ static RemoveContentOverlay parse(Map m) {
+ if (m == null) return null;
+ return new RemoveContentOverlay();
+ }
+
+ RemoveContentOverlay() : super('remove');
+
+ Map toMap() => _stripNullValues({'type': type});
+}
+
+/// A single result from a search request.
+class SearchResult {
+ static SearchResult parse(Map m) {
+ if (m == null) return null;
+ return new SearchResult(
+ Location.parse(m['location']),
+ m['kind'],
+ m['isPotential'],
+ m['path'] == null
+ ? null
+ : new List.from(m['path'].map((obj) => Element.parse(obj))));
+ }
+
+ /// The location of the code that matched the search criteria.
+ final Location location;
+
+ /// The kind of element that was found or the kind of reference that was
+ /// found.
+ final String kind;
+
+ /// True if the result is a potential match but cannot be confirmed to be a
+ /// match. For example, if all references to a method m defined in some class
+ /// were requested, and a reference to a method m from an unknown class were
+ /// found, it would be marked as being a potential match.
+ final bool isPotential;
+
+ /// The elements that contain the result, starting with the most immediately
+ /// enclosing ancestor and ending with the library.
+ final List<Element> path;
+
+ SearchResult(this.location, this.kind, this.isPotential, this.path);
+
+ String toString() =>
+ '[SearchResult location: ${location}, kind: ${kind}, isPotential: ${isPotential}, path: ${path}]';
+}
+
+/// A description of a set of edits that implement a single conceptual change.
+class SourceChange {
+ static SourceChange parse(Map m) {
+ if (m == null) return null;
+ return new SourceChange(
+ m['message'],
+ m['edits'] == null
+ ? null
+ : new List.from(m['edits'].map((obj) => SourceFileEdit.parse(obj))),
+ m['linkedEditGroups'] == null
+ ? null
+ : new List.from(
+ m['linkedEditGroups'].map((obj) => LinkedEditGroup.parse(obj))),
+ selection: Position.parse(m['selection']));
+ }
+
+ /// A human-readable description of the change to be applied.
+ final String message;
+
+ /// A list of the edits used to effect the change, grouped by file.
+ final List<SourceFileEdit> edits;
+
+ /// A list of the linked editing groups used to customize the changes that
+ /// were made.
+ final List<LinkedEditGroup> linkedEditGroups;
+
+ /// The position that should be selected after the edits have been applied.
+ @optional
+ final Position selection;
+
+ SourceChange(this.message, this.edits, this.linkedEditGroups,
+ {this.selection});
+
+ String toString() =>
+ '[SourceChange message: ${message}, edits: ${edits}, linkedEditGroups: ${linkedEditGroups}]';
+}
+
+/// A description of a single change to a single file.
+class SourceEdit implements Jsonable {
+ static SourceEdit parse(Map m) {
+ if (m == null) return null;
+ return new SourceEdit(m['offset'], m['length'], m['replacement'],
+ id: m['id']);
+ }
+
+ /// The offset of the region to be modified.
+ final int offset;
+
+ /// The length of the region to be modified.
+ final int length;
+
+ /// The code that is to replace the specified region in the original code.
+ final String replacement;
+
+ /// An identifier that uniquely identifies this source edit from other edits
+ /// in the same response. This field is omitted unless a containing structure
+ /// needs to be able to identify the edit for some reason.
+ ///
+ /// For example, some refactoring operations can produce edits that might not
+ /// be appropriate (referred to as potential edits). Such edits will have an
+ /// id so that they can be referenced. Edits in the same response that do not
+ /// need to be referenced will not have an id.
+ @optional
+ final String id;
+
+ SourceEdit(this.offset, this.length, this.replacement, {this.id});
+
+ Map toMap() => _stripNullValues({
+ 'offset': offset,
+ 'length': length,
+ 'replacement': replacement,
+ 'id': id
+ });
+
+ String toString() =>
+ '[SourceEdit offset: ${offset}, length: ${length}, replacement: ${replacement}]';
+}
+
+/// A description of a set of changes to a single file.
+class SourceFileEdit {
+ static SourceFileEdit parse(Map m) {
+ if (m == null) return null;
+ return new SourceFileEdit(
+ m['file'],
+ m['fileStamp'],
+ m['edits'] == null
+ ? null
+ : new List.from(m['edits'].map((obj) => SourceEdit.parse(obj))));
+ }
+
+ /// The file containing the code to be modified.
+ final String file;
+
+ /// The modification stamp of the file at the moment when the change was
+ /// created, in milliseconds since the "Unix epoch". Will be -1 if the file
+ /// did not exist and should be created. The client may use this field to make
+ /// sure that the file was not changed since then, so it is safe to apply the
+ /// change.
+ final int fileStamp;
+
+ /// A list of the edits used to effect the change.
+ final List<SourceEdit> edits;
+
+ SourceFileEdit(this.file, this.fileStamp, this.edits);
+
+ String toString() =>
+ '[SourceFileEdit file: ${file}, fileStamp: ${fileStamp}, edits: ${edits}]';
+}
+
+/// A representation of a class in a type hierarchy.
+class TypeHierarchyItem {
+ static TypeHierarchyItem parse(Map m) {
+ if (m == null) return null;
+ return new TypeHierarchyItem(
+ Element.parse(m['classElement']),
+ m['interfaces'] == null ? null : new List.from(m['interfaces']),
+ m['mixins'] == null ? null : new List.from(m['mixins']),
+ m['subclasses'] == null ? null : new List.from(m['subclasses']),
+ displayName: m['displayName'],
+ memberElement: Element.parse(m['memberElement']),
+ superclass: m['superclass']);
+ }
+
+ /// The class element represented by this item.
+ final Element classElement;
+
+ /// The indexes of the items representing the interfaces implemented by this
+ /// class. The list will be empty if there are no implemented interfaces.
+ final List<int> interfaces;
+
+ /// The indexes of the items representing the mixins referenced by this class.
+ /// The list will be empty if there are no classes mixed in to this class.
+ final List<int> mixins;
+
+ /// The indexes of the items representing the subtypes of this class. The list
+ /// will be empty if there are no subtypes or if this item represents a
+ /// supertype of the pivot type.
+ final List<int> subclasses;
+
+ /// The name to be displayed for the class. This field will be omitted if the
+ /// display name is the same as the name of the element. The display name is
+ /// different if there is additional type information to be displayed, such as
+ /// type arguments.
+ @optional
+ final String displayName;
+
+ /// The member in the class corresponding to the member on which the hierarchy
+ /// was requested. This field will be omitted if the hierarchy was not
+ /// requested for a member or if the class does not have a corresponding
+ /// member.
+ @optional
+ final Element memberElement;
+
+ /// The index of the item representing the superclass of this class. This
+ /// field will be omitted if this item represents the class Object.
+ @optional
+ final int superclass;
+
+ TypeHierarchyItem(
+ this.classElement, this.interfaces, this.mixins, this.subclasses,
+ {this.displayName, this.memberElement, this.superclass});
+}
+
+// refactorings
+
+class Refactorings {
+ static const String CONVERT_GETTER_TO_METHOD = 'CONVERT_GETTER_TO_METHOD';
+ static const String CONVERT_METHOD_TO_GETTER = 'CONVERT_METHOD_TO_GETTER';
+ static const String EXTRACT_LOCAL_VARIABLE = 'EXTRACT_LOCAL_VARIABLE';
+ static const String EXTRACT_METHOD = 'EXTRACT_METHOD';
+ static const String INLINE_LOCAL_VARIABLE = 'INLINE_LOCAL_VARIABLE';
+ static const String INLINE_METHOD = 'INLINE_METHOD';
+ static const String MOVE_FILE = 'MOVE_FILE';
+ static const String RENAME = 'RENAME';
+}
+
+/// Create a local variable initialized by the expression that covers the
+/// specified selection.
+///
+/// It is an error if the selection range is not covered by a complete
+/// expression.
+class ExtractLocalVariableRefactoringOptions extends RefactoringOptions {
+ /// The name that the local variable should be given.
+ final String name;
+
+ /// True if all occurrences of the expression within the scope in which the
+ /// variable will be defined should be replaced by a reference to the local
+ /// variable. The expression used to initiate the refactoring will always be
+ /// replaced.
+ final bool extractAll;
+
+ ExtractLocalVariableRefactoringOptions({this.name, this.extractAll});
+
+ Map toMap() => _stripNullValues({'name': name, 'extractAll': extractAll});
+}
+
+/// Create a method whose body is the specified expression or list of
+/// statements, possibly augmented with a return statement.
+///
+/// It is an error if the range contains anything other than a complete
+/// expression (no partial expressions are allowed) or a complete sequence of
+/// statements.
+class ExtractMethodRefactoringOptions extends RefactoringOptions {
+ /// The return type that should be defined for the method.
+ final String returnType;
+
+ /// True if a getter should be created rather than a method. It is an error if
+ /// this field is true and the list of parameters is non-empty.
+ final bool createGetter;
+
+ /// The name that the method should be given.
+ final String name;
+
+ /// The parameters that should be defined for the method.
+ ///
+ /// It is an error if a REQUIRED or NAMED parameter follows a POSITIONAL
+ /// parameter. It is an error if a REQUIRED or POSITIONAL parameter follows a
+ /// NAMED parameter.
+ final List<RefactoringMethodParameter> parameters;
+
+ /// True if all occurrences of the expression or statements should be replaced
+ /// by an invocation of the method. The expression or statements used to
+ /// initiate the refactoring will always be replaced.
+ final bool extractAll;
+
+ ExtractMethodRefactoringOptions(
+ {this.returnType,
+ this.createGetter,
+ this.name,
+ this.parameters,
+ this.extractAll});
+
+ Map toMap() => _stripNullValues({
+ 'returnType': returnType,
+ 'createGetter': createGetter,
+ 'name': name,
+ 'parameters': parameters,
+ 'extractAll': extractAll
+ });
+}
+
+/// Inline a method in place of one or all references to that method.
+///
+/// It is an error if the range contains anything other than all or part of the
+/// name of a single method.
+class InlineMethodRefactoringOptions extends RefactoringOptions {
+ /// True if the method being inlined should be removed. It is an error if this
+ /// field is true and inlineAll is false.
+ final bool deleteSource;
+
+ /// True if all invocations of the method should be inlined, or false if only
+ /// the invocation site used to create this refactoring should be inlined.
+ final bool inlineAll;
+
+ InlineMethodRefactoringOptions({this.deleteSource, this.inlineAll});
+
+ Map toMap() =>
+ _stripNullValues({'deleteSource': deleteSource, 'inlineAll': inlineAll});
+}
+
+/// Move the given file and update all of the references to that file and from
+/// it. The move operation is supported in general case - for renaming a file in
+/// the same folder, moving it to a different folder or both.
+///
+/// The refactoring must be activated before an actual file moving operation is
+/// performed.
+///
+/// The "offset" and "length" fields from the request are ignored, but the file
+/// specified in the request specifies the file to be moved.
+class MoveFileRefactoringOptions extends RefactoringOptions {
+ /// The new file path to which the given file is being moved.
+ final String newFile;
+
+ MoveFileRefactoringOptions({this.newFile});
+
+ Map toMap() => _stripNullValues({'newFile': newFile});
+}
+
+/// Rename a given element and all of the references to that element.
+///
+/// It is an error if the range contains anything other than all or part of the
+/// name of a single function (including methods, getters and setters), variable
+/// (including fields, parameters and local variables), class or function type.
+class RenameRefactoringOptions extends RefactoringOptions {
+ /// The name that the element should have after the refactoring.
+ final String newName;
+
+ RenameRefactoringOptions({this.newName});
+
+ Map toMap() => _stripNullValues({'newName': newName});
+}
+
+abstract class RefactoringFeedback {
+ static RefactoringFeedback parse(String kind, Map m) {
+ if (m == null) return null;
+
+ switch (kind) {
+ case Refactorings.EXTRACT_LOCAL_VARIABLE:
+ return ExtractLocalVariableFeedback.parse(m);
+ case Refactorings.EXTRACT_METHOD:
+ return ExtractMethodFeedback.parse(m);
+ case Refactorings.INLINE_LOCAL_VARIABLE:
+ return InlineLocalVariableFeedback.parse(m);
+ case Refactorings.INLINE_METHOD:
+ return InlineMethodFeedback.parse(m);
+ case Refactorings.RENAME:
+ return RenameFeedback.parse(m);
+ }
+
+ return null;
+ }
+}
+
+/// Feedback class for the `EXTRACT_LOCAL_VARIABLE` refactoring.
+class ExtractLocalVariableFeedback extends RefactoringFeedback {
+ static ExtractLocalVariableFeedback parse(Map m) =>
+ new ExtractLocalVariableFeedback(
+ m['names'] == null ? null : new List.from(m['names']),
+ m['offsets'] == null ? null : new List.from(m['offsets']),
+ m['lengths'] == null ? null : new List.from(m['lengths']),
+ coveringExpressionOffsets: m['coveringExpressionOffsets'] == null
+ ? null
+ : new List.from(m['coveringExpressionOffsets']),
+ coveringExpressionLengths: m['coveringExpressionLengths'] == null
+ ? null
+ : new List.from(m['coveringExpressionLengths']));
+
+ /// The proposed names for the local variable.
+ final List<String> names;
+
+ /// The offsets of the expressions that would be replaced by a reference to
+ /// the variable.
+ final List<int> offsets;
+
+ /// The lengths of the expressions that would be replaced by a reference to
+ /// the variable. The lengths correspond to the offsets. In other words, for a
+ /// given expression, if the offset of that expression is `offsets[i]`, then
+ /// the length of that expression is `lengths[i]`.
+ final List<int> lengths;
+
+ /// The offsets of the expressions that cover the specified selection, from
+ /// the down most to the up most.
+ @optional
+ final List<int> coveringExpressionOffsets;
+
+ /// The lengths of the expressions that cover the specified selection, from
+ /// the down most to the up most.
+ @optional
+ final List<int> coveringExpressionLengths;
+
+ ExtractLocalVariableFeedback(this.names, this.offsets, this.lengths,
+ {this.coveringExpressionOffsets, this.coveringExpressionLengths});
+}
+
+/// Feedback class for the `EXTRACT_METHOD` refactoring.
+class ExtractMethodFeedback extends RefactoringFeedback {
+ static ExtractMethodFeedback parse(Map m) => new ExtractMethodFeedback(
+ m['offset'],
+ m['length'],
+ m['returnType'],
+ m['names'] == null ? null : new List.from(m['names']),
+ m['canCreateGetter'],
+ m['parameters'] == null
+ ? null
+ : new List.from(m['parameters']
+ .map((obj) => RefactoringMethodParameter.parse(obj))),
+ m['offsets'] == null ? null : new List.from(m['offsets']),
+ m['lengths'] == null ? null : new List.from(m['lengths']));
+
+ /// The offset to the beginning of the expression or statements that will be
+ /// extracted.
+ final int offset;
+
+ /// The length of the expression or statements that will be extracted.
+ final int length;
+
+ /// The proposed return type for the method. If the returned element does not
+ /// have a declared return type, this field will contain an empty string.
+ final String returnType;
+
+ /// The proposed names for the method.
+ final List<String> names;
+
+ /// True if a getter could be created rather than a method.
+ final bool canCreateGetter;
+
+ /// The proposed parameters for the method.
+ final List<RefactoringMethodParameter> parameters;
+
+ /// The offsets of the expressions or statements that would be replaced by an
+ /// invocation of the method.
+ final List<int> offsets;
+
+ /// The lengths of the expressions or statements that would be replaced by an
+ /// invocation of the method. The lengths correspond to the offsets. In other
+ /// words, for a given expression (or block of statements), if the offset of
+ /// that expression is `offsets[i]`, then the length of that expression is
+ /// `lengths[i]`.
+ final List<int> lengths;
+
+ ExtractMethodFeedback(this.offset, this.length, this.returnType, this.names,
+ this.canCreateGetter, this.parameters, this.offsets, this.lengths);
+}
+
+/// Feedback class for the `INLINE_LOCAL_VARIABLE` refactoring.
+class InlineLocalVariableFeedback extends RefactoringFeedback {
+ static InlineLocalVariableFeedback parse(Map m) =>
+ new InlineLocalVariableFeedback(m['name'], m['occurrences']);
+
+ /// The name of the variable being inlined.
+ final String name;
+
+ /// The number of times the variable occurs.
+ final int occurrences;
+
+ InlineLocalVariableFeedback(this.name, this.occurrences);
+}
+
+/// Feedback class for the `INLINE_METHOD` refactoring.
+class InlineMethodFeedback extends RefactoringFeedback {
+ static InlineMethodFeedback parse(Map m) =>
+ new InlineMethodFeedback(m['methodName'], m['isDeclaration'],
+ className: m['className']);
+
+ /// The name of the method (or function) being inlined.
+ final String methodName;
+
+ /// True if the declaration of the method is selected. So all references
+ /// should be inlined.
+ final bool isDeclaration;
+
+ /// The name of the class enclosing the method being inlined. If not a class
+ /// member is being inlined, this field will be absent.
+ @optional
+ final String className;
+
+ InlineMethodFeedback(this.methodName, this.isDeclaration, {this.className});
+}
+
+/// Feedback class for the `RENAME` refactoring.
+class RenameFeedback extends RefactoringFeedback {
+ static RenameFeedback parse(Map m) => new RenameFeedback(
+ m['offset'], m['length'], m['elementKindName'], m['oldName']);
+
+ /// The offset to the beginning of the name selected to be renamed.
+ final int offset;
+
+ /// The length of the name selected to be renamed.
+ final int length;
+
+ /// The human-readable description of the kind of element being renamed (such
+ /// as "class" or "function type alias").
+ final String elementKindName;
+
+ /// The old name of the element before the refactoring.
+ final String oldName;
+
+ RenameFeedback(this.offset, this.length, this.elementKindName, this.oldName);
+}
diff --git a/pub/analysis_server_lib/pubspec.yaml b/pub/analysis_server_lib/pubspec.yaml
new file mode 100644
index 0000000..ad26379
--- /dev/null
+++ b/pub/analysis_server_lib/pubspec.yaml
@@ -0,0 +1,17 @@
+name: analysis_server_lib
+description: A library to access Dart's analysis server API.
+
+version: 0.1.2+1
+author: Devon Carew <devoncarew@google.com>
+homepage: https://github.com/devoncarew/analysis_server_lib
+
+environment:
+ sdk: '>=1.21.0 <2.0.0'
+
+dependencies:
+ logging: ^0.11.0
+ path: ^1.0.0
+
+dev_dependencies:
+ html: ^0.13.0
+ test: ^0.12.0
diff --git a/pub/analysis_server_lib/tool/analysis_tester.dart b/pub/analysis_server_lib/tool/analysis_tester.dart
new file mode 100644
index 0000000..114c102
--- /dev/null
+++ b/pub/analysis_server_lib/tool/analysis_tester.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2017, Devon Carew. 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:io';
+
+import 'package:analysis_server_lib/analysis_server_lib.dart';
+import 'package:logging/logging.dart';
+
+Future main(List<String> args) async {
+ if (args.contains('--mini')) {
+ _miniTest();
+ return;
+ }
+
+ Logger.root.level = Level.ALL;
+ Logger.root.onRecord.listen(print);
+
+ AnalysisServer client = await AnalysisServer.create(onRead: (String message) {
+ print('[<-- $message]');
+ }, onWrite: (String message) {
+ print('[--> $message]');
+ });
+ client.processCompleter.future
+ .then((int code) => print('analysis server exited: ${code}'));
+
+ dynamic event = await client.server.onConnected.first;
+ print('server connected: ${event}');
+
+ client.server.onError.listen((ServerError e) {
+ print('server error: ${e.message}');
+ print(e.stackTrace);
+ });
+
+ VersionResult version = await client.server.getVersion();
+ print('version: ${version.version}');
+
+ client.server.setSubscriptions(['STATUS']);
+ client.server.onStatus.listen((ServerStatus status) {
+ if (status.analysis == null) return;
+
+ print('analysis status: ${status.analysis}');
+
+ if (!status.analysis.isAnalyzing) {
+ client.server.shutdown();
+ }
+ });
+
+ client.analysis.onErrors.listen((AnalysisErrors errors) {
+ if (errors.errors.isNotEmpty) {
+ print('${errors.errors.length} errors for ${errors.file}');
+ }
+ });
+ client.analysis.setAnalysisRoots([Directory.current.path], []);
+}
+
+Future _miniTest() async {
+ AnalysisServer client = await AnalysisServer.create(
+ onRead: print,
+ onWrite: print,
+ );
+
+ await client.server.onConnected.first;
+
+ VersionResult result = await client.server.getVersion();
+ print('version: ${result.version}');
+
+ await client.server.shutdown();
+}
diff --git a/pub/analysis_server_lib/tool/generate_analysis.dart b/pub/analysis_server_lib/tool/generate_analysis.dart
new file mode 100644
index 0000000..464d178
--- /dev/null
+++ b/pub/analysis_server_lib/tool/generate_analysis.dart
@@ -0,0 +1,1230 @@
+// Copyright (c) 2017, Devon Carew. 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:collection' show LinkedHashMap;
+import 'dart:io';
+
+import 'package:html/dom.dart';
+import 'package:html/dom_parsing.dart' show TreeVisitor;
+import 'package:html/parser.dart' show parse;
+
+import 'src/src_gen.dart';
+
+Api api;
+
+main(List<String> args) {
+ // Parse spec_input.html into a model.
+ File file = new File('tool/spec_input.html');
+ Document document = parse(file.readAsStringSync());
+ print('Parsed ${file.path}.');
+ Element ver = document.body.querySelector('version');
+ List<Element> domains = document.body.getElementsByTagName('domain');
+ List<Element> typedefs = document.body
+ .getElementsByTagName('types')
+ .first
+ .getElementsByTagName('type');
+ List<Element> refactorings = document.body
+ .getElementsByTagName('refactorings')
+ .first
+ .getElementsByTagName('refactoring');
+
+ // Common common_types_spec.html.
+ File commonTypesFile = new File('tool/common_types_spec.html');
+ Document commonTypesDoc = parse(commonTypesFile.readAsStringSync());
+ print('Parsed ${commonTypesFile.path}.');
+ List<Element> commonTypedefs = commonTypesDoc.body
+ .getElementsByTagName('types')
+ .first
+ .getElementsByTagName('type');
+
+ List<Element> combinedTypeDefs = new List()
+ ..addAll(typedefs)
+ ..addAll(commonTypedefs);
+ combinedTypeDefs.sort((Element a, Element b) {
+ return a.attributes['name'].compareTo(b.attributes['name']);
+ });
+
+ api = new Api(ver.text);
+ api.parse(domains, combinedTypeDefs, refactorings);
+
+ // Generate code from the model.
+ File outputFile = new File('lib/analysis_server_lib.dart');
+ DartGenerator generator = new DartGenerator();
+ api.generate(generator);
+ outputFile.writeAsStringSync(generator.toString());
+ Process.runSync('dartfmt', ['-w', outputFile.path]);
+ print('Wrote ${outputFile.path}.');
+}
+
+class Api {
+ final String version;
+
+ List<Domain> domains;
+ List<TypeDef> typedefs;
+ List<Refactoring> refactorings;
+
+ Api(this.version);
+
+ void parse(List<Element> domainElements, List<Element> typeElements,
+ List<Element> refactoringElements) {
+ typedefs =
+ new List.from(typeElements.map((element) => new TypeDef(element)));
+ domains =
+ new List.from(domainElements.map((element) => new Domain(element)));
+ refactorings =
+ new List.from(refactoringElements.map((e) => new Refactoring(e)));
+
+ // Mark some types as jsonable - we can send them back over the wire.
+ findRef('SourceEdit').setCallParam();
+ findRef('CompletionSuggestion').setCallParam();
+ findRef('Element').setCallParam();
+ findRef('Location').setCallParam();
+ typedefs
+ .where((def) => def.name.endsWith('ContentOverlay'))
+ .forEach((def) => def.setCallParam());
+ }
+
+ TypeDef findRef(String name) =>
+ typedefs.firstWhere((TypeDef t) => t.name == name);
+
+ void generate(DartGenerator gen) {
+ gen.out(_headerCode);
+ gen.writeln("const String generatedProtocolVersion = '${version}';");
+ gen.writeln();
+ gen.writeln("typedef void MethodSend(String methodName);");
+ gen.writeln();
+ gen.writeDocs('''
+A class to communicate with an analysis server instance.
+
+Here's a simple example of starting and communicating with the server:
+
+```dart
+import 'package:analysis_server_lib/analysis_server_lib.dart';
+
+main() async {
+ AnalysisServer server = await AnalysisServer.create();
+ await server.server.onConnected.first;
+
+ VersionResult version = await server.server.getVersion();
+ print(version.version);
+
+ server.dispose();
+}
+```
+''');
+ gen.writeStatement('class AnalysisServer {');
+ gen.writeln(_staticFactory);
+ gen.writeStatement('final Completer<int> processCompleter;');
+ gen.writeStatement('final Function _processKillHandler;');
+ gen.writeln();
+ gen.writeStatement('StreamSubscription _streamSub;');
+ gen.writeStatement('Function _writeMessage;');
+ gen.writeStatement('int _id = 0;');
+ gen.writeStatement('Map<String, Completer> _completers = {};');
+ gen.writeStatement('Map<String, String> _methodNames = {};');
+ gen.writeln(
+ 'JsonCodec _jsonEncoder = new JsonCodec(toEncodable: _toEncodable);');
+ gen.writeStatement('Map<String, Domain> _domains = {};');
+ gen.writeln(
+ "StreamController<String> _onSend = new StreamController.broadcast();");
+ gen.writeln(
+ "StreamController<String> _onReceive = new StreamController.broadcast();");
+ gen.writeln("MethodSend _willSend;");
+ gen.writeln();
+ domains.forEach(
+ (Domain domain) => gen.writeln('${domain.className} _${domain.name};'));
+ gen.writeln();
+ gen.writeDocs('Connect to an existing analysis server instance.');
+ gen.writeStatement(
+ 'AnalysisServer(Stream<String> inStream, void writeMessage(String message), \n'
+ 'this.processCompleter, [this._processKillHandler]) {');
+ gen.writeStatement('configure(inStream, writeMessage);');
+ gen.writeln();
+ domains.forEach((Domain domain) =>
+ gen.writeln('_${domain.name} = new ${domain.className}(this);'));
+ gen.writeln('}');
+ gen.writeln();
+ domains.forEach((Domain domain) => gen
+ .writeln('${domain.className} get ${domain.name} => _${domain.name};'));
+ gen.writeln();
+ gen.out(_serverCode);
+ gen.writeln('}');
+ gen.writeln();
+
+ // abstract Domain
+ gen.out(_domainCode);
+
+ // individual domains
+ domains.forEach((Domain domain) => domain.generate(gen));
+
+ // Object definitions.
+ gen.writeln();
+ gen.writeln('// type definitions');
+ gen.writeln();
+ typedefs
+ .where((t) => t.isObject)
+ .forEach((TypeDef def) => def.generate(gen));
+
+ // Handle the refactorings items.
+ gen.writeln();
+ gen.writeln('// refactorings');
+ gen.writeln();
+ gen.writeStatement('class Refactorings {');
+ refactorings.forEach((Refactoring refactor) {
+ gen.writeStatement(
+ "static const String ${refactor.kind} = '${refactor.kind}';");
+ });
+ gen.writeStatement('}');
+
+ refactorings.forEach((Refactoring refactor) => refactor.generate(gen));
+
+ // Refactoring feedback.
+ gen.writeln();
+
+ gen.writeStatement('abstract class RefactoringFeedback {');
+ gen.writeStatement(
+ 'static RefactoringFeedback parse(String kind, Map m) {');
+ gen.writeStatement('if (m == null) return null;');
+ gen.writeln();
+ gen.writeStatement('switch (kind) {');
+ refactorings.forEach((Refactoring refactor) {
+ if (refactor.feedbackFields.isEmpty) return;
+ gen.writeStatement("case Refactorings.${refactor.kind}: "
+ "return ${refactor.className}Feedback.parse(m);");
+ });
+ gen.writeStatement('}');
+ gen.writeln();
+ gen.writeStatement('return null;');
+ gen.writeStatement('}');
+ gen.writeStatement('}');
+
+ // Refactoring feedback classes.
+ for (Refactoring refactor in refactorings) {
+ if (refactor.feedbackFields.isEmpty) continue;
+
+ List<Field> fields = refactor.feedbackFields;
+ String name = '${refactor.className}Feedback';
+
+ gen.writeln();
+ gen.writeDocs('Feedback class for the `${refactor.kind}` refactoring.');
+ gen.writeStatement('class ${name} extends RefactoringFeedback {');
+ gen.write('static ${name} parse(Map m) => ');
+ gen.write('new ${name}(');
+ gen.write(fields.map((Field field) {
+ String val = "m['${field.name}']";
+ if (field.type.isMap) {
+ val = 'new Map.from($val)';
+ }
+
+ if (field.optional) {
+ return "${field.name}: ${field.type.jsonConvert(val)}";
+ } else {
+ return field.type.jsonConvert(val);
+ }
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln();
+ fields.forEach((field) {
+ if (field.deprecated) {
+ gen.write('@deprecated ');
+ } else {
+ gen.writeDocs(field.docs);
+ }
+ if (field.optional) gen.write('@optional ');
+ gen.writeln('final ${field.type} ${field.name};');
+ });
+ gen.writeln();
+ gen.write('${name}(');
+ gen.write(fields.map((field) {
+ StringBuffer buf = new StringBuffer();
+ if (field.optional && fields.firstWhere((a) => a.optional) == field)
+ buf.write('{');
+ buf.write('this.${field.name}');
+ if (field.optional && fields.lastWhere((a) => a.optional) == field)
+ buf.write('}');
+ return buf.toString();
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln('}');
+ }
+ }
+
+ String toString() => domains.toString();
+}
+
+class Domain {
+ bool experimental = false;
+ String name;
+ String docs;
+
+ List<Request> requests;
+ List<Notification> notifications;
+ Map<String, List<Field>> resultClasses = new LinkedHashMap();
+
+ Domain(Element element) {
+ name = element.attributes['name'];
+ experimental = element.attributes.containsKey('experimental');
+ docs = _collectDocs(element);
+ requests = element
+ .getElementsByTagName('request')
+ .map((element) => new Request(this, element))
+ .toList();
+ notifications = element
+ .getElementsByTagName('notification')
+ .map((element) => new Notification(this, element))
+ .toList();
+ }
+
+ String get className => '${titleCase(name)}Domain';
+
+ void generate(DartGenerator gen) {
+ resultClasses.clear();
+ gen.writeln();
+ gen.writeln('// ${name} domain');
+ gen.writeln();
+ gen.writeDocs(docs);
+ if (experimental) gen.writeln('@experimental');
+ gen.writeStatement('class ${className} extends Domain {');
+ gen.writeStatement(
+ "${className}(AnalysisServer server) : super(server, '${name}');");
+ if (notifications.isNotEmpty) {
+ gen.writeln();
+ notifications
+ .forEach((Notification notification) => notification.generate(gen));
+ }
+ requests.forEach((Request request) => request.generate(gen));
+ gen.writeln('}');
+
+ notifications.forEach(
+ (Notification notification) => notification.generateClass(gen));
+
+ // Result classes
+ for (String name in resultClasses.keys) {
+ List<Field> fields = resultClasses[name];
+
+ gen.writeln();
+ gen.writeStatement('class ${name} {');
+ if (name == 'RefactoringResult') {
+ gen.write('static ${name} parse(String kind, Map m) => ');
+ } else {
+ gen.write('static ${name} parse(Map m) => ');
+ }
+ gen.write('new ${name}(');
+ gen.write(fields.map((Field field) {
+ String val = "m['${field.name}']";
+ if (field.type.isMap) {
+ val = 'new Map.from($val)';
+ }
+
+ if (field.optional) {
+ return "${field.name}: ${field.type.jsonConvert(val)}";
+ } else {
+ return field.type.jsonConvert(val);
+ }
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln();
+ fields.forEach((field) {
+ if (field.deprecated) {
+ gen.write('@deprecated ');
+ } else {
+ gen.writeDocs(field.docs);
+ }
+ if (field.optional) gen.write('@optional ');
+ gen.writeln('final ${field.type} ${field.name};');
+ });
+ gen.writeln();
+ gen.write('${name}(');
+ gen.write(fields.map((field) {
+ StringBuffer buf = new StringBuffer();
+ if (field.optional && fields.firstWhere((a) => a.optional) == field)
+ buf.write('{');
+ buf.write('this.${field.name}');
+ if (field.optional && fields.lastWhere((a) => a.optional) == field)
+ buf.write('}');
+ return buf.toString();
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln('}');
+ }
+ }
+
+ String toString() => "Domain '${name}': ${requests}";
+}
+
+class Request {
+ final Domain domain;
+
+ bool experimental;
+ bool deprecated;
+ String method;
+ String docs;
+
+ List<Field> args = [];
+ List<Field> results = [];
+
+ Request(this.domain, Element element) {
+ experimental = element.attributes.containsKey('experimental');
+ deprecated = element.attributes.containsKey('deprecated');
+ method = element.attributes['method'];
+ docs = _collectDocs(element);
+
+ List paramsList = element.getElementsByTagName('params');
+ if (paramsList.isNotEmpty) {
+ args = new List.from(paramsList.first
+ .getElementsByTagName('field')
+ .map((field) => new Field(field)));
+ }
+
+ List resultsList = element.getElementsByTagName('result');
+ if (resultsList.isNotEmpty) {
+ results = new List.from(resultsList.first
+ .getElementsByTagName('field')
+ .map((field) => new Field(field)));
+ }
+ }
+
+ void generate(DartGenerator gen) {
+ gen.writeln();
+
+ args.forEach((Field field) => field.setCallParam());
+ if (results.isNotEmpty) {
+ domain.resultClasses[resultName] = results;
+ }
+
+ if (deprecated) {
+ gen.writeln('@deprecated');
+ } else {
+ gen.writeDocs(docs);
+ }
+ if (experimental) gen.writeln('@experimental');
+
+ String qName = '${domain.name}.${method}';
+
+ if (results.isEmpty) {
+ if (args.isEmpty) {
+ gen.writeln("Future ${method}() => _call('$qName');");
+ return;
+ }
+
+ if (args.length == 1 && !args.first.optional) {
+ Field arg = args.first;
+ String type = arg.type.toString();
+
+ if (method == 'updateContent') {
+ type = 'Map<String, ContentOverlayType>';
+ }
+ gen.write("Future ${method}(${type} ${arg.name}) => ");
+ gen.writeln("_call('$qName', {'${arg.name}': ${arg.name}});");
+ return;
+ }
+ }
+
+ if (args.isEmpty) {
+ gen.writeln("Future<${resultName}> ${method}() => _call('$qName').then("
+ "${resultName}.parse);");
+ return;
+ }
+
+ if (results.isEmpty) {
+ gen.write('Future ${method}(');
+ } else {
+ gen.write('Future<${resultName}> ${method}(');
+ }
+ gen.write(args.map((arg) {
+ StringBuffer buf = new StringBuffer();
+ if (arg.optional && args.firstWhere((a) => a.optional) == arg)
+ buf.write('{');
+ buf.write('${arg.type} ${arg.name}');
+ if (arg.optional && args.lastWhere((a) => a.optional) == arg)
+ buf.write('}');
+ return buf.toString();
+ }).join(', '));
+ gen.writeStatement(') {');
+ if (args.isEmpty) {
+ gen.write("return _call('$qName')");
+ if (results.isNotEmpty) {
+ gen.write(".then(${resultName}.parse)");
+ }
+ gen.writeln(';');
+ } else {
+ String mapStr = args
+ .where((arg) => !arg.optional)
+ .map((arg) => "'${arg.name}': ${arg.name}")
+ .join(', ');
+ gen.writeStatement('Map m = {${mapStr}};');
+ for (Field arg in args.where((arg) => arg.optional)) {
+ gen.writeStatement(
+ "if (${arg.name} != null) m['${arg.name}'] = ${arg.name};");
+ }
+ gen.write("return _call('$qName', m)");
+ if (results.isNotEmpty) {
+ if (qName == 'edit.getRefactoring') {
+ gen.write(".then((m) => ${resultName}.parse(kind, m))");
+ } else {
+ gen.write(".then(${resultName}.parse)");
+ }
+ }
+ gen.writeln(';');
+ }
+ gen.writeStatement('}');
+ }
+
+ String get resultName {
+ if (results.isEmpty) return 'dynamic';
+ if (method.startsWith('get')) return '${method.substring(3)}Result';
+ return '${titleCase(method)}Result';
+ }
+
+ String toString() => 'Request ${method}()';
+}
+
+class Notification {
+ final Domain domain;
+ String event;
+ String docs;
+ List<Field> fields;
+
+ Notification(this.domain, Element element) {
+ event = element.attributes['event'];
+ docs = _collectDocs(element);
+ fields = new List.from(
+ element.getElementsByTagName('field').map((field) => new Field(field)));
+ fields.sort();
+ }
+
+ String get title => '${domain.name}.${event}';
+
+ String get onName => 'on${titleCase(event)}';
+
+ String get className => '${titleCase(domain.name)}${titleCase(event)}';
+
+ void generate(DartGenerator gen) {
+ gen.writeDocs(docs);
+ gen.writeln("Stream<${className}> get ${onName} {");
+ gen.writeln("return _listen('${title}', ${className}.parse);");
+ gen.writeln("}");
+ }
+
+ void generateClass(DartGenerator gen) {
+ gen.writeln();
+ gen.writeln('class ${className} {');
+ gen.write('static ${className} parse(Map m) => ');
+ gen.write('new ${className}(');
+ gen.write(fields.map((Field field) {
+ String val = "m['${field.name}']";
+ if (field.optional) {
+ return "${field.name}: ${field.type.jsonConvert(val)}";
+ } else {
+ return field.type.jsonConvert(val);
+ }
+ }).join(', '));
+ gen.writeln(');');
+ if (fields.isNotEmpty) {
+ gen.writeln();
+ fields.forEach((field) {
+ if (field.deprecated) {
+ gen.write('@deprecated ');
+ } else {
+ gen.writeDocs(field.docs);
+ }
+ if (field.optional) gen.write('@optional ');
+ gen.writeln('final ${field.type} ${field.name};');
+ });
+ }
+ gen.writeln();
+ gen.write('${className}(');
+ gen.write(fields.map((field) {
+ StringBuffer buf = new StringBuffer();
+ if (field.optional && fields.firstWhere((a) => a.optional) == field)
+ buf.write('{');
+ buf.write('this.${field.name}');
+ if (field.optional && fields.lastWhere((a) => a.optional) == field)
+ buf.write('}');
+ return buf.toString();
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln('}');
+ }
+}
+
+class Field implements Comparable {
+ String name;
+ String docs;
+ bool optional;
+ bool deprecated;
+ Type type;
+
+ Field(Element element) {
+ name = element.attributes['name'];
+ docs = _collectDocs(element);
+ optional = element.attributes['optional'] == 'true';
+ deprecated = element.attributes.containsKey('deprecated');
+ type = Type.create(element.children.first);
+ }
+
+ void setCallParam() => type.setCallParam();
+
+ bool get isJsonable => type.isCallParam();
+
+ String toString() => name;
+
+ int compareTo(other) {
+ if (other is! Field) return 0;
+ if (!optional && other.optional) return -1;
+ if (optional && !other.optional) return 1;
+ return 0;
+ }
+
+ void generate(DartGenerator gen) {
+ if (deprecated) {
+ gen.writeln('@deprecated');
+ } else {
+ gen.writeDocs(docs);
+ }
+ if (optional) gen.write('@optional ');
+ gen.writeStatement('final ${type} ${name};');
+ }
+}
+
+class Refactoring {
+ String kind;
+ String docs;
+ List<Field> optionsFields = [];
+ List<Field> feedbackFields = [];
+
+ Refactoring(Element element) {
+ kind = element.attributes['kind'];
+ docs = _collectDocs(element);
+
+ // Parse <options>
+ // <field name="deleteSource"><ref>bool</ref></field>
+ Element options = element.querySelector('options');
+ if (options != null) {
+ optionsFields = new List.from(options
+ .getElementsByTagName('field')
+ .map((field) => new Field(field)));
+ }
+
+ // Parse <feedback>
+ // <field name="className" optional="true"><ref>String</ref></field>
+ Element feedback = element.querySelector('feedback');
+ if (feedback != null) {
+ feedbackFields = new List.from(feedback
+ .getElementsByTagName('field')
+ .map((field) => new Field(field)));
+ feedbackFields.sort();
+ }
+ }
+
+ String get className {
+ // MOVE_FILE ==> MoveFile
+ return kind.split('_').map((s) => forceTitleCase(s)).join('');
+ }
+
+ void generate(DartGenerator gen) {
+ // Generate the refactoring options.
+ if (optionsFields.isNotEmpty) {
+ gen.writeln();
+ gen.writeDocs(docs);
+ gen.writeStatement(
+ 'class ${className}RefactoringOptions extends RefactoringOptions {');
+ // fields
+ for (Field field in optionsFields) {
+ field.generate(gen);
+ }
+
+ gen.writeln();
+ gen.writeStatement('${className}RefactoringOptions({'
+ '${optionsFields.map((f) => 'this.${f.name}').join(', ')}'
+ '});');
+ gen.writeln();
+
+ // toMap
+ gen.write("Map toMap() => _stripNullValues({");
+ gen.write(optionsFields.map((f) => "'${f.name}': ${f.name}").join(', '));
+ gen.writeStatement("});");
+ gen.writeStatement('}');
+ }
+ }
+}
+
+class TypeDef {
+ static final Set<String> _shouldHaveToString = new Set.from([
+ 'SourceEdit',
+ 'PubStatus',
+ 'Location',
+ 'AnalysisStatus',
+ 'AnalysisError',
+ 'SourceChange',
+ 'SourceFileEdit',
+ 'LinkedEditGroup',
+ 'Position',
+ 'NavigationRegion',
+ 'NavigationTarget',
+ 'CompletionSuggestion',
+ 'Element',
+ 'SearchResult'
+ ]);
+
+ static final Set<String> _shouldHaveEquals =
+ new Set.from(['Location', 'AnalysisError']);
+
+ String name;
+ bool experimental;
+ bool deprecated;
+ String docs;
+ bool isString = false;
+ List<Field> fields;
+ bool _callParam = false;
+
+ TypeDef(Element element) {
+ name = element.attributes['name'];
+ experimental = element.attributes.containsKey('experimental');
+ deprecated = element.attributes.containsKey('deprecated');
+ docs = _collectDocs(element);
+
+ // object, enum, ref
+ Set<String> tags = new Set.from(element.children.map((c) => c.localName));
+
+ if (tags.contains('object')) {
+ Element object = element.getElementsByTagName('object').first;
+ fields = new List.from(
+ object.getElementsByTagName('field').map((f) => new Field(f)));
+ fields.sort();
+ } else if (tags.contains('enum')) {
+ isString = true;
+ } else if (tags.contains('ref')) {
+ Element tag = element.getElementsByTagName('ref').first;
+ String type = tag.text;
+ if (type == 'String') {
+ isString = true;
+ } else {
+ throw 'unknown ref type: ${type}';
+ }
+ } else {
+ throw 'unknown tag: ${tags}';
+ }
+ }
+
+ bool get isObject => fields != null;
+
+ bool get callParam => _callParam;
+
+ void setCallParam() {
+ _callParam = true;
+ }
+
+ bool isCallParam() => _callParam;
+
+ void generate(DartGenerator gen) {
+ if (name == 'RefactoringOptions' ||
+ name == 'RefactoringFeedback' ||
+ name == 'RequestError') {
+ return;
+ }
+
+ bool isContentOverlay = name.endsWith('ContentOverlay');
+ List<Field> _fields = fields;
+ if (isContentOverlay) {
+ _fields = _fields.toList()..removeAt(0);
+ }
+
+ gen.writeln();
+ if (deprecated) {
+ gen.writeln('@deprecated');
+ } else {
+ gen.writeDocs(docs);
+ }
+ if (experimental) gen.writeln('@experimental');
+ gen.write('class ${name}');
+ if (isContentOverlay) gen.write(' extends ContentOverlayType');
+ if (callParam) gen.write(' implements Jsonable');
+ gen.writeln(' {');
+ gen.writeln('static ${name} parse(Map m) {');
+ gen.writeln('if (m == null) return null;');
+ gen.write('return new ${name}(');
+ gen.write(_fields.map((Field field) {
+ String val = "m['${field.name}']";
+ if (field.optional) {
+ return "${field.name}: ${field.type.jsonConvert(val)}";
+ } else {
+ return field.type.jsonConvert(val);
+ }
+ }).join(', '));
+ gen.writeln(');');
+ gen.writeln('}');
+
+ if (_fields.isNotEmpty) {
+ gen.writeln();
+ _fields.forEach((field) {
+ if (field.deprecated) {
+ gen.write('@deprecated ');
+ } else {
+ gen.writeDocs(field.docs);
+ }
+ if (field.optional) gen.write('@optional ');
+ gen.writeln('final ${field.type} ${field.name};');
+ });
+ }
+
+ gen.writeln();
+ gen.write('${name}(');
+ gen.write(_fields.map((field) {
+ StringBuffer buf = new StringBuffer();
+ if (field.optional && fields.firstWhere((a) => a.optional) == field)
+ buf.write('{');
+ buf.write('this.${field.name}');
+ if (field.optional && fields.lastWhere((a) => a.optional) == field)
+ buf.write('}');
+ return buf.toString();
+ }).join(', '));
+ if (isContentOverlay) {
+ String type = name
+ .substring(0, name.length - 'ContentOverlay'.length)
+ .toLowerCase();
+ gen.writeln(") : super('$type');");
+ } else {
+ gen.writeln(');');
+ }
+
+ if (callParam) {
+ gen.writeln();
+ String map = fields.map((f) {
+ if (f.isJsonable && f.type.typeName != 'String') {
+ return "'${f.name}': ${f.name}?.toMap()";
+ }
+ return "'${f.name}': ${f.name}";
+ }).join(', ');
+ gen.writeln("Map toMap() => _stripNullValues({${map}});");
+ }
+
+ if (hasEquals) {
+ gen.writeln();
+ String str = fields.map((f) => "${f.name} == o.${f.name}").join(' && ');
+ gen.writeln("bool operator==(o) => o is ${name} && ${str};");
+ gen.writeln();
+ String str2 = fields
+ .where((f) => !f.optional)
+ .map((f) => "${f.name}.hashCode")
+ .join(' ^ ');
+ gen.writeln("int get hashCode => ${str2};");
+ }
+
+ if (hasToString) {
+ gen.writeln();
+ String str = fields
+ .where((f) => !f.optional)
+ .map((f) => "${f.name}: \${${f.name}}")
+ .join(', ');
+ gen.writeln("String toString() => '[${name} ${str}]';");
+ }
+
+ gen.writeln('}');
+ }
+
+ bool get hasEquals => _shouldHaveEquals.contains(name);
+
+ bool get hasToString => _shouldHaveToString.contains(name);
+
+ String toString() => 'TypeDef ${name}';
+}
+
+abstract class Type {
+ String get typeName;
+
+ static Type create(Element element) {
+ // <ref>String</ref>, or list, or map
+ if (element.localName == 'ref') {
+ String text = element.text;
+ if (text == 'int' ||
+ text == 'bool' ||
+ text == 'String' ||
+ text == 'long') {
+ return new PrimitiveType(text);
+ } else {
+ return new RefType(text);
+ }
+ } else if (element.localName == 'list') {
+ return new ListType(element.children.first);
+ } else if (element.localName == 'map') {
+ return new MapType(element.children[0].children.first,
+ element.children[1].children.first);
+ } else if (element.localName == 'union') {
+ return new PrimitiveType('dynamic');
+ } else {
+ throw 'unknown type: ${element}';
+ }
+ }
+
+ String jsonConvert(String ref);
+
+ void setCallParam();
+
+ bool isCallParam() => false;
+
+ bool get isMap => typeName == 'Map' || typeName.startsWith('Map<');
+
+ String toString() => typeName;
+}
+
+class ListType extends Type {
+ Type subType;
+
+ ListType(Element element) : subType = Type.create(element);
+
+ String get typeName => 'List<${subType.typeName}>';
+
+ String jsonConvert(String ref) {
+ if (subType is PrimitiveType) {
+ return "${ref} == null ? null : new List.from(${ref})";
+ }
+
+ if (subType is RefType && (subType as RefType).isString) {
+ return "${ref} == null ? null : new List.from(${ref})";
+ }
+
+ return "${ref} == null ? null : new List.from(${ref}.map((obj) => ${subType.jsonConvert('obj')}))";
+ }
+
+ void setCallParam() => subType.setCallParam();
+}
+
+class MapType extends Type {
+ Type key;
+ Type value;
+
+ MapType(Element keyElement, Element valueElement) {
+ key = Type.create(keyElement);
+ value = Type.create(valueElement);
+ }
+
+ String get typeName => 'Map<${key.typeName}, ${value.typeName}>';
+
+ String jsonConvert(String ref) => ref;
+
+ void setCallParam() {
+ key.setCallParam();
+ value.setCallParam();
+ }
+}
+
+class RefType extends Type {
+ String text;
+ TypeDef ref;
+
+ RefType(this.text);
+
+ bool get isString {
+ if (ref == null) _resolve();
+ return ref.isString;
+ }
+
+ String get typeName {
+ if (ref == null) _resolve();
+ return ref.isString ? 'String' : ref.name;
+ }
+
+ String jsonConvert(String r) {
+ if (ref == null) _resolve();
+ if (ref.name == 'RefactoringFeedback') {
+ return '${ref.name}.parse(kind, ${r})';
+ }
+ return ref.isString ? r : '${ref.name}.parse(${r})';
+ }
+
+ void setCallParam() {
+ if (ref == null) _resolve();
+ ref.setCallParam();
+ }
+
+ bool isCallParam() => ref.isCallParam();
+
+ void _resolve() {
+ try {
+ ref = api.findRef(text);
+ } catch (e) {
+ print("can't resolve ${text}");
+ rethrow;
+ }
+ }
+}
+
+class PrimitiveType extends Type {
+ final String type;
+
+ PrimitiveType(this.type);
+
+ String get typeName => type == 'long' ? 'int' : type;
+
+ String jsonConvert(String ref) => ref;
+
+ void setCallParam() {}
+}
+
+class _ConcatTextVisitor extends TreeVisitor {
+ final StringBuffer buffer = new StringBuffer();
+
+ String toString() => buffer.toString();
+
+ visitText(Text node) {
+ buffer.write(node.data);
+ }
+
+ visitElement(Element node) {
+ if (node.localName == 'b') {
+ buffer.write('**${node.text}**');
+ } else if (node.localName == 'a') {
+ buffer.write('(${node.text})[${node.attributes['href']}]');
+ } else if (node.localName == 'tt') {
+ buffer.write('`${node.text}`');
+ } else {
+ visitChildren(node);
+ }
+ }
+}
+
+final RegExp _wsRegexp = new RegExp(r'\s+', multiLine: true);
+
+String _collectDocs(Element element) {
+ String str =
+ element.children.where((e) => e.localName == 'p').map((Element e) {
+ _ConcatTextVisitor visitor = new _ConcatTextVisitor();
+ visitor.visit(e);
+ return visitor.toString().trim().replaceAll(_wsRegexp, ' ');
+ }).join('\n\n');
+ return str.isEmpty ? null : str;
+}
+
+final String _headerCode = r'''
+// Copyright (c) 2017, Devon Carew. 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.
+
+// This is a generated file.
+
+/// A library to access the analysis server API.
+///
+/// [AnalysisServer] is the main entry-point to this library.
+library analysis_server_lib;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
+
+/// @optional
+const String optional = 'optional';
+
+/// @experimental
+const String experimental = 'experimental';
+
+final Logger _logger = new Logger('analysis_server');
+
+''';
+
+final String _staticFactory = r'''
+ /// Create and connect to a new analysis server instance.
+ ///
+ /// - [sdkPath] override the default sdk path
+ /// - [scriptPath] override the default entry-point script to use for the
+ /// analysis server
+ /// - [onRead] called every time data is read from the server
+ /// - [onWrite] called every time data is written to the server
+ static Future<AnalysisServer> create(
+ {String sdkPath, String scriptPath, onRead(String), onWrite(String),
+ List<String> vmArgs, List<String> serverArgs,
+ String clientId, String clientVersion}) async {
+ Completer<int> processCompleter = new Completer();
+
+ String vmPath;
+ if (sdkPath != null) {
+ vmPath = path.join(sdkPath, 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
+ } else {
+ sdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
+ vmPath = Platform.resolvedExecutable;
+ }
+ scriptPath ??= '$sdkPath/bin/snapshots/analysis_server.dart.snapshot';
+
+ List<String> args = [scriptPath, '--sdk', sdkPath];
+ if (vmArgs != null) args.insertAll(0, vmArgs);
+ if (serverArgs != null) args.addAll(serverArgs);
+ if (clientId != null) args.add('--client-id=$clientId');
+ if (clientVersion != null) args.add('--client-version=$clientVersion');
+
+ Process process = await Process.start(vmPath, args);
+ process.exitCode.then((code) => processCompleter.complete(code));
+
+ Stream<String> inStream = process.stdout
+ .transform(UTF8.decoder)
+ .transform(const LineSplitter())
+ .map((String message) {
+ if (onRead != null) onRead(message);
+ return message;
+ });
+
+ AnalysisServer server = new AnalysisServer(inStream, (String message) {
+ if (onWrite != null) onWrite(message);
+ process.stdin.writeln(message);
+ }, processCompleter, process.kill);
+
+ return server;
+ }
+
+''';
+
+final String _serverCode = r'''
+ Stream<String> get onSend => _onSend.stream;
+ Stream<String> get onReceive => _onReceive.stream;
+
+ set willSend(MethodSend fn) {
+ _willSend = fn;
+ }
+
+ void configure(Stream<String> inStream, void writeMessage(String message)) {
+ _streamSub = inStream.listen(_processMessage);
+ _writeMessage = writeMessage;
+ }
+
+ void dispose() {
+ if (_streamSub != null) _streamSub.cancel();
+ //_completers.values.forEach((c) => c.completeError('disposed'));
+ _completers.clear();
+
+ if (_processKillHandler != null) {
+ _processKillHandler();
+ }
+ }
+
+ void _processMessage(String message) {
+ _onReceive.add(message);
+
+ if (!message.startsWith('{')) {
+ _logger.warning('unknown message: ${message}');
+ return;
+ }
+
+ try {
+ var json = JSON.decode(message);
+
+ if (json['id'] == null) {
+ // Handle a notification.
+ String event = json['event'];
+ if (event == null) {
+ _logger.severe('invalid message: ${message}');
+ } else {
+ String prefix = event.substring(0, event.indexOf('.'));
+ if (_domains[prefix] == null) {
+ _logger.severe('no domain for notification: ${message}');
+ } else {
+ _domains[prefix]._handleEvent(event, json['params']);
+ }
+ }
+ } else {
+ Completer completer = _completers.remove(json['id']);
+ String methodName = _methodNames.remove(json['id']);
+
+ if (completer == null) {
+ _logger.severe('unmatched request response: ${message}');
+ } else if (json['error'] != null) {
+ completer.completeError(RequestError.parse(methodName, json['error']));
+ } else {
+ completer.complete(json['result']);
+ }
+ }
+ } catch (e) {
+ _logger.severe('unable to decode message: ${message}, ${e}');
+ }
+ }
+
+ Future _call(String method, [Map args]) {
+ String id = '${++_id}';
+ _completers[id] = new Completer();
+ _methodNames[id] = method;
+ Map m = {'id': id, 'method': method};
+ if (args != null) m['params'] = args;
+ String message = _jsonEncoder.encode(m);
+ if (_willSend != null) _willSend(method);
+ _onSend.add(message);
+ _writeMessage(message);
+ return _completers[id].future;
+ }
+
+ static dynamic _toEncodable(obj) => obj is Jsonable ? obj.toMap() : obj;
+''';
+
+final String _domainCode = r'''
+abstract class Domain {
+ final AnalysisServer server;
+ final String name;
+
+ Map<String, StreamController> _controllers = {};
+ Map<String, Stream> _streams = {};
+
+ Domain(this.server, this.name) {
+ server._domains[name] = this;
+ }
+
+ Future _call(String method, [Map args]) => server._call(method, args);
+
+ Stream<dynamic> _listen(String name, Function cvt) {
+ if (_streams[name] == null) {
+ _controllers[name] = new StreamController.broadcast();
+ _streams[name] = _controllers[name].stream.map(cvt);
+ }
+
+ return _streams[name];
+ }
+
+ void _handleEvent(String name, dynamic event) {
+ if (_controllers[name] != null) {
+ _controllers[name].add(event);
+ }
+ }
+
+ String toString() => 'Domain ${name}';
+}
+
+abstract class Jsonable {
+ Map toMap();
+}
+
+abstract class RefactoringOptions implements Jsonable {
+}
+
+abstract class ContentOverlayType {
+ final String type;
+
+ ContentOverlayType(this.type);
+}
+
+class RequestError {
+ static RequestError parse(String method, Map m) {
+ if (m == null) return null;
+ return new RequestError(method, m['code'], m['message'], stackTrace: m['stackTrace']);
+ }
+
+ final String method;
+ final String code;
+ final String message;
+ @optional final String stackTrace;
+
+ RequestError(this.method, this.code, this.message, {this.stackTrace});
+
+ String toString() => '[Analyzer RequestError method: ${method}, code: ${code}, message: ${message}]';
+}
+
+Map _stripNullValues(Map m) {
+ Map copy = {};
+
+ for (var key in m.keys) {
+ var value = m[key];
+ if (value != null) copy[key] = value;
+ }
+
+ return copy;
+}
+''';
diff --git a/pub/analysis_server_lib/tool/src/src_gen.dart b/pub/analysis_server_lib/tool/src/src_gen.dart
new file mode 100644
index 0000000..546feaf
--- /dev/null
+++ b/pub/analysis_server_lib/tool/src/src_gen.dart
@@ -0,0 +1,162 @@
+// Copyright (c) 2017, Devon Carew. 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.
+
+const int RUNE_SPACE = 32;
+const int RUNE_EOL = 10;
+const int RUNE_LEFT_CURLY = 123;
+const int RUNE_RIGHT_CURLY = 125;
+
+final RegExp _wsRegexp = new RegExp(r'\s+');
+
+String collapseWhitespace(String str) => str.replaceAll(_wsRegexp, ' ');
+
+/// foo ==> Foo
+String titleCase(String str) =>
+ str.substring(0, 1).toUpperCase() + str.substring(1);
+
+/// FOO ==> Foo
+String forceTitleCase(String str) {
+ if (str == null || str.isEmpty) return str;
+ return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
+}
+
+String joinLast(Iterable<String> strs, String join, [String last]) {
+ if (strs.isEmpty) return '';
+ List list = strs.toList();
+ if (list.length == 1) return list.first;
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < list.length; i++) {
+ if (i > 0) {
+ if (i + 1 == list.length && last != null) {
+ buf.write(last);
+ } else {
+ buf.write(join);
+ }
+ }
+ buf.write(list[i]);
+ }
+ return buf.toString();
+}
+
+/**
+ * A class used to generate Dart source code. This class facilitates writing out
+ * dartdoc comments, automatically manages indent by counting curly braces, and
+ * automatically wraps doc comments on 80 char column boundaries.
+ */
+class DartGenerator {
+ static const DEFAULT_COLUMN_BOUNDARY = 80;
+
+ final int colBoundary;
+
+ String _indent = "";
+ final StringBuffer _buf = new StringBuffer();
+
+ bool _previousWasEol = false;
+
+ DartGenerator({this.colBoundary: DEFAULT_COLUMN_BOUNDARY});
+
+ /// Write out the given dartdoc text, wrapping lines as necessary to flow
+ /// along the column boundary.
+ void writeDocs(String docs) {
+ if (docs == null) return;
+
+ docs = wrap(docs.trim(), colBoundary - _indent.length - 4);
+ // docs = docs.replaceAll('*/', '/');
+ // docs = docs.replaceAll('/*', r'/\*');
+
+ docs.split('\n').forEach((line) => _writeln('/// ${line}'));
+ }
+
+ /**
+ * Write out the given Dart statement and terminate it with an eol. If the
+ * statement will overflow the column boundary, attempt to wrap it at
+ * reasonable places.
+ */
+ void writeStatement(String str) {
+ if (_indent.length + str.length > colBoundary) {
+ // Split the line on the first '('. Currently, we don't do anything
+ // fancier then that. This takes the edge off the long lines.
+ int index = str.indexOf('(');
+
+ if (index == -1) {
+ writeln(str);
+ } else {
+ writeln(str.substring(0, index + 1));
+ writeln(" ${str.substring(index + 1)}");
+ }
+ } else {
+ writeln(str);
+ }
+ }
+
+ void writeln([String str = ""]) => _write("${str}\n");
+
+ void write(String str) => _write(str);
+
+ void out(String str) => _buf.write(str);
+
+ void _writeln([String str = "", bool ignoreCurlies = false]) =>
+ _write("${str}\n", ignoreCurlies);
+
+ void _write(String str, [bool ignoreCurlies = false]) {
+ for (final int rune in str.runes) {
+ if (!ignoreCurlies) {
+ if (rune == RUNE_LEFT_CURLY) {
+ _indent = "${_indent} ";
+ } else if (rune == RUNE_RIGHT_CURLY && _indent.length >= 2) {
+ _indent = _indent.substring(2);
+ }
+ }
+
+ if (_previousWasEol && rune != RUNE_EOL) {
+ _buf.write(_indent);
+ }
+
+ _buf.write(new String.fromCharCode(rune));
+
+ _previousWasEol = rune == RUNE_EOL;
+ }
+ }
+
+ String toString() => _buf.toString();
+}
+
+/// Wrap a string on column boundaries.
+String wrap(String str, [int col = 80]) {
+ // The given string could contain newlines.
+ List lines = str.split('\n');
+ return lines.map((l) => _simpleWrap(l, col)).join('\n');
+}
+
+/// Wrap a string ignoring newlines.
+String _simpleWrap(String str, [int col = 80]) {
+ List<String> lines = [];
+
+ while (str.length > col) {
+ int index = col;
+
+ while (index > 0 && str.codeUnitAt(index) != RUNE_SPACE) {
+ index--;
+ }
+
+ if (index == 0) {
+ index = str.indexOf(' ');
+
+ if (index == -1) {
+ lines.add(str);
+ str = '';
+ } else {
+ lines.add(str.substring(0, index).trim());
+ str = str.substring(index).trim();
+ }
+ } else {
+ lines.add(str.substring(0, index).trim());
+ str = str.substring(index).trim();
+ }
+ }
+
+ if (str.length > 0) lines.add(str);
+
+ return lines.join('\n');
+}
diff --git a/pub/analysis_server_lib/tool/travis.sh b/pub/analysis_server_lib/tool/travis.sh
new file mode 100755
index 0000000..df9fb39
--- /dev/null
+++ b/pub/analysis_server_lib/tool/travis.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (c) 2017, Devon Carew. 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.
+
+# Fast fail the script on failures.
+set -e
+
+# Verify the library re-generates.
+dart -c tool/generate_analysis.dart
+
+# Verify that the libraries are error free.
+dartanalyzer --fatal-warnings \
+ tool/generate_analysis.dart \
+ tool/analysis_tester.dart \
+ lib/analysis_server_lib.dart \
+ test/domains/*_test.dart
+
+# Run the tests.
+pub run test
diff --git a/pub/mockito/BUILD.gn b/pub/mockito/BUILD.gn
index 8c31e00..972f937 100644
--- a/pub/mockito/BUILD.gn
+++ b/pub/mockito/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for mockito-2.1.0
+# This file is generated by importer.py for mockito-2.2.0
import("//build/dart/dart_package.gni")
@@ -13,5 +13,6 @@
"//third_party/dart-pkg/pub/test",
"//third_party/dart-pkg/pub/matcher",
"//third_party/dart-pkg/pub/meta",
+ "//third_party/dart-pkg/pub/collection",
]
}
diff --git a/pub/mockito/CHANGELOG.md b/pub/mockito/CHANGELOG.md
index eb1384e..4f4bc0a 100644
--- a/pub/mockito/CHANGELOG.md
+++ b/pub/mockito/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.2.0
+
+* Add new feature to wait for an interaction: `untilCalled`. See the README for
+ documentation.
+
## 2.1.0
* Add documentation for `when`, `verify`, `verifyNever`, `resetMockitoState`.
diff --git a/pub/mockito/README.md b/pub/mockito/README.md
index c56a4df..8c8ce97 100644
--- a/pub/mockito/README.md
+++ b/pub/mockito/README.md
@@ -150,6 +150,16 @@
expect(verify(cat.eatFood(captureThat(startsWith("F")).captured, ["Fish"]);
```
+## Waiting for an interaction
+```dart
+//waiting for a call
+cat.eatFood("Fish");
+await untilCalled(cat.chew()); //completes when cat.chew() is called
+//waiting for a call that has already happened
+cat.eatFood("Fish");
+await untilCalled(cat.eatFood(any)); //will complete immediately
+```
+
## Resetting mocks
```dart
//clearing collected interactions
diff --git a/pub/mockito/lib/mockito.dart b/pub/mockito/lib/mockito.dart
index c151683..3aaadc2 100644
--- a/pub/mockito/lib/mockito.dart
+++ b/pub/mockito/lib/mockito.dart
@@ -42,4 +42,5 @@
clearInteractions,
reset,
resetMockitoState,
- logInvocations;
+ logInvocations,
+ untilCalled;
diff --git a/pub/mockito/lib/src/mock.dart b/pub/mockito/lib/src/mock.dart
index 82d8383..fe8895e 100644
--- a/pub/mockito/lib/src/mock.dart
+++ b/pub/mockito/lib/src/mock.dart
@@ -15,14 +15,18 @@
// Warning: Do not import dart:mirrors in this library, as it's exported via
// lib/mockito.dart, which is used for Dart AOT projects such as Flutter.
+import 'dart:async';
+
import 'package:meta/meta.dart';
import 'package:mockito/src/call_pair.dart';
import 'package:mockito/src/invocation_matcher.dart';
import 'package:test/test.dart';
bool _whenInProgress = false;
+bool _untilCalledInProgress = false;
bool _verificationInProgress = false;
_WhenCall _whenCall;
+_UntilCall _untilCall;
final List<_VerifyCall> _verifyCalls = <_VerifyCall>[];
final _TimeStampProvider _timer = new _TimeStampProvider();
final List _capturedArgs = [];
@@ -78,6 +82,8 @@
static const _nullResponse = const CallPair.allInvocations(_answerNull);
+ final StreamController<Invocation> _invocationStreamController =
+ new StreamController.broadcast();
final _realCalls = <RealCall>[];
final _responses = <CallPair>[];
@@ -103,8 +109,12 @@
} else if (_verificationInProgress) {
_verifyCalls.add(new _VerifyCall(this, invocation));
return null;
+ } else if (_untilCalledInProgress) {
+ _untilCall = new _UntilCall(this, invocation);
+ return null;
} else {
_realCalls.add(new RealCall(this, invocation));
+ _invocationStreamController.add(invocation);
var cannedResponse = _responses.lastWhere(
(cr) => cr.call.matches(invocation, {}),
orElse: _defaultResponse);
@@ -470,6 +480,29 @@
}
}
+class _UntilCall {
+ final InvocationMatcher _invocationMatcher;
+ final Mock _mock;
+
+ _UntilCall(this._mock, Invocation invocation)
+ : _invocationMatcher = new InvocationMatcher(invocation);
+
+ bool _matchesInvocation(RealCall realCall) =>
+ _invocationMatcher.matches(realCall.invocation);
+
+ List<RealCall> get _realCalls => _mock._realCalls;
+
+ Future<Invocation> get invocationFuture {
+ if (_realCalls.any(_matchesInvocation)) {
+ return new Future.value(
+ _realCalls.firstWhere(_matchesInvocation).invocation);
+ }
+
+ return _mock._invocationStreamController.stream
+ .firstWhere(_invocationMatcher.matches);
+ }
+}
+
class _VerifyCall {
final Mock mock;
final Invocation verifyInvocation;
@@ -707,6 +740,29 @@
};
}
+typedef Future<Invocation> InvocationLoader(_);
+
+/// Returns a future [Invocation] that will complete upon the first occurrence
+/// of the given invocation.
+///
+/// Usage of this is as follows:
+///
+/// ```dart
+/// cat.eatFood("fish");
+/// await untilCalled(cat.chew());
+/// ```
+///
+/// In the above example, the untilCalled(cat.chew()) will complete only when
+/// that method is called. If the given invocation has already been called, the
+/// future will return immediately.
+InvocationLoader get untilCalled {
+ _untilCalledInProgress = true;
+ return (_) {
+ _untilCalledInProgress = false;
+ return _untilCall.invocationFuture;
+ };
+}
+
/// Print all collected invocations of any mock methods of [mocks].
void logInvocations(List<Mock> mocks) {
List<RealCall> allInvocations =
@@ -728,8 +784,10 @@
/// or in `tearDown`.
void resetMockitoState() {
_whenInProgress = false;
+ _untilCalledInProgress = false;
_verificationInProgress = false;
_whenCall = null;
+ _untilCall = null;
_verifyCalls.clear();
_capturedArgs.clear();
_typedArgs.clear();
diff --git a/pub/mockito/pubspec.yaml b/pub/mockito/pubspec.yaml
index 793d1d1..279241e 100644
--- a/pub/mockito/pubspec.yaml
+++ b/pub/mockito/pubspec.yaml
@@ -1,5 +1,5 @@
name: mockito
-version: 2.1.0
+version: 2.2.0
authors:
- Dmitriy Fibulwinter <fibulwinter@gmail.com>
- Dart Team <misc@dartlang.org>
@@ -8,6 +8,7 @@
environment:
sdk: '>=1.21.0 <2.0.0-dev.infinity'
dependencies:
+ collection: '^1.1.0'
matcher: '^0.12.0'
meta: '^1.0.4'
test: '>=0.12.0 <0.13.0'
diff --git a/pub/protobuf/.travis.yml b/pub/protobuf/.travis.yml
new file mode 100644
index 0000000..7711571
--- /dev/null
+++ b/pub/protobuf/.travis.yml
@@ -0,0 +1,27 @@
+language: dart
+sudo: false
+
+dart:
+- dev
+- stable
+
+dart_task:
+- test: --platform vm
+- test: --platform firefox
+
+# Only run one instance of the formatter and the analyzer, rather than running
+# them against each Dart version.
+matrix:
+ include:
+ - dart: stable
+ dart_task: dartfmt
+ - dart: dev
+ dart_task: dartanalyzer
+
+# Only building master means that we don't run two builds for each pull request.
+branches:
+ only: [master]
+
+cache:
+ directories:
+ - $HOME/.pub-cache
diff --git a/pub/protobuf/BUILD.gn b/pub/protobuf/BUILD.gn
index 0f18689..48e503d 100644
--- a/pub/protobuf/BUILD.gn
+++ b/pub/protobuf/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for protobuf-0.5.4
+# This file is generated by importer.py for protobuf-0.5.5
import("//build/dart/dart_package.gni")
diff --git a/pub/protobuf/CHANGELOG.md b/pub/protobuf/CHANGELOG.md
index 115fc77..ff768bf 100644
--- a/pub/protobuf/CHANGELOG.md
+++ b/pub/protobuf/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.5
+
+* Use real generic syntax instead of comment-based.
+* Support v2 dev SDKs.
+
## 0.5.4
* Unknown enum values are ignored when parsing JSON, instead of throwing an
diff --git a/pub/protobuf/README.md b/pub/protobuf/README.md
index b733fac..5f96440 100644
--- a/pub/protobuf/README.md
+++ b/pub/protobuf/README.md
@@ -1,3 +1,6 @@
+[![Build Status](https://travis-ci.org/dart-lang/protobuf.svg?branch=master)](https://travis-ci.org/dart-lang/protobuf)
+[![pub package](https://img.shields.io/pub/v/protobuf.svg)](https://pub.dartlang.org/packages/protobuf)
+
Provides runtime support for a Dart implementation of protobufs.
Typically one do not need to import this library–only libraries
@@ -8,5 +11,5 @@
### References
-* [protobuf site](https://code.google.com/p/protobuf)
+* [protobuf site](https://github.com/google/protobuf)
* [Protoc plugin project](https://github.com/dart-lang/dart-protoc-plugin)
diff --git a/pub/protobuf/.analysis_options b/pub/protobuf/analysis_options.yaml
similarity index 100%
rename from pub/protobuf/.analysis_options
rename to pub/protobuf/analysis_options.yaml
diff --git a/pub/protobuf/lib/src/protobuf/builder_info.dart b/pub/protobuf/lib/src/protobuf/builder_info.dart
index 8040e0a..f04eee0 100644
--- a/pub/protobuf/lib/src/protobuf/builder_info.dart
+++ b/pub/protobuf/lib/src/protobuf/builder_info.dart
@@ -4,9 +4,7 @@
part of protobuf;
-/**
- * Per-message type setup.
- */
+/// Per-message type setup.
class BuilderInfo {
final String messageName;
final Map<int, FieldInfo> fieldInfo = new Map<int, FieldInfo>();
@@ -18,22 +16,17 @@
BuilderInfo(this.messageName);
- void add/*<T>*/(
- int tagNumber,
- String name,
- int fieldType,
- dynamic defaultOrMaker,
- CreateBuilderFunc subBuilder,
- ValueOfFunc valueOf) {
+ void add<T>(int tagNumber, String name, int fieldType, dynamic defaultOrMaker,
+ CreateBuilderFunc subBuilder, ValueOfFunc valueOf) {
var index = fieldInfo.length;
- addField(new FieldInfo/*<T>*/(name, tagNumber, index, fieldType,
- defaultOrMaker, subBuilder, valueOf));
+ addField(new FieldInfo<T>(name, tagNumber, index, fieldType, defaultOrMaker,
+ subBuilder, valueOf));
}
- void addRepeated/*<T>*/(int tagNumber, String name, int fieldType,
+ void addRepeated<T>(int tagNumber, String name, int fieldType,
CheckFunc check, CreateBuilderFunc subBuilder, ValueOfFunc valueOf) {
var index = fieldInfo.length;
- addField(new FieldInfo/*<T>*/ .repeated(
+ addField(new FieldInfo<T>.repeated(
name, tagNumber, index, fieldType, check, subBuilder, valueOf));
}
@@ -43,39 +36,39 @@
byName[fi.name] = fi;
}
- void a/*<T>*/(int tagNumber, String name, int fieldType,
+ void a<T>(int tagNumber, String name, int fieldType,
[dynamic defaultOrMaker,
CreateBuilderFunc subBuilder,
ValueOfFunc valueOf]) {
- add/*<T>*/(tagNumber, name, fieldType, defaultOrMaker, subBuilder, valueOf);
+ add<T>(tagNumber, name, fieldType, defaultOrMaker, subBuilder, valueOf);
}
// Enum.
- void e/*<T>*/(int tagNumber, String name, int fieldType,
- dynamic defaultOrMaker, ValueOfFunc valueOf) {
- add/*<T>*/(tagNumber, name, fieldType, defaultOrMaker, null, valueOf);
+ void e<T>(int tagNumber, String name, int fieldType, dynamic defaultOrMaker,
+ ValueOfFunc valueOf) {
+ add<T>(tagNumber, name, fieldType, defaultOrMaker, null, valueOf);
}
// Repeated message.
// TODO(skybrian): migrate to pp() and remove.
- void m/*<T>*/(int tagNumber, String name, CreateBuilderFunc subBuilder,
+ void m<T>(int tagNumber, String name, CreateBuilderFunc subBuilder,
MakeDefaultFunc makeDefault) {
- add/*<T>*/(tagNumber, name, PbFieldType._REPEATED_MESSAGE, makeDefault,
+ add<T>(tagNumber, name, PbFieldType._REPEATED_MESSAGE, makeDefault,
subBuilder, null);
}
// Repeated, not a message, group, or enum.
- void p/*<T>*/(int tagNumber, String name, int fieldType) {
+ void p<T>(int tagNumber, String name, int fieldType) {
assert(!_isGroupOrMessage(fieldType) && !_isEnum(fieldType));
- addRepeated/*<T>*/(
+ addRepeated<T>(
tagNumber, name, fieldType, getCheckFunction(fieldType), null, null);
}
// Repeated message, group, or enum.
- void pp/*<T>*/(int tagNumber, String name, int fieldType, CheckFunc check,
+ void pp<T>(int tagNumber, String name, int fieldType, CheckFunc check,
[CreateBuilderFunc subBuilder, ValueOfFunc valueOf]) {
assert(_isGroupOrMessage(fieldType) || _isEnum(fieldType));
- addRepeated/*<T>*/(tagNumber, name, fieldType, check, subBuilder, valueOf);
+ addRepeated<T>(tagNumber, name, fieldType, check, subBuilder, valueOf);
}
bool containsTagNumber(int tagNumber) => fieldInfo.containsKey(tagNumber);
diff --git a/pub/protobuf/lib/src/protobuf/coded_buffer_writer.dart b/pub/protobuf/lib/src/protobuf/coded_buffer_writer.dart
index b23aa96..1c38bc0 100644
--- a/pub/protobuf/lib/src/protobuf/coded_buffer_writer.dart
+++ b/pub/protobuf/lib/src/protobuf/coded_buffer_writer.dart
@@ -5,9 +5,9 @@
part of protobuf;
class CodedBufferWriter {
-
final List<TypedData> _output = <TypedData>[];
int _runningSizeInBytes = 0;
+ int get lengthInBytes => _runningSizeInBytes;
static final _WRITE_FUNCTION_MAP = _makeWriteFunctionMap();
@@ -52,8 +52,8 @@
}
makeWriter(convertor) => ((output, value) {
- output.writeRawBytes(convertor(value));
- });
+ output.writeRawBytes(convertor(value));
+ });
int _encodeZigZag32(int value) => (value << 1) ^ (value >> 31);
@@ -68,81 +68,79 @@
}
return new Map<int, dynamic>()
- ..[PbFieldType._BOOL_BIT] = makeWriter(
- (value) => _int32ToBytes(value ? 1 : 0))
- ..[PbFieldType._BYTES_BIT] = writeBytesNoTag
- ..[PbFieldType._STRING_BIT] = (output, value) {
- writeBytesNoTag(output, _UTF8.encode(value));
+ ..[PbFieldType._BOOL_BIT] =
+ makeWriter((value) => _int32ToBytes(value ? 1 : 0))
+ ..[PbFieldType._BYTES_BIT] = writeBytesNoTag
+ ..[PbFieldType._STRING_BIT] = (output, value) {
+ writeBytesNoTag(output, _UTF8.encode(value));
+ }
+ ..[PbFieldType._DOUBLE_BIT] = makeWriter((double value) {
+ if (value.isNaN)
+ return new ByteData(8)
+ ..setUint32(0, 0x00000000, Endianness.LITTLE_ENDIAN)
+ ..setUint32(4, 0x7ff80000, Endianness.LITTLE_ENDIAN);
+ return new ByteData(8)..setFloat64(0, value, Endianness.LITTLE_ENDIAN);
+ })
+ ..[PbFieldType._FLOAT_BIT] = makeWriter((double value) {
+ const double MIN_FLOAT_DENORM = 1.401298464324817E-45;
+ const double MAX_FLOAT = 3.4028234663852886E38;
+ // TODO(antonm): reevaluate once semantics of odd values
+ // writes is clear.
+ if (value.isNaN) return makeByteData32(0x7fc00000);
+ if (value.abs() < MIN_FLOAT_DENORM) {
+ return makeByteData32(value.isNegative ? 0x80000000 : 0x00000000);
}
- ..[PbFieldType._DOUBLE_BIT] = makeWriter((double value) {
- if (value.isNaN) return new ByteData(8)
- ..setUint32(0, 0x00000000, Endianness.LITTLE_ENDIAN)
- ..setUint32(4, 0x7ff80000, Endianness.LITTLE_ENDIAN);
- return new ByteData(8)
- ..setFloat64(0, value, Endianness.LITTLE_ENDIAN);
- })
- ..[PbFieldType._FLOAT_BIT] = makeWriter((double value) {
- const double MIN_FLOAT_DENORM = 1.401298464324817E-45;
- const double MAX_FLOAT = 3.4028234663852886E38;
- // TODO(antonm): reevaluate once semantics of odd values
- // writes is clear.
- if (value.isNaN) return makeByteData32(0x7fc00000);
- if (value.abs() < MIN_FLOAT_DENORM) {
- return makeByteData32(value.isNegative ? 0x80000000 : 0x00000000);
- }
- if (value.isInfinite || value.abs() > MAX_FLOAT) {
- return makeByteData32(value.isNegative ? 0xff800000 : 0x7f800000);
- }
- return new ByteData(4)
- ..setFloat32(0, value, Endianness.LITTLE_ENDIAN);
- })
- ..[PbFieldType._ENUM_BIT] = makeWriter(
- (value) => _int32ToBytes(value.value))
- ..[PbFieldType._GROUP_BIT] = (output, value) {
- value.writeToCodedBufferWriter(output);
+ if (value.isInfinite || value.abs() > MAX_FLOAT) {
+ return makeByteData32(value.isNegative ? 0xff800000 : 0x7f800000);
}
- ..[PbFieldType._INT32_BIT] = makeWriter(_int32ToBytes)
- ..[PbFieldType._INT64_BIT] = makeWriter(
- (value) => _toVarint64(value))
- ..[PbFieldType._SINT32_BIT] = makeWriter(
- (int value) => _int32ToBytes(_encodeZigZag32(value)))
- ..[PbFieldType._SINT64_BIT] = makeWriter(
- (Int64 value) => _toVarint64(_encodeZigZag64(value)))
- ..[PbFieldType._UINT32_BIT] = makeWriter(_toVarint32)
- ..[PbFieldType._UINT64_BIT] = makeWriter(_toVarint64)
- ..[PbFieldType._FIXED32_BIT] = makeWriter(makeByteData32)
- ..[PbFieldType._FIXED64_BIT] = makeWriter(makeByteData64)
- ..[PbFieldType._SFIXED32_BIT] = makeWriter(makeByteData32)
- ..[PbFieldType._SFIXED64_BIT] = makeWriter(makeByteData64)
- ..[PbFieldType._MESSAGE_BIT] = (output, value) {
- output._withDeferredSizeCalculation(() {
- value.writeToCodedBufferWriter(output);
- });
- };
+ return new ByteData(4)..setFloat32(0, value, Endianness.LITTLE_ENDIAN);
+ })
+ ..[PbFieldType._ENUM_BIT] =
+ makeWriter((value) => _int32ToBytes(value.value))
+ ..[PbFieldType._GROUP_BIT] = (output, value) {
+ value.writeToCodedBufferWriter(output);
+ }
+ ..[PbFieldType._INT32_BIT] = makeWriter(_int32ToBytes)
+ ..[PbFieldType._INT64_BIT] = makeWriter((value) => _toVarint64(value))
+ ..[PbFieldType._SINT32_BIT] =
+ makeWriter((int value) => _int32ToBytes(_encodeZigZag32(value)))
+ ..[PbFieldType._SINT64_BIT] =
+ makeWriter((Int64 value) => _toVarint64(_encodeZigZag64(value)))
+ ..[PbFieldType._UINT32_BIT] = makeWriter(_toVarint32)
+ ..[PbFieldType._UINT64_BIT] = makeWriter(_toVarint64)
+ ..[PbFieldType._FIXED32_BIT] = makeWriter(makeByteData32)
+ ..[PbFieldType._FIXED64_BIT] = makeWriter(makeByteData64)
+ ..[PbFieldType._SFIXED32_BIT] = makeWriter(makeByteData32)
+ ..[PbFieldType._SFIXED64_BIT] = makeWriter(makeByteData64)
+ ..[PbFieldType._MESSAGE_BIT] = (output, value) {
+ output._withDeferredSizeCalculation(() {
+ value.writeToCodedBufferWriter(output);
+ });
+ };
}
static final _OPEN_TAG_MAP = _makeOpenTagMap();
static _makeOpenTagMap() {
return new Map<int, int>()
- ..[PbFieldType._BOOL_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._BYTES_BIT] = WIRETYPE_LENGTH_DELIMITED
- ..[PbFieldType._STRING_BIT] = WIRETYPE_LENGTH_DELIMITED
- ..[PbFieldType._DOUBLE_BIT] = WIRETYPE_FIXED64
- ..[PbFieldType._FLOAT_BIT] = WIRETYPE_FIXED32
- ..[PbFieldType._ENUM_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._GROUP_BIT] = WIRETYPE_START_GROUP
- ..[PbFieldType._INT32_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._INT64_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._SINT32_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._SINT64_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._UINT32_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._UINT64_BIT] = WIRETYPE_VARINT
- ..[PbFieldType._FIXED32_BIT] = WIRETYPE_FIXED32
- ..[PbFieldType._FIXED64_BIT] = WIRETYPE_FIXED64
- ..[PbFieldType._SFIXED32_BIT] = WIRETYPE_FIXED32
- ..[PbFieldType._SFIXED64_BIT] = WIRETYPE_FIXED64
- ..[PbFieldType._MESSAGE_BIT] = WIRETYPE_LENGTH_DELIMITED;
+ ..[PbFieldType._BOOL_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._BYTES_BIT] = WIRETYPE_LENGTH_DELIMITED
+ ..[PbFieldType._STRING_BIT] = WIRETYPE_LENGTH_DELIMITED
+ ..[PbFieldType._DOUBLE_BIT] = WIRETYPE_FIXED64
+ ..[PbFieldType._FLOAT_BIT] = WIRETYPE_FIXED32
+ ..[PbFieldType._ENUM_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._GROUP_BIT] = WIRETYPE_START_GROUP
+ ..[PbFieldType._INT32_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._INT64_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._SINT32_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._SINT64_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._UINT32_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._UINT64_BIT] = WIRETYPE_VARINT
+ ..[PbFieldType._FIXED32_BIT] = WIRETYPE_FIXED32
+ ..[PbFieldType._FIXED64_BIT] = WIRETYPE_FIXED64
+ ..[PbFieldType._SFIXED32_BIT] = WIRETYPE_FIXED32
+ ..[PbFieldType._SFIXED64_BIT] = WIRETYPE_FIXED64
+ ..[PbFieldType._MESSAGE_BIT] = WIRETYPE_LENGTH_DELIMITED;
}
void _withDeferredSizeCalculation(continuation) {
@@ -204,13 +202,23 @@
Uint8List toBuffer() {
Uint8List result = new Uint8List(_runningSizeInBytes);
- int position = 0;
+ writeTo(result);
+ return result;
+ }
+
+ /// Serializes everything written to this writer so far to [buffer], starting
+ /// from [offset] in [buffer]. Returns `true` on success.
+ bool writeTo(List<int> buffer, [int offset = 0]) {
+ if (buffer.length - offset < _runningSizeInBytes) {
+ return false;
+ }
+ int position = offset;
for (var typedData in _output) {
Uint8List asBytes = new Uint8List.view(
typedData.buffer, typedData.offsetInBytes, typedData.lengthInBytes);
- result.setRange(position, position + typedData.lengthInBytes, asBytes);
+ buffer.setRange(position, position + typedData.lengthInBytes, asBytes);
position += typedData.lengthInBytes;
}
- return result;
+ return true;
}
}
diff --git a/pub/protobuf/lib/src/protobuf/event_plugin.dart b/pub/protobuf/lib/src/protobuf/event_plugin.dart
index 29bf05a..cdcf726 100644
--- a/pub/protobuf/lib/src/protobuf/event_plugin.dart
+++ b/pub/protobuf/lib/src/protobuf/event_plugin.dart
@@ -12,7 +12,6 @@
/// to user code, plugins should buffer events and send them asynchronously.
/// (See event_mixin.dart for an example.)
abstract class EventPlugin {
-
/// Initializes the plugin.
///
/// GeneratedMessage calls this once in its constructors.
diff --git a/pub/protobuf/lib/src/protobuf/exceptions.dart b/pub/protobuf/lib/src/protobuf/exceptions.dart
index 5e2edb3..412a8d8 100644
--- a/pub/protobuf/lib/src/protobuf/exceptions.dart
+++ b/pub/protobuf/lib/src/protobuf/exceptions.dart
@@ -11,29 +11,27 @@
String toString() => 'InvalidProtocolBufferException: $message';
- InvalidProtocolBufferException.invalidEndTag() : this._(
- 'Protocol message end-group tag did not match expected tag.');
+ InvalidProtocolBufferException.invalidEndTag()
+ : this._('Protocol message end-group tag did not match expected tag.');
- InvalidProtocolBufferException.invalidTag() : this._(
- 'Protocol message contained an invalid tag (zero).');
+ InvalidProtocolBufferException.invalidTag()
+ : this._('Protocol message contained an invalid tag (zero).');
- InvalidProtocolBufferException.invalidWireType() : this._(
- 'Protocol message tag had invalid wire type.');
+ InvalidProtocolBufferException.invalidWireType()
+ : this._('Protocol message tag had invalid wire type.');
- InvalidProtocolBufferException.malformedVarint() : this._(
- 'CodedBufferReader encountered a malformed varint.');
+ InvalidProtocolBufferException.malformedVarint()
+ : this._('CodedBufferReader encountered a malformed varint.');
InvalidProtocolBufferException.recursionLimitExceeded() : this._('''
Protocol message had too many levels of nesting. May be malicious.
Use CodedBufferReader.setRecursionLimit() to increase the depth limit.
-'''
- );
+''');
InvalidProtocolBufferException.truncatedMessage() : this._('''
While parsing a protocol message, the input ended unexpectedly
in the middle of a field. This could mean either than the
input has been truncated or that an embedded message
misreported its own length.
-'''
- );
+''');
}
diff --git a/pub/protobuf/lib/src/protobuf/extension.dart b/pub/protobuf/lib/src/protobuf/extension.dart
index cfed0a4..0348a87 100644
--- a/pub/protobuf/lib/src/protobuf/extension.dart
+++ b/pub/protobuf/lib/src/protobuf/extension.dart
@@ -4,9 +4,7 @@
part of protobuf;
-/**
- * An object representing an extension field.
- */
+/// An object representing an extension field.
class Extension<T> extends FieldInfo<T> {
final String extendee;
diff --git a/pub/protobuf/lib/src/protobuf/extension_field_set.dart b/pub/protobuf/lib/src/protobuf/extension_field_set.dart
index 8778262..b96e1df 100644
--- a/pub/protobuf/lib/src/protobuf/extension_field_set.dart
+++ b/pub/protobuf/lib/src/protobuf/extension_field_set.dart
@@ -38,12 +38,12 @@
///
/// If it doesn't exist, creates the list and saves the extension.
/// Suitable for public API and decoders.
- List/*<T>*/ _ensureRepeatedField/*<T>*/(Extension/*<T>*/ fi) {
+ List<T> _ensureRepeatedField<T>(Extension<T> fi) {
assert(fi.isRepeated);
assert(fi.extendee == _parent._messageName);
var list = _values[fi.tagNumber];
- if (list != null) return list as List/*<T>*/;
+ if (list != null) return list as List<T>;
// Add info and create list.
_validateInfo(fi);
diff --git a/pub/protobuf/lib/src/protobuf/extension_registry.dart b/pub/protobuf/lib/src/protobuf/extension_registry.dart
index d94225a..667052d 100644
--- a/pub/protobuf/lib/src/protobuf/extension_registry.dart
+++ b/pub/protobuf/lib/src/protobuf/extension_registry.dart
@@ -4,29 +4,23 @@
part of protobuf;
-/**
- * A collection of [Extension] objects, organized by the message type they
- * extend.
- */
+/// A collection of [Extension] objects, organized by the message type they
+/// extend.
class ExtensionRegistry {
final Map<String, Map<int, Extension>> _extensions =
<String, Map<int, Extension>>{};
static const ExtensionRegistry EMPTY = const _EmptyExtensionRegistry();
- /**
- * Store an extension in the registry.
- */
+ /// Stores an [extension] in the registry.
void add(Extension extension) {
var map = _extensions.putIfAbsent(
extension.extendee, () => new Map<int, Extension>());
map[extension.tagNumber] = extension;
}
- /**
- * Retrieve an extension from the registry that adds the given tag
- * number to the given message type.
- */
+ /// Retrieves an extension from the registry that adds tag number [tagNumber]
+ /// to the [messageName] message type.
Extension getExtension(String messageName, int tagNumber) {
var map = _extensions[messageName];
if (map != null) {
@@ -39,7 +33,7 @@
class _EmptyExtensionRegistry implements ExtensionRegistry {
const _EmptyExtensionRegistry();
- // needed to quite missing member warning
+ // Needed to quiet missing member warning.
get _extensions => null;
void add(Extension extension) {
diff --git a/pub/protobuf/lib/src/protobuf/field_error.dart b/pub/protobuf/lib/src/protobuf/field_error.dart
index e4c9a72..100c7d5 100644
--- a/pub/protobuf/lib/src/protobuf/field_error.dart
+++ b/pub/protobuf/lib/src/protobuf/field_error.dart
@@ -185,6 +185,7 @@
bool _isSigned32(int value) => _inRange(-2147483648, value, 2147483647);
bool _isUnsigned32(int value) => _inRange(0, value, 4294967295);
-bool _isFloat32(double value) => value.isNaN ||
+bool _isFloat32(double value) =>
+ value.isNaN ||
value.isInfinite ||
_inRange(-3.4028234663852886E38, value, 3.4028234663852886E38);
diff --git a/pub/protobuf/lib/src/protobuf/field_info.dart b/pub/protobuf/lib/src/protobuf/field_info.dart
index 0c176eb..8e8d08a 100644
--- a/pub/protobuf/lib/src/protobuf/field_info.dart
+++ b/pub/protobuf/lib/src/protobuf/field_info.dart
@@ -4,9 +4,7 @@
part of protobuf;
-/**
- * An object representing a protobuf message field.
- */
+/// An object representing a protobuf message field.
class FieldInfo<T> {
final String name;
final int tagNumber;
@@ -127,7 +125,7 @@
/// be overridden by a mixin.
List<T> _createRepeatedField(GeneratedMessage m) {
assert(isRepeated);
- return m.createRepeatedField/*<T>*/(tagNumber, this);
+ return m.createRepeatedField<T>(tagNumber, this);
}
String toString() => name;
diff --git a/pub/protobuf/lib/src/protobuf/field_set.dart b/pub/protobuf/lib/src/protobuf/field_set.dart
index d45d682..15e6a25 100644
--- a/pub/protobuf/lib/src/protobuf/field_set.dart
+++ b/pub/protobuf/lib/src/protobuf/field_set.dart
@@ -210,14 +210,14 @@
/// Creates and stores the repeated field if it doesn't exist.
/// If it's an extension and the list doesn't exist, validates and stores it.
/// Suitable for decoders.
- List/*<T>*/ _ensureRepeatedField/*<T>*/(FieldInfo/*<T>*/ fi) {
+ List<T> _ensureRepeatedField<T>(FieldInfo<T> fi) {
assert(!_isReadOnly);
assert(fi.isRepeated);
if (fi.index == null) {
return _ensureExtensions()._ensureRepeatedField(fi);
}
var value = _getFieldOrNull(fi);
- if (value != null) return value as List/*<T>*/;
+ if (value != null) return value as List<T>;
var newValue = fi._createRepeatedField(_message);
_setNonExtensionFieldUnchecked(fi, newValue);
@@ -235,12 +235,12 @@
// Generated method implementations
/// The implementation of a generated getter.
- /*=T*/ _$get/*<T>*/(int index, int tagNumber, /*=T*/ defaultValue) {
+ T _$get<T>(int index, int tagNumber, T defaultValue) {
assert(_nonExtensionInfo(tagNumber).index == index);
var value = _values[index];
- if (value != null) return value as dynamic/*=T*/;
+ if (value != null) return value as T;
if (defaultValue != null) return defaultValue;
- return _getDefault(_nonExtensionInfo(tagNumber)) as dynamic/*=T*/;
+ return _getDefault(_nonExtensionInfo(tagNumber)) as T;
}
/// The implementation of a generated has method.
@@ -448,7 +448,6 @@
/// in this message. Repeated fields are appended. Singular sub-messages are
/// recursively merged.
void _mergeFromMessage(_FieldSet other) {
-
// TODO(https://github.com/dart-lang/protobuf/issues/60): Recognize
// when [this] and [other] are the same protobuf (e.g. from cloning). In
// this case, we can merge the non-extension fields without field lookups or
diff --git a/pub/protobuf/lib/src/protobuf/generated_message.dart b/pub/protobuf/lib/src/protobuf/generated_message.dart
index e29bf28..e4657e5 100644
--- a/pub/protobuf/lib/src/protobuf/generated_message.dart
+++ b/pub/protobuf/lib/src/protobuf/generated_message.dart
@@ -221,8 +221,8 @@
/// that the protobuf can be encoded correctly, the returned List must
/// validate all items added to it. This can most easily be done
/// using the FieldInfo.check function.
- List/*<T>*/ createRepeatedField/*<T>*/(int tagNumber, FieldInfo/*<T>*/ fi) {
- return new PbList/*<T>*/(check: fi.check);
+ List<T> createRepeatedField<T>(int tagNumber, FieldInfo<T> fi) {
+ return new PbList<T>(check: fi.check);
}
/// Returns the value of a field, ignoring any defaults.
@@ -279,8 +279,8 @@
void setField(int tagNumber, value) => _fieldSet._setField(tagNumber, value);
/// For generated code only.
- /*=T*/ $_get/*<T>*/(int index, int tagNumber, /*=T*/ defaultValue) =>
- _fieldSet._$get/*<T>*/(index, tagNumber, defaultValue);
+ T $_get<T>(int index, int tagNumber, T defaultValue) =>
+ _fieldSet._$get<T>(index, tagNumber, defaultValue);
/// For generated code only.
bool $_has(int index, int tagNumber) => _fieldSet._$has(index, tagNumber);
diff --git a/pub/protobuf/lib/src/protobuf/pb_list.dart b/pub/protobuf/lib/src/protobuf/pb_list.dart
index b82b363..b4c8a9c 100644
--- a/pub/protobuf/lib/src/protobuf/pb_list.dart
+++ b/pub/protobuf/lib/src/protobuf/pb_list.dart
@@ -37,46 +37,34 @@
return hash;
}
- /**
- * Returns an [Iterator] for the list.
- */
+ /// Returns an [Iterator] for the list.
Iterator<E> get iterator => _wrappedList.iterator;
- /**
- * Returns a new lazy [Iterable] with elements that are created by calling `f`
- * on each element of this `PbList` in iteration order.
- */
- Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e)) => _wrappedList.map/*<T>*/(f);
+ /// Returns a new lazy [Iterable] with elements that are created by calling
+ /// `f` on each element of this `PbList` in iteration order.
+ Iterable<T> map<T>(T f(E e)) => _wrappedList.map<T>(f);
- /**
- * Applies the function [f] to each element of this list in iteration order.
- */
+ /// Applies the function [f] to each element of this list in iteration order.
void forEach(void f(E element)) {
_wrappedList.forEach(f);
}
- /**
- * Returns the element at the given [index] in the list or throws
- * an [IndexOutOfRangeException] if [index] is out of bounds.
- */
+ /// Returns the element at the given [index] in the list or throws an
+ /// [IndexOutOfRangeException] if [index] is out of bounds.
E operator [](int index) => _wrappedList[index];
- /**
- * Sets the entry at the given [index] in the list to [value].
- * Throws an [IndexOutOfRangeException] if [index] is out of bounds.
- */
+ /// Sets the entry at the given [index] in the list to [value].
+ /// Throws an [IndexOutOfRangeException] if [index] is out of bounds.
void operator []=(int index, E value) {
_validate(value);
_wrappedList[index] = value;
}
- /**
- * Unsupported -- violated non-null constraint imposed by protobufs.
- *
- * Changes the length of the list. If [newLength] is greater than
- * the current [length], entries are initialized to [:null:]. Throws
- * an [UnsupportedError] if the list is not extendable.
- */
+ /// Unsupported -- violated non-null constraint imposed by protobufs.
+ ///
+ /// Changes the length of the list. If [newLength] is greater than the current
+ /// [length], entries are initialized to [:null:]. Throws an
+ /// [UnsupportedError] if the list is not extendable.
void set length(int newLength) {
if (newLength > length) {
throw new ArgumentError('Extending protobuf lists is not supported');
@@ -84,33 +72,24 @@
_wrappedList.length = newLength;
}
- /**
- * Adds [value] at the end of the list, extending the length by
- * one. Throws an [UnsupportedError] if the list is not
- * extendable.
- */
+ /// Adds [value] at the end of the list, extending the length by one.
+ /// Throws an [UnsupportedError] if the list is not extendable.
void add(E value) {
_validate(value);
_wrappedList.add(value);
}
- /**
- * Appends all elements of the [collection] to the end of list.
- * Extends the length of the list by the length of [collection].
- * Throws an [UnsupportedError] if the list is not
- * extendable.
- */
+ /// Appends all elements of the [collection] to the end of list.
+ /// Extends the length of the list by the length of [collection].
+ /// Throws an [UnsupportedError] if the list is not extendable.
void addAll(Iterable<E> collection) {
collection.forEach(_validate);
_wrappedList.addAll(collection);
}
- /**
- * Copies [:end - start:] elements of the [from] array, starting
- * from [skipCount], into [:this:], starting at [start].
- * Throws an [UnsupportedError] if the list is
- * not extendable.
- */
+ /// Copies [:end - start:] elements of the [from] array, starting from
+ /// [skipCount], into [:this:], starting at [start].
+ /// Throws an [UnsupportedError] if the list is not extendable.
void setRange(int start, int end, Iterable<E> from, [int skipCount = 0]) {
// NOTE: In case `take()` returns less than `end - start` elements, the
// _wrappedList will fail with a `StateError`.
@@ -118,39 +97,31 @@
_wrappedList.setRange(start, end, from, skipCount);
}
- /**
- * Inserts a new element in the list.
- * The element must be valid (and not nullable) for the PbList type.
- */
+ /// Inserts a new element in the list.
+ /// The element must be valid (and not nullable) for the PbList type.
void insert(int index, E element) {
_validate(element);
_wrappedList.insert(index, element);
}
- /**
- * Inserts all elements of [iterable] at position [index] in the list.
- *
- * Elements in [iterable] must be valid and not nullable for the PbList type.
- */
+ /// Inserts all elements of [iterable] at position [index] in the list.
+ ///
+ /// Elements in [iterable] must be valid and not nullable for the PbList type.
void insertAll(int index, Iterable<E> iterable) {
iterable.forEach(_validate);
_wrappedList.insertAll(index, iterable);
}
- /**
- * Overwrites elements of `this` with elements of [iterable] starting at
- * position [index] in the list.
- *
- * Elements in [iterable] must be valid and not nullable for the PbList type.
- */
+ /// Overwrites elements of `this` with elements of [iterable] starting at
+ /// position [index] in the list.
+ ///
+ /// Elements in [iterable] must be valid and not nullable for the PbList type.
void setAll(int index, Iterable<E> iterable) {
iterable.forEach(_validate);
_wrappedList.setAll(index, iterable);
}
- /**
- * Returns the number of elements in this collection.
- */
+ /// Returns the number of elements in this collection.
int get length => _wrappedList.length;
void _validate(E val) {
diff --git a/pub/protobuf/lib/src/protobuf/readonly_message.dart b/pub/protobuf/lib/src/protobuf/readonly_message.dart
index 57cf769..b8f03e0 100644
--- a/pub/protobuf/lib/src/protobuf/readonly_message.dart
+++ b/pub/protobuf/lib/src/protobuf/readonly_message.dart
@@ -17,7 +17,7 @@
void clearField(int tagNumber) => _readonly("clearField");
- List/*<T>*/ createRepeatedField/*<T>*/(int tagNumber, FieldInfo/*<T>*/ fi) {
+ List<T> createRepeatedField<T>(int tagNumber, FieldInfo<T> fi) {
_readonly("createRepeatedField");
return null; // not reached
}
diff --git a/pub/protobuf/lib/src/protobuf/rpc_client.dart b/pub/protobuf/lib/src/protobuf/rpc_client.dart
index cc6c3ee..78914e2 100644
--- a/pub/protobuf/lib/src/protobuf/rpc_client.dart
+++ b/pub/protobuf/lib/src/protobuf/rpc_client.dart
@@ -19,13 +19,15 @@
/// The protoc plugin generates a client-side stub for each service that
/// takes an RpcClient as a constructor parameter.
abstract class RpcClient {
-
/// Sends a request to a server and returns the reply.
///
/// The implementation should serialize the request as binary or JSON, as
/// appropriate. It should merge the reply into [emptyResponse] and
/// return it.
Future<GeneratedMessage> invoke(
- ClientContext ctx, String serviceName, String methodName,
- GeneratedMessage request, GeneratedMessage emptyResponse);
+ ClientContext ctx,
+ String serviceName,
+ String methodName,
+ GeneratedMessage request,
+ GeneratedMessage emptyResponse);
}
diff --git a/pub/protobuf/lib/src/protobuf/unknown_field_set.dart b/pub/protobuf/lib/src/protobuf/unknown_field_set.dart
index ef4d99b..d304d9c 100644
--- a/pub/protobuf/lib/src/protobuf/unknown_field_set.dart
+++ b/pub/protobuf/lib/src/protobuf/unknown_field_set.dart
@@ -5,7 +5,6 @@
part of protobuf;
class UnknownFieldSet {
-
final Map<int, UnknownFieldSetField> _fields =
new Map<int, UnknownFieldSetField>();
@@ -15,12 +14,12 @@
mergeFromUnknownFieldSet(unknownFieldSet);
}
- UnknownFieldSet clone() => new UnknownFieldSet._clone(this);
+ UnknownFieldSet clone() => new UnknownFieldSet._clone(this);
- bool get isEmpty => _fields.isEmpty;
- bool get isNotEmpty => _fields.isNotEmpty;
+ bool get isEmpty => _fields.isEmpty;
+ bool get isNotEmpty => _fields.isNotEmpty;
- Map<int, UnknownFieldSetField> asMap() => new Map.from(_fields);
+ Map<int, UnknownFieldSetField> asMap() => new Map.from(_fields);
void clear() {
_fields.clear();
@@ -142,12 +141,12 @@
for (var value in field.values) {
if (value is UnknownFieldSet) {
stringBuffer
- ..write('${indent}${tag}: {\n')
- ..write(value._toString('$indent '))
- ..write('${indent}}\n');
+ ..write('${indent}${tag}: {\n')
+ ..write(value._toString('$indent '))
+ ..write('${indent}}\n');
} else {
if (value is ByteData) {
- // TODO(antonm): fix for longs.
+ // TODO(antonm): fix for longs.
value = value.getUint64(0, Endianness.LITTLE_ENDIAN);
}
stringBuffer.write('${indent}${tag}: ${value}\n');
@@ -166,7 +165,6 @@
}
class UnknownFieldSetField {
-
final List<List<int>> lengthDelimited = <List<int>>[];
final List<Int64> varints = <Int64>[];
final List<int> fixed32s = <int>[];
@@ -219,11 +217,11 @@
}
List get values => []
- ..addAll(lengthDelimited)
- ..addAll(varints)
- ..addAll(fixed32s)
- ..addAll(fixed64s)
- ..addAll(groups);
+ ..addAll(lengthDelimited)
+ ..addAll(varints)
+ ..addAll(fixed32s)
+ ..addAll(fixed64s)
+ ..addAll(groups);
void writeTo(int fieldNumber, CodedBufferWriter output) {
write(type, value) {
diff --git a/pub/protobuf/lib/src/protobuf/utils.dart b/pub/protobuf/lib/src/protobuf/utils.dart
index a9993e3..fb6ee44 100644
--- a/pub/protobuf/lib/src/protobuf/utils.dart
+++ b/pub/protobuf/lib/src/protobuf/utils.dart
@@ -35,4 +35,4 @@
return _areListsEqual(asBytes(lhs), asBytes(rhs));
}
-List/*<T>*/ sorted/*<T>*/(Iterable/*<T>*/ list) => new List.from(list)..sort();
+List<T> sorted<T>(Iterable<T> list) => new List.from(list)..sort();
diff --git a/pub/protobuf/pubspec.yaml b/pub/protobuf/pubspec.yaml
index 458317b..48ae1b4 100644
--- a/pub/protobuf/pubspec.yaml
+++ b/pub/protobuf/pubspec.yaml
@@ -1,10 +1,10 @@
name: protobuf
-version: 0.5.4
+version: 0.5.5
author: Dart Team <misc@dartlang.org>
description: Runtime library for protocol buffers support.
homepage: https://github.com/dart-lang/protobuf
environment:
- sdk: '>=1.13.0 <2.0.0'
+ sdk: '>=1.21.0 <2.0.0-dev.infinity'
dependencies:
fixnum: '>=0.9.0 <0.11.0'
dev_dependencies:
diff --git a/pub/shelf_packages_handler/BUILD.gn b/pub/shelf_packages_handler/BUILD.gn
index 877b1ab..9ad7302 100644
--- a/pub/shelf_packages_handler/BUILD.gn
+++ b/pub/shelf_packages_handler/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for shelf_packages_handler-1.0.0
+# This file is generated by importer.py for shelf_packages_handler-1.0.1
import("//build/dart/dart_package.gni")
diff --git a/pub/shelf_packages_handler/CHANGELOG.md b/pub/shelf_packages_handler/CHANGELOG.md
index 2a2d63c..f630263 100644
--- a/pub/shelf_packages_handler/CHANGELOG.md
+++ b/pub/shelf_packages_handler/CHANGELOG.md
@@ -1,4 +1,6 @@
-# Changelog
+## 1.0.1
+
+- Allow dependencies on `package:shelf` v0.7.x
## 0.0.1
diff --git a/pub/shelf_packages_handler/pubspec.yaml b/pub/shelf_packages_handler/pubspec.yaml
index 323adb2..40cc176 100644
--- a/pub/shelf_packages_handler/pubspec.yaml
+++ b/pub/shelf_packages_handler/pubspec.yaml
@@ -1,5 +1,5 @@
name: shelf_packages_handler
-version: 1.0.0
+version: 1.0.1
description: A shelf handler for serving a `packages/` directory.
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/shelf_packages_handler
@@ -10,7 +10,7 @@
dependencies:
async: '^1.1.0'
path: '^1.0.0'
- shelf: '^0.6.0'
+ shelf: '>=0.6.0 <0.8.0'
shelf_static: '^0.2.0'
package_resolver: '^1.0.0'
diff --git a/pub/url_launcher/BUILD.gn b/pub/url_launcher/BUILD.gn
index 7009f97..bead474 100644
--- a/pub/url_launcher/BUILD.gn
+++ b/pub/url_launcher/BUILD.gn
@@ -1,4 +1,4 @@
-# This file is generated by importer.py for url_launcher-0.4.2+1
+# This file is generated by importer.py for url_launcher-0.4.2+4
import("//build/dart/dart_package.gni")
diff --git a/pub/url_launcher/CHANGELOG.md b/pub/url_launcher/CHANGELOG.md
index 9778ae7..b63eb3c 100644
--- a/pub/url_launcher/CHANGELOG.md
+++ b/pub/url_launcher/CHANGELOG.md
@@ -1,3 +1,7 @@
+## [0.4.2+2], [0.4.2+3], [0.4.2+4] - 2017-08-16
+
+* Updated README
+
## [0.4.2+1] - 2017-05-16
* Updated README
diff --git a/pub/url_launcher/README.md b/pub/url_launcher/README.md
index 4650b7a..2ff08c2 100644
--- a/pub/url_launcher/README.md
+++ b/pub/url_launcher/README.md
@@ -37,18 +37,33 @@
## Supported URL schemes
-The `launch` method takes a string argument containing a URL. This URL
+The [`launch`](https://www.dartdocs.org/documentation/url_launcher/latest/url_launcher/launch.html) method
+takes a string argument containing a URL. This URL
can be formatted using a number of different URL schemes. The supported
URL schemes depend on the underlying platform and installed apps.
Common schemes supported by both iOS and Android:
-| Scheme | Example | Action |
-|---|---|---|
-| `http:<URL>` , `https:<URL>` | `http://flutter.io` | Open URL in the default browser |
-| `mailto:<email address>` | `mailto:smith@example.org` | Open <email address> in the default email app |
-| `tel:<phone number>` | `tel:+1 555 010 999` | Make a phone call to <phone number> using the default phone app |
-| `sms:<phone number>` | `sms:5550101234` | Send an SMS message to <phone number> using the default messaging app |
+| Scheme | Action |
+|---|---|
+| `http:<URL>` , `https:<URL>`, e.g. `http://flutter.io` | Open URL in the default browser |
+| `mailto:<email address>?subject=<subject>&body=<body>`, e.g. `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to <email address> in the default email app |
+| `tel:<phone number>`, e.g. `tel:+1 555 010 999` | Make a phone call to <phone number> using the default phone app |
+| `sms:<phone number>`, e.g. `sms:5550101234` | Send an SMS message to <phone number> using the default messaging app |
More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) and [Android](https://developer.android.com/guide/components/intents-common.html)
+## Handling missing URL receivers
+
+A particular mobile device may not be able to receive all supported URL schemes.
+For example, a tablet may not have a cellular radio and thus no support for
+launching a URL using the `sms` scheme, or a device may not have an email app
+and thus no support for launching a URL using the `email` scheme.
+
+We recommend checking which URL schemes are supported using the
+[`canLaunch`](https://www.dartdocs.org/documentation/url_launcher/latest/url_launcher/canLaunch.html)
+method prior to calling `launch`. If the `canLaunch` method returns false, as a
+best practice we suggest adjusting the application UI so that the unsupported
+URL is never triggered; for example, if the `email` scheme is not supported, a
+UI button that would have sent email can be changed to redirect the user to a
+web page using a URL following the `http` scheme.
diff --git a/pub/url_launcher/android/build.gradle b/pub/url_launcher/android/build.gradle
index ba8285e..a8ea4ca 100644
--- a/pub/url_launcher/android/build.gradle
+++ b/pub/url_launcher/android/build.gradle
@@ -1,4 +1,4 @@
-group 'io.flutter.plugins.url_launcher'
+group 'io.flutter.plugins.urllauncher'
version '1.0-SNAPSHOT'
buildscript {
@@ -11,7 +11,7 @@
}
}
-allprojects {
+rootProject.allprojects {
repositories {
jcenter()
}
diff --git a/pub/url_launcher/android/src/main/AndroidManifest.xml b/pub/url_launcher/android/src/main/AndroidManifest.xml
index 415d4e9..8cbe934 100644
--- a/pub/url_launcher/android/src/main/AndroidManifest.xml
+++ b/pub/url_launcher/android/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="io.flutter.plugins.url_launcher"
+ package="io.flutter.plugins.urllauncher"
android:versionCode="1"
android:versionName="0.0.1">
diff --git a/pub/url_launcher/android/src/main/java/io/flutter/plugins/url_launcher/UrlLauncherPlugin.java b/pub/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java
similarity index 97%
rename from pub/url_launcher/android/src/main/java/io/flutter/plugins/url_launcher/UrlLauncherPlugin.java
rename to pub/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java
index 4be1abb..8e5b27e 100644
--- a/pub/url_launcher/android/src/main/java/io/flutter/plugins/url_launcher/UrlLauncherPlugin.java
+++ b/pub/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-package io.flutter.plugins.url_launcher;
+package io.flutter.plugins.urllauncher;
import android.app.Activity;
import android.content.ComponentName;
diff --git a/pub/url_launcher/example/android/app/src/main/AndroidManifest.xml b/pub/url_launcher/example/android/app/src/main/AndroidManifest.xml
index e62ce9d..24c3ed6 100644
--- a/pub/url_launcher/example/android/app/src/main/AndroidManifest.xml
+++ b/pub/url_launcher/example/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="io.flutter.plugins.url_launcher_example"
+ package="io.flutter.plugins.urllauncherexample"
android:versionCode="1"
android:versionName="0.0.1">
@@ -17,7 +17,7 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application android:name="io.flutter.app.FlutterApplication" android:label="url_launcher_example" android:icon="@mipmap/ic_launcher">
- <activity android:name="io.flutter.plugins.url_launcher_example.MainActivity"
+ <activity android:name="io.flutter.plugins.urllauncherexample.MainActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
diff --git a/pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/url_launcher_example/MainActivity.java b/pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/urllauncherexample/MainActivity.java
similarity index 61%
rename from pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/url_launcher_example/MainActivity.java
rename to pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/urllauncherexample/MainActivity.java
index 0f0d5f2..87478bf 100644
--- a/pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/url_launcher_example/MainActivity.java
+++ b/pub/url_launcher/example/android/app/src/main/java/io/flutter/plugins/urllauncherexample/MainActivity.java
@@ -1,4 +1,8 @@
-package io.flutter.plugins.url_launcher_example;
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.urllauncherexample;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
diff --git a/pub/url_launcher/example/ios/Runner/AppDelegate.h b/pub/url_launcher/example/ios/Runner/AppDelegate.h
index 36e21bb..d9e18e9 100644
--- a/pub/url_launcher/example/ios/Runner/AppDelegate.h
+++ b/pub/url_launcher/example/ios/Runner/AppDelegate.h
@@ -1,3 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
diff --git a/pub/url_launcher/example/ios/Runner/AppDelegate.m b/pub/url_launcher/example/ios/Runner/AppDelegate.m
index 1e6ed92..9cf1c77 100644
--- a/pub/url_launcher/example/ios/Runner/AppDelegate.m
+++ b/pub/url_launcher/example/ios/Runner/AppDelegate.m
@@ -1,3 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
diff --git a/pub/url_launcher/example/ios/Runner/main.m b/pub/url_launcher/example/ios/Runner/main.m
index dff6597..bec320c 100644
--- a/pub/url_launcher/example/ios/Runner/main.m
+++ b/pub/url_launcher/example/ios/Runner/main.m
@@ -1,3 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
diff --git a/pub/url_launcher/example/lib/main.dart b/pub/url_launcher/example/lib/main.dart
index 338178a..565e826 100644
--- a/pub/url_launcher/example/lib/main.dart
+++ b/pub/url_launcher/example/lib/main.dart
@@ -69,14 +69,14 @@
children: <Widget>[
new Row(
mainAxisSize: MainAxisSize.min,
- children: [
- new Padding(
- padding: new EdgeInsets.all(16.0),
- child: new Text('https://flutter.io'),
+ children: <Widget>[
+ const Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: const Text('https://flutter.io'),
),
new RaisedButton(
onPressed: _launchUrl,
- child: new Text('Go'),
+ child: const Text('Go'),
),
],
),
diff --git a/pub/url_launcher/lib/url_launcher.dart b/pub/url_launcher/lib/url_launcher.dart
index 9cf5136..77b94e9 100644
--- a/pub/url_launcher/lib/url_launcher.dart
+++ b/pub/url_launcher/lib/url_launcher.dart
@@ -6,7 +6,8 @@
import 'package:flutter/services.dart';
-const _channel = const MethodChannel('plugins.flutter.io/url_launcher');
+const MethodChannel _channel =
+ const MethodChannel('plugins.flutter.io/url_launcher');
/// Parses the specified URL string and delegates handling of it to the
/// underlying platform.
@@ -24,8 +25,9 @@
/// Checks whether the specified URL can be handled by some app installed on the
/// device.
Future<bool> canLaunch(String urlString) async {
- if (urlString == null)
+ if (urlString == null) {
return false;
+ }
return await _channel.invokeMethod(
'canLaunch',
urlString,
diff --git a/pub/url_launcher/pubspec.yaml b/pub/url_launcher/pubspec.yaml
index 3386769..a7366f6 100644
--- a/pub/url_launcher/pubspec.yaml
+++ b/pub/url_launcher/pubspec.yaml
@@ -1,13 +1,13 @@
name: url_launcher
-version: 0.4.2+1
+version: 0.4.2+4
description: A Flutter plugin for launching a URL.
author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher
flutter:
plugin:
- androidPackage: io.flutter.plugins.url_launcher
+ androidPackage: io.flutter.plugins.urllauncher
pluginClass: UrlLauncherPlugin
dependencies:
@@ -15,7 +15,7 @@
sdk: flutter
dev_dependencies:
- test:
+ test: 0.12.21
environment:
sdk: ">=1.8.0 <2.0.0"