| Index: sdk/lib/collection/iterable.dart
|
| diff --git a/sdk/lib/collection/iterable.dart b/sdk/lib/collection/iterable.dart
|
| index 9cbdf897caabe9d6e9ba04664887b3856f1e4660..ae61cd0f66201e164e51384291cbcc1c6b907ea8 100644
|
| --- a/sdk/lib/collection/iterable.dart
|
| +++ b/sdk/lib/collection/iterable.dart
|
| @@ -188,6 +188,8 @@ abstract class IterableMixin<E> implements Iterable<E> {
|
| }
|
| throw new RangeError.value(index);
|
| }
|
| +
|
| + String toString() => _iterableToString(this);
|
| }
|
|
|
| /**
|
| @@ -379,4 +381,131 @@ abstract class IterableBase<E> implements Iterable<E> {
|
| }
|
| throw new RangeError.value(index);
|
| }
|
| +
|
| + /**
|
| + * Returns a string representation of (some of) the elements of `this`.
|
| + *
|
| + * Elements are represented by their own `toString` results.
|
| + *
|
| + * The representation always contains the first three elements.
|
| + * If there are less than a hundred elements in the iterable, it also
|
| + * contains the last two elements.
|
| + *
|
| + * If the resulting string isn't above 80 characters, more elements are
|
| + * included from the start of the iterable.
|
| + *
|
| + * The conversion may omit calling `toString` on some elements if they
|
| + * are known to now occur in the output, and it may stop iterating after
|
| + * a hundred elements.
|
| + */
|
| + String toString() => _iterableToString(this);
|
| +}
|
| +
|
| +String _iterableToString(Iterable iterable) {
|
| + const int LENGTH_LIMIT = 80;
|
| + const int MIN_COUNT = 3; // Always at least this many elements at the start.
|
| + const int MAX_COUNT = 100;
|
| + // Per entry length overhead for ", " (or for "(" and ")" for initial entry)
|
| + const int OVERHEAD = 2;
|
| + const int ELLIPSIS_SIZE = 3; // "...".length.
|
| + if (_toStringVisiting.contains(iterable)) return "(...)";
|
| + _toStringVisiting.add(iterable);
|
| + List result = [];
|
| + try {
|
| + building: { // Break this block to complete the toString.
|
| + int length = 0;
|
| + int count = 0;
|
| + Iterator it = iterable.iterator;
|
| + // Initial run of elements, at least MIN_COUNT, and then continue until
|
| + // passing at most LENGTH_LIMIT characters.
|
| + while (count < MIN_COUNT || length < LENGTH_LIMIT) {
|
| + if (!it.moveNext()) break building;
|
| + String next = "${it.current}";
|
| + result.add(next);
|
| + length += next.length + OVERHEAD; // Includes ")" for the first entry.
|
| + count++;
|
| + }
|
| + String penultimateString;
|
| + String ultimateString;
|
| +
|
| + // Find last two elements. One or more of them may already be in the
|
| + // result array. Include their length in `length`.
|
| + var penultimate = null;
|
| + var ultimate = null;
|
| + if (!it.moveNext()) {
|
| + if (count <= MIN_COUNT + 2) break building;
|
| + ultimateString = result.removeLast();
|
| + penultimateString = result.removeLast();
|
| + } else {
|
| + penultimate = it.current;
|
| + count++;
|
| + if (!it.moveNext()) {
|
| + if (count <= MIN_COUNT + 1) {
|
| + result.add("$penultimate");
|
| + break building;
|
| + }
|
| + ultimateString = "$penultimate";
|
| + penultimateString = result.removeLast();
|
| + length += ultimateString.length + OVERHEAD;
|
| + } else {
|
| + ultimate = it.current;
|
| + count++;
|
| + // Then keep looping, keeping the last two elements in variables.
|
| + assert(count < MAX_COUNT);
|
| + while (it.moveNext()) {
|
| + penultimate = ultimate;
|
| + ultimate = it.current;
|
| + count++;
|
| + if (count > MAX_COUNT) {
|
| + // If we haven't found the end before MAX_COUNT, give up.
|
| + // This cannot happen before because each count increases
|
| + // length by at least two, so there is no way to see more
|
| + // than ~40 elements before this loop.
|
| +
|
| + // Remove any surplus elements until length including ", ...)"
|
| + // is at most LENGTH_LIMIT.
|
| + while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD &&
|
| + count > MIN_COUNT) {
|
| + length -= result.removeLast().length + OVERHEAD;
|
| + count--;
|
| + }
|
| + result.add("...");
|
| + break building;
|
| + }
|
| + }
|
| + penultimateString = "$penultimate";
|
| + ultimateString = "$ultimate";
|
| + length +=
|
| + ultimateString.length + penultimateString.length + 2 * OVERHEAD;
|
| + }
|
| + }
|
| +
|
| + // If there is a gap between the initial run and the last two,
|
| + // prepare to add an ellipsis.
|
| + String elision = null;
|
| + if (count > result.length + 2) {
|
| + elision = "...";
|
| + length += ELLIPSIS_SIZE + OVERHEAD;
|
| + }
|
| +
|
| + // If the last two elements were very long, and we have more than
|
| + // MIN_COUNT elements in the initial run, drop some to make room for
|
| + // the last two.
|
| + while (length > LENGTH_LIMIT && result.length > MIN_COUNT) {
|
| + length -= result.removeLast().length + OVERHEAD;
|
| + if (elision == null) {
|
| + elision = "...";
|
| + length += ELLIPSIS_SIZE + OVERHEAD;
|
| + }
|
| + }
|
| + if (elision != null) {
|
| + result.add(elision);
|
| + }
|
| + result.add(penultimateString);
|
| + result.add(ultimateString);
|
| + }
|
| + } finally {
|
| + _toStringVisiting.remove(iterable);
|
| + }
|
| + return (new StringBuffer("(")..writeAll(result, ", ")..write(")")).toString();
|
| }
|
|
|