private void ParseLineGroup( ParseInput input ) { ParseResult result; if (MarkdownHeading.CanParseFrom(input)) { result = MarkdownHeading.ParseFrom(input); } else if (MarkdownHorizontalRule.CanParseFrom(input)) { result = MarkdownHorizontalRule.ParseFrom(input); } else if (MarkdownQuote.CanParseFrom(input)) { result = MarkdownQuote.ParseFrom(input); } else if (MarkdownList.CanParseFrom(input)) { result = MarkdownList.ParseFrom(input); } else { result = MarkdownParagraph.ParseFrom(input); } foreach (IHtmlable entry in result.GetContent()) { Content.AddLast(entry); } }
private static ParseResult ParseDoubleLineHeading( ParseInput input ) { ArraySegment <string> lines = input.Lines(); ParseResult result = new ParseResult(); int level; if (lines[1].StartsWith("=")) { level = 1; } else { level = 2; } MarkdownHeading element = new MarkdownHeading( level, MarkdownParser.ParseInnerText( new ParseInput( input, lines[0] ) ) ); lines[0] = ""; lines[1] = ""; result.Success = true; result.AddContent( element ); return(result); }
public static bool CanParseFrom( ParseInput input ) { string line = input.FirstLine; ReferencedUrl[] urls = input.Urls; if ( regexImageImmediateNoTitle.Match(line).Success || regexImageImmediateWithTitle.Match(line).Success ) { return(true); } Match linkMatch = regexImageReference.Match(line); if (linkMatch.Success) { string reference = linkMatch.Groups[2].Value; foreach (ReferencedUrl url in urls) { if (url.Reference == reference) { return(true); } } } return(false); }
public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; if (!CanParseFrom(input)) { // Return a failed result if this cannot be parsed ParseResult result = new ParseResult(); result.Line = line; return(result); } // Otherwise, parse and return result if (line.StartsWith("**")) { return(ParseStrongSection( input, "**" )); } else { return(ParseStrongSection( input, "__" )); } }
public static bool CanParseFrom( ParseInput input ) { return( CanParseSingleLineHeading(input) || CanParseDoubleLineHeading(input) ); }
public static bool CanParseFrom( ParseInput input ) { return( regexOrderedListLine.Match(input.FirstLine).Success || regexUnorderedListLine.Match(input.FirstLine).Success ); }
private static bool CanParseDoubleLineHeading( ParseInput input ) { bool isDoubleLineHeading = false; if (input.Lines().Count > 1) { isDoubleLineHeading = regexDoubleLineHeading.Match(input.Lines()[1]).Success; } return(isDoubleLineHeading); }
public ParseInput( ParseInput toCopy, string line ) { Urls = toCopy.Urls; lines = new string[] { line }; startIndex = 0; elements = 1; }
private static void RemoveListIndicators( ParseInput input ) { ArraySegment <string> lines = input.Lines(); for (int i = 0; i < lines.Count; i++) { string truncated = lines[i]; Match lineOrderedContentMatch = regexOrderedListLine.Match(lines[i]); Match lineUnorderedContentMatch = regexUnorderedListLine.Match(lines[i]); if ( lineOrderedContentMatch.Success || lineUnorderedContentMatch.Success ) { if (lineOrderedContentMatch.Success) { truncated = lineOrderedContentMatch.Groups[1].Value; } else if (lineUnorderedContentMatch.Success) { truncated = lineUnorderedContentMatch.Groups[1].Value; } int spaces = 0; // Count spaces while ( (spaces < truncated.Length) && (truncated[spaces] == ' ') ) { spaces++; } // If there are fewer than 5 spaces, remove all if (spaces < 5) { lines[i] = truncated.Substring(spaces); } else { // More than five, just remove one space lines[i] = truncated.Substring(1); } } else { lines[i] = lines[i]; } } }
public static bool CanParseFrom( ParseInput input ) { string line = input.FirstLine; // Check if the format is correct (* or - plus whitespace only) bool correctFormat = regexHorizontalRule.Match(line).Success; // Check there are enough - or * characters (3++) bool enoughNonWhitespace = ( (line.Length - line.Replace("*", "").Replace("-", "").Length) > 2 ); return(correctFormat && enoughNonWhitespace); }
// Given a text snippet, parse a plain text section from its start public static ParseResult ParseFrom( ParseInput input, bool force ) { string line = input.FirstLine; ParseResult result = new ParseResult(); int indexFirstSpecialCharacter = FindUnescapedSpecial( line ); // If force, then ensure at least one character is consumed if ( force && (indexFirstSpecialCharacter == 0) ) { indexFirstSpecialCharacter++; } // If an empty string is `parsed` then fail if (indexFirstSpecialCharacter == 0) { result.Line = line; return(result); } else if (indexFirstSpecialCharacter != line.Length) { // There is some special character in the string // Add as plain text only the content up to where it starts MarkdownText element = new MarkdownText( line.Substring(0, indexFirstSpecialCharacter) ); result.AddContent(element); result.Line = line.Substring(indexFirstSpecialCharacter); result.Success = true; } else { // If there are no special sections, everything is plain text MarkdownText element = new MarkdownText( line ); result.AddContent(element); result.Line = ""; result.Success = true; } return(result); }
public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { // Fail immediately if we cannot parse this text as strikethrough result.Line = line; return(result); } int j = 2; // Find closing tildes while ( (j < line.Length) && !( (line.Substring(j - 1, 2) == "~~") && (line[j - 2] != '\\') ) ) { j++; } if (j >= line.Length) { // Fail if we cannot find the closing squiggles result.Line = line; return(result); } // Parse everything inside the stars MarkdownStrikethrough element = new MarkdownStrikethrough( MarkdownParser.ParseInnerText( new ParseInput( input, line.Substring(2, j - 3) ) ) ); result.AddContent(element); result.Line = line.Substring(j + 1); result.Success = true; return(result); }
// Shared code for parsing emphasis sections public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { // Fail immediately if this string cannot be parsed result.Line = line; return(result); } int j = 1; // Find closing ` while ( (j < line.Length) && !( (line[j] == '`') && (line[j - 1] != '\\') ) ) { j++; } if (j >= line.Length) { // If we cannot parse, then return line as is result.Line = line; return(result); } // Parse everything inside the backticks MarkdownCodeInline element = new MarkdownCodeInline( MarkdownParser.ParseInnerText( new ParseInput( input, line.Substring(1, j - 1) ) ) ); result.AddContent(element); result.Line = line.Substring(j + 1); result.Success = true; return(result); }
public static ParseResult ParseFrom( ParseInput input ) { ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { return(result); } input.FirstLine = ""; result.Success = true; result.AddContent( new MarkdownHorizontalRule() ); return(result); }
public static ParseResult ParseFrom( ParseInput input ) { if (CanParseSingleLineHeading(input)) { return(ParseSingleLineHeading(input)); } else if (CanParseDoubleLineHeading(input)) { return(ParseDoubleLineHeading(input)); } else { return(new ParseResult()); } }
public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { // Return a failed result if cannot parse from this line result.Line = line; return(result); } // Otherwise parse and return result Match contentMatch = regexParseable.Match(line); string content; if (contentMatch.Groups[1].Value.Length != 0) { content = contentMatch.Groups[1].Value; } else { content = contentMatch.Groups[2].Value; } // Parse everything inside the stars MarkdownEmphasis element = new MarkdownEmphasis( MarkdownParser.ParseInnerText( new ParseInput( input, content ) ) ); result.AddContent(element); result.Line = line.Substring( content.Length + 2 ); result.Success = true; return(result); }
// Shared code for parsing strong sections private static ParseResult ParseStrongSection( ParseInput input, string delimiter ) { string line = input.FirstLine; ParseResult result = new ParseResult(); int j = 2; // Find closing two characters while ( (j < line.Length) && !( (line.Substring(j - 1, 2) == delimiter) && (line[j - 2] != '\\') ) ) { j++; } if (j >= line.Length) { // Fail if closing characters cannot be found result.Line = line; return(result); } // Parse everything inside the strong section delimiters MarkdownStrong element = new MarkdownStrong( MarkdownParser.ParseInnerText( new ParseInput( input, line.Substring(2, j - 3) ) ) ); result.AddContent(element); result.Line = line.Substring(j + 1); result.Success = true; // Return the line string minus the content we parsed return(result); }
public static ParseResult ParseFrom( ParseInput lines, bool innerParagraph ) { ParseResult result = new ParseResult(); ParseResult innerResult; IHtmlable returnedElement; // If the list items content contains another list if (MarkdownList.CanParseFrom(lines)) { innerResult = MarkdownList.ParseFrom(lines); returnedElement = new MarkdownListItem( innerResult.GetContent() ); } else { // Otherwise, if the item content should go in a paragraph if (innerParagraph) { innerResult = MarkdownParagraph.ParseFrom(lines); returnedElement = new MarkdownListItem( innerResult.GetContent() ); } else { // line item content should not go in a paragraph returnedElement = new MarkdownListItem( MarkdownParser.ParseInnerText(lines) ); } } result.Success = true; result.AddContent( returnedElement ); return(result); }
public MarkdownParser( string[] lines ) { // Assume success Success = true; // Store parsed content as we go Content = new LinkedList <IHtmlable>(); // Urls potentially referenced by anchors/images Urls = new LinkedList <ReferencedUrl>(); // Extract all 'footnote' style urls ParseReferencedUrls(lines); // Create new parse input to pass to parsers ParseInput input = new ParseInput( Utils.LinkedListToArray(Urls), lines, 0, lines.Length ); // Parsing printed content int currentIndex = 0; while (currentIndex < lines.Length) { // Parse some lines ParseLineGroup( input.Slice( currentIndex, lines.Length - currentIndex ) ); // Move to the next non-empty line while ( (currentIndex < lines.Length) && ContainsOnlyWhitespace(lines[currentIndex]) ) { currentIndex++; } } }
private static ParseResult ParseSingleLineHeading( ParseInput input ) { ArraySegment <string> lines = input.Lines(); ParseResult result = new ParseResult(); // Calculate heading level, (maximum 6) int level = 0; while ( (level < 6) && (lines[0][level] == '#') ) { level++; } Match contentMatch = regexSingleLineHeading.Match(lines[0]); string content = StripLeadingCharacter( StripTrailingCharacter( contentMatch.Groups[1].Value, '#' ), ' ' ); lines[0] = ""; result.Success = true; result.AddContent( new MarkdownHeading( level, MarkdownParser.ParseInnerText( new ParseInput( input, content ) ) ) ); return(result); }
public static bool CanParseFrom( ParseInput input ) { ArraySegment <string> lines = input.Lines(); if (!regexBacktickSectionOpen.Match(lines[0]).Success) { return(false); } else { for (int i = 1; i < lines.Count; i++) { if (regexBacktickSectionClose.Match(lines[i]).Success) { return(true); } } } return(false); }
public static ParseResult ParseFrom( ParseInput input ) { ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { return(result); } ArraySegment <string> lines = input.Lines(); lines[0] = ""; LinkedList <IHtmlable> innerContent = new LinkedList <IHtmlable>(); int i = 1; while (!regexBacktickSectionClose.Match(lines[i]).Success) { innerContent.AddLast( new MarkdownText( lines[i] ) ); lines[i] = ""; i++; } // Remember to clear final line (closing backticks) lines[i] = ""; MarkdownCodeBlock blockCodeElement = new MarkdownCodeBlock( Utils.LinkedListToArray(innerContent) ); result.Success = true; result.AddContent(blockCodeElement); return(result); }
public static ParseResult ParseFrom( ParseInput input ) { ArraySegment <string> lines = input.Lines(); ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { return(result); } int endQuoteSection = FindEndOfQuoteSection( lines ); string[] truncatedLines = new string[endQuoteSection]; // Remove quote arrows and spaces, if needed for (int i = 0; i < endQuoteSection; i++) { string truncated = lines[i]; if (lines[i].StartsWith(">")) { truncated = truncated.Substring(1); int spaces = 0; // Count spaces while ( (spaces < truncated.Length) && (truncated[spaces] == ' ') ) { spaces++; } // If there are fewer than 5 spaces, remove all if (spaces < 5) { truncatedLines[i] = truncated.Substring(spaces); } else { // More than five, just remove one space truncatedLines[i] = truncated.Substring(1); } } else { truncatedLines[i] = lines[i]; } // Remove original line lines[i] = ""; } /* * The truncated lines should be parsed as any other line group * and wrapped in a blockquote element */ MarkdownParser parser = new MarkdownParser( truncatedLines ); MarkdownQuote quoteElement = new MarkdownQuote( parser.ContentAsArray() ); result.Success = true; result.AddContent( quoteElement ); return(result); }
public static bool CanParseFrom( ParseInput input ) { return(input.FirstLine.StartsWith(">")); }
public static ParseResult ParseFrom( ParseInput input ) { ArraySegment <string> lines = input.Lines(); ParseResult result = new ParseResult(); if (!CanParseFrom(input)) { return(result); } /* * Work out the list type from the first line * before we start to mangle the line contents */ MarkdownElementType type; if (regexOrderedListLine.Match(input.FirstLine).Success) { type = MarkdownElementType.OrderedList; } else { type = MarkdownElementType.UnorderedList; } int endListSection = FindEndOfListSection( lines ); // New array segment with only parsed content ArraySegment <string> listLines = new ArraySegment <string>( lines.Array, lines.Offset, endListSection ); // Need to split into groups per list item int currentIndex = 0; // Hold the parsed list items as we go LinkedList <IHtmlable> listItems = new LinkedList <IHtmlable>(); // Track whether list item contents should be in a paragraph bool whitespaceLineBefore = false; bool whitespaceLineAfter = false; while (currentIndex < endListSection) { int endIndex = FindEndOfListItem( listLines, currentIndex ); /* * There is a whitespace line between * this list item and the following one * and this list item isn't the final one */ whitespaceLineAfter = ( (endIndex < listLines.Count) && ( ContainsOnlyWhitespace( listLines[endIndex - 1] ) ) ); // Create new parse input for this list item ParseInput listItemLines = new ParseInput( input.Urls, listLines.Array, listLines.Offset + currentIndex, endIndex - currentIndex ); RemoveListIndicators( listItemLines ); ParseResult nextListItem = MarkdownListItem.ParseFrom( listItemLines, whitespaceLineBefore || whitespaceLineAfter ); foreach ( IHtmlable entry in nextListItem.GetContent() ) { listItems.AddLast( entry ); } // Jump over lines just parsed currentIndex += (endIndex - currentIndex); // Whitespace after previous entry becomes before next entry whitespaceLineBefore = whitespaceLineAfter; /* * If there's further whitespace after parsed section * (for some reason) then jump over this too */ while ( (currentIndex < listLines.Count) && ( ContainsOnlyWhitespace( lines[currentIndex] ) ) ) { currentIndex++; } } result.Success = true; result.AddContent( new MarkdownList( Utils.LinkedListToArray( listItems ), type ) ); return(result); }
public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; ReferencedUrl[] urls = input.Urls; ParseResult result = new ParseResult(); if ( !CanParseFrom(input) ) { return(result); } Match linkMatch; // Format: [text](url) linkMatch = regexLinkImmediate.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; string url = linkMatch.Groups[2].Value; result.Success = true; result.Line = regexLinkImmediate.Replace( line, "" ); result.AddContent( new MarkdownLink( MarkdownParser.ParseInnerText( new ParseInput( input, text ) ), url ) ); } // Format: [text][id] [id]: url linkMatch = regexLinkReference.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; string reference = linkMatch.Groups[2].Value; foreach (ReferencedUrl url in urls) { if (url.Reference == reference) { result.Success = true; result.Line = regexLinkReference.Replace( line, "" ); result.AddContent( new MarkdownLink( MarkdownParser.ParseInnerText( new ParseInput( input, text ) ), url.Url ) ); } } } // Format: [text] [text]: url linkMatch = regexLinkSelfReference.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; foreach (ReferencedUrl url in urls) { if (url.Reference == text) { result.Success = true; result.Line = regexLinkSelfReference.Replace( line, "" ); result.AddContent( new MarkdownLink( MarkdownParser.ParseInnerText( new ParseInput( input, text ) ), url.Url ) ); } } } return(result); }
public static bool CanParseFrom( ParseInput input ) { return(regexParseable.Match(input.FirstLine).Success); }
public static ParseResult ParseFrom( ParseInput input ) { string line = input.FirstLine; ReferencedUrl[] urls = input.Urls; ParseResult result = new ParseResult(); if ( !CanParseFrom(input) ) { return(result); } Match linkMatch; // Format: ![text](url) linkMatch = regexImageImmediateNoTitle.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; string url = linkMatch.Groups[2].Value; string title = ""; result.Success = true; result.Line = regexImageImmediateNoTitle.Replace( line, "" ); result.AddContent( new MarkdownImage( url, text, title ) ); } // Format: ![text](url "title") linkMatch = regexImageImmediateWithTitle.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; string url = linkMatch.Groups[2].Value; string title = linkMatch.Groups[3].Value; result.Success = true; result.Line = regexImageImmediateWithTitle.Replace( line, "" ); result.AddContent( new MarkdownImage( url, text, title ) ); } // Format: [text][id] [id]: url (title optional) linkMatch = regexImageReference.Match(line); if (linkMatch.Success) { string text = linkMatch.Groups[1].Value; string reference = linkMatch.Groups[2].Value; foreach (ReferencedUrl url in urls) { if (url.Reference == reference) { result.Success = true; result.Line = regexImageReference.Replace( line, "" ); result.AddContent( new MarkdownImage( url.Url, text, url.Title ) ); } } } return(result); }
// Given a single line of text, parse this, including special (emph, etc...) sections public static IHtmlable[] ParseInnerText( ParseInput input ) { // Store parsed content as we go LinkedList <IHtmlable> content = new LinkedList <IHtmlable>(); // Until the whole string has been consumed while (input.FirstLine.Length > 0) { ParseResult result; if (MarkdownStrong.CanParseFrom(input)) { result = MarkdownStrong.ParseFrom(input); } else if (MarkdownStrikethrough.CanParseFrom(input)) { result = MarkdownStrikethrough.ParseFrom(input); } else if (MarkdownEmphasis.CanParseFrom(input)) { result = MarkdownEmphasis.ParseFrom(input); } else if (MarkdownCodeInline.CanParseFrom(input)) { result = MarkdownCodeInline.ParseFrom(input); } else if (MarkdownLink.CanParseFrom(input)) { result = MarkdownLink.ParseFrom(input); } else if (MarkdownImage.CanParseFrom(input)) { result = MarkdownImage.ParseFrom(input); } else { result = MarkdownText.ParseFrom( input, false ); } /* * If no parsing method suceeded * for once character to be parsed as text */ if (!result.Success) { result = MarkdownText.ParseFrom( input, true ); } // Extract parsed content foreach (IHtmlable entry in result.GetContent()) { content.AddLast(entry); } // Update text to be parsed input.FirstLine = result.Line; } IHtmlable[] contentArray = new IHtmlable[content.Count]; content.CopyTo( contentArray, 0 ); return(contentArray); }
private static bool CanParseSingleLineHeading( ParseInput input ) { return(regexSingleLineHeading.Match(input.FirstLine).Success); }