Chromium Code Reviews| Index: sdk/lib/collection/iterable.dart |
| diff --git a/sdk/lib/collection/iterable.dart b/sdk/lib/collection/iterable.dart |
| index 9cbdf897caabe9d6e9ba04664887b3856f1e4660..fa51f35589ba9f00b0d4a23fef024c2b589261a6 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,132 @@ abstract class IterableBase<E> implements Iterable<E> { |
| } |
| throw new RangeError.value(index); |
| } |
| + |
| + /** |
| + * Returns a string representation of (some of) the elements of this |
| + * Iterabele. |
|
floitsch
2013/10/11 09:29:43
of `this`.
I don't like to repeat the name of the
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
|
| + * |
| + * 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 |
|
floitsch
2013/10/11 09:29:43
Not sure we want to keep this sentence. If it's no
Lasse Reichstein Nielsen
2013/10/11 20:25:37
I think it's worth emphasizing, to avoid someone t
|
| + * 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 (length < LENGTH_LIMIT || count < MIN_COUNT) { |
|
floitsch
2013/10/11 09:29:43
nit. reorder:
while (count < MIN_COUNT || length <
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
Although for efficiency, I'd expect the leng
|
| + if (!it.moveNext()) break building; |
| + String next = "${it.current}"; |
|
floitsch
2013/10/11 09:29:43
it.current.toString() is more explicit.
But ok to
Lasse Reichstein Nielsen
2013/10/11 20:25:37
It may be slightly faster to call toString because
|
| + 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; |
|
floitsch
2013/10/11 09:29:43
Add comment?:
// No need for ellipsis.
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
|
| + 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. |
|
floitsch
2013/10/11 09:29:43
"Then" doesn't fit.
Loop, keeping ...
Lasse Reichstein Nielsen
2013/10/11 20:25:37
Done.
|
| + 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(); |
| } |