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); }
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, }); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }