/// <summary> /// Inline processing /// </summary> /// <param name="i"></param> /// <returns></returns> protected string ProcessInline(MarkdownInline i) { string para = string.Empty; switch (i.Type) { //case MarkdownInlineType.Comment: // break; //case MarkdownInlineType.TextRun: // break; case MarkdownInlineType.Bold: para += ANSI.WriteMode(ANSICodes.Modes.Bold) + i.ToString().Trim('*') + ANSI.WriteMode(); break; //case MarkdownInlineType.Italic: // break; case MarkdownInlineType.MarkdownLink: MarkdownLinkInline mitLink = (MarkdownLinkInline)i; if (mitLink.Url.ToLower().EndsWith(".md")) { actionsCount++; para += $"{mitLink.Inlines[0]}[{actionsCount}]"; Data.Actions.Add(actionsCount.ToString(), new BBSCodeResult.Action() { module = "MarkdownScreen", data = (mitLink.Url.StartsWith("http") ? "" : basePath) + mitLink.Url }); } else { para += mitLink.ToString(); } break; //case MarkdownInlineType.RawHyperlink: // break; //case MarkdownInlineType.RawSubreddit: // break; //case MarkdownInlineType.Strikethrough: // break; //case MarkdownInlineType.Superscript: // break; //case MarkdownInlineType.Subscript: // break; //case MarkdownInlineType.Code: // break; //case MarkdownInlineType.Image: // break; //case MarkdownInlineType.Emoji: // break; //case MarkdownInlineType.LinkReference: // break; default: para += i.ToString(); break; } return(para); }
/// <summary> /// Renders a link element /// </summary> /// <param name="inlineCollection"> The list to add to. </param> /// <param name="element"> The parsed inline element to render. </param> /// <param name="parent"> The container element. </param> /// <param name="context"> Persistent state. </param> private void RenderMarkdownLink(InlineCollection inlineCollection, MarkdownLinkInline element, TextElement parent, RenderContext context) { // Avoid crash when link text is empty. if (element.Inlines.Count == 0) { return; } // Attempt to resolve references. element.ResolveReference(_document); if (element.Url == null) { // The element couldn't be resolved, just render it as text. RenderInlineChildren(inlineCollection, element.Inlines, parent, context); return; } // HACK: Superscript is not allowed within a hyperlink. But if we switch it around, so // that the superscript is outside the hyperlink, then it will render correctly. // This assumes that the entire hyperlink is to be rendered as superscript. if (AllTextIsSuperscript(element) == false) { // Regular ol' hyperlink. var link = new Hyperlink(); // Register the link _linkRegister.RegisterNewHyperLink(link, element.Url); // Remove superscripts. RemoveSuperscriptRuns(element, insertCaret: true); // Render the children into the link inline. var childContext = context.Clone(); childContext.WithinHyperlink = true; RenderInlineChildren(link.Inlines, element.Inlines, link, childContext); context.TrimLeadingWhitespace = childContext.TrimLeadingWhitespace; // Add it to the current inlines inlineCollection.Add(link); } else { // THE HACK IS ON! // Create a fake superscript element. var fakeSuperscript = new SuperscriptTextInline { Inlines = new List <MarkdownInline> { element } }; // Remove superscripts. RemoveSuperscriptRuns(element, insertCaret: false); // Now render it. RenderSuperscriptRun(inlineCollection, fakeSuperscript, parent, context); } }
/// <summary> /// Verifies if the link is valid, before processing into a link, or plain text. /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected void CheckRenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { // Avoid processing when link text is empty. if (element.Inlines.Count == 0) { return; } // Attempt to resolve references. element.ResolveReference(Document); if (element.Url == null) { // The element couldn't be resolved, just render it as text. RenderInlineChildren(element.Inlines, context); return; } foreach (MarkdownInline inline in element.Inlines) { if (inline is ImageInline imageInline) { // this is an image, create Image. if (!string.IsNullOrEmpty(imageInline.ReferenceId)) { imageInline.ResolveReference(Document); } imageInline.Url = element.Url; RenderImage(imageInline, context); return; } } RenderMarkdownLink(element, context); }
/// <summary> /// Renders a link element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { if (!(context is InlineRenderContext localContext)) { throw new RenderContextIncorrectException(); } // HACK: Superscript is not allowed within a hyperlink. But if we switch it around, so // that the superscript is outside the hyperlink, then it will render correctly. // This assumes that the entire hyperlink is to be rendered as superscript. if (AllTextIsSuperscript(element) == false) { var link = new Hyperlink(); LinkRegister.RegisterNewHyperLink(link, element.Url); RemoveSuperscriptRuns(element, insertCaret: true); var childContext = new InlineRenderContext(link.Inlines, context) { Parent = link, WithinHyperlink = true }; if (localContext.OverrideForeground) { link.Foreground = localContext.Foreground; } else if (LinkForeground != null) { link.Foreground = LinkForeground; } RenderInlineChildren(element.Inlines, childContext); context.TrimLeadingWhitespace = childContext.TrimLeadingWhitespace; ToolTipService.SetToolTip(link, element.Tooltip ?? element.Url); localContext.InlineCollection.Add(link); } else { // THE HACK IS ON! // Create a fake superscript element. var fakeSuperscript = new SuperscriptTextInline { Inlines = new List <MarkdownInline> { element } }; RemoveSuperscriptRuns(element, insertCaret: false); RenderSuperscriptRun(fakeSuperscript, context); } }
private static Hyperlink CreateHyperlink(MarkdownLinkInline markdownLinkInline) { var hyperlink = new Hyperlink() { NavigateUri = new Uri(markdownLinkInline.Url, UriKind.Absolute) }; hyperlink.Inlines.AddRange(CreateInlines(markdownLinkInline.Inlines)); hyperlink.RequestNavigate += NavigateFromHyperlink; return(hyperlink); }
static Common() { BoldTextInline.AddTripChars(_triggerList); ItalicTextInline.AddTripChars(_triggerList); MarkdownLinkInline.AddTripChars(_triggerList); HyperlinkInline.AddTripChars(_triggerList); StrikethroughTextInline.AddTripChars(_triggerList); SuperscriptTextInline.AddTripChars(_triggerList); CodeInline.AddTripChars(_triggerList); // Create an array of characters to search against using IndexOfAny. _tripCharacters = _triggerList.Select(trigger => trigger.FirstChar).Distinct().ToArray(); }
/// <summary> /// Renders a link element /// </summary> /// <param name="element"></param> /// <param name="currentInlines"></param> /// <param name="trimTextStart">If true this element should trin the start of the text and set to fales.</param> private void RenderMarkdownLink(MarkdownLinkInline element, InlineCollection currentInlines, ref bool trimTextStart) { // Create the text run Hyperlink link = new Hyperlink(); // Register the link m_linkRegister.RegisterNewHyperLink(link, element.Url); // Render the children into the link inline. RenderInlineChildren(element, link.Inlines, ref trimTextStart); // Add it to the current inlines currentInlines.Add(link); }
/// <summary> /// Returns a list of trip chars for all of the inlines. These are used to detect the /// possible beginning of an inline. /// </summary> /// <returns></returns> public static List <InlineTripCharHelper> GetTripCharsList() { lock (s_tripCharList) { if (s_tripCharList.Count == 0) { s_tripCharList.Add(BoldTextElement.GetTripChars()); s_tripCharList.Add(ItalicTextElement.GetTripChars()); s_tripCharList.Add(MarkdownLinkInline.GetTripChars()); s_tripCharList.Add(RawHyperlinkInline.GetTripChars()); s_tripCharList.Add(RawSubredditInline.GetTripChars()); // Text run doesn't have one. } } return(s_tripCharList); }
static Common() { BoldItalicTextInline.AddTripChars(_triggerList); UnderlineTextInline.AddTripChars(_triggerList); BoldTextInline.AddTripChars(_triggerList); ItalicTextInline.AddTripChars(_triggerList); MarkdownLinkInline.AddTripChars(_triggerList); HyperlinkInline.AddTripChars(_triggerList); StrikethroughTextInline.AddTripChars(_triggerList); CodeInline.AddTripChars(_triggerList); ImageInline.AddTripChars(_triggerList); EmojiInline.AddTripChars(_triggerList); LinkAnchorInline.AddTripChars(_triggerList); DiscordInline.AddTripChars(_triggerList); SpoilerTextInline.AddTripChars(_triggerList); // Create an array of characters to search against using IndexOfAny. _tripCharacters = _triggerList.Select(trigger => trigger.FirstChar).Distinct().ToArray(); }
/// <summary> /// Verifies if the link is valid, before processing into a link, or plain text. /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected void CheckRenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { // Avoid processing when link text is empty. if (element.Inlines.Count == 0) { return; } // Attempt to resolve references. element.ResolveReference(Document); if (element.Url == null) { // The element couldn't be resolved, just render it as text. RenderInlineChildren(element.Inlines, context); } else { // Url is valid, create Link. RenderMarkdownLink(element, context); } }
/// <summary> /// Renders a link element. /// </summary> /// <param name="inlineCollection"> The list to add to. </param> /// <param name="element"> The parsed inline element to render. </param> /// <param name="parent"> The container element. </param> /// <param name="context"> Persistent state. </param> private void RenderMarkdownLink(InlineCollection inlineCollection, MarkdownLinkInline element, TextElement parent, RenderContext context) { // Avoid crash when link text is empty. if (element.Inlines.Count == 0) { return; } // Attempt to resolve references. element.ResolveReference(_document); if (element.Url == null || _document.EnableHiddenLinks == false) { // The element couldn't be resolved, just render it as text. RenderInlineChildren(inlineCollection, element.Inlines, parent, context); return; } // Regular ol' hyperlink. var link = new Hyperlink(); // Register the link _linkRegister.RegisterNewHyperLink(link, element.Url); // Render the children into the link inline. var childContext = context.Clone(); childContext.WithinHyperlink = true; if (LinkForeground != null) { link.Foreground = LinkForeground; } RenderInlineChildren(link.Inlines, element.Inlines, link, childContext); context.TrimLeadingWhitespace = childContext.TrimLeadingWhitespace; // Add it to the current inlines inlineCollection.Add(link); }
/// <summary> /// Renders a link element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { if (!(context is InlineRenderContext localContext)) { throw new RenderContextIncorrectException(); } // Regular ol' hyperlink. var link = new Hyperlink(); // Register the link LinkRegister.RegisterNewHyperLink(link, element.Url); // Render the children into the link inline. var childContext = new InlineRenderContext(link.Inlines, context) { Parent = link, WithinHyperlink = true }; if (localContext.OverrideForeground) { link.Foreground = localContext.Foreground; } else if (LinkForeground != null) { link.Foreground = LinkForeground; } RenderInlineChildren(element.Inlines, childContext); context.TrimLeadingWhitespace = childContext.TrimLeadingWhitespace; ToolTipService.SetToolTip(link, element.Tooltip ?? element.Url); // Add it to the current inlines localContext.InlineCollection.Add(link); }
protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { var text = string.Join(string.Empty, element.Inlines); RenderLink(text, element.Url, context); }
/// <summary> /// Finds the next inline element by matching trip chars and verifying the match. /// </summary> /// <param name="markdown"> The markdown text to parse. </param> /// <param name="start"> The position to start parsing. </param> /// <param name="end"> The position to stop parsing. </param> /// <param name="ignoreLinks"> Indicates whether to parse links. </param> /// <returns>Returns the next element</returns> private static InlineParseResult FindNextInlineElement(string markdown, int start, int end, bool ignoreLinks) { // Search for the next inline sequence. for (int pos = start; pos < end; pos++) { // IndexOfAny should be the fastest way to skip characters we don't care about. pos = markdown.IndexOfAny(_tripCharacters, pos, end - pos); if (pos < 0) { break; } // Find the trigger(s) that matched. char currentChar = markdown[pos]; foreach (InlineTripCharHelper currentTripChar in _triggerList) { // Check if our current char matches the suffix char. if (currentChar == currentTripChar.FirstChar) { // Don't match if the previous character was a backslash. if (pos > start && markdown[pos - 1] == '\\') { continue; } // If we are here we have a possible match. Call into the inline class to verify. InlineParseResult parseResult = null; switch (currentTripChar.Method) { case InlineParseMethod.BoldItalic: parseResult = BoldItalicTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Comment: parseResult = CommentInline.Parse(markdown, pos, end); break; case InlineParseMethod.LinkReference: parseResult = LinkAnchorInline.Parse(markdown, pos, end); break; case InlineParseMethod.Bold: parseResult = BoldTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Italic: parseResult = ItalicTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.MarkdownLink: if (!ignoreLinks) { parseResult = MarkdownLinkInline.Parse(markdown, pos, end); } break; case InlineParseMethod.AngleBracketLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseAngleBracketLink(markdown, pos, end); } break; case InlineParseMethod.Url: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseUrl(markdown, pos, end); } break; case InlineParseMethod.RedditLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseRedditLink(markdown, pos, end); } break; case InlineParseMethod.PartialLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParsePartialLink(markdown, pos, end); } break; case InlineParseMethod.Email: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseEmailAddress(markdown, start, pos, end); } break; case InlineParseMethod.Strikethrough: parseResult = StrikethroughTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Superscript: parseResult = SuperscriptTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Subscript: parseResult = SubscriptTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Code: parseResult = CodeInline.Parse(markdown, pos, end); break; case InlineParseMethod.Image: parseResult = ImageInline.Parse(markdown, pos, end); break; case InlineParseMethod.Emoji: parseResult = EmojiInline.Parse(markdown, pos, end); break; } if (parseResult != null) { return(parseResult); } } } } // If we didn't find any elements we have a normal text block. // Let us consume the entire range. return(new InlineParseResult(TextRunInline.Parse(markdown, start, end), start, end)); }
protected override void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context) { }
/// <summary> /// Renders a link element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected abstract void RenderMarkdownLink(MarkdownLinkInline element, IRenderContext context);
/// <summary> /// Finds the next inline element by matching trip chars and verifying the match. /// </summary> /// <returns></returns> public static MarkdownInline FindNextInlineElement(ref string markdown, int startingPos, int endingPos, ref int nextElementStart, ref int nextElementEnd) { // Get the list of trip chars List <InlineTripCharHelper> tripChars = GetTripCharsList(); // Loop though all of the chars in this run and look for a trip char. for (int i = startingPos; i < endingPos; i++) { char currentChar = Char.ToLower(markdown[i]); // Try to match each trip char to the char foreach (InlineTripCharHelper currentTripChar in tripChars) { // Check if our current char matches the sufex char. if (currentChar == currentTripChar.FirstChar) { // We have a match! See if there is a suffix and if so if it matches. if (currentTripChar.FirstCharSuffix != null) { // We need to loop through the sufex and see if it matches the next n chars in the markdown. int suffexCharCounter = i + 1; bool suffexFound = true; foreach (char suffexChar in currentTripChar.FirstCharSuffix) { char test = Char.ToLower(markdown[suffexCharCounter]); if (suffexCharCounter >= endingPos || suffexChar != Char.ToLower(markdown[suffexCharCounter])) { suffexFound = false; break; } suffexCharCounter++; } // If the suffex didn't match this isn't a possibility. if (!suffexFound) { continue; } } // If we are here we have a possible match. Call into the inline class to verify. // Note! The order of bold and italic here is important because they both start with * // otherwise italic will consume bold's opening tag. switch (currentTripChar.Type) { case MarkdownInlineType.Bold: if (BoldTextElement.VerifyMatch(ref markdown, i, endingPos, ref nextElementStart, ref nextElementEnd)) { return(new BoldTextElement()); } break; case MarkdownInlineType.Italic: if (ItalicTextElement.VerifyMatch(ref markdown, i, endingPos, ref nextElementStart, ref nextElementEnd)) { return(new ItalicTextElement()); } break; case MarkdownInlineType.MarkdownLink: if (MarkdownLinkInline.VerifyMatch(ref markdown, i, endingPos, ref nextElementStart, ref nextElementEnd)) { return(new MarkdownLinkInline()); } break; case MarkdownInlineType.RawHyperlink: if (RawHyperlinkInline.VerifyMatch(ref markdown, i, endingPos, ref nextElementStart, ref nextElementEnd)) { return(new RawHyperlinkInline()); } break; case MarkdownInlineType.RawSubreddit: if (RawSubredditInline.VerifyMatch(ref markdown, i, endingPos, ref nextElementStart, ref nextElementEnd)) { return(new RawSubredditInline()); } break; } } } } // If we didn't find any elements we have a normal text block. // Let is consume the entire range. nextElementStart = startingPos; nextElementEnd = endingPos; return(new TextRunInline()); }