Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite Descendants iteratively #288

Merged
merged 7 commits into from
Jan 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Markdig.Tests/Markdig.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
</Compile>
<Compile Include="Specs\TestEmphasisPlus.cs" />
<Compile Include="TestEmphasisExtraOptions.cs" />
<Compile Include="TestDescendantsOrder.cs" />
<Compile Include="TestConfigureNewLine.cs" />
<Compile Include="TestHtmlAttributes.cs" />
<Compile Include="TestHtmlHelper.cs" />
Expand Down
85 changes: 85 additions & 0 deletions src/Markdig.Tests/TestDescendantsOrder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using NUnit.Framework;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.Linq;
using System.Collections.Generic;

namespace Markdig.Tests
{
[TestFixture]
public class TestDescendantsOrder
{
[Test]
public void TestSchemas()
{
foreach (var markdown in TestParser.SpecsMarkdown)
{
AssertSameDescendantsOrder(markdown);
}
}

private void AssertSameDescendantsOrder(string markdown)
{
var syntaxTree = Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());

var descendants_legacy = Descendants_Legacy(syntaxTree).ToList();
var descendants_new = syntaxTree.Descendants().ToList();

Assert.AreEqual(descendants_legacy.Count, descendants_new.Count);

for (int i = 0; i < descendants_legacy.Count; i++)
{
Assert.AreSame(descendants_legacy[i], descendants_new[i]);
}
}

private static IEnumerable<MarkdownObject> Descendants_Legacy(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 Descendants_Legacy(subBlock))
{
yield return sub;
}

// Visit leaf block that have inlines
var leafBlock = subBlock as LeafBlock;
if (leafBlock?.Inline != null)
{
foreach (var subInline in Descendants_Legacy(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 Descendants_Legacy(child))
{
yield return sub;
}

child = next;
}
}
}
}
}
}
16 changes: 16 additions & 0 deletions src/Markdig.Tests/TestParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Markdig.Extensions.JiraLinks;
Expand Down Expand Up @@ -155,5 +156,20 @@ private static string Compact(string html)
html = html.Normalize(NormalizationForm.FormKD);
return html;
}

public static readonly string[] SpecsMarkdown;
static TestParser()
{
string assemblyDir = Path.GetDirectoryName(typeof(TestParser).Assembly.Location);
string specsDir = Path.GetFullPath(Path.Combine(assemblyDir, "../../Specs"));

var files = Directory.GetFiles(specsDir).Where(file => file.EndsWith(".md", StringComparison.Ordinal)).ToList();
SpecsMarkdown = new string[files.Count];

for (int i = 0; i < files.Count; i++)
{
SpecsMarkdown[i] = File.ReadAllText(files[i]);
}
}
}
}
45 changes: 28 additions & 17 deletions src/Markdig/Syntax/Inlines/ContainerInline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,36 @@ public bool ContainsChild(Inline childToFind)
/// <returns>An enumeration of T</returns>
public IEnumerable<T> FindDescendants<T>() where T : Inline
{
var child = FirstChild;
while (child != null)
{
var next = child.NextSibling;
// Fast-path an empty container to avoid allocating a Stack
if (LastChild == null) yield break;

if (child is T)
{
yield return (T)child;
}
Stack<Inline> stack = new Stack<Inline>();

if (child is ContainerInline)
{
foreach (var subChild in ((ContainerInline) child).FindDescendants<T>())
{
yield return subChild;
}
}

child = next;
var child = LastChild;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
}

while (stack.Count > 0)
{
child = stack.Pop();

if (child is T childT)
{
yield return childT;
}

if (child is ContainerInline containerInline)
{
child = containerInline.LastChild;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
}
}
}
}

Expand Down
Loading