예제 #1
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            var result = TryContinue(processor, null);

            if (result == BlockState.Continue)
            {
                // Save the column where we need to go back
                var column = processor.Column;
                var sourceStartPosition = processor.Start;

                // Unwind all indents all spaces before in order to calculate correct span
                processor.UnwindAllIndents();

                var codeBlock = new CodeBlock(this)
                {
                    Column      = processor.Column,
                    Span        = new SourceSpan(processor.Start, processor.Line.End),
                    LinesBefore = processor.UseLinesBefore(),
                    NewLine     = processor.Line.NewLine,
                };
                var codeBlockLine = new CodeBlockLine
                {
                    TriviaBefore = processor.UseTrivia(sourceStartPosition - 1)
                };
                codeBlock.CodeBlockLines.Add(codeBlockLine);
                processor.NewBlocks.Push(codeBlock);

                // Go back to the correct column
                processor.GoToColumn(column);
            }
            return(result);
        }
예제 #2
0
 protected override FencedCodeBlock CreateFencedBlock(BlockProcessor processor)
 {
     return(new FencedCodeBlock(this)
     {
         IndentCount = processor.Indent,
         LinesBefore = processor.UseLinesBefore(),
         TriviaBefore = processor.UseTrivia(processor.Start - 1),
         NewLine = processor.Line.NewLine,
     });
 }
예제 #3
0
 private BlockState CreateHtmlBlock(BlockProcessor state, HtmlBlockType type, int startColumn, int startPosition)
 {
     state.NewBlocks.Push(new HtmlBlock(this)
     {
         Column = startColumn,
         Type   = type,
         // By default, setup to the end of line
         Span = new SourceSpan(startPosition, startPosition + state.Line.End),
         //BeforeWhitespace = state.PopBeforeWhitespace(startPosition - 1),
         LinesBefore = state.UseLinesBefore(),
         NewLine     = state.Line.NewLine,
     });
     return(BlockState.Continue);
 }
예제 #4
0
        private void ProcessBlocks()
        {
            while (true)
            {
                // Get the precise position of the begining of the line
                var lineText = lineReader.ReadLine();

                // If this is the end of file and the last line is empty
                if (lineText.Text is null)
                {
                    if (trackTrivia)
                    {
                        Block?lastBlock = blockProcessor.LastBlock;
                        if (lastBlock is null && document.Count == 0)
                        {
                            // this means we have unassigned characters
                            var noBlocksFoundBlock         = new EmptyBlock(null);
                            List <StringSlice> linesBefore = blockProcessor.UseLinesBefore();
                            noBlocksFoundBlock.LinesAfter = new List <StringSlice>();
                            noBlocksFoundBlock.LinesAfter.AddRange(linesBefore);
                            document.Add(noBlocksFoundBlock);
                        }
                        else if (lastBlock != null && blockProcessor.LinesBefore != null)
                        {
                            // this means we're out of lines, but still have unassigned empty lines.
                            // thus, we'll assign the empty unsassigned lines to the last block
                            // of the document.
                            var rootMostContainerBlock = Block.FindRootMostContainerParent(lastBlock);
                            rootMostContainerBlock.LinesAfter ??= new List <StringSlice>();
                            var linesBefore = blockProcessor.UseLinesBefore();
                            rootMostContainerBlock.LinesAfter.AddRange(linesBefore);
                        }
                    }
                    break;
                }
                blockProcessor.ProcessLine(lineText);
            }
예제 #5
0
        protected override FencedCodeBlock CreateFencedBlock(BlockProcessor processor)
        {
            var codeBlock = new FencedCodeBlock(this)
            {
                IndentCount = processor.Indent,
            };

            if (processor.TrackTrivia)
            {
                codeBlock.LinesBefore  = processor.UseLinesBefore();
                codeBlock.TriviaBefore = processor.UseTrivia(processor.Start - 1);
                codeBlock.NewLine      = processor.Line.NewLine;
            }

            return(codeBlock);
        }
예제 #6
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            if (processor.IsBlankLine)
            {
                return(BlockState.None);
            }

            // We continue trying to match by default
            processor.NewBlocks.Push(new ParagraphBlock(this)
            {
                Column      = processor.Column,
                Span        = new SourceSpan(processor.Line.Start, processor.Line.End),
                LinesBefore = processor.UseLinesBefore(),
                NewLine     = processor.Line.NewLine,
            });
            return(BlockState.Continue);
        }
예제 #7
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            if (processor.IsBlankLine)
            {
                return(BlockState.None);
            }

            // We continue trying to match by default
            var paragraph = new ParagraphBlock(this)
            {
                Column = processor.Column,
                Span   = new SourceSpan(processor.Line.Start, processor.Line.End),
            };

            if (processor.TrackTrivia)
            {
                paragraph.LinesBefore = processor.UseLinesBefore();
                paragraph.NewLine     = processor.Line.NewLine;
            }

            processor.NewBlocks.Push(paragraph);
            return(BlockState.Continue);
        }
예제 #8
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);
        }
예제 #9
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            if (processor.IsCodeIndent)
            {
                return(BlockState.None);
            }

            var sourcePosition = processor.Start;

            // 5.1 Block quotes
            // A block quote marker consists of 0-3 spaces of initial indent, plus (a) the character > together with a following space, or (b) a single character > not followed by a space.
            var quoteChar = processor.CurrentChar;
            var column    = processor.Column;
            var c         = processor.NextChar();

            var quoteBlock = new QuoteBlock(this)
            {
                QuoteChar   = quoteChar,
                Column      = column,
                Span        = new SourceSpan(sourcePosition, processor.Line.End),
                LinesBefore = processor.UseLinesBefore()
            };

            bool hasSpaceAfterQuoteChar = false;

            if (c == ' ')
            {
                processor.NextColumn();
                hasSpaceAfterQuoteChar         = true;
                processor.SkipFirstUnwindSpace = true;
            }
            else if (c == '\t')
            {
                processor.NextColumn();
            }

            var         triviaBefore = processor.UseTrivia(sourcePosition - 1);
            StringSlice triviaAfter  = StringSlice.Empty;
            bool        wasEmptyLine = false;

            if (processor.Line.IsEmptyOrWhitespace())
            {
                processor.TriviaStart = processor.Start;
                triviaAfter           = processor.UseTrivia(processor.Line.End);
                wasEmptyLine          = true;
            }
            quoteBlock.QuoteLines.Add(new QuoteBlockLine
            {
                TriviaBefore           = triviaBefore,
                TriviaAfter            = triviaAfter,
                QuoteChar              = true,
                HasSpaceAfterQuoteChar = hasSpaceAfterQuoteChar,
                NewLine = processor.Line.NewLine,
            });
            processor.NewBlocks.Push(quoteBlock);
            if (!wasEmptyLine)
            {
                processor.TriviaStart = processor.Start;
            }
            return(BlockState.Continue);
        }
예제 #10
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            if (processor.IsCodeIndent)
            {
                return(BlockState.None);
            }

            var startPosition = processor.Start;
            var line          = processor.Line;

            // 4.1 Thematic breaks
            // A line consisting of 0-3 spaces of indentation, followed by a sequence of three or more matching -, _, or * characters, each followed optionally by any number of spaces
            int  breakCharCount          = 0;
            var  breakChar               = line.CurrentChar;
            bool hasSpacesSinceLastMatch = false;
            bool hasInnerSpaces          = false;
            var  c = breakChar;

            while (c != '\0')
            {
                if (c == breakChar)
                {
                    if (hasSpacesSinceLastMatch)
                    {
                        hasInnerSpaces = true;
                    }

                    breakCharCount++;
                }
                else if (c.IsSpaceOrTab())
                {
                    hasSpacesSinceLastMatch = true;
                }
                else
                {
                    return(BlockState.None);
                }

                c = line.NextChar();
            }

            // If it as less than 3 chars or it is a setex heading and we are already in a paragraph, let the paragraph handle it
            var previousParagraph = processor.CurrentBlock as ParagraphBlock;

            var isSetexHeading = previousParagraph != null && breakChar == '-' && !hasInnerSpaces;

            if (isSetexHeading)
            {
                var parent = previousParagraph !.Parent !;
                if (previousParagraph.Column != processor.Column && (parent is QuoteBlock or ListItemBlock))
                {
                    isSetexHeading = false;
                }
            }

            if (breakCharCount < 3 || isSetexHeading)
            {
                return(BlockState.None);
            }

            // Push a new block
            var thematicBreak = new ThematicBreakBlock(this)
            {
                Column            = processor.Column,
                Span              = new SourceSpan(startPosition, line.End),
                ThematicChar      = breakChar,
                ThematicCharCount = breakCharCount,
                // TODO: should we separate whitespace before/after?
                //BeforeWhitespace = beforeWhitespace,
                //AfterWhitespace = processor.PopBeforeWhitespace(processor.CurrentLineStartPosition),
                Content = new StringSlice(line.Text, processor.TriviaStart, line.End, line.NewLine), //include whitespace for now
            };

            if (processor.TrackTrivia)
            {
                thematicBreak.LinesBefore = processor.UseLinesBefore();
                thematicBreak.NewLine     = processor.Line.NewLine;
            }

            processor.NewBlocks.Push(thematicBreak);
            return(BlockState.BreakDiscard);
        }