public static void AppendStackEntry(InlineStack entry, Subject subj) { if (subj.LastPendingInline != null) { entry.Previous = subj.LastPendingInline; subj.LastPendingInline.Next = entry; } if (subj.FirstPendingInline is null) { subj.FirstPendingInline = entry; } subj.LastPendingInline = entry; }
public static InlineStack FindMatchingOpener(InlineStack searchBackwardsFrom, InlineStackPriority priority, char delimiter, int closerDelimiterCount, bool closerCanOpen, out bool canClose) { canClose = true; var istack = searchBackwardsFrom; while (true) { if (istack is null) { // this cannot be a closer since there is no opener available. canClose = false; return(null); } if (istack.Priority > priority || (istack.Delimiter == delimiter && 0 != (istack.Flags & InlineStackFlags.Closer))) { // there might be a closer further back but we cannot go there yet because a higher priority element is blocking // the other option is that the stack entry could be a closer for the same char - this means // that any opener we might find would first have to be matched against this closer. return(null); } if (istack.Delimiter == delimiter) { // interior closer of size 2 does not match opener of size 1 and vice versa. // for more details, see https://github.com/jgm/cmark/commit/c50197bab81d7105c9c790548821b61bcb97a62a var oddMatch = (closerCanOpen || (istack.Flags & InlineStackFlags.CloserOriginally) > 0) && istack.DelimiterCount != closerDelimiterCount && ((istack.DelimiterCount + closerDelimiterCount) % 3 == 0); if (!oddMatch) { return(istack); } } istack = istack.Previous; } }
public static void PostProcessInlineStack(Subject subj, InlineStack first, InlineStack last, InlineStackPriority ignorePriority) { while (ignorePriority > 0) { var istack = first; while (istack != null) { if (istack.Priority >= ignorePriority) { RemoveStackEntry(istack, subj, istack); } else if (0 != (istack.Flags & InlineStackFlags.Closer)) { var iopener = FindMatchingOpener(istack.Previous, istack.Priority, istack.Delimiter, istack.DelimiterCount, (istack.Flags & InlineStackFlags.Opener) > 0, out bool canClose); if (iopener != null) { bool retry = false; if (iopener.Delimiter == '~') { InlineMethods.MatchInlineStack(iopener, subj, istack.DelimiterCount, istack, (InlineTag)0, InlineTag.Strikethrough); if (istack.DelimiterCount > 1) { retry = true; } } else { var useDelims = InlineMethods.MatchInlineStack(iopener, subj, istack.DelimiterCount, istack, InlineTag.Emphasis, InlineTag.Strong); if (istack.DelimiterCount > 0) { retry = true; } } if (retry) { // remove everything between opened and closer (not inclusive). if (istack.Previous != null && iopener.Next != istack.Previous) { RemoveStackEntry(iopener.Next, subj, istack.Previous); } continue; } else { // remove opener, everything in between, and the closer RemoveStackEntry(iopener, subj, istack); } } else if (!canClose) { // this case means that a matching opener does not exist // remove the Closer flag so that a future Opener can be matched against it. istack.Flags &= ~InlineStackFlags.Closer; } } if (istack == last) { break; } istack = istack.Next; } ignorePriority--; } }
/// <summary> /// Removes a subset of the stack. /// </summary> /// <param name="first">The first entry to be removed.</param> /// <param name="subj">The subject associated with this stack. Can be <see langword="null"/> if the pointers in the subject should not be updated.</param> /// <param name="last">The last entry to be removed. Can be <see langword="null"/> if everything starting from <paramref name="first"/> has to be removed.</param> public static void RemoveStackEntry(InlineStack first, Subject subj, InlineStack last) { var curPriority = first.Priority; if (last is null) { if (first.Previous != null) { first.Previous.Next = null; } else if (subj != null) { subj.FirstPendingInline = null; } if (subj != null) { last = subj.LastPendingInline; subj.LastPendingInline = first.Previous; } first = first.Next; } else { if (first.Previous != null) { first.Previous.Next = last.Next; } else if (subj != null) { subj.FirstPendingInline = last.Next; } if (last.Next != null) { last.Next.Previous = first.Previous; } else if (subj != null) { subj.LastPendingInline = first.Previous; } if (first == last) { return; } first = first.Next; last = last.Previous; } if (last is null || first is null) { return; } first.Previous = null; last.Next = null; // handle case like [*b*] (the whole [..] is being removed but the inner *..* must still be matched). // this is not done automatically because the initial * is recognized as a potential closer (assuming // potential scenario '*[*' ). if (curPriority > 0) { PostProcessInlineStack(null, first, last, curPriority); } }