Index: sdk/lib/_internal/pub_generated/lib/src/utils.dart |
diff --git a/sdk/lib/_internal/pub/lib/src/utils.dart b/sdk/lib/_internal/pub_generated/lib/src/utils.dart |
similarity index 50% |
copy from sdk/lib/_internal/pub/lib/src/utils.dart |
copy to sdk/lib/_internal/pub_generated/lib/src/utils.dart |
index 7af41e5f7ec03bef60391c16a6976a58a347ddf8..dcaf04417d560df5efb84f6c588b3bcf6a077584 100644 |
--- a/sdk/lib/_internal/pub/lib/src/utils.dart |
+++ b/sdk/lib/_internal/pub_generated/lib/src/utils.dart |
@@ -1,108 +1,61 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/// Generic utility functions. Stuff that should possibly be in core. |
library pub.utils; |
- |
import 'dart:async'; |
import "dart:convert"; |
import 'dart:io'; |
- |
-// This is used by [libraryPath]. It must be kept up-to-date with all libraries |
-// whose paths are looked up using that function. |
@MirrorsUsed(targets: const ['pub.io', 'test_pub']) |
import 'dart:mirrors'; |
- |
import "package:crypto/crypto.dart"; |
import 'package:path/path.dart' as path; |
import "package:stack_trace/stack_trace.dart"; |
- |
import 'exceptions.dart'; |
import 'log.dart' as log; |
- |
export '../../asset/dart/utils.dart'; |
- |
-/// A pair of values. |
class Pair<E, F> { |
E first; |
F last; |
- |
Pair(this.first, this.last); |
- |
String toString() => '($first, $last)'; |
- |
- bool operator==(other) { |
+ bool operator ==(other) { |
if (other is! Pair) return false; |
return other.first == first && other.last == last; |
} |
- |
int get hashCode => first.hashCode ^ last.hashCode; |
} |
- |
-/// A completer that waits until all added [Future]s complete. |
-// TODO(rnystrom): Copied from web_components. Remove from here when it gets |
-// added to dart:core. (See #6626.) |
class FutureGroup<T> { |
int _pending = 0; |
Completer<List<T>> _completer = new Completer<List<T>>(); |
final List<Future<T>> futures = <Future<T>>[]; |
bool completed = false; |
- |
final List<T> _values = <T>[]; |
- |
- /// Wait for [task] to complete. |
Future<T> add(Future<T> task) { |
if (completed) { |
throw new StateError("The FutureGroup has already completed."); |
} |
- |
_pending++; |
futures.add(task.then((value) { |
if (completed) return; |
- |
_pending--; |
_values.add(value); |
- |
if (_pending <= 0) { |
completed = true; |
_completer.complete(_values); |
} |
}).catchError((e, stackTrace) { |
if (completed) return; |
- |
completed = true; |
_completer.completeError(e, stackTrace); |
})); |
- |
return task; |
} |
- |
Future<List> get future => _completer.future; |
} |
- |
-/// Like [new Future], but avoids around issue 11911 by using [new Future.value] |
-/// under the covers. |
Future newFuture(callback()) => new Future.value().then((_) => callback()); |
- |
-/// Like [new Future.sync], but automatically wraps the future in a |
-/// [Chain.track] call. |
Future syncFuture(callback()) => Chain.track(new Future.sync(callback)); |
- |
-/// Runs [callback] in an error zone and pipes any unhandled error to the |
-/// returned [Future]. |
-/// |
-/// If the returned [Future] produces an error, its stack trace will always be a |
-/// [Chain]. By default, this chain will contain only the local stack trace, but |
-/// if [captureStackChains] is passed, it will contain the full stack chain for |
-/// the error. |
Future captureErrors(Future callback(), {bool captureStackChains: false}) { |
var completer = new Completer(); |
var wrappedCallback = () { |
- new Future.sync(callback).then(completer.complete) |
- .catchError((e, stackTrace) { |
- // [stackTrace] can be null if we're running without [captureStackChains], |
- // since dart:io will often throw errors without stack traces. |
+ new Future.sync( |
+ callback).then(completer.complete).catchError((e, stackTrace) { |
if (stackTrace != null) { |
stackTrace = new Chain.forTrace(stackTrace); |
} else { |
@@ -111,7 +64,6 @@ Future captureErrors(Future callback(), {bool captureStackChains: false}) { |
completer.completeError(e, stackTrace); |
}); |
}; |
- |
if (captureStackChains) { |
Chain.capture(wrappedCallback, onError: completer.completeError); |
} else { |
@@ -124,14 +76,8 @@ Future captureErrors(Future callback(), {bool captureStackChains: false}) { |
completer.completeError(e, stackTrace); |
}); |
} |
- |
return completer.future; |
} |
- |
-/// Like [Future.wait], but prints all errors from the futures as they occur and |
-/// only returns once all Futures have completed, successfully or not. |
-/// |
-/// This will wrap the first error thrown in a [SilentException] and rethrow it. |
Future waitAndPrintErrors(Iterable<Future> futures) { |
return Future.wait(futures.map((future) { |
return future.catchError((error, stackTrace) { |
@@ -142,105 +88,55 @@ Future waitAndPrintErrors(Iterable<Future> futures) { |
throw new SilentException(error, stackTrace); |
}); |
} |
- |
-/// Returns a [StreamTransformer] that will call [onDone] when the stream |
-/// completes. |
-/// |
-/// The stream will be passed through unchanged. |
StreamTransformer onDoneTransformer(void onDone()) { |
return new StreamTransformer.fromHandlers(handleDone: (sink) { |
onDone(); |
sink.close(); |
}); |
} |
- |
-// TODO(rnystrom): Move into String? |
-/// Pads [source] to [length] by adding spaces at the end. |
String padRight(String source, int length) { |
final result = new StringBuffer(); |
result.write(source); |
- |
while (result.length < length) { |
result.write(' '); |
} |
- |
return result.toString(); |
} |
- |
-/// Returns a sentence fragment listing the elements of [iter]. |
-/// |
-/// This converts each element of [iter] to a string and separates them with |
-/// commas and/or "and" where appropriate. |
String toSentence(Iterable iter) { |
if (iter.length == 1) return iter.first.toString(); |
return iter.take(iter.length - 1).join(", ") + " and ${iter.last}"; |
} |
- |
-/// Returns [name] if [number] is 1, or the plural of [name] otherwise. |
-/// |
-/// By default, this just adds "s" to the end of [name] to get the plural. If |
-/// [plural] is passed, that's used instead. |
String pluralize(String name, int number, {String plural}) { |
if (number == 1) return name; |
if (plural != null) return plural; |
return '${name}s'; |
} |
- |
-/// Escapes any regex metacharacters in [string] so that using as a [RegExp] |
-/// pattern will match the string literally. |
-// TODO(rnystrom): Remove when #4706 is fixed. |
String quoteRegExp(String string) { |
- // Note: make sure "\" is done first so that we don't escape the other |
- // escaped characters. We could do all of the replaces at once with a regexp |
- // but string literal for regex that matches all regex metacharacters would |
- // be a bit hard to read. |
for (var metacharacter in r"\^$.*+?()[]{}|".split("")) { |
string = string.replaceAll(metacharacter, "\\$metacharacter"); |
} |
- |
return string; |
} |
- |
-/// Creates a URL string for [address]:[port]. |
-/// |
-/// Handles properly formatting IPv6 addresses. |
Uri baseUrlForAddress(InternetAddress address, int port) { |
if (address.isLoopback) { |
return new Uri(scheme: "http", host: "localhost", port: port); |
} |
- |
- // IPv6 addresses in URLs need to be enclosed in square brackets to avoid |
- // URL ambiguity with the ":" in the address. |
if (address.type == InternetAddressType.IP_V6) { |
return new Uri(scheme: "http", host: "[${address.address}]", port: port); |
} |
- |
return new Uri(scheme: "http", host: address.address, port: port); |
} |
- |
-/// Returns whether [host] is a host for a localhost or loopback URL. |
-/// |
-/// Unlike [InternetAddress.isLoopback], this hostnames from URLs as well as |
-/// from [InternetAddress]es, including "localhost". |
bool isLoopback(String host) { |
if (host == 'localhost') return true; |
- |
- // IPv6 hosts in URLs are surrounded by square brackets. |
if (host.startsWith("[") && host.endsWith("]")) { |
host = host.substring(1, host.length - 1); |
} |
- |
try { |
return new InternetAddress(host).isLoopback; |
} on ArgumentError catch (_) { |
- // The host isn't an IP address and isn't "localhost', so it's almost |
- // certainly not a loopback host. |
return false; |
} |
} |
- |
-/// Flattens nested lists inside an iterable into a single list containing only |
-/// non-list elements. |
List flatten(Iterable nested) { |
var result = []; |
helper(list) { |
@@ -255,41 +151,28 @@ List flatten(Iterable nested) { |
helper(nested); |
return result; |
} |
- |
-/// Returns a set containing all elements in [minuend] that are not in |
-/// [subtrahend]. |
Set setMinus(Iterable minuend, Iterable subtrahend) { |
var minuendSet = new Set.from(minuend); |
minuendSet.removeAll(subtrahend); |
return minuendSet; |
} |
- |
-/// Returns a list containing the sorted elements of [iter]. |
List ordered(Iterable<Comparable> iter) { |
var list = iter.toList(); |
list.sort(); |
return list; |
} |
- |
-/// Returns the element of [iter] for which [f] returns the minimum value. |
minBy(Iterable iter, Comparable f(element)) { |
var min = null; |
var minComparable = null; |
for (var element in iter) { |
var comparable = f(element); |
- if (minComparable == null || |
- comparable.compareTo(minComparable) < 0) { |
+ if (minComparable == null || comparable.compareTo(minComparable) < 0) { |
min = element; |
minComparable = comparable; |
} |
} |
return min; |
} |
- |
-/// Returns every pair of consecutive elements in [iter]. |
-/// |
-/// For example, if [iter] is `[1, 2, 3, 4]`, this will return `[(1, 2), (2, 3), |
-/// (3, 4)]`. |
Iterable<Pair> pairs(Iterable iter) { |
var previous = iter.first; |
return iter.skip(1).map((element) { |
@@ -298,54 +181,34 @@ Iterable<Pair> pairs(Iterable iter) { |
return new Pair(oldPrevious, element); |
}); |
} |
- |
-/// Creates a new map from [map] with new keys and values. |
-/// |
-/// The return values of [key] are used as the keys and the return values of |
-/// [value] are used as the values for the new map. |
-/// |
-/// [key] defaults to returning the original key and [value] defaults to |
-/// returning the original value. |
Map mapMap(Map map, {key(key, value), value(key, value)}) { |
if (key == null) key = (key, _) => key; |
if (value == null) value = (_, value) => value; |
- |
var result = {}; |
map.forEach((mapKey, mapValue) { |
result[key(mapKey, mapValue)] = value(mapKey, mapValue); |
}); |
return result; |
} |
- |
-/// Like [Map.fromIterable], but [key] and [value] may return [Future]s. |
-Future<Map> mapFromIterableAsync(Iterable iter, {key(element), |
- value(element)}) { |
+Future<Map> mapFromIterableAsync(Iterable iter, {key(element), value(element)}) |
+ { |
if (key == null) key = (element) => element; |
if (value == null) value = (element) => element; |
- |
var map = new Map(); |
return Future.wait(iter.map((element) { |
- return Future.wait([ |
- syncFuture(() => key(element)), |
- syncFuture(() => value(element)) |
- ]).then((results) { |
+ return Future.wait( |
+ [ |
+ syncFuture(() => key(element)), |
+ syncFuture(() => value(element))]).then((results) { |
map[results[0]] = results[1]; |
}); |
})).then((_) => map); |
} |
- |
-/// Returns the transitive closure of [graph]. |
-/// |
-/// This assumes [graph] represents a graph with a vertex for each key and an |
-/// edge betweek each key and the values for that key. |
Map<dynamic, Set> transitiveClosure(Map<dynamic, Iterable> graph) { |
- // This uses the Floyd-Warshall algorithm |
- // (https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm). |
var result = {}; |
graph.forEach((vertex, edges) { |
result[vertex] = new Set.from(edges)..add(vertex); |
}); |
- |
for (var vertex1 in graph.keys) { |
for (var vertex2 in graph.keys) { |
for (var vertex3 in graph.keys) { |
@@ -356,15 +219,8 @@ Map<dynamic, Set> transitiveClosure(Map<dynamic, Iterable> graph) { |
} |
} |
} |
- |
return result; |
} |
- |
-/// Given a list of filenames, returns a set of patterns that can be used to |
-/// filter for those filenames. |
-/// |
-/// For a given path, that path ends with some string in the returned set if |
-/// and only if that path's basename is in [files]. |
Set<String> createFileFilter(Iterable<String> files) { |
return files.expand((file) { |
var result = ["/$file"]; |
@@ -372,42 +228,28 @@ Set<String> createFileFilter(Iterable<String> files) { |
return result; |
}).toSet(); |
} |
- |
-/// Given a blacklist of directory names, returns a set of patterns that can |
-/// be used to filter for those directory names. |
-/// |
-/// For a given path, that path contains some string in the returned set if |
-/// and only if one of that path's components is in [dirs]. |
Set<String> createDirectoryFilter(Iterable<String> dirs) { |
return dirs.expand((dir) { |
var result = ["/$dir/"]; |
if (Platform.operatingSystem == 'windows') { |
- result..add("/$dir\\")..add("\\$dir/")..add("\\$dir\\"); |
+ result |
+ ..add("/$dir\\") |
+ ..add("\\$dir/") |
+ ..add("\\$dir\\"); |
} |
return result; |
}).toSet(); |
} |
- |
-/// Returns the maximum value in [iter] by [compare]. |
-/// |
-/// [compare] defaults to [Comparable.compare]. |
maxAll(Iterable iter, [int compare(element1, element2)]) { |
if (compare == null) compare = Comparable.compare; |
- return iter.reduce((max, element) => |
- compare(element, max) > 0 ? element : max); |
+ return iter.reduce( |
+ (max, element) => compare(element, max) > 0 ? element : max); |
} |
- |
-/// Returns the minimum value in [iter] by [compare]. |
-/// |
-/// [compare] defaults to [Comparable.compare]. |
minAll(Iterable iter, [int compare(element1, element2)]) { |
if (compare == null) compare = Comparable.compare; |
- return iter.reduce((max, element) => |
- compare(element, max) < 0 ? element : max); |
+ return iter.reduce( |
+ (max, element) => compare(element, max) < 0 ? element : max); |
} |
- |
-/// Replace each instance of [matcher] in [source] with the return value of |
-/// [fn]. |
String replace(String source, Pattern matcher, String fn(Match)) { |
var buffer = new StringBuffer(); |
var start = 0; |
@@ -419,75 +261,40 @@ String replace(String source, Pattern matcher, String fn(Match)) { |
buffer.write(source.substring(start)); |
return buffer.toString(); |
} |
- |
-/// Returns whether or not [str] ends with [matcher]. |
bool endsWithPattern(String str, Pattern matcher) { |
for (var match in matcher.allMatches(str)) { |
if (match.end == str.length) return true; |
} |
return false; |
} |
- |
-/// Returns the hex-encoded sha1 hash of [source]. |
String sha1(String source) { |
var sha = new SHA1(); |
sha.add(source.codeUnits); |
return CryptoUtils.bytesToHex(sha.close()); |
} |
- |
-/// Configures [future] so that its result (success or exception) is passed on |
-/// to [completer]. |
void chainToCompleter(Future future, Completer completer) { |
future.then(completer.complete, onError: completer.completeError); |
} |
- |
-/// Ensures that [stream] can emit at least one value successfully (or close |
-/// without any values). |
-/// |
-/// For example, reading asynchronously from a non-existent file will return a |
-/// stream that fails on the first chunk. In order to handle that more |
-/// gracefully, you may want to check that the stream looks like it's working |
-/// before you pipe the stream to something else. |
-/// |
-/// This lets you do that. It returns a [Future] that completes to a [Stream] |
-/// emitting the same values and errors as [stream], but only if at least one |
-/// value can be read successfully. If an error occurs before any values are |
-/// emitted, the returned Future completes to that error. |
Future<Stream> validateStream(Stream stream) { |
var completer = new Completer<Stream>(); |
var controller = new StreamController(sync: true); |
- |
StreamSubscription subscription; |
subscription = stream.listen((value) { |
- // We got a value, so the stream is valid. |
if (!completer.isCompleted) completer.complete(controller.stream); |
controller.add(value); |
}, onError: (error, [stackTrace]) { |
- // If the error came after values, it's OK. |
if (completer.isCompleted) { |
controller.addError(error, stackTrace); |
return; |
} |
- |
- // Otherwise, the error came first and the stream is invalid. |
completer.completeError(error, stackTrace); |
- |
- // We don't be returning the stream at all in this case, so unsubscribe |
- // and swallow the error. |
subscription.cancel(); |
}, onDone: () { |
- // It closed with no errors, so the stream is valid. |
if (!completer.isCompleted) completer.complete(controller.stream); |
controller.close(); |
}); |
- |
return completer.future; |
} |
- |
-// TODO(nweiz): remove this when issue 7964 is fixed. |
-/// Returns a [Future] that will complete to the first element of [stream]. |
-/// |
-/// Unlike [Stream.first], this is safe to use with single-subscription streams. |
Future streamFirst(Stream stream) { |
var completer = new Completer(); |
var subscription; |
@@ -501,25 +308,16 @@ Future streamFirst(Stream stream) { |
}, cancelOnError: true); |
return completer.future; |
} |
- |
-/// Returns a wrapped version of [stream] along with a [StreamSubscription] that |
-/// can be used to control the wrapped stream. |
Pair<Stream, StreamSubscription> streamWithSubscription(Stream stream) { |
- var controller = |
- stream.isBroadcast ? new StreamController.broadcast(sync: true) |
- : new StreamController(sync: true); |
- var subscription = stream.listen(controller.add, |
+ var controller = stream.isBroadcast ? |
+ new StreamController.broadcast(sync: true) : |
+ new StreamController(sync: true); |
+ var subscription = stream.listen( |
+ controller.add, |
onError: controller.addError, |
onDone: controller.close); |
return new Pair<Stream, StreamSubscription>(controller.stream, subscription); |
} |
- |
-// TODO(nweiz): remove this when issue 7787 is fixed. |
-/// Creates two single-subscription [Stream]s that each emit all values and |
-/// errors from [stream]. |
-/// |
-/// This is useful if [stream] is single-subscription but multiple subscribers |
-/// are necessary. |
Pair<Stream, Stream> tee(Stream stream) { |
var controller1 = new StreamController(sync: true); |
var controller2 = new StreamController(sync: true); |
@@ -535,99 +333,63 @@ Pair<Stream, Stream> tee(Stream stream) { |
}); |
return new Pair<Stream, Stream>(controller1.stream, controller2.stream); |
} |
- |
-/// Merges [stream1] and [stream2] into a single stream that emits events from |
-/// both sources. |
Stream mergeStreams(Stream stream1, Stream stream2) { |
var doneCount = 0; |
var controller = new StreamController(sync: true); |
- |
for (var stream in [stream1, stream2]) { |
- stream.listen( |
- controller.add, |
- onError: controller.addError, |
- onDone: () { |
+ stream.listen(controller.add, onError: controller.addError, onDone: () { |
doneCount++; |
if (doneCount == 2) controller.close(); |
}); |
} |
- |
return controller.stream; |
} |
- |
-/// A regular expression matching a trailing CR character. |
final _trailingCR = new RegExp(r"\r$"); |
- |
-// TODO(nweiz): Use `text.split(new RegExp("\r\n?|\n\r?"))` when issue 9360 is |
-// fixed. |
-/// Splits [text] on its line breaks in a Windows-line-break-friendly way. |
List<String> splitLines(String text) => |
- text.split("\n").map((line) => line.replaceFirst(_trailingCR, "")).toList(); |
- |
-/// Converts a stream of arbitrarily chunked strings into a line-by-line stream. |
-/// |
-/// The lines don't include line termination characters. A single trailing |
-/// newline is ignored. |
+ text.split("\n").map((line) => line.replaceFirst(_trailingCR, "")).toList(); |
Stream<String> streamToLines(Stream<String> stream) { |
var buffer = new StringBuffer(); |
- return stream.transform(new StreamTransformer.fromHandlers( |
- handleData: (chunk, sink) { |
- var lines = splitLines(chunk); |
- var leftover = lines.removeLast(); |
- for (var line in lines) { |
- if (!buffer.isEmpty) { |
- buffer.write(line); |
- line = buffer.toString(); |
- buffer = new StringBuffer(); |
- } |
- |
- sink.add(line); |
- } |
- buffer.write(leftover); |
- }, |
- handleDone: (sink) { |
- if (!buffer.isEmpty) sink.add(buffer.toString()); |
- sink.close(); |
- })); |
-} |
- |
-/// Like [Iterable.where], but allows [test] to return [Future]s and uses the |
-/// results of those [Future]s as the test. |
+ return stream.transform( |
+ new StreamTransformer.fromHandlers(handleData: (chunk, sink) { |
+ var lines = splitLines(chunk); |
+ var leftover = lines.removeLast(); |
+ for (var line in lines) { |
+ if (!buffer.isEmpty) { |
+ buffer.write(line); |
+ line = buffer.toString(); |
+ buffer = new StringBuffer(); |
+ } |
+ sink.add(line); |
+ } |
+ buffer.write(leftover); |
+ }, handleDone: (sink) { |
+ if (!buffer.isEmpty) sink.add(buffer.toString()); |
+ sink.close(); |
+ })); |
+} |
Future<Iterable> futureWhere(Iterable iter, test(value)) { |
return Future.wait(iter.map((e) { |
var result = test(e); |
if (result is! Future) result = new Future.value(result); |
return result.then((result) => new Pair(e, result)); |
- })) |
- .then((pairs) => pairs.where((pair) => pair.last)) |
- .then((pairs) => pairs.map((pair) => pair.first)); |
-} |
- |
-// TODO(nweiz): unify the following functions with the utility functions in |
-// pkg/http. |
- |
-/// Like [String.split], but only splits on the first occurrence of the pattern. |
-/// |
-/// This always returns an array of two elements or fewer. |
+ })).then( |
+ (pairs) => |
+ pairs.where( |
+ (pair) => pair.last)).then((pairs) => pairs.map((pair) => pair.first)); |
+} |
List<String> split1(String toSplit, String pattern) { |
if (toSplit.isEmpty) return <String>[]; |
- |
var index = toSplit.indexOf(pattern); |
if (index == -1) return [toSplit]; |
- return [toSplit.substring(0, index), |
- toSplit.substring(index + pattern.length)]; |
+ return [ |
+ toSplit.substring(0, index), |
+ toSplit.substring(index + pattern.length)]; |
} |
- |
-/// Adds additional query parameters to [url], overwriting the original |
-/// parameters if a name conflict occurs. |
Uri addQueryParameters(Uri url, Map<String, String> parameters) { |
var queryMap = queryToMap(url.query); |
queryMap.addAll(parameters); |
return url.resolve("?${mapToQuery(queryMap)}"); |
} |
- |
-/// Convert a URL query string (or `application/x-www-form-urlencoded` body) |
-/// into a [Map] from parameter names to values. |
Map<String, String> queryToMap(String queryList) { |
var map = {}; |
for (var pair in queryList.split("&")) { |
@@ -639,14 +401,12 @@ Map<String, String> queryToMap(String queryList) { |
} |
return map; |
} |
- |
-/// Convert a [Map] from parameter names to values to a URL query string. |
String mapToQuery(Map<String, String> map) { |
var pairs = <List<String>>[]; |
map.forEach((key, value) { |
key = Uri.encodeQueryComponent(key); |
- value = (value == null || value.isEmpty) |
- ? null : Uri.encodeQueryComponent(value); |
+ value = |
+ (value == null || value.isEmpty) ? null : Uri.encodeQueryComponent(value); |
pairs.add([key, value]); |
}); |
return pairs.map((pair) { |
@@ -654,29 +414,13 @@ String mapToQuery(Map<String, String> map) { |
return "${pair[0]}=${pair[1]}"; |
}).join("&"); |
} |
- |
-/// Returns the union of all elements in each set in [sets]. |
Set unionAll(Iterable<Set> sets) => |
- sets.fold(new Set(), (union, set) => union.union(set)); |
- |
-// TODO(nweiz): remove this when issue 9068 has been fixed. |
-/// Whether [uri1] and [uri2] are equal. |
-/// |
-/// This consider HTTP URIs to default to port 80, and HTTPs URIs to default to |
-/// port 443. |
+ sets.fold(new Set(), (union, set) => union.union(set)); |
bool urisEqual(Uri uri1, Uri uri2) => |
- canonicalizeUri(uri1) == canonicalizeUri(uri2); |
- |
-/// Return [uri] with redundant port information removed. |
+ canonicalizeUri(uri1) == canonicalizeUri(uri2); |
Uri canonicalizeUri(Uri uri) { |
return uri; |
} |
- |
-/// Returns a human-friendly representation of [inputPath]. |
-/// |
-/// If [inputPath] isn't too distant from the current working directory, this |
-/// will return the relative path to it. Otherwise, it will return the absolute |
-/// path. |
String nicePath(String inputPath) { |
var relative = path.relative(inputPath); |
var split = path.split(relative); |
@@ -685,39 +429,23 @@ String nicePath(String inputPath) { |
} |
return relative; |
} |
- |
-/// Returns a human-friendly representation of [duration]. |
String niceDuration(Duration duration) { |
var result = duration.inMinutes > 0 ? "${duration.inMinutes}:" : ""; |
- |
var s = duration.inSeconds % 59; |
var ms = (duration.inMilliseconds % 1000) ~/ 100; |
return result + "$s.${ms}s"; |
} |
- |
-/// Decodes a URL-encoded string. |
-/// |
-/// Unlike [Uri.decodeComponent], this includes replacing `+` with ` `. |
String urlDecode(String encoded) => |
- Uri.decodeComponent(encoded.replaceAll("+", " ")); |
- |
-/// Takes a simple data structure (composed of [Map]s, [Iterable]s, scalar |
-/// objects, and [Future]s) and recursively resolves all the [Future]s contained |
-/// within. |
-/// |
-/// Completes with the fully resolved structure. |
+ Uri.decodeComponent(encoded.replaceAll("+", " ")); |
Future awaitObject(object) { |
- // Unroll nested futures. |
if (object is Future) return object.then(awaitObject); |
if (object is Iterable) { |
return Future.wait(object.map(awaitObject).toList()); |
} |
if (object is! Map) return new Future.value(object); |
- |
var pairs = <Future<Pair>>[]; |
object.forEach((key, value) { |
- pairs.add(awaitObject(value) |
- .then((resolved) => new Pair(key, resolved))); |
+ pairs.add(awaitObject(value).then((resolved) => new Pair(key, resolved))); |
}); |
return Future.wait(pairs).then((resolvedPairs) { |
var map = {}; |
@@ -727,78 +455,34 @@ Future awaitObject(object) { |
return map; |
}); |
} |
- |
-/// Returns the path to the library named [libraryName]. |
-/// |
-/// The library name must be globally unique, or the wrong library path may be |
-/// returned. Any libraries accessed must be added to the [MirrorsUsed] |
-/// declaration in the import above. |
String libraryPath(String libraryName) { |
var lib = currentMirrorSystem().findLibrary(new Symbol(libraryName)); |
return path.fromUri(lib.uri); |
} |
- |
-/// Whether "special" strings such as Unicode characters or color escapes are |
-/// safe to use. |
-/// |
-/// On Windows or when not printing to a terminal, only printable ASCII |
-/// characters should be used. |
-bool get canUseSpecialChars => !runningAsTest && |
- Platform.operatingSystem != 'windows' && |
- stdioType(stdout) == StdioType.TERMINAL; |
- |
-/// Gets a "special" string (ANSI escape or Unicode). |
-/// |
-/// On Windows or when not printing to a terminal, returns something else since |
-/// those aren't supported. |
+bool get canUseSpecialChars => |
+ !runningAsTest && |
+ Platform.operatingSystem != 'windows' && |
+ stdioType(stdout) == StdioType.TERMINAL; |
String getSpecial(String special, [String onWindows = '']) => |
canUseSpecialChars ? special : onWindows; |
- |
-/// Prepends each line in [text] with [prefix]. |
-/// |
-/// If [firstPrefix] is passed, the first line is prefixed with that instead. |
String prefixLines(String text, {String prefix: '| ', String firstPrefix}) { |
var lines = text.split('\n'); |
if (firstPrefix == null) { |
return lines.map((line) => '$prefix$line').join('\n'); |
} |
- |
var firstLine = "$firstPrefix${lines.first}"; |
lines = lines.skip(1).map((line) => '$prefix$line').toList(); |
lines.insert(0, firstLine); |
return lines.join('\n'); |
} |
- |
-/// Whether pub is running as a subprocess in an integration test or in a unit |
-/// test that has explicitly set this. |
bool runningAsTest = Platform.environment.containsKey('_PUB_TESTING'); |
- |
-/// Whether today is April Fools' day. |
bool get isAprilFools { |
- // Tests should never see April Fools' output. |
if (runningAsTest) return false; |
- |
var date = new DateTime.now(); |
return date.month == 4 && date.day == 1; |
} |
- |
-/// Wraps [fn] to guard against several different kinds of stack overflow |
-/// exceptions: |
-/// |
-/// * A sufficiently long [Future] chain can cause a stack overflow if there are |
-/// no asynchronous operations in it (issue 9583). |
-/// * A recursive function that recurses too deeply without an asynchronous |
-/// operation can cause a stack overflow. |
-/// * Even if the former is guarded against by adding asynchronous operations, |
-/// returning a value through the [Future] chain can still cause a stack |
-/// overflow. |
Future resetStack(fn()) { |
- // Using a [Completer] breaks the [Future] chain for the return value and |
- // avoids the third case described above. |
var completer = new Completer(); |
- |
- // Using [new Future] adds an asynchronous operation that works around the |
- // first and second cases described above. |
newFuture(fn).then((val) { |
scheduleMicrotask(() => completer.complete(val)); |
}).catchError((err, stackTrace) { |
@@ -806,71 +490,43 @@ Future resetStack(fn()) { |
}); |
return completer.future; |
} |
- |
-/// The subset of strings that don't need quoting in YAML. |
-/// |
-/// This pattern does not strictly follow the plain scalar grammar of YAML, |
-/// which means some strings may be unnecessarily quoted, but it's much simpler. |
final _unquotableYamlString = new RegExp(r"^[a-zA-Z_-][a-zA-Z_0-9-]*$"); |
- |
-/// Converts [data], which is a parsed YAML object, to a pretty-printed string, |
-/// using indentation for maps. |
String yamlToString(data) { |
var buffer = new StringBuffer(); |
- |
_stringify(bool isMapValue, String indent, data) { |
- // TODO(nweiz): Serialize using the YAML library once it supports |
- // serialization. |
- |
- // Use indentation for (non-empty) maps. |
if (data is Map && !data.isEmpty) { |
if (isMapValue) { |
buffer.writeln(); |
indent += ' '; |
} |
- |
- // Sort the keys. This minimizes deltas in diffs. |
var keys = data.keys.toList(); |
keys.sort((a, b) => a.toString().compareTo(b.toString())); |
- |
var first = true; |
for (var key in keys) { |
if (!first) buffer.writeln(); |
first = false; |
- |
var keyString = key; |
if (key is! String || !_unquotableYamlString.hasMatch(key)) { |
keyString = JSON.encode(key); |
} |
- |
buffer.write('$indent$keyString:'); |
_stringify(true, indent, data[key]); |
} |
- |
return; |
} |
- |
- // Everything else we just stringify using JSON to handle escapes in |
- // strings and number formatting. |
var string = data; |
- |
- // Don't quote plain strings if not needed. |
if (data is! String || !_unquotableYamlString.hasMatch(data)) { |
string = JSON.encode(data); |
} |
- |
if (isMapValue) { |
buffer.write(' $string'); |
} else { |
buffer.write('$indent$string'); |
} |
} |
- |
_stringify(false, '', data); |
return buffer.toString(); |
} |
- |
-/// Throw a [ApplicationException] with [message]. |
void fail(String message, [innerError, StackTrace innerTrace]) { |
if (innerError != null) { |
throw new WrappedException(message, innerError, innerTrace); |
@@ -878,9 +534,4 @@ void fail(String message, [innerError, StackTrace innerTrace]) { |
throw new ApplicationException(message); |
} |
} |
- |
-/// Throw a [DataException] with [message] to indicate that the command has |
-/// failed because of invalid input data. |
-/// |
-/// This will report the error and cause pub to exit with [exit_codes.DATA]. |
void dataError(String message) => throw new DataException(message); |