/// <summary> /// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/> and filters by the type <typeparamref name="T"/>. /// <para>The descendant elements are returned in DFS-like order.</para> /// </summary> /// <typeparam name="T">Type to use for filtering the descendants</typeparam> /// <param name="markdownObject">The markdown object.</param> /// <returns>An iteration over the descendant elements</returns> public static IEnumerable <T> Descendants <T>(this MarkdownObject markdownObject) where T : MarkdownObject { if (typeof(T).IsSubclassOf(typeof(Block))) { if (markdownObject is ContainerBlock containerBlock && containerBlock.Count > 0) { return(BlockDescendantsInternal <T>(containerBlock)); } } else // typeof(T).IsSubclassOf(typeof(Inline))) { if (markdownObject is ContainerBlock containerBlock) { if (containerBlock.Count > 0) { return(InlineDescendantsInternal <T>(containerBlock)); } } else if (markdownObject is ContainerInline containerInline && containerInline.FirstChild != null) { return(containerInline.FindDescendantsInternal <T>()); } } return(ArrayHelper.Empty <T>()); }
/// <summary> /// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/>. /// <para>The descendant elements are returned in DFS-like order.</para> /// </summary> /// <param name="markdownObject">The markdown object.</param> /// <returns>An iteration over the descendant elements</returns> public static IEnumerable <MarkdownObject> Descendants(this MarkdownObject markdownObject) { // Fast-path an object with no children to avoid allocating Stack objects if (!(markdownObject is ContainerBlock) && !(markdownObject is ContainerInline)) { yield break; } // TODO: A single Stack<(MarkdownObject block, bool push)> when ValueTuples are available Stack <MarkdownObject> stack = new Stack <MarkdownObject>(); Stack <bool> pushStack = new Stack <bool>(); stack.Push(markdownObject); pushStack.Push(false); while (stack.Count > 0) { var block = stack.Pop(); if (pushStack.Pop()) { yield return(block); } if (block is ContainerBlock containerBlock) { int subBlockIndex = containerBlock.Count; while (subBlockIndex-- > 0) { var subBlock = containerBlock[subBlockIndex]; if (subBlock is LeafBlock leafBlock) { if (leafBlock.Inline != null) { stack.Push(leafBlock.Inline); pushStack.Push(false); } } stack.Push(subBlock); pushStack.Push(true); } } else if (block is ContainerInline containerInline) { var child = containerInline.LastChild; while (child != null) { stack.Push(child); pushStack.Push(true); child = child.PreviousSibling; } } } }
/// <summary> /// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/>. /// </summary> /// <param name="markdownObject">The markdown object.</param> /// <returns>An iteration over the descendant elements</returns> public static IEnumerable <MarkdownObject> Descendants(this MarkdownObject markdownObject) { // TODO: implement a recursiveless method var block = markdownObject as ContainerBlock; if (block != null) { foreach (var subBlock in block) { yield return(subBlock); foreach (var sub in subBlock.Descendants()) { yield return(sub); } // Visit leaf block that have inlines var leafBlock = subBlock as LeafBlock; if (leafBlock?.Inline != null) { foreach (var subInline in Descendants(leafBlock.Inline)) { yield return(subInline); } } } } else { var inline = markdownObject as ContainerInline; if (inline != null) { var child = inline.FirstChild; while (child != null) { var next = child.NextSibling; yield return(child); foreach (var sub in child.Descendants()) { yield return(sub); } child = next; } } } }
/// <summary> /// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/>. /// <para>The descendant elements are returned in DFS-like order.</para> /// </summary> /// <param name="markdownObject">The markdown object.</param> /// <returns>An iteration over the descendant elements</returns> public static IEnumerable <MarkdownObject> Descendants(this MarkdownObject markdownObject) { Stack <MarkdownObject> stack = new Stack <MarkdownObject>(); Stack <bool> pushStack = new Stack <bool>(); stack.Push(markdownObject); pushStack.Push(false); while (stack.Count > 0) { var block = stack.Pop(); if (pushStack.Pop()) { yield return(block); } if (block is ContainerBlock containerBlock) { int subBlockIndex = containerBlock.Count; while (subBlockIndex-- > 0) { var subBlock = containerBlock[subBlockIndex]; if (subBlock is LeafBlock leafBlock) { if (leafBlock.Inline != null) { stack.Push(leafBlock.Inline); pushStack.Push(false); } } stack.Push(subBlock); pushStack.Push(true); } } else if (block is ContainerInline containerInline) { var child = containerInline.LastChild; while (child != null) { stack.Push(child); pushStack.Push(true); child = child.PreviousSibling; } } } }
/// <summary> /// Determines whether the given <paramref name="url"/> points to anywhere inside the given Markdig <paramref name="element"/>. /// </summary> /// <param name="url">The URL.</param> /// <param name="element">The Markdig element.</param> /// <returns> /// <c>true</c> if the given <paramref name="url"/> points to anywhere inside the given Markdig <paramref name="element"/>.; otherwise, <c>false</c>. /// </returns> public static bool IsLinkInElement(string url, Markdig.Syntax.MarkdownObject element) { if (url.StartsWith("#")) { url = url.Substring(1); } foreach (var child in MarkdownUtilities.EnumerateAllMarkdownObjectsRecursively(element)) { var attr = (Markdig.Renderers.Html.HtmlAttributes)child.GetData(typeof(Markdig.Renderers.Html.HtmlAttributes)); string uniqueAddress = attr?.Id; // this header has a user defined address if (uniqueAddress == url) { return(true); } } return(false); }
/// <summary> /// Gets the childs of a markdown object. Null is returned if no childs were to be found. /// </summary> /// <param name="parent">The markdown object from which to get the childs.</param> /// <returns>The childs of the given markdown object, or null.</returns> public static IEnumerable <Markdig.Syntax.MarkdownObject> GetChilds(this Markdig.Syntax.MarkdownObject parent) { if (parent is Markdig.Syntax.LeafBlock leafBlock) { return(leafBlock.Inline); } else if (parent is Markdig.Syntax.Inlines.ContainerInline containerInline) { return(containerInline); } else if (parent is Markdig.Syntax.ContainerBlock containerBlock) { return(containerBlock); } else { return(null); } }
/// <summary> /// Gets the childs of a markdown object. Null is returned if no childs were to be found. /// </summary> /// <param name="parent">The markdown object from which to get the childs.</param> /// <returns>The childs of the given markdown object, or null.</returns> public static IReadOnlyList <Markdig.Syntax.MarkdownObject> GetChildList(this Markdig.Syntax.MarkdownObject parent) { if (parent is Markdig.Syntax.LeafBlock leafBlock) { return(leafBlock.Inline?.ToArray <Markdig.Syntax.MarkdownObject>()); } else if (parent is Markdig.Syntax.Inlines.ContainerInline containerInline) { return(containerInline.ToArray <Markdig.Syntax.MarkdownObject>()); } else if (parent is Markdig.Syntax.ContainerBlock containerBlock) { return(containerBlock); } else { return(null); } }
/// <summary> /// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/> and filters by the type <typeparamref name="T"/>. /// <para>The descendant elements are returned in DFS-like order.</para> /// </summary> /// <typeparam name="T">Type to use for filtering the descendants</typeparam> /// <param name="markdownObject">The markdown object.</param> /// <returns>An iteration over the descendant elements</returns> public static IEnumerable <T> Descendants <T>(this MarkdownObject markdownObject) where T : MarkdownObject { #if UAP foreach (MarkdownObject descendant in markdownObject.Descendants()) { if (descendant is T descendantT) { yield return(descendantT); } } #else if (typeof(T).IsSubclassOf(typeof(Block))) { if (markdownObject is ContainerBlock containerBlock && containerBlock.Count > 0) { return(BlockDescendantsInternal <T>(containerBlock)); } } else // typeof(T).IsSubclassOf(typeof(Inline))) { if (markdownObject is ContainerBlock containerBlock) { if (containerBlock.Count > 0) { return(InlineDescendantsInternal <T>(containerBlock)); } } else if (markdownObject is ContainerInline containerInline && containerInline.FirstChild != null) { return(containerInline.FindDescendantsInternal <T>()); } } return(ArrayHelper <T> .Empty); #endif }
/// <summary> /// Enumerates all objects in a markdown parse tree recursively, starting with the given element. /// </summary> /// <param name="startElement">The start element.</param> /// <returns>All text element (the given text element and all its childs).</returns> public static IEnumerable <Markdig.Syntax.MarkdownObject> EnumerateAllMarkdownObjectsRecursively(this Markdig.Syntax.MarkdownObject startElement) { yield return(startElement); var childList = GetChildList(startElement); if (null != childList) { foreach (var child in GetChildList(startElement)) { foreach (var childAndSub in EnumerateAllMarkdownObjectsRecursively(child)) { yield return(childAndSub); } } } }