Ejemplo n.º 1
0
        private bool ProcessLinkReference(InlineProcessor state, string label, SourceSpan labelSpan, LinkDelimiterInline parent, int endPosition)
        {
            bool isValidLink = false;
            LinkReferenceDefinition linkRef;

            if (state.Document.TryGetLinkReferenceDefinition(label, out linkRef))
            {
                Inline link = null;
                // Try to use a callback directly defined on the LinkReferenceDefinition
                if (linkRef.CreateLinkInline != null)
                {
                    link = linkRef.CreateLinkInline(state, linkRef, parent.FirstChild);
                }

                // Create a default link if the callback was not found
                if (link == null)
                {
                    // Inline Link
                    link = new LinkInline()
                    {
                        Url       = HtmlHelper.Unescape(linkRef.Url),
                        Title     = HtmlHelper.Unescape(linkRef.Title),
                        Label     = label,
                        LabelSpan = labelSpan,
                        IsImage   = parent.IsImage,
                        Reference = linkRef,
                        Span      = new SourceSpan(parent.Span.Start, endPosition),
                        Line      = parent.Line,
                        Column    = parent.Column,
                    };
                }

                var containerLink = link as ContainerInline;
                if (containerLink != null)
                {
                    var child = parent.FirstChild;
                    if (child == null)
                    {
                        child = new LiteralInline()
                        {
                            Content  = new StringSlice(label),
                            IsClosed = true,
                            // Not exact but we leave it like this
                            Span   = parent.Span,
                            Line   = parent.Line,
                            Column = parent.Column,
                        };
                        containerLink.AppendChild(child);
                    }
                    else
                    {
                        // Insert all child into the link
                        while (child != null)
                        {
                            var next = child.NextSibling;
                            child.Remove();
                            containerLink.AppendChild(child);
                            child = next;
                        }
                    }
                }

                link.IsClosed = true;

                // Process emphasis delimiters
                state.PostProcessInlines(0, link, null, false);

                state.Inline = link;
                isValidLink  = true;
            }
            //else
            //{
            //    // Else output a literal, leave it opened as we may have literals after
            //    // that could be append to this one
            //    var literal = new LiteralInline()
            //    {
            //        ContentBuilder = processor.StringBuilders.Get().Append('[').Append(label).Append(']')
            //    };
            //    processor.Inline = literal;
            //}
            return(isValidLink);
        }
Ejemplo n.º 2
0
        private bool TryProcessLinkOrImage(InlineProcessor inlineState, ref StringSlice text)
        {
            LinkDelimiterInline openParent = null;

            foreach (var parent in inlineState.Inline.FindParentOfType <LinkDelimiterInline>())
            {
                openParent = parent;
                break;
            }

            if (openParent != null)
            {
                // If we do find one, but it’s not active,
                // we remove the inactive delimiter from the stack,
                // and return a literal text node ].
                if (!openParent.IsActive)
                {
                    inlineState.Inline = new LiteralInline()
                    {
                        Content = new StringSlice("["),
                        Span    = openParent.Span,
                        Line    = openParent.Line,
                        Column  = openParent.Column,
                    };
                    openParent.ReplaceBy(inlineState.Inline);
                    return(false);
                }

                // If we find one and it’s active,
                // then we parse ahead to see if we have
                // an inline link/image, reference link/image,
                // compact reference link/image,
                // or shortcut reference link/image
                var parentDelimiter = openParent.Parent;
                switch (text.CurrentChar)
                {
                case '(':
                    string     url;
                    string     title;
                    SourceSpan linkSpan;
                    SourceSpan titleSpan;
                    if (LinkHelper.TryParseInlineLink(ref text, out url, out title, out linkSpan, out titleSpan))
                    {
                        // Inline Link
                        var link = new LinkInline()
                        {
                            Url       = HtmlHelper.Unescape(url),
                            Title     = HtmlHelper.Unescape(title),
                            IsImage   = openParent.IsImage,
                            LabelSpan = openParent.LabelSpan,
                            UrlSpan   = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
                            TitleSpan = inlineState.GetSourcePositionFromLocalSpan(titleSpan),
                            Span      = new SourceSpan(openParent.Span.Start, inlineState.GetSourcePosition(text.Start - 1)),
                            Line      = openParent.Line,
                            Column    = openParent.Column,
                        };

                        openParent.ReplaceBy(link);
                        // Notifies processor as we are creating an inline locally
                        inlineState.Inline = link;

                        // Process emphasis delimiters
                        inlineState.PostProcessInlines(0, link, null, false);

                        // If we have a link (and not an image),
                        // we also set all [ delimiters before the opening delimiter to inactive.
                        // (This will prevent us from getting links within links.)
                        if (!openParent.IsImage)
                        {
                            MarkParentAsInactive(parentDelimiter);
                        }

                        link.IsClosed = true;

                        return(true);
                    }
                    break;

                default:

                    var    labelSpan        = SourceSpan.Empty;
                    string label            = null;
                    bool   isLabelSpanLocal = true;
                    // Handle Collapsed links
                    if (text.CurrentChar == '[')
                    {
                        if (text.PeekChar(1) == ']')
                        {
                            label            = openParent.Label;
                            labelSpan        = openParent.LabelSpan;
                            isLabelSpanLocal = false;
                            text.NextChar();     // Skip [
                            text.NextChar();     // Skip ]
                        }
                    }
                    else
                    {
                        label = openParent.Label;
                    }

                    if (label != null || LinkHelper.TryParseLabel(ref text, true, out label, out labelSpan))
                    {
                        if (isLabelSpanLocal)
                        {
                            labelSpan = inlineState.GetSourcePositionFromLocalSpan(labelSpan);
                        }

                        if (ProcessLinkReference(inlineState, label, labelSpan, openParent, inlineState.GetSourcePosition(text.Start - 1)))
                        {
                            // Remove the open parent
                            openParent.Remove();
                            if (!openParent.IsImage)
                            {
                                MarkParentAsInactive(parentDelimiter);
                            }
                        }
                        else
                        {
                            return(false);
                        }
                        return(true);
                    }
                    break;
                }

                // We have a nested [ ]
                // firstParent.Remove();
                // The opening [ will be transformed to a literal followed by all the childrens of the [

                var literal = new LiteralInline()
                {
                    Span    = openParent.Span,
                    Content = new StringSlice(openParent.IsImage ? "![" : "[")
                };

                inlineState.Inline = openParent.ReplaceBy(literal);
                return(false);
            }

            return(false);
        }
Ejemplo n.º 3
0
        private bool ProcessLinkReference(InlineProcessor state, string label, bool isShortcut, SourceSpan labelSpan, LinkDelimiterInline parent, int endPosition)
        {
            if (!state.Document.TryGetLinkReferenceDefinition(label, out LinkReferenceDefinition linkRef))
            {
                return(false);
            }

            Inline link = null;

            // Try to use a callback directly defined on the LinkReferenceDefinition
            if (linkRef.CreateLinkInline != null)
            {
                link = linkRef.CreateLinkInline(state, linkRef, parent.FirstChild);
            }

            // Create a default link if the callback was not found
            if (link == null)
            {
                // Inline Link
                link = new LinkInline()
                {
                    Url        = HtmlHelper.Unescape(linkRef.Url),
                    Title      = HtmlHelper.Unescape(linkRef.Title),
                    Label      = label,
                    LabelSpan  = labelSpan,
                    UrlSpan    = linkRef.UrlSpan,
                    IsImage    = parent.IsImage,
                    IsShortcut = isShortcut,
                    Reference  = linkRef,
                    Span       = new SourceSpan(parent.Span.Start, endPosition),
                    Line       = parent.Line,
                    Column     = parent.Column,
                };
            }

            if (link is ContainerInline containerLink)
            {
                var child = parent.FirstChild;
                if (child == null)
                {
                    child = new LiteralInline()
                    {
                        Content  = StringSlice.Empty,
                        IsClosed = true,
                        // Not exact but we leave it like this
                        Span   = parent.Span,
                        Line   = parent.Line,
                        Column = parent.Column,
                    };
                    containerLink.AppendChild(child);
                }
                else
                {
                    // Insert all child into the link
                    while (child != null)
                    {
                        var next = child.NextSibling;
                        child.Remove();
                        containerLink.AppendChild(child);
                        child = next;
                    }
                }
            }

            link.IsClosed = true;

            // Process emphasis delimiters
            state.PostProcessInlines(0, link, null, false);

            state.Inline = link;

            return(true);
        }
Ejemplo n.º 4
0
        public bool PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex, bool isFinalProcessing)
        {
            var container  = root as ContainerInline;
            var tableState = state.ParserStates[Index] as TableState;

            // If the delimiters are being processed by an image link, we need to transform them back to literals
            if (!isFinalProcessing)
            {
                if (container == null || tableState == null)
                {
                    return(true);
                }

                var child = container.LastChild;
                List <PipeTableDelimiterInline> delimitersToRemove = null;

                while (child != null)
                {
                    var pipeDelimiter = child as PipeTableDelimiterInline;
                    if (pipeDelimiter != null)
                    {
                        if (delimitersToRemove == null)
                        {
                            delimitersToRemove = new List <PipeTableDelimiterInline>();
                        }
                        delimitersToRemove.Add(pipeDelimiter);
                    }

                    if (child == lastChild)
                    {
                        break;
                    }

                    var subContainer = child as ContainerInline;
                    child = subContainer?.LastChild;
                }

                // If we have found any delimiters, transform them to literals
                if (delimitersToRemove != null)
                {
                    bool leftIsDelimiter  = false;
                    bool rightIsDelimiter = false;
                    for (int i = 0; i < delimitersToRemove.Count; i++)
                    {
                        var pipeDelimiter = delimitersToRemove[i];
                        pipeDelimiter.ReplaceByLiteral();

                        // Check that the pipe that is being removed is not going to make a line without pipe delimiters
                        var tableDelimiters = tableState.ColumnAndLineDelimiters;
                        var delimiterIndex  = tableDelimiters.IndexOf(pipeDelimiter);

                        if (i == 0)
                        {
                            leftIsDelimiter = delimiterIndex > 0 && tableDelimiters[delimiterIndex - 1] is PipeTableDelimiterInline;
                        }
                        else if (i + 1 == delimitersToRemove.Count)
                        {
                            rightIsDelimiter = delimiterIndex + 1 < tableDelimiters.Count &&
                                               tableDelimiters[delimiterIndex + 1] is PipeTableDelimiterInline;
                        }
                        // Remove this delimiter from the table processor
                        tableState.ColumnAndLineDelimiters.Remove(pipeDelimiter);
                    }

                    // If we didn't have any delimiter before and after the delimiters we jsut removed, we mark the processor of the current line as no pipe
                    if (!leftIsDelimiter && !rightIsDelimiter)
                    {
                        tableState.LineHasPipe = false;
                    }
                }

                return(true);
            }

            // Remove previous state
            state.ParserStates[Index] = null;

            // Continue
            if (tableState == null || container == null || tableState.IsInvalidTable || !tableState.LineHasPipe)  //|| tableState.LineIndex != state.LocalLineIndex)
            {
                return(true);
            }

            // Detect the header row
            var delimiters = tableState.ColumnAndLineDelimiters;
            // TODO: we could optimize this by merging FindHeaderRow and the cell loop
            var aligns = FindHeaderRow(delimiters);

            if (Options.RequireHeaderSeparator && aligns == null)
            {
                return(true);
            }

            var table = new Table();

            // If the current paragraph block has any attributes attached, we can copy them
            var attributes = state.Block.TryGetAttributes();

            if (attributes != null)
            {
                attributes.CopyTo(table.GetAttributes());
            }

            state.BlockNew = table;
            var cells = tableState.Cells;

            cells.Clear();

            //delimiters[0].DumpTo(state.DebugLog);

            // delimiters contain a list of `|` and `\n` delimiters
            // The `|` delimiters are created as child containers.
            // So the following:
            // | a | b \n
            // | d | e \n
            //
            // Will generate a tree of the following node:
            // |
            //   a
            //   |
            //     b
            //     \n
            //     |
            //       d
            //       |
            //         e
            //         \n
            // When parsing delimiters, we need to recover whether a row is of the following form:
            // 0)  | a | b | \n
            // 1)  | a | b \n
            // 2)    a | b \n
            // 3)    a | b | \n

            // If the last element is not a line break, add a line break to homogenize parsing in the next loop
            var lastElement = delimiters[delimiters.Count - 1];

            if (!(lastElement is LineBreakInline))
            {
                while (true)
                {
                    if (lastElement is ContainerInline)
                    {
                        var nextElement = ((ContainerInline)lastElement).LastChild;
                        if (nextElement != null)
                        {
                            lastElement = nextElement;
                            continue;
                        }
                    }
                    break;
                }

                var endOfTable = new LineBreakInline();
                lastElement.InsertAfter(endOfTable);
                delimiters.Add(endOfTable);
                tableState.EndOfLines.Add(endOfTable);
            }

            // Cell loop
            // Reconstruct the table from the delimiters
            TableRow row      = null;
            TableRow firstRow = null;

            for (int i = 0; i < delimiters.Count; i++)
            {
                var delimiter     = delimiters[i];
                var pipeSeparator = delimiter as PipeTableDelimiterInline;
                var isLine        = delimiter is LineBreakInline;

                if (row == null)
                {
                    row = new TableRow();
                    if (firstRow == null)
                    {
                        firstRow = row;
                    }

                    // If the first delimiter is a pipe and doesn't have any parent or previous sibling, for cases like:
                    // 0)  | a | b | \n
                    // 1)  | a | b \n
                    if (pipeSeparator != null && (delimiter.PreviousSibling == null || delimiter.PreviousSibling is LineBreakInline))
                    {
                        delimiter.Remove();
                        continue;
                    }
                }

                // We need to find the beginning/ending of a cell from a right delimiter. From the delimiter 'x', we need to find a (without the delimiter start `|`)
                // So we iterate back to the first pipe or line break
                //         x
                // 1)  | a | b \n
                // 2)    a | b \n
                Inline endOfCell     = null;
                Inline beginOfCell   = null;
                var    cellContentIt = delimiter;
                while (true)
                {
                    cellContentIt = cellContentIt.PreviousSibling ?? cellContentIt.Parent;

                    if (cellContentIt == null || cellContentIt is LineBreakInline)
                    {
                        break;
                    }

                    // The cell begins at the first effective child after a | or the top ContainerInline (which is not necessary to bring into the tree + it contains an invalid span calculation)
                    if (cellContentIt is PipeTableDelimiterInline || (cellContentIt.GetType() == typeof(ContainerInline) && cellContentIt.Parent == null))
                    {
                        beginOfCell = ((ContainerInline)cellContentIt).FirstChild;
                        if (endOfCell == null)
                        {
                            endOfCell = beginOfCell;
                        }
                        break;
                    }

                    beginOfCell = cellContentIt;
                    if (endOfCell == null)
                    {
                        endOfCell = beginOfCell;
                    }
                }


                // If the current deilimiter is a pipe `|` OR
                // the beginOfCell/endOfCell are not null and
                // either they are :
                // - different
                // - they contain a single element, but it is not a line break (\n) or an empty/whitespace Literal.
                // Then we can add a cell to the current row
                if (!isLine || (beginOfCell != null && endOfCell != null && (beginOfCell != endOfCell || !(beginOfCell is LineBreakInline || (beginOfCell is LiteralInline && ((LiteralInline)beginOfCell).Content.IsEmptyOrWhitespace())))))
                {
                    if (!isLine)
                    {
                        // If the delimiter is a pipe, we need to remove it from the tree
                        // so that previous loop looking for a parent will not go further on subsequent cells
                        delimiter.Remove();
                    }

                    // We trim whitespace at the beginning and ending of the cell
                    TrimStart(beginOfCell);
                    TrimEnd(endOfCell);

                    var cellContainer = new ContainerInline();

                    // Copy elements from beginOfCell on the first level
                    var cellIt = beginOfCell;
                    while (cellIt != null && !IsLine(cellIt) && !(cellIt is PipeTableDelimiterInline))
                    {
                        var nextSibling = cellIt.NextSibling;
                        cellIt.Remove();
                        if (cellContainer.Span.IsEmpty)
                        {
                            cellContainer.Line   = cellIt.Line;
                            cellContainer.Column = cellIt.Column;
                            cellContainer.Span   = cellIt.Span;
                        }
                        cellContainer.AppendChild(cellIt);
                        cellContainer.Span.End = cellIt.Span.End;
                        cellIt = nextSibling;
                    }

                    // Create the cell and add it to the pending row
                    var tableParagraph = new ParagraphBlock()
                    {
                        Span   = cellContainer.Span,
                        Line   = cellContainer.Line,
                        Column = cellContainer.Column,
                        Inline = cellContainer
                    };

                    var tableCell = new TableCell()
                    {
                        Span   = cellContainer.Span,
                        Line   = cellContainer.Line,
                        Column = cellContainer.Column,
                    };

                    tableCell.Add(tableParagraph);
                    if (row.Span.IsEmpty)
                    {
                        row.Span   = cellContainer.Span;
                        row.Line   = cellContainer.Line;
                        row.Column = cellContainer.Column;
                    }
                    row.Add(tableCell);
                    cells.Add(tableCell);
                }

                // If we have a new line, we can add the row
                if (isLine)
                {
                    Debug.Assert(row != null);
                    if (table.Span.IsEmpty)
                    {
                        table.Span   = row.Span;
                        table.Line   = row.Line;
                        table.Column = row.Column;
                    }
                    table.Add(row);
                    row = null;
                }
            }

            // Once we are done with the cells, we can remove all end of lines in the table tree
            foreach (var endOfLine in tableState.EndOfLines)
            {
                endOfLine.Remove();
            }

            // If we have a header row, we can remove it
            // TODO: we could optimize this by merging FindHeaderRow and the previous loop
            if (aligns != null)
            {
                table.RemoveAt(1);
                var tableRow = (TableRow)table[0];
                table.ColumnDefinitions.AddRange(aligns);
                tableRow.IsHeader = true;
            }

            // Perform delimiter processor that are coming after this processor
            foreach (var cell in cells)
            {
                var paragraph = (ParagraphBlock)cell[0];
                state.PostProcessInlines(postInlineProcessorIndex + 1, paragraph.Inline, null, true);
            }

            // Clear cells when we are done
            cells.Clear();

            // We don't want to continue procesing delimiters, as we are already processing them here
            return(false);
        }
Ejemplo n.º 5
0
        public bool PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex, bool isFinalProcessing)
        {
            var container  = root as ContainerInline;
            var tableState = state.ParserStates[Index] as TableState;

            // If the delimiters are being processed by an image link, we need to transform them back to literals
            if (!isFinalProcessing)
            {
                if (container == null || tableState == null)
                {
                    return(true);
                }

                var child = container.LastChild;
                List <PiprTableDelimiterInline> delimitersToRemove = null;

                while (child != null)
                {
                    var pipeDelimiter = child as PiprTableDelimiterInline;
                    if (pipeDelimiter != null)
                    {
                        if (delimitersToRemove == null)
                        {
                            delimitersToRemove = new List <PiprTableDelimiterInline>();
                        }
                        delimitersToRemove.Add(pipeDelimiter);
                    }

                    if (child == lastChild)
                    {
                        break;
                    }

                    var subContainer = child as ContainerInline;
                    child = subContainer?.LastChild;
                }

                // If we have found any delimiters, transform them to literals
                if (delimitersToRemove != null)
                {
                    bool leftIsDelimiter  = false;
                    bool rightIsDelimiter = false;
                    for (int i = 0; i < delimitersToRemove.Count; i++)
                    {
                        var pipeDelimiter = delimitersToRemove[i];
                        var literalInline = new LiteralInline()
                        {
                            Content = new StringSlice("|"), IsClosed = true
                        };
                        pipeDelimiter.ReplaceBy(literalInline);

                        // Check that the pipe that is being removed is not going to make a line without pipe delimiters
                        var tableDelimiters = tableState.ColumnAndLineDelimiters;
                        var delimiterIndex  = tableDelimiters.IndexOf(pipeDelimiter);

                        if (i == 0)
                        {
                            leftIsDelimiter = delimiterIndex > 0 && tableDelimiters[delimiterIndex - 1] is PiprTableDelimiterInline;
                        }
                        else if (i + 1 == delimitersToRemove.Count)
                        {
                            rightIsDelimiter = delimiterIndex + 1 < tableDelimiters.Count &&
                                               tableDelimiters[delimiterIndex + 1] is PiprTableDelimiterInline;
                        }
                        // Remove this delimiter from the table processor
                        tableState.ColumnAndLineDelimiters.Remove(pipeDelimiter);
                    }

                    // If we didn't have any delimiter before and after the delimiters we jsut removed, we mark the processor of the current line as no pipe
                    if (!leftIsDelimiter && !rightIsDelimiter)
                    {
                        tableState.LineHasPipe = false;
                    }
                }

                return(true);
            }

            // Continue
            if (tableState == null || container == null || tableState.IsInvalidTable || !tableState.LineHasPipe)  //|| tableState.LineIndex != state.LocalLineIndex)
            {
                return(true);
            }

            var delimiters = tableState.ColumnAndLineDelimiters;

            delimiters.Add(null);
            var aligns = FindHeaderRow(delimiters);

            if (Options.RequireHeaderSeparator && aligns == null)
            {
                return(true);
            }

            var table = new Table();

            // If the current paragraph block has any attributes attached, we can copy them
            var attributes = state.Block.TryGetAttributes();

            if (attributes != null)
            {
                attributes.CopyTo(table.GetAttributes());
            }

            state.BlockNew = table;
            TableRow firstRow  = null;
            int      maxColumn = 0;
            var      cells     = tableState.Cells;

            cells.Clear();

            Inline column = container.FirstChild;

            if (column is PiprTableDelimiterInline)
            {
                column = ((PiprTableDelimiterInline)column).FirstChild;
            }

            // TODO: This is not accurate for the table
            table.Span.Start = column.Span.Start;
            table.Span.End   = column.Span.End;
            table.Line       = column.Line;
            table.Column     = column.Column;

            int lastIndex = 0;

            for (int i = 0; i < delimiters.Count; i++)
            {
                var delimiter = delimiters[i];
                if (delimiter == null || IsLine(delimiter))
                {
                    var beforeDelimiter = delimiter?.PreviousSibling;
                    var nextLineColumn  = delimiter?.NextSibling;

                    TableRow row = null;

                    for (int j = lastIndex; j <= i; j++)
                    {
                        var columnSeparator = delimiters[j];
                        var pipeSeparator   = columnSeparator as PiprTableDelimiterInline;

                        var endOfColumn = columnSeparator?.PreviousSibling;

                        // This is the first column empty
                        if (j == lastIndex && pipeSeparator != null && endOfColumn == null)
                        {
                            columnSeparator.Remove();
                            column = pipeSeparator.FirstChild;
                            continue;
                        }

                        if (pipeSeparator != null && IsTrailingColumnDelimiter(pipeSeparator))
                        {
                            TrimEnd(endOfColumn);
                            columnSeparator.Remove();
                            continue;
                        }

                        var cellContainer = new ContainerInline();
                        var item          = column;
                        var isFirstItem   = true;
                        TrimStart(item);
                        while (item != null && !IsLine(item) && !(item is PiprTableDelimiterInline))
                        {
                            var nextSibling = item.NextSibling;
                            item.Remove();
                            cellContainer.AppendChild(item);
                            if (isFirstItem)
                            {
                                cellContainer.Line       = item.Line;
                                cellContainer.Column     = item.Column;
                                cellContainer.Span.Start = item.Span.Start;
                                isFirstItem = false;
                            }
                            cellContainer.Span.End = item.Span.End;
                            item = nextSibling;
                        }

                        var tableParagraph = new ParagraphBlock()
                        {
                            Span   = cellContainer.Span,
                            Line   = cellContainer.Line,
                            Column = cellContainer.Column,
                            Inline = cellContainer
                        };

                        var tableCell = new TableCell()
                        {
                            Span   = cellContainer.Span,
                            Line   = cellContainer.Line,
                            Column = cellContainer.Column,
                        };

                        tableCell.Add(tableParagraph);

                        if (row == null)
                        {
                            row = new TableRow()
                            {
                                Span   = cellContainer.Span,
                                Line   = cellContainer.Line,
                                Column = cellContainer.Column,
                            };
                        }
                        row.Add(tableCell);
                        cells.Add(tableCell);

                        // If we have reached the end, we can add remaining delimiters as pure child of the current cell
                        if (row.Count == maxColumn && columnSeparator is PiprTableDelimiterInline)
                        {
                            columnSeparator.Remove();
                            tableParagraph.Inline.AppendChild(columnSeparator);
                            break;
                        }
                        TrimEnd(endOfColumn);

                        //TrimEnd(previousSibling);
                        if (columnSeparator != null)
                        {
                            if (pipeSeparator != null)
                            {
                                column = pipeSeparator.FirstChild;
                            }
                            columnSeparator.Remove();
                        }
                    }

                    if (row != null)
                    {
                        table.Add(row);
                    }

                    TrimEnd(beforeDelimiter);

                    if (delimiter != null)
                    {
                        delimiter.Remove();
                    }

                    if (nextLineColumn != null)
                    {
                        column = nextLineColumn;
                    }

                    if (firstRow == null)
                    {
                        firstRow  = row;
                        maxColumn = firstRow.Count;
                    }

                    lastIndex = i + 1;
                }
            }

            // If we have a header row, we can remove it
            if (aligns != null)
            {
                table.RemoveAt(1);
                var tableRow = (TableRow)table[0];
                table.ColumnDefinitions.AddRange(aligns);
                tableRow.IsHeader = true;
            }

            // Perform delimiter processor that are coming after this processor
            foreach (var cell in cells)
            {
                var paragraph = (ParagraphBlock)cell[0];
                state.PostProcessInlines(postInlineProcessorIndex + 1, paragraph.Inline, null, true);
            }

            // Clear cells when we are done
            cells.Clear();

            // We don't want to continue procesing delimiters, as we are already processing them here
            return(false);
        }
Ejemplo n.º 6
0
        private bool TryProcessLinkOrImage(InlineProcessor inlineState, ref StringSlice text)
        {
            LinkDelimiterInline?openParent = inlineState.Inline !.FirstParentOfType <LinkDelimiterInline>();

            if (openParent is null)
            {
                return(false);
            }

            // If we do find one, but it’s not active,
            // we remove the inactive delimiter from the stack,
            // and return a literal text node ].
            if (!openParent.IsActive)
            {
                inlineState.Inline = new LiteralInline()
                {
                    Content = new StringSlice("["),
                    Span    = openParent.Span,
                    Line    = openParent.Line,
                    Column  = openParent.Column,
                };
                openParent.ReplaceBy(inlineState.Inline);
                return(false);
            }

            // If we find one and it’s active,
            // then we parse ahead to see if we have
            // an inline link/image, reference link/image,
            // compact reference link/image,
            // or shortcut reference link/image
            var parentDelimiter = openParent.Parent;
            var savedText       = text;

            if (text.CurrentChar == '(')
            {
                if (inlineState.TrackTrivia)
                {
                    if (LinkHelper.TryParseInlineLinkTrivia(
                            ref text,
                            out string?url,
                            out SourceSpan unescapedUrlSpan,
                            out string?title,
                            out SourceSpan unescapedTitleSpan,
                            out char titleEnclosingCharacter,
                            out SourceSpan linkSpan,
                            out SourceSpan titleSpan,
                            out SourceSpan triviaBeforeLink,
                            out SourceSpan triviaAfterLink,
                            out SourceSpan triviaAfterTitle,
                            out bool urlHasPointyBrackets))
                    {
                        var wsBeforeLink   = new StringSlice(text.Text, triviaBeforeLink.Start, triviaBeforeLink.End);
                        var wsAfterLink    = new StringSlice(text.Text, triviaAfterLink.Start, triviaAfterLink.End);
                        var wsAfterTitle   = new StringSlice(text.Text, triviaAfterTitle.Start, triviaAfterTitle.End);
                        var unescapedUrl   = new StringSlice(text.Text, unescapedUrlSpan.Start, unescapedUrlSpan.End);
                        var unescapedTitle = new StringSlice(text.Text, unescapedTitleSpan.Start, unescapedTitleSpan.End);
                        // Inline Link
                        var link = new LinkInline()
                        {
                            TriviaBeforeUrl         = wsBeforeLink,
                            Url                     = HtmlHelper.Unescape(url),
                            UnescapedUrl            = unescapedUrl,
                            UrlHasPointyBrackets    = urlHasPointyBrackets,
                            TriviaAfterUrl          = wsAfterLink,
                            Title                   = HtmlHelper.Unescape(title),
                            UnescapedTitle          = unescapedTitle,
                            TitleEnclosingCharacter = titleEnclosingCharacter,
                            TriviaAfterTitle        = wsAfterTitle,
                            IsImage                 = openParent.IsImage,
                            LabelSpan               = openParent.LabelSpan,
                            UrlSpan                 = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
                            TitleSpan               = inlineState.GetSourcePositionFromLocalSpan(titleSpan),
                            Span                    = new SourceSpan(openParent.Span.Start, inlineState.GetSourcePosition(text.Start - 1)),
                            Line                    = openParent.Line,
                            Column                  = openParent.Column,
                        };

                        openParent.ReplaceBy(link);
                        // Notifies processor as we are creating an inline locally
                        inlineState.Inline = link;

                        // Process emphasis delimiters
                        inlineState.PostProcessInlines(0, link, null, false);

                        // If we have a link (and not an image),
                        // we also set all [ delimiters before the opening delimiter to inactive.
                        // (This will prevent us from getting links within links.)
                        if (!openParent.IsImage)
                        {
                            MarkParentAsInactive(parentDelimiter);
                        }

                        link.IsClosed = true;

                        return(true);
                    }
                }
                else
                {
                    if (LinkHelper.TryParseInlineLink(ref text, out string?url, out string?title, out SourceSpan linkSpan, out SourceSpan titleSpan))
                    {
                        // Inline Link
                        var link = new LinkInline()
                        {
                            Url       = HtmlHelper.Unescape(url),
                            Title     = HtmlHelper.Unescape(title),
                            IsImage   = openParent.IsImage,
                            LabelSpan = openParent.LabelSpan,
                            UrlSpan   = inlineState.GetSourcePositionFromLocalSpan(linkSpan),
                            TitleSpan = inlineState.GetSourcePositionFromLocalSpan(titleSpan),
                            Span      = new SourceSpan(openParent.Span.Start, inlineState.GetSourcePosition(text.Start - 1)),
                            Line      = openParent.Line,
                            Column    = openParent.Column,
                        };

                        openParent.ReplaceBy(link);
                        // Notifies processor as we are creating an inline locally
                        inlineState.Inline = link;

                        // Process emphasis delimiters
                        inlineState.PostProcessInlines(0, link, null, false);

                        // If we have a link (and not an image),
                        // we also set all [ delimiters before the opening delimiter to inactive.
                        // (This will prevent us from getting links within links.)
                        if (!openParent.IsImage)
                        {
                            MarkParentAsInactive(parentDelimiter);
                        }

                        link.IsClosed = true;

                        return(true);
                    }
                }

                text = savedText;
            }

            var        labelSpan        = SourceSpan.Empty;
            string?    label            = null;
            SourceSpan labelWithTrivia  = SourceSpan.Empty;
            bool       isLabelSpanLocal = true;

            bool       isShortcut = false;
            LocalLabel localLabel = LocalLabel.Local;

            // Handle Collapsed links
            if (text.CurrentChar == '[')
            {
                if (text.PeekChar() == ']')
                {
                    label            = openParent.Label;
                    labelSpan        = openParent.LabelSpan;
                    isLabelSpanLocal = false;
                    localLabel       = LocalLabel.Empty;
                    text.SkipChar(); // Skip [
                    text.SkipChar(); // Skip ]
                }
            }
            else
            {
                localLabel = LocalLabel.None;
                label      = openParent.Label;
                isShortcut = true;
            }
            if (label != null || LinkHelper.TryParseLabelTrivia(ref text, true, out label, out labelSpan))
            {
                labelWithTrivia = new SourceSpan(labelSpan.Start, labelSpan.End);
                if (isLabelSpanLocal)
                {
                    labelSpan = inlineState.GetSourcePositionFromLocalSpan(labelSpan);
                }

                if (ProcessLinkReference(inlineState, text, label !, labelWithTrivia, isShortcut, labelSpan, openParent, inlineState.GetSourcePosition(text.Start - 1), localLabel))
                {
                    // Remove the open parent
                    openParent.Remove();
                    if (!openParent.IsImage)
                    {
                        MarkParentAsInactive(parentDelimiter);
                    }
                    return(true);
                }
                else if (text.CurrentChar != ']' && text.CurrentChar != '[')
                {
                    return(false);
                }
            }