mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
Add cycle detection to tree rendering
This commit is contained in:
parent
dee3c01629
commit
994540d97f
@ -111,6 +111,7 @@ Spectre.Consoleでできることを見るために、
|
|||||||
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
|
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
|
||||||
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
|
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
|
||||||
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
|
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
|
||||||
|
│ Trees │ examples/Trees/Trees.csproj │ Demonstrates how to render trees in a console. │
|
||||||
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
|
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Shouldly;
|
||||||
using Spectre.Console.Testing;
|
using Spectre.Console.Testing;
|
||||||
using Spectre.Verify.Extensions;
|
using Spectre.Verify.Extensions;
|
||||||
using VerifyXunit;
|
using VerifyXunit;
|
||||||
@ -56,5 +57,29 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
// Then
|
// Then
|
||||||
return Verifier.Verify(console.Output);
|
return Verifier.Verify(console.Output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Tree_Contains_Cycles()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
var child2 = new TreeNode(new Text("child 2"));
|
||||||
|
var child3 = new TreeNode(new Text("child 3"));
|
||||||
|
var child1 = new TreeNode(new Text("child 1"));
|
||||||
|
child1.AddNodes(child2, child3);
|
||||||
|
var root = new TreeNode(new Text("Branch Node"));
|
||||||
|
root.AddNodes(child1);
|
||||||
|
child2.AddNode(root);
|
||||||
|
|
||||||
|
var tree = new Tree("root node");
|
||||||
|
tree.AddNodes(root);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => console.Render(tree));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<CircularTreeException>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
15
src/Spectre.Console/Widgets/CircularTreeException.cs
Normal file
15
src/Spectre.Console/Widgets/CircularTreeException.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the tree being rendered includes a cycle, and cannot be rendered.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CircularTreeException : Exception
|
||||||
|
{
|
||||||
|
internal CircularTreeException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,8 @@ using Spectre.Console.Rendering;
|
|||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A renderable tree.
|
/// Representation of non-circular tree data.
|
||||||
|
/// Each node added to the tree may only be present in it a single time, in order to facilitate cycle detection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Tree : Renderable, IHasTreeNodes
|
public sealed class Tree : Renderable, IHasTreeNodes
|
||||||
{
|
{
|
||||||
@ -54,6 +55,7 @@ namespace Spectre.Console
|
|||||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
{
|
{
|
||||||
var result = new List<Segment>();
|
var result = new List<Segment>();
|
||||||
|
var visitedNodes = new HashSet<TreeNode>();
|
||||||
|
|
||||||
var stack = new Stack<Queue<TreeNode>>();
|
var stack = new Stack<Queue<TreeNode>>();
|
||||||
stack.Push(new Queue<TreeNode>(new[] { _root }));
|
stack.Push(new Queue<TreeNode>(new[] { _root }));
|
||||||
@ -77,6 +79,10 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
var isLastChild = stackNode.Count == 1;
|
var isLastChild = stackNode.Count == 1;
|
||||||
var current = stackNode.Dequeue();
|
var current = stackNode.Dequeue();
|
||||||
|
if (!visitedNodes.Add(current))
|
||||||
|
{
|
||||||
|
throw new CircularTreeException("Cycle detected in tree - unable to render.");
|
||||||
|
}
|
||||||
|
|
||||||
stack.Push(stackNode);
|
stack.Push(stackNode);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user