예제 #1
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            // If we are in a CodeIndent, early exit
            if (processor.IsCodeIndent)
            {
                return(BlockState.None);
            }

            // 4.2 ATX headings
            // An ATX heading consists of a string of characters, parsed as inline content,
            // between an opening sequence of 1–6(configurable) unescaped # characters and an optional
            // closing sequence of any number of unescaped # characters. The opening sequence
            // of # characters must be followed by a space or by the end of line. The optional
            // closing sequence of #s must be preceded by a space and may be followed by spaces
            // only. The opening # character may be indented 0-3 spaces. The raw contents of
            // the heading are stripped of leading and trailing spaces before being parsed as
            // inline content. The heading level is equal to the number of # characters in the
            // opening sequence.
            var column         = processor.Column;
            var line           = processor.Line;
            var sourcePosition = line.Start;
            var c            = line.CurrentChar;
            var matchingChar = c;

            Debug.Assert(MaxLeadingCount > 0);
            int leadingCount = 0;

            while (c != '\0' && leadingCount <= MaxLeadingCount)
            {
                if (c != matchingChar)
                {
                    break;
                }
                c = processor.NextChar();
                leadingCount++;
            }

            // A space is required after leading #
            if (leadingCount > 0 && leadingCount <= MaxLeadingCount && (c.IsSpaceOrTab() || c == '\0'))
            {
                StringSlice trivia = StringSlice.Empty;
                if (processor.TrackTrivia && c.IsSpaceOrTab())
                {
                    trivia = new StringSlice(processor.Line.Text, processor.Start, processor.Start);
                    processor.NextChar();
                }
                // Move to the content
                var headingBlock = new HeadingBlock(this)
                {
                    HeaderChar = matchingChar,
                    TriviaAfterAtxHeaderChar = trivia,
                    Level        = leadingCount,
                    Column       = column,
                    Span         = { Start = sourcePosition },
                    TriviaBefore = processor.UseTrivia(sourcePosition - 1),
                    LinesBefore  = processor.UseLinesBefore(),
                    NewLine      = processor.Line.NewLine,
                };
                processor.NewBlocks.Push(headingBlock);
                if (!processor.TrackTrivia)
                {
                    processor.GoToColumn(column + leadingCount + 1);
                }

                // Gives a chance to parse attributes
                TryParseAttributes?.Invoke(processor, ref processor.Line, headingBlock);

                // The optional closing sequence of #s must be preceded by a space and may be followed by spaces only.
                int endState         = 0;
                int countClosingTags = 0;
                int sourceEnd        = processor.Line.End;
                for (int i = processor.Line.End; i >= processor.Line.Start - 1; i--)  // Go up to Start - 1 in order to match the space after the first ###
                {
                    c = processor.Line.Text[i];
                    if (endState == 0)
                    {
                        if (c.IsSpaceOrTab())
                        {
                            continue;
                        }
                        endState = 1;
                    }
                    if (endState == 1)
                    {
                        if (c == matchingChar)
                        {
                            countClosingTags++;
                            continue;
                        }

                        if (countClosingTags > 0)
                        {
                            if (c.IsSpaceOrTab())
                            {
                                processor.Line.End = i - 1;
                            }
                            break;
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                // Setup the source end position of this element
                headingBlock.Span.End = processor.Line.End;

                if (processor.TrackTrivia)
                {
                    var wsa = new StringSlice(processor.Line.Text, processor.Line.End + 1, sourceEnd);
                    headingBlock.TriviaAfter = wsa;
                    if (wsa.Overlaps(headingBlock.TriviaAfterAtxHeaderChar))
                    {
                        // prevent double whitespace allocation in case of closing # i.e. "# #"
                        headingBlock.TriviaAfterAtxHeaderChar = StringSlice.Empty;
                    }
                }

                // We expect a single line, so don't continue
                return(BlockState.Break);
            }

            // Else we don't have an header
            processor.Line.Start = sourcePosition;
            processor.Column     = column;
            return(BlockState.None);
        }