示例#1
0
        public void TestParse()
        {
            const string markdown = "This is a text with some *emphasis*";

            var pipeline = new MarkdownPipelineBuilder()
                           .UsePreciseSourceLocation()
                           .Build();

            MarkdownDocument document = Markdown.Parse(markdown, pipeline);

            Assert.AreEqual(1, document.LineCount);
            Assert.AreEqual(markdown.Length, document.Span.Length);
            Assert.AreEqual(1, document.LineStartIndexes.Count);
            Assert.AreEqual(0, document.LineStartIndexes[0]);

            Assert.AreEqual(1, document.Count);
            ParagraphBlock paragraph = document[0] as ParagraphBlock;

            Assert.NotNull(paragraph);
            Assert.AreEqual(markdown.Length, paragraph.Span.Length);
            LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;

            Assert.NotNull(literal);
            Assert.AreEqual("This is a text with some ", literal.ToString());
            EmphasisInline emphasis = literal.NextSibling as EmphasisInline;

            Assert.NotNull(emphasis);
            Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
            LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;

            Assert.NotNull(emphasisLiteral);
            Assert.AreEqual("emphasis", emphasisLiteral.ToString());
            Assert.Null(emphasisLiteral.NextSibling);
            Assert.Null(emphasis.NextSibling);
        }
        public void Heading_should_construct_from_HeadingBlock_correctly()
        {
            var headingBlock = new HeadingBlock(new ParagraphBlockParser());

            headingBlock.Inline = new ContainerInline();

            var firstContent = new LiteralInline("myheading");

            headingBlock.Inline.AppendChild(firstContent);

            var firstHeading = new Heading(headingBlock);

            Assert.AreEqual("myheading", firstHeading.Title);

            var secondContent = new LiteralInline("-myotherheading");

            headingBlock.Inline.AppendChild(secondContent);

            var secondHeading = new Heading(headingBlock);

            Assert.AreEqual("myheading-myotherheading", secondHeading.Title);

            var codeContent = new CodeInline();

            codeContent.Content = "awesomeclassname";
            headingBlock.Inline.AppendChild(codeContent);

            var codeHeading = new Heading(headingBlock);

            Assert.AreEqual("myheading-myotherheadingawesomeclassname", codeHeading.Title);
        }
示例#3
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>();
        public static LiteralInline UpdateContent(this LiteralInline literal, Func <string, string> update)
        {
            string current = literal.Content.ToString();

            current         = update(current);
            literal.Content = new StringSlice(current);
            return(literal);
        }
示例#5
0
 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 LiteralInlineRenderer();
     inline     = new LiteralInline("sample literal");
 }
示例#6
0
        public static bool Contains(this LiteralInline literal, char value, out int index)
        {
            index = literal.Content.Text.IndexOf(value, literal.Content.Start, literal.Content.Length);
            if (index == -1)
            {
                return(false);
            }

            index -= literal.Content.Start;
            return(true);
        }
示例#7
0
    public void TestTextConverter(string markdownInput)
    {
        var inline = new LiteralInline
        {
            Content = new StringSlice(markdownInput),
        };
        var textConverter = new TextElementConverter();

        var result = textConverter.Execute(inline);

        Assert.Equal(markdownInput, result.Content.Replace("\n", ""));
    }
示例#8
0
        public void CreateLinkInline_CreatesLinkInline(string dummyLabel, string dummyName, string expectedContent)
        {
            // Arrange
            const string            dummyUrl                = "dummyUrl";
            var                     dummyLiteralInline      = new LiteralInline(dummyLabel);
            var                     linkReferenceDefinition = new LinkReferenceDefinition(dummyLabel, dummyUrl, dummyName); // We use the title property to hold the block's name
            FlexiFigureBlockFactory testSubject             = CreateFlexiFigureBlockFactory();

            // Act
            Inline result = testSubject.CreateLinkInline(null, linkReferenceDefinition, dummyLiteralInline);

            // Assert
            Assert.IsType <LinkInline>(result);
            Assert.Equal(dummyUrl, ((LinkInline)result).Url);
            Assert.Equal(expectedContent, dummyLiteralInline.ToString());
        }
示例#9
0
        static void AddTicketLink(InlineProcessor processor,
                                  StringSlice slice,
                                  int charactersConsumedInReference,
                                  Uri uri)
        {
            int line, column;

            var startPosition = slice.Start;
            var endPosition   = startPosition + charactersConsumedInReference - 1;
            var linkText      = new StringSlice(slice.Text, startPosition, endPosition);

            var link = new TicketLinkInline {
                Span =
                {
                    Start = processor.GetSourcePosition(startPosition, out line, out column),
                },
                Line       = line,
                Column     = column,
                Url        = uri.ToString(),
                IsClosed   = true,
                IsAutoLink = true,
                Title      = $"Navigate to {linkText.ToString()}",
            };

            link.Span.End = endPosition;
            link.UrlSpan  = link.Span;
            link.GetAttributes().AddClass(TicketLinkClass);

            var linkContent = new LiteralInline {
                Span     = link.Span,
                Line     = line,
                Column   = column,
                Content  = linkText,
                IsClosed = true,
            };

            link.AppendChild(linkContent);

            processor.Inline = link;
        }
示例#10
0
 public static bool Contains(this LiteralInline literal, char value)
 => literal.Content.Text.IndexOf(value, literal.Span.Start, literal.Span.Length) != -1;
示例#11
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);
        }
示例#12
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);
        }
示例#13
0
        bool IPostInlineProcessor.PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex,
                                              bool isFinalProcessing)
        {
            // Don't try to process anything if there are no dash
            var quotePants = state.ParserStates[Index] as ListSmartyPants;

            if (quotePants == null || !quotePants.HasDash)
            {
                return(true);
            }

            var child             = root;
            var pendingContainers = new Stack <Inline>();

            while (true)
            {
                while (child != null)
                {
                    var next = child.NextSibling;

                    if (child is LiteralInline)
                    {
                        var literal = (LiteralInline)child;

                        var startIndex = 0;

                        var indexOfDash = literal.Content.IndexOf("--", startIndex);
                        if (indexOfDash >= 0)
                        {
                            var type = SmartyPantType.Dash2;
                            if (literal.Content.PeekCharAbsolute(indexOfDash + 2) == '-')
                            {
                                type = SmartyPantType.Dash3;
                            }
                            var nextContent  = literal.Content;
                            var originalSpan = literal.Span;
                            literal.Span.End   -= literal.Content.End - indexOfDash + 1;
                            literal.Content.End = indexOfDash - 1;
                            nextContent.Start   = indexOfDash + (type == SmartyPantType.Dash2 ? 2 : 3);

                            var pant = new SmartyPant()
                            {
                                Span             = new SourceSpan(literal.Content.End + 1, nextContent.Start - 1),
                                Line             = literal.Line,
                                Column           = literal.Column,
                                OpeningCharacter = '-',
                                Type             = type
                            };
                            literal.InsertAfter(pant);

                            var postLiteral = new LiteralInline()
                            {
                                Span    = new SourceSpan(pant.Span.End + 1, originalSpan.End),
                                Line    = literal.Line,
                                Column  = literal.Column,
                                Content = nextContent
                            };
                            pant.InsertAfter(postLiteral);

                            // Use the pending literal to proceed further
                            next = postLiteral;
                        }
                    }
                    else if (child is ContainerInline)
                    {
                        pendingContainers.Push(((ContainerInline)child).FirstChild);
                    }

                    child = next;
                }
                if (pendingContainers.Count > 0)
                {
                    child = pendingContainers.Pop();
                }
                else
                {
                    break;
                }
            }
            return(true);
        }
 private static string GetTextFromLiteral(LiteralInline literal)
 {
     return(GetTextFromSlice(literal.Content));
 }
示例#15
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);
        }
示例#16
0
        public void WalkAndBindParseEvents(MarkdownObject markdownObject)
        {
            foreach (MarkdownObject child in markdownObject.Descendants())
            {
                // LinkInline can be both an <img.. or a <a href="...">
                LinkInline linkInline = child as LinkInline;
                if (linkInline != null)
                {
                    EnsureAttributesInLink(linkInline);

                    if (linkInline.IsImage)
                    {
                        string altText = "";

                        var descendentForAltTag = child.Descendants().FirstOrDefault();
                        if (descendentForAltTag != null)
                        {
                            altText = descendentForAltTag.ToString();
                        }

                        string title = altText;

                        if (_imageDelegate != null)
                        {
                            HtmlImageTag args = InvokeImageParsedEvent(linkInline.Url, altText);

                            if (!string.IsNullOrEmpty(args.Alt))
                            {
                                altText = args.Alt;
                            }

                            if (!string.IsNullOrEmpty(args.Title))
                            {
                                title = args.Title;
                            }

                            // Update the HTML from the data the event gives back
                            linkInline.Url = args.Src;
                        }

                        // Replace to alt= attribute, it's a literal
                        var literalInline = new LiteralInline(altText);
                        linkInline.FirstChild.ReplaceBy(literalInline);

                        // HTML5 the tag
                        linkInline.Title = title;

                        // Necessary for links and Bootstrap 3
                        AddAttribute(linkInline, "border", "0");

                        // Make all images expand via this Bootstrap class
                        AddClass(linkInline, "img-responsive");
                    }
                    else
                    {
                        if (_linkDelegate != null)
                        {
                            string text = linkInline.Title;
                            var    descendentForAltTag = child.Descendants().FirstOrDefault();
                            if (descendentForAltTag != null)
                            {
                                text = descendentForAltTag.ToString();
                            }

                            HtmlLinkTag args = InvokeLinkParsedEvent(linkInline.Url, text, linkInline.Label);

                            // Update the HTML from the data the event gives back
                            linkInline.Url = args.Href;

                            if (!string.IsNullOrEmpty(args.Target))
                            {
                                AddAttribute(linkInline, "target", args.Target);
                            }

                            if (!string.IsNullOrEmpty(args.CssClass))
                            {
                                AddClass(linkInline, args.CssClass);
                            }

                            // Replace the link's text
                            var literalInline = new LiteralInline(args.Text);
                            linkInline.FirstChild.ReplaceBy(literalInline);
                        }

                        // Markdig TODO: make these configurable (external-links: [])
                        if (!string.IsNullOrEmpty(linkInline.Url))
                        {
                            string upperUrl = linkInline.Url.ToUpperInvariant();
                            if (upperUrl.StartsWith("HTTP://", StringComparison.Ordinal) ||
                                upperUrl.StartsWith("HTTPS://", StringComparison.Ordinal) ||
                                upperUrl.StartsWith("MAILTO:", StringComparison.Ordinal))
                            {
                                AddAttribute(linkInline, "rel", "nofollow");
                            }
                        }
                    }
                }

                WalkAndBindParseEvents(child);
            }
        }
示例#17
0
        private OpenXmlElement ConvertLiteralInline(LiteralInline literalInline, bool isBoldInherited = false, bool isItalicInherited = false)
        {
            var normalText = literalInline.Content.ToString();

            return(Manipulator.ElementCreator.CreateRunElement(normalText, isBoldInherited, isItalicInherited));
        }
 private static string GetTextToTranslateFromLiteralInline(LiteralInline literalInline)
 {
     return(literalInline.Content.ToString());
 }
        private void ProcessEmphasis(List <EmphasisDelimiterInline> delimiters)
        {
            // The following method is inspired by the "An algorithm for parsing nested emphasis and links"
            // at the end of the CommonMark specs.

            // Move current_position forward in the delimiter stack (if needed) until
            // we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input – the first one in parse order.)
            for (int i = 0; i < delimiters.Count; i++)
            {
                var closeDelimiter = delimiters[i];
                // Skip delimiters not supported by this instance
                if (emphasisMap[closeDelimiter.DelimiterChar] == null)
                {
                    continue;
                }

                if ((closeDelimiter.Type & DelimiterType.Close) != 0 && closeDelimiter.DelimiterCount > 0)
                {
                    while (true)
                    {
                        // Now, look back in the stack (staying above stack_bottom and the openers_bottom for this delimiter type)
                        // for the first matching potential opener (“matching” means same delimiter).
                        EmphasisDelimiterInline openDelimiter = null;
                        int openDelimiterIndex = -1;
                        for (int j = i - 1; j >= 0; j--)
                        {
                            var previousOpenDelimiter = delimiters[j];

                            var isOddMatch = ((closeDelimiter.Type & DelimiterType.Open) != 0 ||
                                              (previousOpenDelimiter.Type & DelimiterType.Close) != 0) &&
                                             previousOpenDelimiter.DelimiterCount != closeDelimiter.DelimiterCount &&
                                             (previousOpenDelimiter.DelimiterCount + closeDelimiter.DelimiterCount) % 3 == 0;

                            if (previousOpenDelimiter.DelimiterChar == closeDelimiter.DelimiterChar &&
                                (previousOpenDelimiter.Type & DelimiterType.Open) != 0 &&
                                previousOpenDelimiter.DelimiterCount > 0 && !isOddMatch)
                            {
                                openDelimiter      = previousOpenDelimiter;
                                openDelimiterIndex = j;
                                break;
                            }
                        }

                        if (openDelimiter != null)
                        {
process_delims:
                            bool isStrong = openDelimiter.DelimiterCount >= 2 && closeDelimiter.DelimiterCount >= 2;

                            // Insert an emph or strong emph node accordingly, after the text node corresponding to the opener.
                            var emphasis = CreateEmphasisInline?.Invoke(closeDelimiter.DelimiterChar, isStrong)
                                           ?? new EmphasisInline()
                            {
                                DelimiterChar = closeDelimiter.DelimiterChar,
                                IsDouble      = isStrong
                            };

                            // Update position for emphasis
                            var closeDelimitercount = closeDelimiter.DelimiterCount;
                            var delimiterDelta      = isStrong ? 2 : 1;

                            emphasis.Span.Start = openDelimiter.Span.Start;
                            emphasis.Line       = openDelimiter.Line;
                            emphasis.Column     = openDelimiter.Column;
                            emphasis.Span.End   = closeDelimiter.Span.End - closeDelimitercount + delimiterDelta;

                            openDelimiter.Span.Start  += delimiterDelta;
                            openDelimiter.Column      += delimiterDelta;
                            closeDelimiter.Span.Start += delimiterDelta;
                            closeDelimiter.Column     += delimiterDelta;

                            openDelimiter.DelimiterCount  -= delimiterDelta;
                            closeDelimiter.DelimiterCount -= delimiterDelta;

                            var embracer = (ContainerInline)openDelimiter;

                            // Copy attributes attached to delimiter to the emphasis
                            var attributes = closeDelimiter.TryGetAttributes();
                            if (attributes != null)
                            {
                                emphasis.SetAttributes(attributes);
                            }

                            // Embrace all delimiters
                            embracer.EmbraceChildrenBy(emphasis);

                            // Remove any intermediate emphasis
                            for (int k = i - 1; k >= openDelimiterIndex + 1; k--)
                            {
                                var literalDelimiter = delimiters[k];
                                var literal          = new LiteralInline()
                                {
                                    Content  = new StringSlice(literalDelimiter.ToLiteral()),
                                    IsClosed = true,
                                    Span     = literalDelimiter.Span,
                                    Line     = literalDelimiter.Line,
                                    Column   = literalDelimiter.Column
                                };

                                literalDelimiter.ReplaceBy(literal);

                                delimiters.RemoveAt(k);
                                i--;
                            }

                            if (closeDelimiter.DelimiterCount == 0)
                            {
                                var newParent = openDelimiter.DelimiterCount > 0 ? emphasis : emphasis.Parent;
                                closeDelimiter.MoveChildrenAfter(newParent);
                                closeDelimiter.Remove();
                                delimiters.RemoveAt(i);
                                i--;

                                // Remove the open delimiter if it is also empty
                                if (openDelimiter.DelimiterCount == 0)
                                {
                                    openDelimiter.MoveChildrenAfter(openDelimiter);
                                    openDelimiter.Remove();
                                    delimiters.RemoveAt(openDelimiterIndex);
                                    i--;
                                }
                                break;
                            }

                            // The current delimiters are matching
                            if (openDelimiter.DelimiterCount > 0)
                            {
                                goto process_delims;
                            }
                            else
                            {
                                // Remove the open delimiter if it is also empty
                                var firstChild = openDelimiter.FirstChild;
                                firstChild.Remove();
                                openDelimiter.ReplaceBy(firstChild);
                                firstChild.IsClosed = true;
                                closeDelimiter.Remove();
                                firstChild.InsertAfter(closeDelimiter);
                                delimiters.RemoveAt(openDelimiterIndex);
                                i--;
                            }
                        }
                        else if ((closeDelimiter.Type & DelimiterType.Open) == 0)
                        {
                            var literal = new LiteralInline()
                            {
                                Content  = new StringSlice(closeDelimiter.ToLiteral()),
                                IsClosed = true,
                                Span     = closeDelimiter.Span,
                                Line     = closeDelimiter.Line,
                                Column   = closeDelimiter.Column
                            };

                            closeDelimiter.ReplaceBy(literal);
                            // Notifies processor as we are creating an inline locally
                            delimiters.RemoveAt(i);
                            i--;
                            break;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }

            // Any delimiters left must be literal
            for (int i = 0; i < delimiters.Count; i++)
            {
                var delimiter = delimiters[i];
                var literal   = new LiteralInline()
                {
                    Content  = new StringSlice(delimiter.ToLiteral()),
                    IsClosed = true,
                    Span     = delimiter.Span,
                    Line     = delimiter.Line,
                    Column   = delimiter.Column
                };

                delimiter.ReplaceBy(literal);
            }
            delimiters.Clear();
        }
示例#20
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);
        }
示例#21
0
        private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline inline)
        {
            inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;

            var abbreviations = inlineProcessor.Document.GetAbbreviations();

            // Should not happen, but another extension could decide to remove them, so...
            if (abbreviations == null)
            {
                return;
            }

            // Build a text matcher from the abbreviations labels
            var labels  = new HashSet <string>(abbreviations.Keys);
            var matcher = new TextMatchHelper(labels);

            inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
            {
                var literal         = (LiteralInline)processor.Inline;
                var originalLiteral = literal;

                ContainerInline container = null;

                // This is slow, but we don't have much the choice
                var content = literal.Content;
                var text    = content.Text;
                for (int i = content.Start; i < content.End; i++)
                {
                    string match;
                    if (matcher.TryMatch(text, i, content.End - i + 1, out match) && IsValidAbbreviation(match, content, i))
                    {
                        var indexAfterMatch = i + match.Length;

                        // We should have a match, but in case...
                        Abbreviation abbr;
                        if (!abbreviations.TryGetValue(match, out abbr))
                        {
                            continue;
                        }

                        // If we don't have a container, create a new one
                        if (container == null)
                        {
                            container = literal.Parent ??
                                        new ContainerInline
                            {
                                Span   = originalLiteral.Span,
                                Line   = originalLiteral.Line,
                                Column = originalLiteral.Column,
                            };
                        }

                        int line;
                        int column;
                        var abbrInline = new AbbreviationInline(abbr)
                        {
                            Span =
                            {
                                Start = processor.GetSourcePosition(i, out line, out column),
                            },
                            Line   = line,
                            Column = column
                        };
                        abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;

                        // Append the previous literal
                        if (i > content.Start)
                        {
                            if (literal.Parent == null)
                            {
                                container.AppendChild(literal);
                            }
                        }
                        literal.Span.End = abbrInline.Span.Start - 1;
                        // Truncate it before the abbreviation
                        literal.Content.End = i - 1;


                        // Appned the abbreviation
                        container.AppendChild(abbrInline);

                        // If this is the end of the string, clear the literal
                        // and exit
                        if (content.End == indexAfterMatch - 1)
                        {
                            literal = null;
                            break;
                        }

                        // Process the remaining literal
                        literal = new LiteralInline()
                        {
                            Span   = new SourceSpan(abbrInline.Span.End + 1, literal.Span.End),
                            Line   = line,
                            Column = column + match.Length,
                        };
                        content.Start   = indexAfterMatch;
                        literal.Content = content;

                        i = indexAfterMatch - 1;
                    }
                }

                if (container != null)
                {
                    if (literal != null)
                    {
                        container.AppendChild(literal);
                    }
                    processor.Inline = container;
                }
            };
        }
示例#22
0
        private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline inline)
        {
            inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;

            var abbreviations = inlineProcessor.Document.GetAbbreviations();

            // Should not happen, but another extension could decide to remove them, so...
            if (abbreviations == null)
            {
                return;
            }

            // Build a text matcher from the abbreviations labels
            var prefixTree = new CompactPrefixTree <Abbreviation>(abbreviations);

            inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
            {
                var literal         = (LiteralInline)processor.Inline;
                var originalLiteral = literal;

                ContainerInline container = null;

                // This is slow, but we don't have much the choice
                var content = literal.Content;
                var text    = content.Text;

                for (int i = content.Start; i <= content.End; i++)
                {
                    // Abbreviation must be a whole word == start at the start of a line or after a whitespace
                    if (i != 0)
                    {
                        for (i = i - 1; i <= content.End; i++)
                        {
                            if (text[i].IsWhitespace())
                            {
                                i++;
                                goto ValidAbbreviationStart;
                            }
                        }
                        break;
                    }

                    ValidAbbreviationStart :;

                    if (prefixTree.TryMatchLongest(text, i, content.End - i + 1, out KeyValuePair <string, Abbreviation> abbreviationMatch))
                    {
                        var match = abbreviationMatch.Key;
                        if (!IsValidAbbreviationEnding(match, content, i))
                        {
                            continue;
                        }

                        var indexAfterMatch = i + match.Length;

                        // If we don't have a container, create a new one
                        if (container == null)
                        {
                            container = literal.Parent ??
                                        new ContainerInline
                            {
                                Span   = originalLiteral.Span,
                                Line   = originalLiteral.Line,
                                Column = originalLiteral.Column,
                            };
                        }

                        var abbrInline = new AbbreviationInline(abbreviationMatch.Value)
                        {
                            Span =
                            {
                                Start = processor.GetSourcePosition(i, out int line, out int column),
                            },
                            Line   = line,
                            Column = column
                        };
                        abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;

                        // Append the previous literal
                        if (i > content.Start && literal.Parent == null)
                        {
                            container.AppendChild(literal);
                        }

                        literal.Span.End = abbrInline.Span.Start - 1;
                        // Truncate it before the abbreviation
                        literal.Content.End = i - 1;


                        // Append the abbreviation
                        container.AppendChild(abbrInline);

                        // If this is the end of the string, clear the literal and exit
                        if (content.End == indexAfterMatch - 1)
                        {
                            literal = null;
                            break;
                        }

                        // Process the remaining literal
                        literal = new LiteralInline()
                        {
                            Span   = new SourceSpan(abbrInline.Span.End + 1, literal.Span.End),
                            Line   = line,
                            Column = column + match.Length,
                        };
                        content.Start   = indexAfterMatch;
                        literal.Content = content;

                        i = indexAfterMatch - 1;
                    }
示例#23
0
 protected virtual void AddHtmlInLineText(string text, LiteralInline literalInline)
 => AddText(text, t => t.Colour = Color4.MediumPurple);
示例#24
0
文件: Text.cs 项目: modo-lv/Forkdown
 public Text(LiteralInline text) : base(text) =>
     this.Content = text.Content.ToString();