Beispiel #1
0
        public override List <string> parseChildLines(BlockParser parser)
        {
            var childLines = new List <string>();

            while (!parser.isDone)
            {
                var match = this.pattern.Match(parser.current);
                if (match.Success)
                {
                    childLines.Add(match.Groups[1].Value);
                    parser.advance();
                    continue;
                }

                if (parser.blockSyntaxes.First(s => s.canParse(parser)) is ParagraphSyntax)
                {
                    childLines.Add(parser.current);
                    parser.advance();
                }
                else
                {
                    break;
                }
            }

            return(childLines);
        }
Beispiel #2
0
        public override bool canParse(BlockParser parser)
        {
            if (!_interperableAsParagraph(parser.current))
            {
                return(false);
            }

            var i = 1;

            while (true)
            {
                var nextLine = parser.peek(i);
                if (nextLine == null)
                {
                    // We never reached an underline.
                    return(false);
                }

                if (BlockParser._setextPattern.hasMatch(nextLine))
                {
                    return(true);
                }

                if (!_interperableAsParagraph(nextLine))
                {
                    return(false);
                }

                i++;
            }
        }
Beispiel #3
0
        /// Parses the given [lines] of Markdown to a series of AST nodes.
        public List <Node> parseLines(List <string> lines)
        {
            var nodes = new BlockParser(lines, this).parseLines();

            _parseInlineContent(nodes);
            return(nodes);
        }
Beispiel #4
0
        public override List <string> parseChildLines(BlockParser parser)
        {
            // Grab all of the lines that form the blockquote, stripping off the ">".
            var childLines = new List <string>();

            while (!parser.isDone)
            {
                var match = pattern.Match(parser.current);
                if (match.Success)
                {
                    childLines.Add(match.Groups[1].Value);
                    parser.advance();
                    continue;
                }

                // A paragraph continuation is OK. This is content that cannot be parsed
                // as any other syntax except Paragraph, and it doesn't match the bar in
                // a Setext header.
                if (parser.blockSyntaxes.First(s => s.canParse(parser)) is ParagraphSyntax)
                {
                    childLines.Add(parser.current);
                    parser.advance();
                }
                else
                {
                    break;
                }
            }

            return(childLines);
        }
Beispiel #5
0
        public override Node parse(BlockParser parser)
        {
            var element = base.parse(parser) as Element;

            element.generatedId = BlockSyntax.generateAnchorHash(element);
            return(element);
        }
Beispiel #6
0
        public override Node parse(BlockParser parser)
        {
            var childLines = new List <string>();

            // Eat until we hit something that ends a paragraph.
            while (!BlockSyntax.isAtBlockEnd(parser))
            {
                childLines.Add(parser.current);
                parser.advance();
            }

            var paragraphLines = _extractReflinkDefinitions(parser, childLines);

            if (paragraphLines == null)
            {
                // Paragraph consisted solely of reference link definitions.
                return(new TextNode(""));
            }
            else
            {
                var contents = new UnparsedContent(paragraphLines.join('\n'));
                return(new Element("p", new List <Node>()
                {
                    contents
                }));
            }
        }
Beispiel #7
0
        public override List <string> parseChildLines(BlockParser parser)
        {
            var childLines = new List <string>();

            while (!parser.isDone)
            {
                var match = pattern.Match(parser.current);
                if (match.Success)
                {
                    childLines.Add(match.Groups[1].Value);
                    parser.advance();
                }
                else
                {
                    // If there's a codeblock, then a newline, then a codeblock, keep the
                    // code blocks together.
                    var nextMatch =
                        parser.next != null?pattern.Match(parser.next) : null;

                    if (parser.current.Trim() == "" && nextMatch != null)
                    {
                        childLines.Add("");
                        childLines.Add(nextMatch.Groups[1].Value);
                        parser.advance();
                        parser.advance();
                    }
                    else
                    {
                        break;
                    }
                }
            }

            return(childLines);
        }
Beispiel #8
0
        List <String> parseChildLines(BlockParser parser, String endBlock = "")
        {
            if (endBlock == null)
            {
                endBlock = "";
            }

            var childLines = new List <string>();

            parser.advance();

            while (!parser.isDone)
            {
                var match = pattern.Match(parser.current);
                if (!match.Success || !match.Groups[1].Value.StartsWith(endBlock))
                {
                    childLines.Add(parser.current);
                    parser.advance();
                }
                else
                {
                    parser.advance();
                    break;
                }
            }

            return(childLines);
        }
Beispiel #9
0
        public override Node parse(BlockParser parser)
        {
            parser.encounteredBlankLine = true;
            parser.advance();

            return(null);
        }
Beispiel #10
0
        public override Node parse(BlockParser parser)
        {
            var    lines = new List <string>();
            string tag   = string.Empty;

            while (!parser.isDone)
            {
                var match = BlockParser._setextPattern.Match(parser.current);
                if (!match.Success)
                {
                    lines.Add(parser.current);
                    parser.advance();
                    continue;
                }
                else
                {
                    tag = match.Groups[1].Value[0] == '=' ? "h1" : "h2";
                    parser.advance();
                    break;
                }
            }


            var contents = new UnparsedContent(lines.join('\n'));

            return(new Element(tag, new List <Node>()
            {
                contents
            }));
        }
Beispiel #11
0
        public override Node parse(BlockParser parser)
        {
            var childLines = this.parseChildLines(parser);

            var children = new BlockParser(childLines, parser.document).parseLines();

            return(new Element("blockquote", children));
        }
Beispiel #12
0
        /// Parses a table into its three parts:
        ///
        /// * a head row of head cells (`<th>` cells)
        /// * a divider of hyphens and pipes (not rendered)
        /// * many body rows of body cells (`<td>` cells)
        public override Node parse(BlockParser parser)
        {
            var alignments  = parseAlignments(parser.next);
            var columnCount = alignments.Count;
            var headRow     = parseRow(parser, alignments, "th");

            if (headRow.children.Count != columnCount)
            {
                return(null);
            }

            var head = new Element("thead", new List <Node>()
            {
                headRow
            });

            // Advance past the divider of hyphens.
            parser.advance();

            var rows = new List <Element>()
            {
            };

            while (!parser.isDone && !BlockSyntax.isAtBlockEnd(parser))
            {
                var row = parseRow(parser, alignments, "td");
                while (row.children.Count < columnCount)
                {
                    // Insert synthetic empty cells.
                    row.children.Add(Element.empty("td"));
                }

                while (row.children.Count > columnCount)
                {
                    row.children.removeLast();
                }

                rows.Add(row);
            }

            if (rows.isEmpty())
            {
                return(new Element("table", new List <Node>()
                {
                    head
                }));
            }
            else
            {
                var body = new Element("tbody", rows);

                return(new Element("table", new List <Node>()
                {
                    head, body
                }));
            }
        }
Beispiel #13
0
        protected static bool isAtBlockEnd(BlockParser parser)
        {
            if (parser.isDone)
            {
                return(true);
            }

            return(parser.blockSyntaxes.Any(s => s.canParse(parser) && s.canEndBlock));
        }
Beispiel #14
0
        public override Node parse(BlockParser parser)
        {
            var childLines = parseChildLines(parser);

            // Recursively parse the contents of the blockquote.
            var children = new BlockParser(childLines, parser.document).parseLines();

            return(new Element("blockquote", children));
        }
Beispiel #15
0
        // Parse [contents] as a reference link definition.
        //
        // Also adds the reference link definition to the document.
        //
        // Returns whether [contents] could be parsed as a reference link definition.
        bool _parseReflinkDefinition(BlockParser parser, string contents)
        {
            var pattern =
                new Regex(
                    @"^[ ]{0,3}\[((?:\\\]|[^\]])+)\]:\s*(?:<(\S+)>|(\S+))\s*(""[^""]+""|'[^']+'|\([^)]+\)|)\s*$",
                    RegexOptions.Multiline);

            var match = pattern.Match(contents);

            if (!match.Success)
            {
                // Not a reference link definition.
                return(false);
            }

            if (match.Groups[0].Length < contents.Length)
            {
                // Trailing text. No good.
                return(false);
            }

            var label       = match.Groups[1].Value;
            var destination = string.IsNullOrEmpty(match.Groups[2].Value)
                ? match.Groups[3].Value
                : match.Groups[2].Value;
            var title = match.Groups[4].Value;

            // The label must contain at least one non-whitespace character.
            if (_whitespacePattern.hasMatch(label))
            {
                return(false);
            }

            if (string.IsNullOrEmpty(title))
            {
                // No title.
                title = null;
            }
            else
            {
                // Remove "", '', or ().
                title = title.substring(1, title.Length - 1);
            }

            // References are case-insensitive, and internal whitespace is compressed.
            label =
                label.ToLower().Trim().replaceAll(BlockParser._oneOrMoreWhitespacePattern, ' ');

            parser.document.linkReferences
            .putIfAbsent(label, () => new LinkReference(label, destination, title));
            return(true);
        }
Beispiel #16
0
        public override Node parse(BlockParser parser)
        {
            var match = pattern.Match(parser.current);

            parser.advance();
            var level    = match.Groups[1].Length;
            var contents = new UnparsedContent(match.Groups[2].Value.Trim());

            return(new Element("h" + level, new List <Node>()
            {
                contents
            }));
        }
Beispiel #17
0
        public override Node parse(BlockParser parser)
        {
            var childLines = new List <string>();

            // Eat until we hit a blank line.
            while (!parser.isDone && !parser.matches(BlockParser._emptyPattern))
            {
                childLines.Add(parser.current);
                parser.advance();
            }

            return(new TextNode(childLines.join("\n")));
        }
Beispiel #18
0
        public override Node parse(BlockParser parser)
        {
            var childLines = parseChildLines(parser);

            // The Markdown tests expect a trailing newline.
            childLines.Add("");

            var escaped = Utils.escapeHtml(childLines.@join("\n"));

            return(new Element("pre", new List <Node>()
            {
                Element.text("code", escaped)
            }));
        }
Beispiel #19
0
        Element parseRow(
            BlockParser parser, List <string> alignments, string cellType)
        {
            var line = parser.current
                       .replaceFirst(_openingPipe, "")
                       .replaceFirst(_closingPipe, "");
            var cells = line.split(_pipePattern);

            parser.advance();
            var    row     = new List <Node>();
            string preCell = string.Empty;

            for (var index = 0; index < cells.Length; index++)
            {
                var cell = cells[index];
                if (preCell != null)
                {
                    cell    = preCell + cell;
                    preCell = null;
                }

                if (cell.EndsWith("\\"))
                {
                    preCell = cell.Substring(0, cell.Length - 1) + '|';
                    continue;
                }

                var contents = new UnparsedContent(cell);
                row.Add(new Element(cellType, new List <Node>()
                {
                    contents
                }));
            }

            for (var i = 0; i < row.Count && i < alignments.Count; i++)
            {
                if (alignments[i] == null)
                {
                    continue;
                }

                ((Element)row[i]).attributes["style"] = "text-align: " + alignments[i] + ';';
            }

            return(new Element("tr", row));
        }
Beispiel #20
0
        public override Node parse(BlockParser parser)
        {
            var childLines = new List <string>();

            // Eat until we hit [endPattern].
            while (!parser.isDone)
            {
                childLines.Add(parser.current);
                if (parser.matches(_endPattern))
                {
                    break;
                }
                parser.advance();
            }

            parser.advance();
            return(new TextNode(childLines.join("\n")));
        }
Beispiel #21
0
        public virtual List <string> parseChildLines(BlockParser parser)
        {
            var childLines = new List <string>();

            while (!parser.isDone)
            {
                var match = pattern.Match(parser.current);
                if (!match.Success)
                {
                    break;
                }

                childLines.Add(match.Groups[1].Value);

                parser.advance();
            }

            return(childLines);
        }
Beispiel #22
0
        public override Node parse(BlockParser parser)
        {
            // Get the syntax identifier, if there is one.
            var match      = pattern.Match(parser.current);
            var endBlock   = match.Groups[1].Value;
            var infoString = match.Groups[2].Value;

            var childLines = parseChildLines(parser, endBlock);

            // The Markdown tests expect a trailing newline.
            childLines.Add("");

            var text = childLines.join('\n');

            if (parser.document.encodeHtml)
            {
                // Escape the code.
                text = Utils.escapeHtml(text);
            }

            var code = Element.text("code", text);

            // the info-string should be trimmed
            // http://spec.commonmark.org/0.22/#example-100
            infoString = infoString.Trim();
            if (infoString.isNotEmpty())
            {
                // only use the first word in the syntax
                // http://spec.commonmark.org/0.22/#example-100
                infoString = infoString.Split(' ').first();
                code.attributes["class"] = "language-" + infoString;
            }

            var element = new Element("pre", new List <Node>()
            {
                code
            });

            return(element);
        }
Beispiel #23
0
        /// Extract reference link definitions from the front of the paragraph, and
        /// return the remaining paragraph lines.
        List <String> _extractReflinkDefinitions(
            BlockParser parser, List <String> lines)
        {
            var i = 0;

loopOverDefinitions:
            while (true)
            {
//                Debug.LogWarning("--->" + i);
                // Check for reflink definitions.
                if (!lineStartsReflinkDefinition(lines, i))
                {
                    // It's paragraph content from here on out.
                    break;
                }

                var contents = lines[i];
                var j        = i + 1;
                while (j < lines.Count)
                {
                    // Check to see if the _next_ line might start a new reflink definition.
                    // Even if it turns out not to be, but it started with a '[', then it
                    // is not a part of _this_ possible reflink definition.
                    if (lineStartsReflinkDefinition(lines, j))
                    {
                        // Try to parse [contents] as a reflink definition.
                        if (_parseReflinkDefinition(parser, contents))
                        {
                            // Loop again, starting at the next possible reflink definition.
                            i = j;
                            Debug.Log("--->" + i);
                            goto loopOverDefinitions;
                        }
                        else
                        {
                            // Could not parse [contents] as a reflink definition.
                            break;
                        }
                    }
                    else
                    {
                        contents = contents + '\n' + lines[j];
                        j++;
                    }
                }

                // End of the block.
                if (_parseReflinkDefinition(parser, contents))
                {
                    i = j;
                    break;
                }

                // It may be that there is a reflink definition starting at [i], but it
                // does not extend all the way to [j], such as:
                //
                //     [link]: url     // line i
                //     "title"
                //     garbage
                //     [link2]: url   // line j
                //
                // In this case, [i, i+1] is a reflink definition, and the rest is
                // paragraph content.
                while (j >= i)
                {
                    // This isn't the most efficient loop, what with this big ole'
                    // Iterable allocation (`getRange`) followed by a big 'ole String
                    // allocation, but we
                    // must walk backwards, checking each range.
                    contents = lines.getRange(i, j).join('\n');
                    if (_parseReflinkDefinition(parser, contents))
                    {
                        // That is the last reflink definition. The rest is paragraph
                        // content.
                        i = j;
                        break;
                    }

                    j--;
                }
                // The ending was not a reflink definition at all. Just paragraph
                // content.

                break;
            }

            if (i == lines.Count)
            {
                // No paragraph content.
                return(null);
            }
            else
            {
                // Ends with paragraph content.
                return(lines.sublist(i));
            }
        }
Beispiel #24
0
 public virtual bool canParse(BlockParser parser)
 {
     return(pattern.hasMatch(parser.current));
 }
Beispiel #25
0
 public override Node parse(BlockParser parser)
 {
     parser.advance();
     return(Element.empty("hr"));
 }
Beispiel #26
0
 public override bool canParse(BlockParser parser)
 {
     // Note: matches *next* line, not the current one. We're looking for the
     // bar separating the head row from the body rows.
     return(parser.matchesNext(BlockParser._tablePattern));
 }
Beispiel #27
0
 bool tryMatch(Regex pattern, BlockParser parser, ref Match match)
 {
     match = pattern.Match(parser.current);
     return(match.Success);
 }
Beispiel #28
0
 public override bool canParse(BlockParser parser) => true;
Beispiel #29
0
        public override Node parse(BlockParser parser)
        {
            var items      = new List <ListItem>();
            var childLines = new List <string>();


            Match match = null;


            string listMarker = null;
            string indent     = null;
            // In case the first number in an ordered list is not 1, use it as the
            // "start".
            int startNumber = 0;

            while (!parser.isDone)
            {
                var leadingSpace             = _whitespaceRe.matchAsPrefix(parser.current).Groups[0].Value;
                var leadingExpandedTabLength = _expandedTabLength(leadingSpace);
                if (tryMatch(BlockParser._emptyPattern, parser, ref match))
                {
                    if (BlockParser._emptyPattern.Match(parser.next ?? "").Success)
                    {
                        // Two blank lines ends a list.
                        break;
                    }

                    // Add a blank line to the current list item.
                    childLines.Add("");
                }
                else if (!string.IsNullOrEmpty(indent) && indent.Length <= leadingExpandedTabLength)
                {
                    // Strip off indent and add to current item.
                    var line = parser.current
                               .replaceFirst(leadingSpace, new string(' ', leadingExpandedTabLength))
                               .replaceFirst(indent, "");
                    childLines.Add(line);
                }
                else if (tryMatch(BlockParser._hrPattern, parser, ref match))
                {
                    // Horizontal rule takes precedence to a new list item.
                    break;
                }
                else if (tryMatch(BlockParser._ulPattern, parser, ref match) ||
                         tryMatch(BlockParser._olPattern, parser, ref match))
                {
                    var precedingWhitespace = match.Groups[1].Value;
                    var digits = match.Groups[2].Value ?? "";
                    if (startNumber == 0 && digits.isNotEmpty())
                    {
                        startNumber = int.Parse(digits);
                    }

                    var marker          = match.Groups[3];
                    var firstWhitespace = match.Groups[5].Value ?? "";
                    var restWhitespace  = match.Groups[6].Value ?? "";
                    var content         = match.Groups[7].Value ?? "";
                    var isBlank         = content.isEmpty();
                    if (listMarker != null && listMarker != marker.Value)
                    {
                        // Changing the bullet or ordered list delimiter starts a new list.
                        break;
                    }

                    listMarker = marker.Value;
                    var markerAsSpaces = new string(' ', digits.Length + marker.Length);
                    if (isBlank)
                    {
                        // See http://spec.commonmark.org/0.28/#list-items under "3. Item
                        // starting with a blank line."
                        //
                        // If the list item starts with a blank line, the final piece of the
                        // indentation is just a single space.
                        indent = precedingWhitespace + markerAsSpaces + ' ';
                    }
                    else if (restWhitespace.Length >= 4)
                    {
                        // See http://spec.commonmark.org/0.28/#list-items under "2. Item
                        // starting with indented code."
                        //
                        // If the list item starts with indented code, we need to _not_ count
                        // any indentation past the required whitespace character.
                        indent = precedingWhitespace + markerAsSpaces + firstWhitespace;
                    }
                    else
                    {
                        indent = precedingWhitespace +
                                 markerAsSpaces +
                                 firstWhitespace +
                                 restWhitespace;
                    }

                    // End the current list item and start a new one.
                    endItem(ref childLines, items);
                    childLines.Add(restWhitespace + content);
                }
                else if (BlockSyntax.isAtBlockEnd(parser))
                {
                    // Done with the list.
                    break;
                }
                else
                {
                    // If the previous item is a blank line, this means we're done with the
                    // list and are starting a new top-level paragraph.
                    if ((childLines.isNotEmpty()) && (childLines.last() == ""))
                    {
                        parser.encounteredBlankLine = true;
                        break;
                    }

                    // Anything else is paragraph continuation text.
                    childLines.Add(parser.current);
                }

                parser.advance();
            }

            endItem(ref childLines, items);
            var itemNodes = new List <Element>();

            items.ForEach(removeLeadingEmptyLine);
            var anyEmptyLines = removeTrailingEmptyLines(items);
            var anyEmptyLinesBetweenBlocks = false;

            foreach (var item in items)
            {
                var itemParser = new BlockParser(item.lines, parser.document);
                var children   = itemParser.parseLines();
                itemNodes.Add(new Element("li", children));
                anyEmptyLinesBetweenBlocks =
                    anyEmptyLinesBetweenBlocks || itemParser.encounteredBlankLine;
            }

            // Must strip paragraph tags if the list is "tight".
            // http://spec.commonmark.org/0.28/#lists
            var listIsTight = !anyEmptyLines && !anyEmptyLinesBetweenBlocks;

            if (listIsTight)
            {
                // We must post-process the list items, converting any top-level paragraph
                // elements to just text elements.
                foreach (var item in itemNodes)
                {
                    for (var i = 0; i < item.children.Count; i++)
                    {
                        var child = item.children[i];
                        var ele   = child as Element;
                        if (ele != null && ele.tag == "p")
                        {
                            item.children.RemoveAt(i);
                            item.children.InsertRange(i, ele.children);
                        }
                    }
                }
            }

            if (listTag == "ol" && startNumber != 1)
            {
                var element = new Element(listTag, itemNodes);
                element.attributes["start"] = startNumber.ToString();
                return(element);
            }
            else
            {
                return(new Element(listTag, itemNodes));
            }
        }
Beispiel #30
0
 public abstract Node parse(BlockParser parser);