diff --git a/src/classes/GRMustacheBuffer.m b/src/classes/GRMustacheBuffer.m index 4483c4ff..c8920fe5 100644 --- a/src/classes/GRMustacheBuffer.m +++ b/src/classes/GRMustacheBuffer.m @@ -53,14 +53,14 @@ - (instancetype)initWithContentType:(GRMustacheContentType)contentType outputBuf #pragma mark - Abstract class GRMustacheBuffer @interface GRMustacheBuffer() -@property (nonatomic, readonly) GRMustacheContentType contentType; +@property (nonatomic, retain) NSString *prefix; - (id)initWithContentType:(GRMustacheContentType)contentType; -- (void)appendSafeString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix; -- (NSString *)appendSafeRendering:(NSString *)string; +- (void)appendSafeString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType; @end @implementation GRMustacheBuffer @synthesize contentType=_contentType; +@synthesize prefix=_prefix; + (instancetype)bufferWithContentType:(GRMustacheContentType)contentType outputString:(NSMutableString *)outputString { @@ -82,47 +82,102 @@ - (id)initWithContentType:(GRMustacheContentType)contentType self = [super init]; if (self) { _contentType = contentType; + _swallowsBlankEndOfLine = YES; } return self; } -- (void)appendString:(NSString *)string contentType:(GRMustacheContentType)contentType blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix +- (NSString *)appendString:(NSString *)string contentType:(GRMustacheContentType)contentType inputType:(GRMustacheBufferInputType)inputType { - if (string == nil) return; - if (_contentType == GRMustacheContentTypeHTML && contentType != GRMustacheContentTypeHTML) { - string = [GRMustache escapeHTML:string]; + if (string == nil) { + string = @""; } - - [self appendSafeString:string blank:blank prefix:prefix suffix:suffix]; -} - -- (NSString *)appendRendering:(NSString *)string contentType:(GRMustacheContentType)contentType -{ - if (string == nil) return @""; if (_contentType == GRMustacheContentTypeHTML && contentType != GRMustacheContentTypeHTML) { string = [GRMustache escapeHTML:string]; } - return [self appendSafeRendering:string]; -} - -- (void)flush -{ - -} - -- (void)appendSafeString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix -{ - NSAssert(NO, @"Subclasses must override"); + switch (inputType) { + case GRMustacheBufferInputTypeUserContent: + if (string.length > 0) { + if (self.prefix) { + [self appendSafeString:self.prefix inputType:GRMustacheBufferInputTypeBlankPrefix]; + self.prefix = nil; + } + _swallowsBlankEndOfLine = NO; + [self appendSafeString:string inputType:inputType]; + return string; + } else { + return @""; + } + break; + + case GRMustacheBufferInputTypeContent: + if (self.prefix) { + [self appendSafeString:self.prefix inputType:GRMustacheBufferInputTypeBlankPrefix]; + self.prefix = nil; + } + _swallowsBlankEndOfLine = NO; + [self appendSafeString:string inputType:inputType]; + return string; + break; + + case GRMustacheBufferInputTypeContentEndOfLine: + if (self.prefix) { + [self appendSafeString:self.prefix inputType:GRMustacheBufferInputTypeBlankPrefix]; + self.prefix = nil; + } + _swallowsBlankEndOfLine = YES; + [self appendSafeString:string inputType:inputType]; + return string; + break; + + case GRMustacheBufferInputTypeBlankLine: + _swallowsBlankEndOfLine = YES; + [self appendSafeString:string inputType:inputType]; + return string; + break; + + case GRMustacheBufferInputTypeBlankEndOfLine: + if (_swallowsBlankEndOfLine) { + return @""; + } else { + if (self.prefix) { + [self appendSafeString:self.prefix inputType:GRMustacheBufferInputTypeBlankPrefix]; + self.prefix = nil; + } + _swallowsBlankEndOfLine = YES; + [self appendSafeString:string inputType:inputType]; + return string; + } + break; + + case GRMustacheBufferInputTypeBlankPrefix: + self.prefix = string; + _swallowsBlankEndOfLine = YES; + return string; + break; + + case GRMustacheBufferInputTypeBlankSuffix: + if (_swallowsBlankEndOfLine) { + return @""; + } else { + if (self.prefix) { + [self appendSafeString:self.prefix inputType:GRMustacheBufferInputTypeBlankPrefix]; + self.prefix = nil; + } + _swallowsBlankEndOfLine = NO; + [self appendSafeString:string inputType:inputType]; + return string; + } + break; + } } -- (NSString *)appendSafeRendering:(NSString *)string +- (void)appendSafeString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType { NSAssert(NO, @"Subclasses must override"); - return nil; } - @end @@ -146,17 +201,11 @@ - (instancetype)initWithContentType:(GRMustacheContentType)contentType outputStr return self; } -- (void)appendSafeString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix +- (void)appendSafeString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType { [_outputString appendString:string]; } -- (NSString *)appendSafeRendering:(NSString *)string -{ - [_outputString appendString:string]; - return string; -} - @end @@ -181,14 +230,9 @@ - (instancetype)initWithContentType:(GRMustacheContentType)contentType outputBuf } -- (void)appendSafeString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix -{ - [_outputBuffer appendString:string contentType:self.contentType blank:blank prefix:prefix suffix:suffix]; -} - -- (NSString *)appendSafeRendering:(NSString *)string +- (void)appendSafeString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType { - return [_outputBuffer appendRendering:string contentType:self.contentType]; + [_outputBuffer appendString:string contentType:self.contentType inputType:inputType]; } @end diff --git a/src/classes/GRMustacheBuffer_private.h b/src/classes/GRMustacheBuffer_private.h index 60c01042..2f7e2985 100644 --- a/src/classes/GRMustacheBuffer_private.h +++ b/src/classes/GRMustacheBuffer_private.h @@ -24,36 +24,44 @@ #import "GRMustacheAvailabilityMacros_private.h" #import "GRMustacheConfiguration_private.h" +typedef NS_ENUM(NSInteger, GRMustacheBufferInputType) { + GRMustacheBufferInputTypeUserContent, + GRMustacheBufferInputTypeContent, + GRMustacheBufferInputTypeContentEndOfLine, + GRMustacheBufferInputTypeBlankLine, + GRMustacheBufferInputTypeBlankEndOfLine, + GRMustacheBufferInputTypeBlankPrefix, + GRMustacheBufferInputTypeBlankSuffix, +}; + /** * TODO */ @interface GRMustacheBuffer : NSObject { @private GRMustacheContentType _contentType; + NSString *_prefix; + BOOL _swallowsBlankEndOfLine; } /** * TODO */ -+ (instancetype)bufferWithContentType:(GRMustacheContentType)contentType outputString:(NSMutableString *)outputString GRMUSTACHE_API_INTERNAL; +@property (nonatomic, readonly) GRMustacheContentType contentType; /** * TODO */ -+ (instancetype)bufferWithContentType:(GRMustacheContentType)contentType outputBuffer:(GRMustacheBuffer *)outputBuffer GRMUSTACHE_API_INTERNAL; ++ (instancetype)bufferWithContentType:(GRMustacheContentType)contentType outputString:(NSMutableString *)outputString GRMUSTACHE_API_INTERNAL; /** * TODO */ -- (void)appendString:(NSString *)string contentType:(GRMustacheContentType)contentType blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix GRMUSTACHE_API_INTERNAL; ++ (instancetype)bufferWithContentType:(GRMustacheContentType)contentType outputBuffer:(GRMustacheBuffer *)outputBuffer GRMUSTACHE_API_INTERNAL; /** * TODO */ -- (NSString *)appendRendering:(NSString *)string contentType:(GRMustacheContentType)contentType GRMUSTACHE_API_INTERNAL; +- (NSString *)appendString:(NSString *)string contentType:(GRMustacheContentType)contentType inputType:(GRMustacheBufferInputType)inputType GRMUSTACHE_API_INTERNAL; -/** - * TODO - */ -- (void)flush GRMUSTACHE_API_INTERNAL; @end diff --git a/src/classes/GRMustacheCompiler.m b/src/classes/GRMustacheCompiler.m index e20a426f..65077914 100644 --- a/src/classes/GRMustacheCompiler.m +++ b/src/classes/GRMustacheCompiler.m @@ -239,7 +239,7 @@ - (BOOL)parser:(GRMustacheParser *)parser shouldContinueAfterParsingToken:(GRMus NSAssert(token.templateSubstring.length > 0, @"WTF parser?"); // Success: append GRMustacheTextComponent - [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring blank:NO prefix:NO suffix:NO]]; + [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring inputType:GRMustacheBufferInputTypeContent]]; break; @@ -248,7 +248,7 @@ - (BOOL)parser:(GRMustacheParser *)parser shouldContinueAfterParsingToken:(GRMus NSAssert(token.templateSubstring.length > 0, @"WTF parser?"); // Success: append GRMustacheTextComponent - [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring blank:YES prefix:YES suffix:YES]]; + [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring inputType:GRMustacheBufferInputTypeBlankLine]]; break; case GRMustacheTokenTypeBlankEndOfLine: @@ -256,7 +256,7 @@ - (BOOL)parser:(GRMustacheParser *)parser shouldContinueAfterParsingToken:(GRMus NSAssert(token.templateSubstring.length > 0, @"WTF parser?"); // Success: append GRMustacheTextComponent - [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring blank:YES prefix:NO suffix:YES]]; + [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring inputType:GRMustacheBufferInputTypeBlankEndOfLine]]; break; case GRMustacheTokenTypeBlankPrefix: @@ -264,7 +264,7 @@ - (BOOL)parser:(GRMustacheParser *)parser shouldContinueAfterParsingToken:(GRMus NSAssert(token.templateSubstring.length > 0, @"WTF parser?"); // Success: append GRMustacheTextComponent - [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring blank:YES prefix:YES suffix:NO]]; + [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring inputType:GRMustacheBufferInputTypeBlankPrefix]]; break; case GRMustacheTokenTypeBlankSuffix: @@ -272,7 +272,7 @@ - (BOOL)parser:(GRMustacheParser *)parser shouldContinueAfterParsingToken:(GRMus NSAssert(token.templateSubstring.length > 0, @"WTF parser?"); // Success: append GRMustacheTextComponent - [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring blank:YES prefix:NO suffix:NO]]; + [_currentComponents addObject:[GRMustacheTextComponent textComponentWithString:token.templateSubstring inputType:GRMustacheBufferInputTypeBlankSuffix]]; break; diff --git a/src/classes/GRMustacheParser.m b/src/classes/GRMustacheParser.m index 63f2a61c..10dbaa21 100644 --- a/src/classes/GRMustacheParser.m +++ b/src/classes/GRMustacheParser.m @@ -550,13 +550,23 @@ - (void)parseTemplateString:(NSString *)templateString templateID:(id)templateID break; case stateSpaceRun: { - // Blank suffix - GRMustacheToken *token = [GRMustacheToken tokenWithType:GRMustacheTokenTypeBlankSuffix - templateString:templateString - templateID:templateID - line:lineNumber - range:(NSRange){ .location = start, .length = i-start}]; - if (![self.delegate parser:self shouldContinueAfterParsingToken:token]) return; + if (start == lineStart) { + // Content + GRMustacheToken *token = [GRMustacheToken tokenWithType:GRMustacheTokenTypeContent + templateString:templateString + templateID:templateID + line:lineNumber + range:(NSRange){ .location = start, .length = i-start}]; + if (![self.delegate parser:self shouldContinueAfterParsingToken:token]) return; + } else { + // Blank suffix + GRMustacheToken *token = [GRMustacheToken tokenWithType:GRMustacheTokenTypeBlankSuffix + templateString:templateString + templateID:templateID + line:lineNumber + range:(NSRange){ .location = start, .length = i-start}]; + if (![self.delegate parser:self shouldContinueAfterParsingToken:token]) return; + } } break; case stateContent: { diff --git a/src/classes/GRMustacheSectionTag.m b/src/classes/GRMustacheSectionTag.m index ef1f7f74..a46fa0d4 100644 --- a/src/classes/GRMustacheSectionTag.m +++ b/src/classes/GRMustacheSectionTag.m @@ -84,7 +84,6 @@ - (NSString *)renderContentWithContext:(GRMustacheContext *)context HTMLSafe:(BO if (HTMLSafe != NULL) { *HTMLSafe = (self.contentType == GRMustacheContentTypeHTML); } - [buffer flush]; return rendering; } diff --git a/src/classes/GRMustacheTag.m b/src/classes/GRMustacheTag.m index bf65a801..a04b4ab7 100644 --- a/src/classes/GRMustacheTag.m +++ b/src/classes/GRMustacheTag.m @@ -223,7 +223,7 @@ - (BOOL)renderContentType:(GRMustacheContentType)requiredContentType inBuffer:(G // promote renderingContentType = GRMustacheContentTypeHTML; } - rendering = [buffer appendRendering:rendering contentType:renderingContentType]; + rendering = [buffer appendString:rendering contentType:renderingContentType inputType:GRMustacheBufferInputTypeUserContent]; // Tag delegates post-rendering callbacks diff --git a/src/classes/GRMustacheTemplate.m b/src/classes/GRMustacheTemplate.m index 1139493b..86534696 100644 --- a/src/classes/GRMustacheTemplate.m +++ b/src/classes/GRMustacheTemplate.m @@ -109,7 +109,6 @@ - (NSString *)renderContentWithContext:(GRMustacheContext *)context HTMLSafe:(BO if (HTMLSafe != NULL) { *HTMLSafe = (self.contentType == GRMustacheContentTypeHTML); } - [buffer flush]; return rendering; } @@ -138,8 +137,8 @@ - (BOOL)renderContentType:(GRMustacheContentType)requiredContentType inBuffer:(G return NO; } - GRMustacheBuffer *localBuffer = [GRMustacheBuffer bufferWithContentType:_contentType outputBuffer:buffer]; - + GRMustacheBuffer *localBuffer = (_contentType == buffer.contentType) ? buffer : [GRMustacheBuffer bufferWithContentType:_contentType outputBuffer:buffer]; + for (id component in _components) { // component may be overriden by a GRMustacheTemplateOverride: resolve it. component = [context resolveTemplateComponent:component]; @@ -150,8 +149,6 @@ - (BOOL)renderContentType:(GRMustacheContentType)requiredContentType inBuffer:(G } } - [localBuffer flush]; - return YES; } diff --git a/src/classes/GRMustacheTextComponent.m b/src/classes/GRMustacheTextComponent.m index 0cebb5f3..fcff6943 100644 --- a/src/classes/GRMustacheTextComponent.m +++ b/src/classes/GRMustacheTextComponent.m @@ -26,16 +26,16 @@ @interface GRMustacheTextComponent() @property (nonatomic, retain) NSString *string; -- (id)initWithString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix; +- (id)initWithString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType; @end @implementation GRMustacheTextComponent @synthesize string=_string; -+ (instancetype)textComponentWithString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix ++ (instancetype)textComponentWithString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType { - return [[[self alloc] initWithString:string blank:blank prefix:prefix suffix:suffix] autorelease]; + return [[[self alloc] initWithString:string inputType:inputType] autorelease]; } - (void)dealloc @@ -48,7 +48,7 @@ - (void)dealloc - (BOOL)renderContentType:(GRMustacheContentType)requiredContentType inBuffer:(GRMustacheBuffer *)buffer withContext:(GRMustacheContext *)context error:(NSError **)error { - [buffer appendString:_string contentType:requiredContentType blank:_blank prefix:_prefix suffix:_suffix]; + [buffer appendString:_string contentType:requiredContentType inputType:_inputType]; return YES; } @@ -60,15 +60,13 @@ - (BOOL)renderContentType:(GRMustacheContentType)requiredContentType inBuffer:(G #pragma mark Private -- (id)initWithString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix +- (id)initWithString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType { NSAssert(string, @"WTF"); self = [self init]; if (self) { self.string = string; - _blank = blank; - _prefix = prefix; - _suffix = suffix; + _inputType = inputType; } return self; } diff --git a/src/classes/GRMustacheTextComponent_private.h b/src/classes/GRMustacheTextComponent_private.h index 788213db..e4184655 100644 --- a/src/classes/GRMustacheTextComponent_private.h +++ b/src/classes/GRMustacheTextComponent_private.h @@ -22,6 +22,7 @@ #import "GRMustacheAvailabilityMacros_private.h" #import "GRMustacheTemplateComponent_private.h" +#import "GRMustacheBuffer_private.h" /** * A GRMustacheTextComponent is a template component that renders raw template @@ -38,18 +39,18 @@ @interface GRMustacheTextComponent: NSObject { @private NSString *_string; - BOOL _blank; - BOOL _prefix; - BOOL _suffix; + GRMustacheBufferInputType _inputType; } /** * Builds and returns a GRMustacheTextComponent. * + * TODO + * * @param string The string that should be rendered. * @return a GRMustacheTextComponent */ -+ (instancetype)textComponentWithString:(NSString *)string blank:(BOOL)blank prefix:(BOOL)prefix suffix:(BOOL)suffix GRMUSTACHE_API_INTERNAL; ++ (instancetype)textComponentWithString:(NSString *)string inputType:(GRMustacheBufferInputType)inputType GRMUSTACHE_API_INTERNAL; @end