private BlockState TryParseListItem(BlockProcessor state, Block block) { var currentListItem = block as ListItemBlock; var currentParent = block as ListBlock ?? (ListBlock)currentListItem?.Parent; // We can early exit if we have a code indent and we are either (1) not in a ListItem, (2) preceded by a blank line, (3) in an unordered list if (state.IsCodeIndent && (currentListItem is null || currentListItem.LastChild is BlankLineBlock || !currentParent.IsOrdered)) { return(BlockState.None); } var initColumnBeforeIndent = state.ColumnBeforeIndent; var initColumn = state.Column; var sourcePosition = state.Start; var sourceEndPosition = state.Line.End; var c = state.CurrentChar; var itemParser = mapItemParsers[c]; bool isOrdered = itemParser is OrderedListItemParser; if (itemParser == null) { return(BlockState.None); } // Try to parse the list item ListInfo listInfo; if (!itemParser.TryParse(state, currentParent?.BulletType ?? '\0', out listInfo)) { // Reset to an a start position state.GoToColumn(initColumn); return(BlockState.None); } // Gets the current character after a successful parsing of the list information c = state.CurrentChar; // Item starting with a blank line int columnWidth; // Do we have a blank line right after the bullet? if (c == '\0') { // Use a negative number to store the number of expected chars columnWidth = -(state.Column - initColumnBeforeIndent + 1); } else { if (!c.IsSpaceOrTab()) { state.GoToColumn(initColumn); return(BlockState.None); } // Parse the following indent state.RestartIndent(); var columnBeforeIndent = state.Column; state.ParseIndent(); // We expect at most 4 columns after // If we have more, we reset the position if (state.Indent > 4) { state.GoToColumn(columnBeforeIndent + 1); } // Number of spaces required for the following content to be part of this list item // If the list item starts with a blank line, the number of spaces // following the list marker doesn't change the required indentation columnWidth = (state.IsBlankLine ? columnBeforeIndent : state.Column) - initColumnBeforeIndent; } // Starts/continue the list unless: // - an empty list item follows a paragraph // - an ordered list is not starting by '1' var isPreviousParagraph = (block ?? state.LastBlock) is ParagraphBlock; if (isPreviousParagraph) { var isOpen = state.IsOpen(block ?? state.LastBlock); if (state.IsBlankLine || (isOpen && listInfo.BulletType == '1' && listInfo.OrderedStart != "1")) { state.GoToColumn(initColumn); return(BlockState.None); } } int.TryParse(listInfo.OrderedStart, out int order); var newListItem = new ListItemBlock(this) { Column = initColumn, ColumnWidth = columnWidth, Order = order, Span = new SourceSpan(sourcePosition, sourceEndPosition) }; state.NewBlocks.Push(newListItem); if (currentParent != null) { // If we have a new list item, close the previous one if (currentListItem != null) { state.Close(currentListItem); } // Reset the list if it is a new list or a new type of bullet if (currentParent.IsOrdered != isOrdered || currentParent.OrderedDelimiter != listInfo.OrderedDelimiter || currentParent.BulletType != listInfo.BulletType) { state.Close(currentParent); currentParent = null; } } if (currentParent == null) { var newList = new ListBlock(this) { Column = initColumn, Span = new SourceSpan(sourcePosition, sourceEndPosition), IsOrdered = isOrdered, BulletType = listInfo.BulletType, OrderedDelimiter = listInfo.OrderedDelimiter, DefaultOrderedStart = listInfo.DefaultOrderedStart, OrderedStart = listInfo.OrderedStart, }; state.NewBlocks.Push(newList); } return(BlockState.Continue); }
private BlockState TryContinueListItem(BlockProcessor state, ListItemBlock listItem) { 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 = state.IsBlankLine; var isCurrentBlockBreakable = state.CurrentBlock != null && state.CurrentBlock.IsBreakable; if (isBlankLine) { if (isCurrentBlockBreakable) { if (!(state.NextContinue is ListBlock)) { list.CountAllBlankLines++; listItem.Add(new BlankLineBlock()); } list.CountBlankLinesReset++; } if (list.CountBlankLinesReset == 1 && listItem.ColumnWidth < 0) { state.Close(listItem); // Leave the list open list.IsOpen = true; return(BlockState.Continue); } // Update list-item source end position listItem.UpdateSpanEnd(state.Line.End); return(BlockState.Continue); } list.CountBlankLinesReset = 0; int columnWidth = listItem.ColumnWidth; if (columnWidth < 0) { columnWidth = -columnWidth; } if (state.Indent >= columnWidth) { if (state.Indent > columnWidth && state.IsCodeIndent) { state.GoToColumn(state.ColumnBeforeIndent + columnWidth); } // Update list-item source end position listItem.UpdateSpanEnd(state.Line.End); return(BlockState.Continue); } return(BlockState.None); }