// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System; using System.Collections.Generic; using System.Diagnostics; using Markdig.Syntax.Inlines; namespace Markdig.Syntax { /// /// Extensions for visiting or /// public static class MarkdownObjectExtensions { /// /// Iterates over the descendant elements for the specified markdown element, including and . /// The descendant elements are returned in DFS-like order. /// /// The markdown object. /// An iteration over the descendant elements public static IEnumerable Descendants(this MarkdownObject markdownObject) { Stack stack = new Stack(); Stack pushStack = new Stack(); 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; } } } } /// /// Iterates over the descendant elements for the specified markdown element, including and and filters by the type . /// The descendant elements are returned in DFS-like order. /// /// Type to use for filtering the descendants /// The markdown object. /// An iteration over the descendant elements public static IEnumerable Descendants(this MarkdownObject markdownObject) where T : MarkdownObject { if (typeof(T).IsSubclassOf(typeof(Block))) { if (markdownObject is ContainerBlock containerBlock && containerBlock.Count > 0) { return BlockDescendantsInternal(containerBlock); } } else // typeof(T).IsSubclassOf(typeof(Inline))) { if (markdownObject is ContainerBlock containerBlock) { if (containerBlock.Count > 0) { return InlineDescendantsInternal(containerBlock); } } else if (markdownObject is ContainerInline containerInline && containerInline.FirstChild != null) { return containerInline.FindDescendantsInternal(); } } return Array.Empty(); } /// /// Iterates over the descendant elements for the specified markdown element and filters by the type . /// /// Type to use for filtering the descendants /// The inline markdown object. /// /// An iteration over the descendant elements /// public static IEnumerable Descendants(this ContainerInline inline) where T : Inline => inline.FindDescendants(); /// /// Iterates over the descendant elements for the specified markdown element and filters by the type . /// /// Type to use for filtering the descendants /// The markdown object. /// /// An iteration over the descendant elements /// public static IEnumerable Descendants(this ContainerBlock block) where T : Block { if (block != null && block.Count > 0) { return BlockDescendantsInternal(block); } else { return Array.Empty(); } } private static IEnumerable BlockDescendantsInternal(ContainerBlock block) where T : MarkdownObject { Debug.Assert(typeof(T).IsSubclassOf(typeof(Block))); Stack stack = new Stack(); int childrenCount = block.Count; while (childrenCount-- > 0) { stack.Push(block[childrenCount]); } while (stack.Count > 0) { var subBlock = stack.Pop(); if (subBlock is T subBlockT) { yield return subBlockT; } if (subBlock is ContainerBlock subBlockContainer) { childrenCount = subBlockContainer.Count; while (childrenCount-- > 0) { stack.Push(subBlockContainer[childrenCount]); } } } } private static IEnumerable InlineDescendantsInternal(ContainerBlock block) where T : MarkdownObject { Debug.Assert(typeof(T).IsSubclassOf(typeof(Inline))); foreach (MarkdownObject descendant in block.Descendants()) { if (descendant is T descendantT) { yield return descendantT; } } } } }