internal static void MatchSquareBracketStack(InlineStack opener, Subject subj, Reference details) { if (details != null) { var inl = opener.StartingInline; var isImage = 0 != (opener.Flags & InlineStack.InlineStackFlags.ImageLink); inl.Tag = isImage ? InlineTag.Image : (details.IsPlaceholder ? InlineTag.Placeholder : InlineTag.Link); inl.FirstChild = inl.NextSibling; inl.NextSibling = null; inl.SourceLastPosition = subj.Position; inl.TargetUrl = details.Url; inl.LiteralContent = details.Title; if (!isImage && !details.IsPlaceholder) { // since there cannot be nested links, remove any other link openers before this var temp = opener.Previous; while (temp != null && temp.Priority <= InlineStack.InlineStackPriority.Links) { if (temp.Delimiter == '[' && temp.Flags == opener.Flags) { // mark the previous entries as "inactive" if (temp.DelimiterCount == -1) { break; } temp.DelimiterCount = -1; } temp = temp.Previous; } } InlineStack.RemoveStackEntry(opener, subj, null); subj.LastInline = inl; } else { // this looked like a link, but was not. // remove the opener stack entry but leave the inbetween intact InlineStack.RemoveStackEntry(opener, subj, opener); var inl = new Inline("]", subj.Position - 1, subj.Position); subj.LastInline.LastSibling.NextSibling = inl; subj.LastInline = inl; } }
private static Inline HandleRightSquareBracket(Subject subj, bool supportPlaceholderBrackets) { // move past ']' subj.Position++; bool canClose; var istack = InlineStack.FindMatchingOpener(subj.LastPendingInline, InlineStack.InlineStackPriority.Links, '[', 1, false, out canClose); if (istack != null) { // if the opener is "inactive" then it means that there was a nested link if (istack.DelimiterCount == -1) { InlineStack.RemoveStackEntry(istack, subj, istack); return(new Inline("]", subj.Position - 1, subj.Position)); } var endpos = subj.Position; // try parsing details for '[foo](/url "title")' or '[foo][bar]' //var details = ParseLinkDetails(subj); var details = ParseLinkDetails(subj, supportPlaceholderBrackets); // try lookup of the brackets themselves if (details == null || details == Reference.SelfReference) { var startpos = istack.StartPosition; var label = new StringPart(subj.Buffer, startpos, endpos - startpos - 1); details = LookupReference(subj.DocumentData, label); } if (details == Reference.InvalidReference) { details = null; } MatchSquareBracketStack(istack, subj, details); return(null); } var inlText = new Inline("]", subj.Position - 1, subj.Position); if (canClose) { // note that the current implementation will not work if there are other inlines with priority // higher than Links. // to fix this the parsed link details should be added to the closer element in the stack. throw new NotSupportedException("It is not supported to have inline stack priority higher than Links."); ////istack = new InlineStack(); ////istack.Delimiter = '['; ////istack.StartingInline = inlText; ////istack.StartPosition = subj.Position; ////istack.Priority = InlineStack.InlineStackPriority.Links; ////istack.Flags = InlineStack.InlineStackFlags.Closer; ////InlineStack.AppendStackEntry(istack, subj); } return(inlText); }
internal static int MatchInlineStack(InlineStack opener, Subject subj, int closingDelimiterCount, InlineStack closer, InlineTag singleCharTag, InlineTag doubleCharTag) { // calculate the actual number of delimiters used from this closer int useDelims; var openerDelims = opener.DelimiterCount; if (closingDelimiterCount < 3 || openerDelims < 3) { useDelims = closingDelimiterCount <= openerDelims ? closingDelimiterCount : openerDelims; if (useDelims == 1 && singleCharTag == 0) { return(0); } } else if (singleCharTag == 0) { useDelims = 2; } else if (doubleCharTag == 0) { useDelims = 1; } else { useDelims = closingDelimiterCount % 2 == 0 ? 2 : 1; } Inline inl = opener.StartingInline; InlineTag tag = useDelims == 1 ? singleCharTag : doubleCharTag; if (openerDelims == useDelims) { // the opener is completely used up - remove the stack entry and reuse the inline element inl.Tag = tag; inl.LiteralContent = null; inl.FirstChild = inl.NextSibling; inl.NextSibling = null; InlineStack.RemoveStackEntry(opener, subj, closer != null ? closer.Previous : null); } else { // the opener will only partially be used - stack entry remains (truncated) and a new inline is added. opener.DelimiterCount -= useDelims; inl.LiteralContent = inl.LiteralContent.Substring(0, opener.DelimiterCount); inl.SourceLastPosition -= useDelims; inl.NextSibling = new Inline(tag, inl.NextSibling); inl = inl.NextSibling; inl.SourcePosition = opener.StartingInline.SourcePosition + opener.DelimiterCount; } // there are two callers for this method, distinguished by the `closer` argument. // if closer == null it means the method is called during the initial subject parsing and the closer // characters are at the current position in the subject. The main benefit is that there is nothing // parsed that is located after the matched inline element. // if closer != null it means the method is called when the second pass for previously unmatched // stack elements is done. The drawback is that there can be other elements after the closer. if (closer != null) { var clInl = closer.StartingInline; if ((closer.DelimiterCount -= useDelims) > 0) { // a new inline element must be created because the old one has to be the one that // finalizes the children of the emphasis var newCloserInline = new Inline(clInl.LiteralContent.Substring(useDelims)); newCloserInline.SourcePosition = inl.SourceLastPosition = clInl.SourcePosition + useDelims; newCloserInline.SourceLength = closer.DelimiterCount; newCloserInline.NextSibling = clInl.NextSibling; clInl.LiteralContent = null; clInl.NextSibling = null; inl.NextSibling = closer.StartingInline = newCloserInline; } else { inl.SourceLastPosition = clInl.SourceLastPosition; clInl.LiteralContent = null; inl.NextSibling = clInl.NextSibling; clInl.NextSibling = null; } } else if (subj != null) { inl.SourceLastPosition = subj.Position - closingDelimiterCount + useDelims; subj.LastInline = inl; } return(useDelims); }