示例#1
0
        /// <summary>
        /// Returns whether the specified <see cref="DeliminatorInfo"/> is followed by a punctuation character.
        /// </summary>
        /// <param name="text">The string which contains <paramref name="info"/>.</param>
        /// <param name="info">The <see cref="DeliminatorInfo"/> object.</param>
        /// <returns>
        /// <c>true</c> when the specified <see cref="DeliminatorInfo"/> is followed by a punctuation character,
        /// otherwise <c>false</c>.
        /// </returns>
        private static bool IsFollowedByPunctuation(string text, DeliminatorInfo info)
        {
            if (text.Length <= info.Index + info.DeliminatorLength)
            {
                return(false);
            }

            return(MarkdownElementBase.asciiPunctuationChars
                   .Contains(text[info.Index + info.DeliminatorLength]));
        }
示例#2
0
        /// <summary>
        /// Returns whether the specified deliminator is right flanking.
        /// See https://spec.commonmark.org/0.28/#right-flanking-delimiter-run for more information.
        /// </summary>
        /// <param name="text">The string object which contains <paramref name="info"/>.</param>
        /// <param name="info">
        /// The <see cref="DeliminatorInfo"/> to determine whether right flanking or not.
        /// </param>
        /// <returns>Whether the specified deliminator is right flanking.</returns>
        private static bool IsRightFlanking(string text, DeliminatorInfo info)
        {
            var prevChar = info.Index == 0 ? ' ' : text[info.Index - 1];
            var nextChar = info.Index + info.DeliminatorLength >= text.Length
                ? ' '
                : text[info.Index + info.DeliminatorLength];

            if (char.IsWhiteSpace(prevChar))
            {
                return(false);
            }

            if (!MarkdownElementBase.asciiPunctuationChars.Contains(prevChar))
            {
                return(true);
            }

            return(char.IsWhiteSpace(nextChar) || MarkdownElementBase.asciiPunctuationChars.Contains(nextChar));
        }
示例#3
0
 /// <summary>
 /// Returns whether the specified <see cref="DeliminatorInfo"/> is preceded by a punctuation character.
 /// </summary>
 /// <param name="text">The string which contains <paramref name="info"/>.</param>
 /// <param name="info">The <see cref="DeliminatorInfo"/> object.</param>
 /// <returns>
 /// <c>true</c> when the specified <see cref="DeliminatorInfo"/> is preceded by a punctuation character,
 /// otherwise <c>false</c>.
 /// </returns>
 private static bool IsPrecededByPunctuation(string text, DeliminatorInfo info)
 {
     return(info.Index != 0 && MarkdownElementBase.asciiPunctuationChars.Contains(text[info.Index - 1]));
 }
示例#4
0
        /// <summary>
        /// Parses the structure and add the <see cref="InlineSpan"/> to <paramref name="delimSpans"/>.
        /// </summary>
        /// <param name="deliminators">The list of <see cref="DeliminatorInfo"/>.</param>
        /// <param name="delimSpans">The list of <see cref="InlineSpan"/>.</param>
        /// <param name="stackBottom">The stack bottom.</param>
        private static void ParseEmphasis(LinkedList <DeliminatorInfo> deliminators,
                                          SortedList <int, InlineSpan> delimSpans, DeliminatorInfo stackBottom)
        {
            if (deliminators.Count == 0)
            {
                return;
            }

            LinkedListNode <DeliminatorInfo> firstClose = null;
            LinkedListNode <DeliminatorInfo> infoNode   =
                stackBottom == null ? deliminators.First : deliminators.Find(stackBottom);

            while (infoNode != null)
            {
                if (infoNode.Value.CanClose)
                {
                    firstClose = infoNode;
                    break;
                }

                infoNode = infoNode.Next;
            }

            if (firstClose == null)
            {
                return;
            }

            LinkedListNode <DeliminatorInfo> startDelimNode = null;

            infoNode = firstClose;
            while ((infoNode = infoNode.Previous) != null && infoNode.Value != stackBottom)
            {
                if (infoNode.Value.CanOpen &&
                    infoNode.Value.Type == firstClose.Value.Type &&
                    ((infoNode.Value.DeliminatorLength + firstClose.Value.DeliminatorLength) % 3 != 0 ||
                     infoNode.Value.DeliminatorLength % 3 == 0 ||
                     (!firstClose.Value.CanOpen && !infoNode.Value.CanClose)))
                {
                    startDelimNode = infoNode;
                    break;
                }
            }

            if (startDelimNode == null)
            {
                if (!firstClose.Value.CanOpen)
                {
                    deliminators.Remove(firstClose);
                }

                firstClose = firstClose.Next;
                if (firstClose == null)
                {
                    return;
                }

                ParseEmphasis(deliminators, delimSpans, firstClose.Value);
                return;
            }

            DeliminatorInfo openInfo    = startDelimNode.Value;
            DeliminatorInfo closeInfo   = firstClose.Value;
            int             delimLength = openInfo.DeliminatorLength > 1 && closeInfo.DeliminatorLength > 1 ? 2 : 1;
            var             delimSpan   = new InlineSpan
            {
                Begin    = openInfo.Index + openInfo.DeliminatorLength - delimLength,
                End      = closeInfo.Index + delimLength,
                SpanType =
                    delimLength > 1 ? InlineSpanType.StrongEmphasis : InlineSpanType.Emphasis,
            };

            delimSpan.ParseBegin = delimSpan.Begin + delimLength;
            delimSpan.ParseEnd   = delimSpan.End - delimLength;
            delimSpans.Add(delimSpan.Begin, delimSpan);
            while ((infoNode = infoNode.Next) != null && infoNode != firstClose)
            {
                deliminators.Remove(infoNode);
            }

            openInfo.DeliminatorLength -= delimLength;
            if (openInfo.DeliminatorLength <= 0)
            {
                deliminators.Remove(openInfo);
            }

            closeInfo.DeliminatorLength -= delimLength;
            closeInfo.Index             += delimLength;
            if (closeInfo.DeliminatorLength <= 0)
            {
                deliminators.Remove(closeInfo);
            }

            ParseEmphasis(deliminators, delimSpans,
                          deliminators.Find(firstClose.Value) == null ? firstClose.Next?.Value : firstClose.Value);
        }
示例#5
0
        /// <summary>
        /// Parses inline elements to Links, Images Emphasis and Line breaks.
        /// </summary>
        /// <param name="text">The string object to @arse.</param>
        /// <param name="higherSpans">Delim Spans which represents higher priority.</param>
        /// <returns>The parse result.</returns>
        private IEnumerable <InlineElement> ParseLinkEmphasis(string text, List <InlineSpan> higherSpans)
        {
            var deliminators = new LinkedList <DeliminatorInfo>();
            var inlineSpans  = new SortedList <int, InlineSpan>();

            for (int i = 0; i < text.Length; i++)
            {
                // If the character is escaped or contained in higherSpans
                if (IsEscaped(text, i) || higherSpans.Any(d => d.Begin <= i && d.End > i))
                {
                    continue;
                }

                if (text[i] == '*')
                {
                    int length    = CountSameChars(text, i);
                    var delimInfo = DeliminatorInfo.Create(DeliminatorType.Star, i, length);
                    delimInfo.CanOpen  = IsLeftFlanking(text, delimInfo);
                    delimInfo.CanClose = IsRightFlanking(text, delimInfo);

                    deliminators.AddLast(delimInfo);
                    i += length - 1;
                }
                else if (text[i] == '_')
                {
                    int  length        = CountSameChars(text, i);
                    var  delimInfo     = DeliminatorInfo.Create(DeliminatorType.UnderBar, i, length);
                    bool leftFranking  = IsLeftFlanking(text, delimInfo);
                    bool rightFlanking = IsRightFlanking(text, delimInfo);

                    delimInfo.CanOpen  = leftFranking && (!rightFlanking || IsPrecededByPunctuation(text, delimInfo));
                    delimInfo.CanClose = rightFlanking && (!leftFranking || IsFollowedByPunctuation(text, delimInfo));
                    deliminators.AddLast(delimInfo);
                    i += length - 1;
                }
                else if (text[i] == '[')
                {
                    deliminators.AddLast(DeliminatorInfo.Create(DeliminatorType.OpenLink, i, 1));
                }
                else if (BeginWith(text, i, "!["))
                {
                    deliminators.AddLast(DeliminatorInfo.Create(DeliminatorType.OpenImage, i, 2));
                    i++;
                }
                else if (i > 0 && text[i] == ']')
                {
                    DeliminatorInfo openInfo = deliminators
                                               .LastOrDefault(info => info.Type == DeliminatorType.OpenImage ||
                                                              info.Type == DeliminatorType.OpenLink);
                    if (openInfo == null)
                    {
                        continue;
                    }

                    if (!openInfo.Active)
                    {
                        deliminators.Remove(openInfo);
                        continue;
                    }

                    int linkLabel  = GetStartIndexOfLinkLabel(text, 0, i + 1, higherSpans);
                    int linkBody   = GetLinkBodyEndIndex(text, i + 1, out var dest, out var title);
                    int linkLabel2 = GetEndIndexOfLinkLabel(text, i + 1, higherSpans);

                    // Inline Link/Image
                    if (text[Math.Min(i + 1, text.Length - 1)] == '(' && linkLabel >= 0 && linkBody >= 0)
                    {
                        inlineSpans.Add(openInfo.Index,
                                        InlineSpan.FromDeliminatorInfo(openInfo, linkBody, i, dest ?? string.Empty, title));
                        i = linkBody - 1;
                    }
                    // Collapsed Reference Link
                    else if (BeginWith(text, i + 1, "[]") &&
                             TryGetReference(text.Substring(openInfo.Index + openInfo.DeliminatorLength,
                                                            i - openInfo.Index - openInfo.DeliminatorLength), out var definition))
                    {
                        inlineSpans.Add(openInfo.Index, InlineSpan.FromDeliminatorInfo(openInfo, i + 3, i, definition));
                        i += 2;
                    }
                    // Full Reference Link
                    else if (linkLabel2 >= 0 &&
                             TryGetReference(text.Substring(i + 2, linkLabel2 - i - 3), out definition))
                    {
                        inlineSpans.Add(openInfo.Index,
                                        InlineSpan.FromDeliminatorInfo(openInfo, linkLabel2, i, definition));
                        i = linkLabel2 - 1;
                    }
                    // shortcut link
                    else if (TryGetReference(text.Substring(openInfo.Index + openInfo.DeliminatorLength,
                                                            i - openInfo.Index - openInfo.DeliminatorLength), out definition) &&
                             GetEndIndexOfLinkLabel(text, i + 1, higherSpans) < 0)
                    {
                        inlineSpans.Add(openInfo.Index, InlineSpan.FromDeliminatorInfo(openInfo, i + 1, i, definition));
                    }
                    else
                    {
                        deliminators.Remove(openInfo);
                        continue;
                    }

                    if (openInfo.Type == DeliminatorType.OpenLink)
                    {
                        foreach (var item in deliminators.TakeWhile(c => c != openInfo))
                        {
                            if (item.Type == DeliminatorType.OpenLink)
                            {
                                item.Active = false;
                            }
                        }
                    }

                    ParseEmphasis(deliminators, inlineSpans, openInfo);
                    deliminators.Remove(openInfo);
                }
            }

            ParseEmphasis(deliminators, inlineSpans, null);

            foreach (var item in higherSpans)
            {
                inlineSpans.Add(item.Begin, item);
            }

            var tree = GetInlineTree(inlineSpans, text.Length);

            return(ToInlines(text, tree));
        }