Commit c0d326b1 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #1218 from Hixie/toString

Improve debugging aids for widgets, rendering.
parents a89fdd92 970c8ce8
......@@ -588,8 +588,8 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
});
}
String toStringName() {
String header = super.toStringName();
String toString() {
String header = super.toString();
if (_overflow is double && _overflow > 0.0)
header += ' OVERFLOWING';
return header;
......
......@@ -1041,18 +1041,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
// You must not add yourself to /result/ if you return false.
String toString([String prefix = '']) {
RenderObject debugPreviousActiveLayout = _debugActiveLayout;
_debugActiveLayout = null;
String header = toStringName();
prefix += ' ';
String result = '${header}\n${debugDescribeSettings(prefix)}${debugDescribeChildren(prefix)}';
_debugActiveLayout = debugPreviousActiveLayout;
return result;
}
/// Returns a human understandable name
String toStringName() {
String toString() {
String header = '${runtimeType}';
if (_relayoutSubtreeRoot != null && _relayoutSubtreeRoot != this) {
int count = 1;
......@@ -1070,7 +1060,25 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
return header;
}
/// Returns a description of the tree rooted at this node.
/// If the prefix argument is provided, then every line in the output
/// will be prefixed by that string.
String toStringDeep([String prefix = '']) {
RenderObject debugPreviousActiveLayout = _debugActiveLayout;
_debugActiveLayout = null;
prefix += ' ';
String result = '$this\n${debugDescribeSettings(prefix)}${debugDescribeChildren(prefix)}';
_debugActiveLayout = debugPreviousActiveLayout;
return result;
}
/// Returns a string describing the current node's fields, one field per line,
/// with each line prefixed by the prefix argument. Subclasses should override
/// this to have their information included in toStringDeep().
String debugDescribeSettings(String prefix) => '${prefix}parentData: ${parentData}\n${prefix}constraints: ${constraints}\n';
/// Returns a string describing the current node's descendants. Each line of
/// the subtree in the output should be indented by the prefix argument.
String debugDescribeChildren(String prefix) => '';
}
......@@ -1112,7 +1120,7 @@ abstract class RenderObjectWithChildMixin<ChildType extends RenderObject> implem
}
String debugDescribeChildren(String prefix) {
if (child != null)
return '${prefix}child: ${child.toString(prefix)}';
return '${prefix}child: ${child.toStringDeep(prefix)}';
return '';
}
}
......@@ -1352,7 +1360,7 @@ abstract class ContainerRenderObjectMixin<ChildType extends RenderObject, Parent
int count = 1;
ChildType child = _firstChild;
while (child != null) {
result += '${prefix}child ${count}: ${child.toString(prefix)}';
result += '${prefix}child ${count}: ${child.toStringDeep(prefix)}';
count += 1;
child = child.parentData.nextSibling;
}
......
......@@ -171,12 +171,9 @@ class SkyBinding extends HitTestTarget {
GestureArena.instance.close(event.pointer);
return EventDisposition.processed;
}
}
String toString() => 'Render Tree:\n${_renderView}';
/// Prints a textual representation of the entire render tree
void debugDumpRenderTree() {
toString().split('\n').forEach(print);
}
/// Prints a textual representation of the entire render tree
void debugDumpRenderTree() {
SkyBinding.instance.renderView.toStringDeep().split('\n').forEach(print);
}
......@@ -235,8 +235,10 @@ class Focus extends StatefulComponent {
}
}
String toStringName() {
return '${super.toStringName()}(focusedScope=$_focusedScope; focusedWidget=$_focusedWidget)';
void debugAddDetails(List<String> details) {
super.debugAddDetails(details);
details.add('focusedScope=$_focusedScope');
details.add('focusedWidget=$_focusedWidget');
}
}
......@@ -139,8 +139,8 @@ abstract class GlobalKey extends Key {
assert(() {
String message = '';
for (GlobalKey key in _debugDuplicates.keys) {
message += "Duplicate GlobalKey found amongst mounted widgets: $key (${_debugDuplicates[key]} instances)\n";
message += "Most recently registered instance is:\n${_registry[key]}\n";
message += 'Duplicate GlobalKey found amongst mounted widgets: $key (${_debugDuplicates[key]} instances)\n';
message += 'Most recently registered instance is:\n${_registry[key]}\n';
}
if (!_debugDuplicates.isEmpty)
throw message;
......@@ -288,7 +288,7 @@ abstract class Widget {
static void _notifyMountStatusChanged() {
try {
sky.tracing.begin("Widget._notifyMountStatusChanged");
sky.tracing.begin('Widget._notifyMountStatusChanged');
_notifyingMountStatus = true;
for (Widget node in _mountedChanged) {
if (node._wasMounted != node._mounted) {
......@@ -302,7 +302,7 @@ abstract class Widget {
_mountedChanged.clear();
} finally {
_notifyingMountStatus = false;
sky.tracing.end("Widget._notifyMountStatusChanged");
sky.tracing.end('Widget._notifyMountStatusChanged');
}
GlobalKey._notifyListeners();
}
......@@ -404,7 +404,7 @@ abstract class Widget {
String debugDetails;
assert(() {
// we save this information early because by the time the exception fires we might have changed everything around
debugDetails = " old child: ${oldNode?.toStringName()}\n new child: ${newNode?.toStringName()}";
debugDetails = ' old child: $oldNode\n new child: $newNode';
return true;
});
try {
......@@ -508,36 +508,28 @@ abstract class Widget {
return newNode;
} catch (e, stack) {
_debugReportException('syncing children of ${this.toStringName()}\n$debugDetails', e, stack);
_debugReportException('syncing children of $this\n$debugDetails', e, stack);
return null;
}
}
String _adjustPrefixWithParentCheck(Widget child, String prefix) {
if (child.parent == this)
return prefix;
if (child.parent == null)
return '$prefix [[DISCONNECTED]] ';
return '$prefix [[PARENT IS ${child.parent.toStringName()}]] ';
// This function can be safely called when the layout is valid.
// For example Listener or SizeObserver callbacks can safely call
// globalToLocal().
Point globalToLocal(Point point) {
assert(mounted);
assert(renderObject is RenderBox);
return (renderObject as RenderBox).globalToLocal(point);
}
String toString([String prefix = '', String startPrefix = '']) {
String childrenString = '';
List<Widget> children = new List<Widget>();
walkChildren(children.add);
if (children.length > 0) {
Widget lastChild = children.removeLast();
String nextStartPrefix = prefix + ' +-';
String nextPrefix = prefix + ' | ';
for (Widget child in children)
childrenString += child.toString(nextPrefix, _adjustPrefixWithParentCheck(child, nextStartPrefix));
nextStartPrefix = prefix + ' \'-';
nextPrefix = prefix + ' ';
childrenString += lastChild.toString(nextPrefix, _adjustPrefixWithParentCheck(lastChild, nextStartPrefix));
}
return '$startPrefix${toStringName()}\n$childrenString';
// See globalToLocal().
Point localToGlobal(Point point) {
assert(mounted);
assert(renderObject is RenderBox);
return (renderObject as RenderBox).localToGlobal(point);
}
String toStringName() {
String toString() {
List<String> details = <String>[];
debugAddDetails(details);
String detailString = details.join('; ');
......@@ -558,21 +550,28 @@ abstract class Widget {
}
}
}
// This function can be safely called when the layout is valid.
// For example Listener or SizeObserver callbacks can safely call
// globalToLocal().
Point globalToLocal(Point point) {
assert(mounted);
assert(renderObject is RenderBox);
return (renderObject as RenderBox).globalToLocal(point);
String toStringDeep([String prefix = '', String startPrefix = '']) {
String childrenString = '';
List<Widget> children = new List<Widget>();
walkChildren(children.add);
if (children.length > 0) {
Widget lastChild = children.removeLast();
String nextStartPrefix = prefix + ' +-';
String nextPrefix = prefix + ' | ';
for (Widget child in children)
childrenString += child.toStringDeep(nextPrefix, _adjustPrefixWithParentCheck(child, nextStartPrefix));
nextStartPrefix = prefix + ' \'-';
nextPrefix = prefix + ' ';
childrenString += lastChild.toStringDeep(nextPrefix, _adjustPrefixWithParentCheck(lastChild, nextStartPrefix));
}
return '$startPrefix$this\n$childrenString';
}
// See globalToLocal().
Point localToGlobal(Point point) {
assert(mounted);
assert(renderObject is RenderBox);
return (renderObject as RenderBox).localToGlobal(point);
String _adjustPrefixWithParentCheck(Widget child, String prefix) {
if (child.parent == this)
return prefix;
if (child.parent == null)
return '$prefix [[DISCONNECTED]] ';
return '$prefix [[PARENT IS ${child.parent}]] ';
}
}
......@@ -807,7 +806,7 @@ abstract class Component extends Widget {
_child = build();
assert(_child != null);
} catch (e, stack) {
_debugReportException("building ${this.toStringName()}", e, stack);
_debugReportException('building $this', e, stack);
}
_currentOrder = lastOrder;
assert(() { _debugChildTaken = false; return true; });
......@@ -817,7 +816,7 @@ abstract class Component extends Widget {
assert(!_debugChildTaken); // we shouldn't be able to lose our child when we're syncing it!
assert(_child == null || _child.parent == this);
} catch (e, stack) {
_debugReportException('syncing build output of ${this.toStringName()}\n old child: ${oldChild?.toStringName()}\n new child: ${_child?.toStringName()}', e, stack);
_debugReportException('syncing build output of $this\n old child: $oldChild\n new child: $_child', e, stack);
_child = null;
}
assert(() {
......@@ -1100,7 +1099,7 @@ abstract class RenderObjectWrapper extends Widget {
assert(_renderObject != null);
}
assert(() {
_renderObject.debugExceptionContext = Component._debugComponentBuildTree.fold(' Widget build stack:', (String s, Component c) => s + '\n ${c.toStringName()}');
_renderObject.debugExceptionContext = Component._debugComponentBuildTree.fold(' Widget build stack:', (String result, Component component) => result + '\n $component');
return true;
});
assert(_renderObject == renderObject); // in case a subclass reintroduces it
......@@ -1443,7 +1442,7 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper {
continue; // when these nodes are reordered, we just reassign the data
if (!idSet.add(child.key)) {
throw '''If multiple keyed nodes exist as children of another node, they must have unique keys. ${toStringName()} has multiple children with key "${child.key}".''';
throw 'If multiple keyed nodes exist as children of another node, they must have unique keys. $this has multiple children with key "${child.key}".';
}
}
return false;
......@@ -1579,11 +1578,13 @@ void runApp(App app, { RenderView renderViewOverride, bool enableProfilingLoop:
});
}
}
/// Prints a textual representation of the entire widget tree
void debugDumpApp() {
if (_container != null)
_container.toString().split('\n').forEach(print);
_container.toStringDeep().split('\n').forEach(print);
else
print("runApp() not yet called");
print('runApp() not yet called');
}
......@@ -1639,7 +1640,7 @@ void _debugReportException(String context, dynamic exception, StackTrace stack)
print('Stack trace:');
'$stack'.split('\n').forEach(print);
print('Build stack:');
Component._debugComponentBuildTree.forEach((Component c) { print(' ${c.toStringName()}'); });
Component._debugComponentBuildTree.forEach((Component component) { print(' $component'); });
print('Current application widget tree:');
debugDumpApp();
print('------------------------------------------------------------------------');
......
......@@ -171,7 +171,7 @@ class RenderScaffold extends RenderBox {
}
String debugDescribeChildren(String prefix) {
return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toString(prefix)}').join();
return _slots.keys.map((slot) => '${prefix}${slot}: ${_slots[slot].toStringDeep(prefix)}').join();
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment