Esempio n. 1
0
        /// <summary>
        /// Convert a Markdown element to a Forkdown one.
        /// </summary>
        /// <param name="mdo">Markdown object to convert.</param>
        public static Element ToForkdown(IMarkdownObject mdo)
        {
            Element result = mdo switch {
                MarkdownDocument _ => new Document(),
                HeadingBlock h => new Heading(h),
                ListBlock l => new Listing(l),
                ListItemBlock li => new ListItem(li),
                ParagraphBlock p => new Paragraph(p),
                CustomContainer c => new Section(c),
                CustomContainerInline c => new ExplicitInlineContainer(c),
                CodeInline c => new Code(c),
                FencedCodeBlock c => new CodeBlock(c),
                LinkInline l => new Link(l),
                LiteralInline t => new Text(t),
                LineBreakInline _ => new LineBreak(),
                ThematicBreakBlock _ => new Separator(),
                Tables.Table t => new Table(t),
                Tables.TableRow tr => new TableRow(tr),
                Tables.TableCell tc => new TableCell(tc),
                EmphasisInline e => new Emphasis(e),
                HtmlBlock b => new Html(b),
                _ => new Placeholder(mdo),
            };

            var subs = mdo switch {
                LeafBlock b => b.Inline,
                IEnumerable <MarkdownObject> e => e,
                _ => null
            } ?? Nil.E <MarkdownObject>();
Esempio n. 2
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var startPosition = slice.Start;
            // Go to escape character
            var c = slice.NextChar();
            int line;
            int column;

            if (c.IsAsciiPunctuation())
            {
                processor.Inline = new LiteralInline()
                {
                    Content = new StringSlice(slice.Text, slice.Start, slice.Start),
                    Span    = { Start = processor.GetSourcePosition(startPosition, out line, out column) },
                    Line    = line,
                    Column  = column,
                    IsFirstCharacterEscaped = true,
                };
                processor.Inline.Span.End = processor.Inline.Span.Start + 1;
                slice.SkipChar();
                return(true);
            }

            // A backslash at the end of the line is a [hard line break]:
            if (c == '\n' || c == '\r')
            {
                var newLine = c == '\n' ? NewLine.LineFeed : NewLine.CarriageReturn;
                if (c == '\r' && slice.PeekChar() == '\n')
                {
                    newLine = NewLine.CarriageReturnLineFeed;
                }
                var inline = new LineBreakInline()
                {
                    IsHard      = true,
                    IsBackslash = true,
                    Span        = { Start = processor.GetSourcePosition(startPosition, out line, out column) },
                    Line        = line,
                    Column      = column,
                };
                processor.Inline = inline;

                if (processor.TrackTrivia)
                {
                    inline.NewLine = newLine;
                }

                inline.Span.End = inline.Span.Start + 1;
                slice.SkipChar(); // Skip \n or \r alone
                if (newLine == NewLine.CarriageReturnLineFeed)
                {
                    slice.SkipChar(); // Skip \r\n
                }
                return(true);
            }

            return(false);
        }
Esempio n. 3
0
        private OpenXmlElement[] ConvertLineBreakInline(LineBreakInline lineBreakInline)
        {
            if (lineBreakInline.IsHard)
            {
                return(new OpenXmlElement[] { Manipulator.ElementCreator.CreateBreakElement() });
            }

            return(new OpenXmlElement[] { });
        }
 public void Setup()
 {
     document = new Document();
     // Workaround for a quirk in the migradoc API.
     _             = document.AddSection().Elements;
     pdfBuilder    = new PdfBuilder(document, PdfOptions.Default);
     renderer      = new LineBreakInlineRenderer();
     inline        = new LineBreakInline();
     inline.IsHard = true;
 }
Esempio n. 5
0
        protected override void Write(RoundtripRenderer renderer, QuoteBlock quoteBlock)
        {
            renderer.RenderLinesBefore(quoteBlock);
            renderer.Write(quoteBlock.TriviaBefore);

            var indents = new string[quoteBlock.QuoteLines.Count];

            for (int i = 0; i < quoteBlock.QuoteLines.Count; i++)
            {
                var quoteLine           = quoteBlock.QuoteLines[i];
                var wsb                 = quoteLine.TriviaBefore.ToString();
                var quoteChar           = quoteLine.QuoteChar ? ">" : "";
                var spaceAfterQuoteChar = quoteLine.HasSpaceAfterQuoteChar ? " " : "";
                var wsa                 = quoteLine.TriviaAfter.ToString();
                indents[i] = (wsb + quoteChar + spaceAfterQuoteChar + wsa);
            }
            bool noChildren = false;

            if (quoteBlock.Count == 0)
            {
                noChildren = true;
                // since this QuoteBlock instance has no children, indents will not be rendered. We
                // work around this by adding empty LineBreakInlines to a ParagraphBlock.
                // Wanted: a more elegant/better solution (although this is not *that* bad).
                foreach (var quoteLine in quoteBlock.QuoteLines)
                {
                    var emptyLeafBlock = new ParagraphBlock
                    {
                        NewLine = quoteLine.NewLine
                    };
                    var newLine = new LineBreakInline
                    {
                        NewLine = quoteLine.NewLine
                    };
                    var container = new ContainerInline();
                    container.AppendChild(newLine);
                    emptyLeafBlock.Inline = container;
                    quoteBlock.Add(emptyLeafBlock);
                }
            }

            renderer.PushIndent(indents);
            renderer.WriteChildren(quoteBlock);
            renderer.PopIndent();

            if (!noChildren)
            {
                renderer.RenderLinesAfter(quoteBlock);
            }
        }
Esempio n. 6
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);
        }