Skip to content

Commit

Permalink
Merge pull request #101 from mdiep/infinite-list-loop
Browse files Browse the repository at this point in the history
Fix infinite loop in list parsing
  • Loading branch information
mdiep committed Apr 19, 2016
2 parents b05594c + 46fb011 commit 07b9952
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 32 deletions.
47 changes: 16 additions & 31 deletions Source/MMParser.m
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ - (MMElement *)_parseBlockElementWithScanner:(MMScanner *)scanner
return element;

[scanner beginTransaction];
element = [self _parseListWithScanner:scanner atIndentationLevel:0];
element = [self _parseListWithScanner:scanner];
[scanner commitTransaction:element != nil];
if (element)
return element;
Expand Down Expand Up @@ -515,7 +515,7 @@ - (NSArray *)_parseCodeLinesWithScanner:(MMScanner *)scanner
// Add a newline
MMElement *newline = [MMElement new];
newline.type = MMElementTypeNone;
newline.range = NSMakeRange(0, 0);
newline.range = NSMakeRange(scanner.location, 0);
[children addObject:newline];
}
}
Expand All @@ -541,7 +541,7 @@ - (MMElement *)_parseCodeBlockWithScanner:(MMScanner *)scanner
NSUInteger numOfEmptyLines = [scanner skipEmptyLines];
for (NSUInteger idx=0; idx<numOfEmptyLines; idx++)
{
[element addInnerRange:NSMakeRange(0, 0)];
[element addInnerRange:NSMakeRange(scanner.location, 0)];
}

// Need 4 spaces to continue the code block
Expand Down Expand Up @@ -729,7 +729,7 @@ - (BOOL)_parseListMarkerWithScanner:(MMScanner *)scanner
return NO;
}

- (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel:(NSUInteger)level
- (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner
{
BOOL canContainBlocks = NO;

Expand All @@ -738,18 +738,7 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel
canContainBlocks = YES;
}

// Make sure there's enough leading space
[scanner beginTransaction];
NSUInteger toSkip = 4 * level;
NSUInteger skipped = [scanner skipIndentationUpTo:toSkip];
if (skipped != toSkip)
{
[scanner commitTransaction:NO];
return nil;
}
[scanner commitTransaction:YES];

[scanner skipIndentationUpTo:3]; // Additional optional space
[scanner skipIndentationUpTo:3]; // Optional space

BOOL foundAnItem = [self _parseListMarkerWithScanner:scanner];
if (!foundAnItem)
Expand Down Expand Up @@ -781,7 +770,7 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel

// Check for the start of a new list item
[scanner beginTransaction];
[scanner skipIndentationUpTo:4*level + 3];
[scanner skipIndentationUpTo:3];
BOOL newMarker = [self _parseListMarkerWithScanner:scanner];
[scanner commitTransaction:NO];
if (newMarker)
Expand All @@ -796,13 +785,13 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel

// Check for a nested list
[scanner beginTransaction];
[scanner skipIndentationUpTo:4*(level + 1) + 3];
[scanner skipIndentationUpTo:4 + 3];
[scanner beginTransaction];
BOOL newList = [self _parseListMarkerWithScanner:scanner];
[scanner commitTransaction:NO];
if (newList && nestedListIndex == NSNotFound)
{
[element addInnerRange:NSMakeRange(0, 0)];
[element addInnerRange:NSMakeRange(scanner.location, 0)];
nestedListIndex = element.innerRanges.count;
[element addInnerRange:scanner.currentRange];

Expand All @@ -817,9 +806,8 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel
{
// Must be 4 spaces past the indentation level to start a new paragraph
[scanner beginTransaction];
NSUInteger newLevel = 4*(1+level);
NSUInteger indentation = [scanner skipIndentationUpTo:newLevel];
if (indentation < newLevel)
NSUInteger indentation = [scanner skipIndentationUpTo:4];
if (indentation < 4)
{
[scanner commitTransaction:NO];
[scanner commitTransaction:NO];
Expand All @@ -828,7 +816,7 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel
[scanner commitTransaction:YES];
[scanner commitTransaction:YES];

[element addInnerRange:NSMakeRange(0, 0)];
[element addInnerRange:NSMakeRange(scanner.location, 0)];
canContainBlocks = YES;
}
else
Expand All @@ -837,7 +825,7 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel

// Don't skip past where a nested list would start because that list
// could have its own nested list, so the whitespace will be needed.
[scanner skipIndentationUpTo:4*(level + 1)];
[scanner skipIndentationUpTo:4];
}

if (nestedListIndex != NSNotFound)
Expand Down Expand Up @@ -890,20 +878,17 @@ - (MMElement *)_parseListItemWithScanner:(MMScanner *)scanner atIndentationLevel
return element;
}

- (MMElement *)_parseListWithScanner:(MMScanner *)scanner atIndentationLevel:(NSUInteger)level
- (MMElement *)_parseListWithScanner:(MMScanner *)scanner
{
[scanner beginTransaction];
// Check the amount of leading whitespace
NSUInteger toSkip = 4 * level;
NSUInteger skipped = [scanner skipIndentationUpTo:toSkip];

[scanner skipIndentationUpTo:3]; // Additional optional space
[scanner skipIndentationUpTo:3]; // Optional space
unichar nextChar = scanner.nextCharacter;
BOOL isBulleted = (nextChar == '*' || nextChar == '-' || nextChar == '+');
BOOL hasMarker = [self _parseListMarkerWithScanner:scanner];
[scanner commitTransaction:NO];

if (toSkip != skipped || !hasMarker)
if (!hasMarker)
return nil;

MMElement *element = [MMElement new];
Expand All @@ -922,7 +907,7 @@ - (MMElement *)_parseListWithScanner:(MMScanner *)scanner atIndentationLevel:(NS
break;

[scanner beginTransaction];
MMElement *item = [self _parseListItemWithScanner:scanner atIndentationLevel:level];
MMElement *item = [self _parseListItemWithScanner:scanner];
if (!item)
{
[scanner commitTransaction:NO];
Expand Down
2 changes: 1 addition & 1 deletion Source/MMScanner.m
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ - (NSUInteger)skipEmptyLines
{
[self beginTransaction];
[self skipWhitespace];
if (![self atEndOfLine])
if (!self.atEndOfLine)
{
[self commitTransaction:NO];
break;
Expand Down
28 changes: 28 additions & 0 deletions Tests/MMListTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,34 @@ - (void)testNestedLists_trailingNested
MMAssertMarkdownEqualsHTML(markdown, html);
}

- (void)testNestedListWithMultipleEmptyLinesAndBacktracking
{
// https://github.com/mdiep/MMMarkdown/issues/100
NSString *markdown =
@"* A\n"
" * B\n"
"\n"
" * C\n"
"\n"
" D\n";
NSString *html =
@"<ul>\n"
"<li>\n"
"<p>A</p>\n"
"<ul>\n"
"<li>\n"
"<p>B</p>\n"
"</li>\n"
"<li>\n"
"<p>C</p>\n"
"</li>\n"
"</ul>\n"
"<p>D</p>\n"
"</li>\n"
"</ul>";
MMAssertMarkdownEqualsHTML(markdown, html);
}

- (void)testList_followedByHorizontalRule
{
NSString *markdown = @"* One\n"
Expand Down

0 comments on commit 07b9952

Please sign in to comment.