Files
markdig/src/Textamina.Markdig/Syntax/ListBlock.cs

134 lines
4.8 KiB
C#

using Textamina.Markdig.Helpers;
using Textamina.Markdig.Parsing;
namespace Textamina.Markdig.Syntax
{
public class ListBlock : ContainerBlock
{
public static readonly BlockParser Parser = new ParserInternal();
public char BulletChar { get; set; }
private int consecutiveBlankLines;
private class ParserInternal : BlockParser
{
public override MatchLineResult Match(MatchLineState state)
{
var liner = state.Line;
// 5.2 List items
// TODO: Check with specs, it is not clear that list marker or bullet marker must be followed by at least 1 space
var saveLiner = liner.Save();
// If we have already a ListItemBlock, we are going to try to append to it
if (state.Block != null)
{
var listItem = (ListItemBlock) state.Block;
var list = (ListBlock) listItem.Parent;
// Allow all blanks lines if the last block is a fenced code block
// Allow 1 blank line inside a list
// If > 1 blank line, terminate this list
var isBlankLine = liner.IsBlankLine();
if (isBlankLine && !(state.LastBlock is FencedCodeBlock))
{
list.consecutiveBlankLines++;
if (list.consecutiveBlankLines > 1)
{
return MatchLineResult.LastDiscard;
}
return MatchLineResult.Continue;
}
list.consecutiveBlankLines = 0;
var c = liner.Current;
var startPosition = liner.Column;
while (CharHelper.IsSpaceOrTab(c))
{
c = liner.NextChar();
var endPosition = liner.Column;
if ((endPosition - startPosition) >= listItem.NumberOfSpaces)
{
return MatchLineResult.Continue;
}
}
liner.Restore(ref saveLiner);
}
return TryParseListItem(ref state);
}
private MatchLineResult TryParseListItem(ref MatchLineState state)
{
var liner = state.Line;
var preStartPosition = liner.Start;
liner.SkipLeadingSpaces3();
var preIndent = liner.Start - preStartPosition;
var c = liner.Current;
if (CharHelper.IsBulletListMarker(c))
{
var listType = c;
var startPosition = -1;
var endPosition = -1;
var tabToSpacesCount = 0;
int deltaPositions = 0;
for (int i = 0; i < 4; i++)
{
c = liner.NextChar();
if (!CharHelper.IsSpaceOrTab(c))
{
break;
}
if (i == 0)
{
startPosition = liner.Column;
}
endPosition = liner.Column;
deltaPositions = endPosition - startPosition;
if (deltaPositions >= 4)
{
liner.SpaceHeaderCount = deltaPositions - 4;
deltaPositions = 4;
break;
}
}
// If we haven't matched any spaces, early exit
if (startPosition < 0)
{
return MatchLineResult.None;
}
// Number of spaces required for the following content to be part of this list item
var numberOfSpaces = deltaPositions + 1 + preIndent;
var listItem = new ListItemBlock() {NumberOfSpaces = numberOfSpaces};
var parentList = (state.Block as ListItemBlock)?.Parent as ListBlock;
// Reset the list if it is a new list or a new type of bullet
if (parentList == null || parentList.BulletChar != listType)
{
parentList = new ListBlock() { BulletChar = listType };
}
parentList.Children.Add(listItem);
listItem.Parent = parentList;
state.Block = listItem;
return MatchLineResult.Continue;
}
return MatchLineResult.None;
}
}
}
}