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 0 : String prettyPrint(Object? object, {int? maxLineLength, int? maxItems}) { 19 0 : String _prettyPrint(Object? object, int indent, Set<Object?> seen, bool top) { 20 : // If the object is a matcher, use its description. 21 0 : if (object is Matcher) { 22 0 : var description = 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({object}); 30 0 : String pp(Object? 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 0 : object is RegExp || 108 0 : object is MapEntry || 109 0 : object is Expando || 110 : object == null || 111 : defaultToString) { 112 : return value; 113 : } else { 114 0 : return '${_typeName(object)}:$value'; 115 : } 116 : } 117 : } 118 : 119 : return _prettyPrint(object, 0, <Object?>{}, true); 120 : } 121 : 122 0 : String _indent(int length) => List.filled(length, ' ').join(''); 123 : 124 : /// Returns the name of the type of [x] with fallbacks for core types with 125 : /// private implementations. 126 0 : String _typeName(Object x) { 127 0 : if (x is Type) return 'Type'; 128 0 : if (x is Uri) return 'Uri'; 129 0 : if (x is Set) return 'Set'; 130 0 : if (x is BigInt) return 'BigInt'; 131 0 : return '${x.runtimeType}'; 132 : } 133 : 134 : /// Returns [source] with any control characters replaced by their escape 135 : /// sequences. 136 : /// 137 : /// This doesn't add quotes to the string, but it does escape single quote 138 : /// characters so that single quotes can be applied externally. 139 0 : String _escapeString(String source) => escape(source).replaceAll("'", r"\'");