private void ProcessLine(ParserContext context) { var groups = new string[0]; var subject = new Subject(context.Line); context.Container = context.Document; while (context.Container.LastChild != null && context.Container.LastChild.IsOpen) { context.Container = context.Container.LastChild; if (!context.Container.MatchNextLine(subject)) { context.Container = context.Container.Parent; // back up to last matching block break; } } var lastMatchedContainer = context.Container; // This function is used to finalize and close any unmatched // blocks. We aren't ready to do this now, because we might // have a lazy paragraph continuation, in which case we don't // want to close unmatched blocks. So we store this closure for // use later, when we have more information. var oldtip = context.Tip; context.CloseUnmatchedBlocks = () => { // finalize any blocks not matched while (oldtip != lastMatchedContainer) { oldtip.Close(context); oldtip = oldtip.Parent; } context.CloseUnmatchedBlocks = () => { }; }; // Check to see if we've hit 2nd blank line; if so break out of list: if (subject.IsBlank && context.Container.LastLineIsBlank) { BreakOutOfLists(context, context.Container, context.LineNumber); } // Unless last matched context.Container is a code block, try new context.Container starts, // adding children to the last matched context.Container: while (!context.Container.IsCode && !context.BlocksParsed) { var parsed = context.Parsers.IndentedCodeParser.Parse(context, subject); parsed = parsed || context.Parsers.LazyParagraphContinuationParser.Parse(context, subject); parsed = parsed || context.Parsers.BlockQuoteParser.Parse(context, subject); parsed = parsed || context.Parsers.ATXHeaderParser.Parse(context, subject); parsed = parsed || context.Parsers.FencedCodeParser.Parse(context, subject); parsed = parsed || context.Parsers.HtmlBlockParser.Parse(context, subject); parsed = parsed || context.Parsers.SetExtHeaderParser.Parse(context, subject); parsed = parsed || context.Parsers.HorizontalRuleParser.Parse(context, subject); parsed = parsed || context.Parsers.ListParser.Parse(context, subject); if (!parsed || context.Container.AcceptsLines) { // if none were found or it's a line context.Container, it can't contain other containers context.BlocksParsed = true; } } // What remains at the offset is a text line. Add the text to the // appropriate context.Container. // First check for a lazy paragraph continuation: if (context.Tip != lastMatchedContainer && !subject.IsBlank && context.Tip is Paragraph && context.Tip.Strings.Any()) { // lazy paragraph continuation //context.Tip.LastLineIsBlank = false; context.Tip.AddLine(subject.Rest); } else { // not a lazy continuation // finalize any blocks not matched context.CloseUnmatchedBlocks(); // Block quote lines are never blank as they start with > // and we don't count blanks in fenced code for purposes of tight/loose // lists or breaking out of lists. We also don't set last_line_blank // on an empty list item. if (!subject.IsBlank || context.Container is BlockQuote || context.Container is FencedCode) { context.Container.LastLineIsBlank = false; } else if (context.Container is ListItem) { context.Container.LastLineIsBlank = context.Container.StartLine < context.LineNumber; } else { context.Container.LastLineIsBlank = true; } var cont = context.Container; while (cont.Parent is Block) { cont.Parent.LastLineIsBlank = false; cont = cont.Parent; } if (context.Container is IndentedCode || context.Container is HtmlBlock) { context.Tip.AddLine(subject.Rest); } else if (context.Container is FencedCode) { var fencedCode = context.Container as FencedCode; var saved = subject.Save(); // check for closing code fence: var match = subject.Indent <= 3; subject.AdvanceWhile(c => c == ' '); match = match && subject.Char == fencedCode.Char && subject.AdvanceWhile(c => c == fencedCode.Char) >= fencedCode.Length; subject.AdvanceWhile(c => c == ' '); if (match && subject.EndOfString) { // don't add closing fence to context.Container; instead, close it: context.Container.Close(context); } else { saved.Restore(); context.Tip.AddLine(subject.Rest); } } else if (context.Container is ATXHeader || context.Container is SetExtHeader || context.Container is HorizontalRule) { // nothing to do; we already added the contents. } else { if (context.Container.AcceptsLines) { subject.AdvanceToFirstNonSpace(); context.Tip.AddLine(subject.Rest); } else if (subject.IsBlank) { // do nothing } else if (!(context.Container is HorizontalRule || context.Container is SetExtHeader)) { // create paragraph context.Container for line context.AddBlock(new Paragraph()); subject.AdvanceToFirstNonSpace(); context.Tip.AddLine(subject.Rest); } else { throw new Exception( "Line " + context.LineNumber.ToString() + " with context.Container type " + context.Container.GetType().Name + " did not match any condition." ); } } } }
public SavedSubject(Subject subject) { _subject = subject; _index = _subject.Index; _char = subject.Char; _endOfString = subject.EndOfString; }
public SavedSubject(Subject subject) { _subject = subject; _index = _subject._index; }