Exemple #1
0
        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);
        }