public override BlockState TryContinue(BlockProcessor processor, Block block) { if (processor.IsCodeIndent) { return(BlockState.None); } var quote = (QuoteBlock)block; // 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 c = processor.CurrentChar; if (c != quote.QuoteChar) { block.Span.End = processor.Start - 1; return(processor.IsBlankLine ? BlockState.BreakDiscard : BlockState.None); } c = processor.NextChar(); // Skip opening char if (c.IsSpace()) { processor.NextChar(); // Skip following space } block.Span.End = processor.Line.End; return(BlockState.Continue); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var column = processor.Column; 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 c = processor.NextChar(); if (c.IsSpaceOrTab()) { processor.NextColumn(); } processor.NewBlocks.Push(new QuoteBlock(this) { QuoteChar = quoteChar, Column = column, Span = new SourceSpan(sourcePosition, processor.Line.End), }); return(BlockState.Continue); }
public override bool TryParse(BlockProcessor state, char pendingBulletType, out ListInfo result) { result = new ListInfo(); var c = state.CurrentChar; var sourcePosition = state.Start; int countDigit = 0; int startChar = -1; int endChar = 0; while (c.IsDigit()) { endChar = state.Start; // Trim left 0 if (startChar < 0 && c != '0') { startChar = endChar; } c = state.NextChar(); countDigit++; } var sourceBullet = new StringSlice(state.Line.Text, sourcePosition, state.Start - 1); if (startChar < 0) { startChar = endChar; } // Note that ordered list start numbers must be nine digits or less: if (countDigit > 9 || !TryParseDelimiter(state, out char orderedDelimiter)) { return(false); } if (startChar == endChar) { // Common case: a single digit character result.OrderedStart = CharHelper.SmallNumberToString(state.Line.Text[startChar] - '0'); } else { result.OrderedStart = state.Line.Text.Substring(startChar, endChar - startChar + 1); } result.OrderedDelimiter = orderedDelimiter; result.BulletType = '1'; result.DefaultOrderedStart = "1"; result.SourceBullet = sourceBullet; return(true); }
public override BlockState TryContinue(BlockProcessor processor, Block block) { var result = base.TryContinue(processor, block); if (result == BlockState.Continue) { var fence = (FencedCodeBlock)block; // Remove any indent spaces var c = processor.CurrentChar; var indentCount = fence.IndentCount; while (indentCount > 0 && c.IsSpace()) { indentCount--; c = processor.NextChar(); } } return(result); }
public override bool TryParse(BlockProcessor state, char pendingBulletType, out ListInfo result) { result = new ListInfo(); var c = state.CurrentChar; int countDigit = 0; int startChar = -1; int endChar = 0; while (c.IsDigit()) { endChar = state.Start; // Trim left 0 if (startChar < 0 && c != '0') { startChar = endChar; } c = state.NextChar(); countDigit++; } if (startChar < 0) { startChar = endChar; } // Note that ordered list start numbers must be nine digits or less: char orderedDelimiter; if (countDigit > 9 || !TryParseDelimiter(state, out orderedDelimiter)) { return(false); } result.OrderedStart = state.Line.Text.Substring(startChar, endChar - startChar + 1); result.OrderedDelimiter = orderedDelimiter; result.BulletType = '1'; result.DefaultOrderedStart = "1"; return(true); }
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 TryContinue(BlockProcessor processor, Block block) { if (processor.IsCodeIndent) { return(BlockState.None); } var quote = (QuoteBlock)block; 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 c = processor.CurrentChar; bool hasSpaceAfterQuoteChar = false; if (c != quote.QuoteChar) { if (processor.IsBlankLine) { return(BlockState.BreakDiscard); } else { quote.QuoteLines.Add(new QuoteBlockLine { QuoteChar = false, NewLine = processor.Line.NewLine, }); return(BlockState.None); } } c = processor.NextChar(); // Skip quote marker char if (c == ' ') { processor.NextColumn(); hasSpaceAfterQuoteChar = true; processor.SkipFirstUnwindSpace = true; } else if (c == '\t') { processor.NextColumn(); } var TriviaSpaceBefore = 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; } quote.QuoteLines.Add(new QuoteBlockLine { QuoteChar = true, HasSpaceAfterQuoteChar = hasSpaceAfterQuoteChar, TriviaBefore = TriviaSpaceBefore, TriviaAfter = triviaAfter, NewLine = processor.Line.NewLine, }); if (!wasEmptyLine) { processor.TriviaStart = processor.Start; } block.UpdateSpanEnd(processor.Line.End); return(BlockState.Continue); }
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); }