// 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();
}
}
#if NET452
return new T[0];
#else
return Array.Empty();
#endif
}
///
/// 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
{
#if NET452
return new T[0];
#else
return Array.Empty();
#endif
}
}
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;
}
}
}
}
}