Merge pull request #751 from rcancro/debugBox

Ascii art visual debugging for ASLayoutSpecs
This commit is contained in:
appleguy
2015-10-25 21:19:09 -07:00
11 changed files with 359 additions and 3 deletions

View File

@@ -12,7 +12,7 @@
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <AsyncDisplayKit/ASLayoutable.h>
@class ASDisplayNode;
@@ -519,7 +519,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node);
* Convenience methods for debugging.
*/
@interface ASDisplayNode (Debugging)
@interface ASDisplayNode (Debugging) <ASLayoutableAsciiArtProtocol>
/**
* @abstract Return a description of the node hierarchy.

View File

@@ -2137,6 +2137,18 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
return subtree;
}
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
return NSStringFromClass([self class]);
}
@end
// We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <Foundation/Foundation.h>
@protocol ASLayoutableAsciiArtProtocol <NSObject>
/**
* Returns an ascii-art representation of this object and its children.
* For example, an ASInsetSpec may return something like this:
*
* --ASInsetLayoutSpec--
* | ASTextNode |
* ---------------------
*/
- (NSString *)asciiArtString;
/**
* returns the name of this object that will display in the ascii art. Usually this can
* simply be NSStringFromClass([self class]).
*/
- (NSString *)asciiArtName;
@end
/**
* A that takes a parent and its children and renders as ascii art box.
*/
@interface ASAsciiArtBoxCreator : NSObject
/**
* Renders an ascii art box with the children aligned horizontally
* Example:
* ------------ASStackLayoutSpec-----------
* | ASTextNode ASTextNode ASTextNode |
* ----------------------------------------
*/
+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent;
/**
* Renders an ascii art box with the children aligned vertically.
* Example:
* --ASStackLayoutSpec--
* | ASTextNode |
* | ASTextNode |
* | ASTextNode |
* ---------------------
*/
+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent;
@end

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
@import UIKit;
#import "ASAsciiArtBoxCreator.h"
static const NSUInteger kDebugBoxPadding = 2;
typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation)
{
PIDebugBoxPaddingLocationFront,
PIDebugBoxPaddingLocationEnd,
PIDebugBoxPaddingLocationBoth
};
@interface NSString(PIDebugBox)
@end
@implementation NSString(PIDebugBox)
+ (instancetype)debugbox_stringWithString:(NSString *)stringToRepeat repeatedCount:(NSUInteger)repeatCount
{
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[stringToRepeat length] * repeatCount];
for (NSUInteger index = 0; index < repeatCount; index++) {
[string appendString:stringToRepeat];
}
return [string copy];
}
- (NSString *)debugbox_stringByAddingPadding:(NSString *)padding count:(NSUInteger)count location:(PIDebugBoxPaddingLocation)location
{
NSString *paddingString = [NSString debugbox_stringWithString:padding repeatedCount:count];
switch (location) {
case PIDebugBoxPaddingLocationFront:
return [NSString stringWithFormat:@"%@%@", paddingString, self];
case PIDebugBoxPaddingLocationEnd:
return [NSString stringWithFormat:@"%@%@", self, paddingString];
case PIDebugBoxPaddingLocationBoth:
return [NSString stringWithFormat:@"%@%@%@", paddingString, self, paddingString];
}
return [self copy];
}
@end
@implementation ASAsciiArtBoxCreator
+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
{
if ([children count] == 0) {
return parent;
}
NSMutableArray *childrenLines = [NSMutableArray array];
// split the children into lines
NSUInteger lineCountPerChild = 0;
for (NSString *child in children) {
NSArray *lines = [child componentsSeparatedByString:@"\n"];
lineCountPerChild = MAX(lineCountPerChild, [lines count]);
}
for (NSString *child in children) {
NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
NSUInteger topPadding = ceilf((CGFloat)(lineCountPerChild - [lines count])/2.0);
NSUInteger bottomPadding = (lineCountPerChild - [lines count])/2.0;
NSUInteger lineLength = [lines[0] length];
for (NSUInteger index = 0; index < topPadding; index++) {
[lines insertObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength] atIndex:0];
}
for (NSUInteger index = 0; index < bottomPadding; index++) {
[lines addObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength]];
}
[childrenLines addObject:lines];
}
NSMutableArray *concatenatedLines = [NSMutableArray array];
NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding];
for (NSUInteger index = 0; index < lineCountPerChild; index++) {
NSMutableString *line = [[NSMutableString alloc] init];
[line appendFormat:@"|%@",padding];
for (NSArray *childLines in childrenLines) {
[line appendFormat:@"%@%@", childLines[index], padding];
}
[line appendString:@"|"];
[concatenatedLines addObject:line];
}
// surround the lines in a box
NSUInteger totalLineLength = [concatenatedLines[0] length];
if (totalLineLength < [parent length]) {
NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - totalLineLength;
NSUInteger leftPadding = ceilf((CGFloat)difference/2.0);
NSUInteger rightPadding = difference/2;
NSString *leftString = [@"|" debugbox_stringByAddingPadding:@" " count:leftPadding location:PIDebugBoxPaddingLocationEnd];
NSString *rightString = [@"|" debugbox_stringByAddingPadding:@" " count:rightPadding location:PIDebugBoxPaddingLocationFront];
NSMutableArray *paddedLines = [NSMutableArray array];
for (NSString *line in concatenatedLines) {
NSString *paddedLine = [line stringByReplacingOccurrencesOfString:@"|" withString:leftString options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)];
paddedLine = [paddedLine stringByReplacingOccurrencesOfString:@"|" withString:rightString options:NSCaseInsensitiveSearch range:NSMakeRange([paddedLine length] - 1, 1)];
[paddedLines addObject:paddedLine];
}
concatenatedLines = paddedLines;
totalLineLength += difference;
}
concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent];
return [concatenatedLines componentsJoinedByString:@"\n"];
}
+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
{
if ([children count] == 0) {
return parent;
}
NSMutableArray *childrenLines = [NSMutableArray array];
NSUInteger maxChildLength = 0;
for (NSString *child in children) {
NSArray *lines = [child componentsSeparatedByString:@"\n"];
maxChildLength = MAX(maxChildLength, [lines[0] length]);
}
NSUInteger rightPadding = 0;
NSUInteger leftPadding = 0;
if (maxChildLength < [parent length]) {
NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - maxChildLength;
leftPadding = ceilf((CGFloat)difference/2.0);
rightPadding = difference/2;
}
NSString *rightPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightPadding + kDebugBoxPadding];
NSString *leftPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftPadding + kDebugBoxPadding];
for (NSString *child in children) {
NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
NSUInteger leftLinePadding = ceilf((CGFloat)(maxChildLength - [lines[0] length])/2.0);
NSUInteger rightLinePadding = (maxChildLength - [lines[0] length])/2.0;
for (NSString *line in lines) {
NSString *rightLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightLinePadding];
rightLinePaddingString = [NSString stringWithFormat:@"%@%@|", rightLinePaddingString, rightPaddingString];
NSString *leftLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftLinePadding];
leftLinePaddingString = [NSString stringWithFormat:@"|%@%@", leftLinePaddingString, leftPaddingString];
NSString *paddingLine = [NSString stringWithFormat:@"%@%@%@", leftLinePaddingString, line, rightLinePaddingString];
[childrenLines addObject:paddingLine];
}
}
childrenLines = [self appendTopAndBottomToBoxString:childrenLines parent:parent];
return [childrenLines componentsJoinedByString:@"\n"];
}
+ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent
{
NSUInteger totalLineLength = [boxStrings[0] length];
[boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]];
NSUInteger leftPadding = ceilf(((CGFloat)(totalLineLength - [parent length]))/2.0);
NSUInteger rightPadding = (totalLineLength - [parent length])/2;
NSString *topLine = [parent debugbox_stringByAddingPadding:@"-" count:leftPadding location:PIDebugBoxPaddingLocationFront];
topLine = [topLine debugbox_stringByAddingPadding:@"-" count:rightPadding location:PIDebugBoxPaddingLocationEnd];
[boxStrings insertObject:topLine atIndex:0];
return boxStrings;
}
@end

View File

@@ -9,6 +9,7 @@
*/
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
@interface ASLayoutSpec : NSObject <ASLayoutable>
@@ -94,5 +95,13 @@
/** Returns all children added to this layout spec. */
- (NSArray *)children;
@end
@interface ASLayoutSpec (Debugging) <ASLayoutableAsciiArtProtocol>
/**
* Used by other layout specs to create ascii art debug strings
*/
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction;
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName;
@end

View File

@@ -122,5 +122,40 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
return self.layoutChildren[kDefaultChildrenKey];
}
@end
@implementation ASLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction
{
NSMutableArray *childStrings = [NSMutableArray array];
for (id<ASLayoutableAsciiArtProtocol> layoutChild in children) {
NSString *childString = [layoutChild asciiArtString];
if (childString) {
[childStrings addObject:childString];
}
}
if (direction == ASStackLayoutDirectionHorizontal) {
return [ASAsciiArtBoxCreator horizontalBoxStringForChildren:childStrings parent:parentName];
}
return [ASAsciiArtBoxCreator verticalBoxStringForChildren:childStrings parent:parentName];
}
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName
{
return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal];
}
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:@[self.child] parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
return NSStringFromClass([self class]);
}
@end

View File

@@ -73,3 +73,14 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey";
}
@end
@implementation ASOverlayLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)debugBoxString
{
return [ASLayoutSpec asciiArtStringForChildren:@[self.overlay, self.child] parentName:[self asciiArtName]];
}
@end

View File

@@ -87,3 +87,14 @@
}
@end
@implementation ASRatioLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)asciiArtName
{
return [NSString stringWithFormat:@"%@ (%.1f)", NSStringFromClass([self class]), self.ratio];
}
@end

View File

@@ -136,3 +136,14 @@
}
@end
@implementation ASStackLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName] direction:self.direction];
}
@end

View File

@@ -85,3 +85,14 @@
}
@end
@implementation ASStaticLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)debugBoxString
{
return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName]];
}
@end