Line data Source code
1 : // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'description.dart';
6 : import 'interfaces.dart';
7 : import 'util.dart';
8 :
9 : /// Returns a pretty-printed representation of [object].
10 : ///
11 : /// If [maxLineLength] is passed, this will attempt to ensure that each line is
12 : /// no longer than [maxLineLength] characters long. This isn't guaranteed, since
13 : /// individual objects may have string representations that are too long, but
14 : /// most lines will be less than [maxLineLength] long.
15 : ///
16 : /// If [maxItems] is passed, [Iterable]s and [Map]s will only print their first
17 : /// [maxItems] members or key/value pairs, respectively.
18 : String prettyPrint(object, {int maxLineLength, int maxItems}) {
19 : String _prettyPrint(object, int indent, Set seen, bool top) {
20 : // If the object is a matcher, use its description.
21 0 : if (object is Matcher) {
22 0 : var description = new StringDescription();
23 0 : object.describe(description);
24 0 : return "<$description>";
25 : }
26 :
27 : // Avoid looping infinitely on recursively-nested data structures.
28 0 : if (seen.contains(object)) return "(recursive)";
29 0 : seen = seen.union(new Set.from([object]));
30 0 : String pp(child) => _prettyPrint(child, indent + 2, seen, false);
31 :
32 0 : if (object is Iterable) {
33 : // Print the type name for non-List iterables.
34 0 : var type = object is List ? "" : _typeName(object) + ":";
35 :
36 : // Truncate the list of strings if it's longer than [maxItems].
37 0 : var strings = object.map(pp).toList();
38 0 : if (maxItems != null && strings.length > maxItems) {
39 0 : strings.replaceRange(maxItems - 1, strings.length, ['...']);
40 : }
41 :
42 : // If the printed string is short and doesn't contain a newline, print it
43 : // as a single line.
44 0 : var singleLine = "$type[${strings.join(', ')}]";
45 : if ((maxLineLength == null ||
46 0 : singleLine.length + indent <= maxLineLength) &&
47 0 : !singleLine.contains("\n")) {
48 : return singleLine;
49 : }
50 :
51 : // Otherwise, print each member on its own line.
52 0 : return "$type[\n" +
53 0 : strings.map((string) {
54 0 : return _indent(indent + 2) + string;
55 0 : }).join(",\n") +
56 0 : "\n" +
57 0 : _indent(indent) +
58 : "]";
59 0 : } else if (object is Map) {
60 : // Convert the contents of the map to string representations.
61 0 : var strings = object.keys.map((key) {
62 0 : return '${pp(key)}: ${pp(object[key])}';
63 0 : }).toList();
64 :
65 : // Truncate the list of strings if it's longer than [maxItems].
66 0 : if (maxItems != null && strings.length > maxItems) {
67 0 : strings.replaceRange(maxItems - 1, strings.length, ['...']);
68 : }
69 :
70 : // If the printed string is short and doesn't contain a newline, print it
71 : // as a single line.
72 0 : var singleLine = "{${strings.join(", ")}}";
73 : if ((maxLineLength == null ||
74 0 : singleLine.length + indent <= maxLineLength) &&
75 0 : !singleLine.contains("\n")) {
76 : return singleLine;
77 : }
78 :
79 : // Otherwise, print each key/value pair on its own line.
80 0 : return "{\n" +
81 0 : strings.map((string) {
82 0 : return _indent(indent + 2) + string;
83 0 : }).join(",\n") +
84 0 : "\n" +
85 0 : _indent(indent) +
86 : "}";
87 0 : } else if (object is String) {
88 : // Escape strings and print each line on its own line.
89 0 : var lines = object.split("\n");
90 0 : return "'" +
91 0 : lines.map(_escapeString).join("\\n'\n${_indent(indent + 2)}'") +
92 : "'";
93 : } else {
94 0 : var value = object.toString().replaceAll("\n", _indent(indent) + "\n");
95 0 : var defaultToString = value.startsWith("Instance of ");
96 :
97 : // If this is the top-level call to [prettyPrint], wrap the value on angle
98 : // brackets to set it apart visually.
99 0 : if (top) value = "<$value>";
100 :
101 : // Print the type of objects with custom [toString] methods. Primitive
102 : // objects and objects that don't implement a custom [toString] don't need
103 : // to have their types printed.
104 0 : if (object is num ||
105 0 : object is bool ||
106 0 : object is Function ||
107 : object == null ||
108 : defaultToString) {
109 : return value;
110 : } else {
111 0 : return "${_typeName(object)}:$value";
112 : }
113 : }
114 : }
115 :
116 0 : return _prettyPrint(object, 0, new Set(), true);
117 : }
118 :
119 0 : String _indent(int length) => new List.filled(length, ' ').join('');
120 :
121 : /// Returns the name of the type of [x], or "Unknown" if the type name can't be
122 : /// determined.
123 : String _typeName(x) {
124 : // dart2js blows up on some objects (e.g. window.navigator).
125 : // So we play safe here.
126 : try {
127 : if (x == null) return "null";
128 0 : var type = x.runtimeType.toString();
129 : // TODO(nweiz): if the object's type is private, find a public superclass to
130 : // display once there's a portable API to do that.
131 0 : return type.startsWith("_") ? "?" : type;
132 : } catch (e) {
133 : return "?";
134 : }
135 : }
136 :
137 : /// Returns [source] with any control characters replaced by their escape
138 : /// sequences.
139 : ///
140 : /// This doesn't add quotes to the string, but it does escape single quote
141 : /// characters so that single quotes can be applied externally.
142 0 : String _escapeString(String source) => escape(source).replaceAll("'", r"\'");
|