mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 09:09:25 +08:00 
			
		
		
		
	Add support for multiple tree roots
This commit is contained in:
		 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							4bfb24bfcb
						
					
				
				
					commit
					1aa958ced3
				
			| @@ -0,0 +1,43 @@ | ||||
| ├── Root node | ||||
| │   ├── child1 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 0 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 1 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 2 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 3 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 4 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 5 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 6 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 7 | ||||
| │   │   ├── multiple  | ||||
| │   │   │    line 8 | ||||
| │   │   └── multiple  | ||||
| │   │        line 9 | ||||
| │   ├── child2 | ||||
| │   │   └── child2Child | ||||
| │   │       └── Child 2 child | ||||
| │   │            child | ||||
| │   └── child3 | ||||
| │       └── single leaf | ||||
| │            multiline | ||||
| │           └──                2020 January                 | ||||
| │               ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ | ||||
| │               │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ | ||||
| │               ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤ | ||||
| │               │     │     │     │ 1   │ 2   │ 3   │ 4   │ | ||||
| │               │ 5   │ 6   │ 7   │ 8   │ 9   │ 10  │ 11  │ | ||||
| │               │ 12  │ 13  │ 14  │ 15  │ 16  │ 17  │ 18  │ | ||||
| │               │ 19  │ 20  │ 21  │ 22  │ 23  │ 24  │ 25  │ | ||||
| │               │ 26  │ 27  │ 28  │ 29  │ 30  │ 31  │     │ | ||||
| │               │     │     │     │     │     │     │     │ | ||||
| │               └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ | ||||
| └── child2Child | ||||
|     └── Child 2 child | ||||
|          child | ||||
| @@ -1,9 +1,6 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using Shouldly; | ||||
| using Spectre.Console.Rendering; | ||||
| using Spectre.Console.Testing; | ||||
| using VerifyXunit; | ||||
| using Xunit; | ||||
| @@ -14,77 +11,7 @@ namespace Spectre.Console.Tests.Unit | ||||
|     public class TreeTests | ||||
|     { | ||||
|         [Fact] | ||||
|         public void Should_Measure_Tree_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var nestedChildren = | ||||
|                 Enumerable.Range(0, 10) | ||||
|                     .Select(x => new TreeNode(new Text($"multiple \n line {x}"))); | ||||
|             var child3 = new TreeNode(new Text("child3")); | ||||
|             child3.AddChild(new TreeNode(new Text("single leaf\n multiline"))); | ||||
|             var children = new List<TreeNode> | ||||
|             { | ||||
|                 new(new Text("child1"), nestedChildren), new(new Text("child2")), child3, | ||||
|             }; | ||||
|             var root = new TreeNode(new Text("Root node"), children); | ||||
|             var tree = new Tree(root); | ||||
|  | ||||
|             // When | ||||
|             var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); | ||||
|  | ||||
|             // Then | ||||
|             measurement.Min.ShouldBe(17); | ||||
|             measurement.Max.ShouldBe(19); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Should_Measure_Tree_Correctly_With_Regard_To_Max_Width() | ||||
|         { | ||||
|             // Given | ||||
|             var root = new TreeNode(new Text("Root node")); | ||||
|             var currentNode = root; | ||||
|             foreach (var i in Enumerable.Range(0, 100)) | ||||
|             { | ||||
|                 var newNode = new TreeNode(new Text(string.Empty)); | ||||
|                 currentNode.AddChild(newNode); | ||||
|                 currentNode = newNode; | ||||
|             } | ||||
|  | ||||
|             var tree = new Tree(root); | ||||
|  | ||||
|             // When | ||||
|             var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); | ||||
|  | ||||
|             // Then | ||||
|             measurement.Min.ShouldBe(400); | ||||
|             measurement.Max.ShouldBe(80); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public void Measure_Leaf_Dominated_Width() | ||||
|         { | ||||
|             // Given | ||||
|             var root = new TreeNode(new Text("Root node")); | ||||
|             var currentNode = root; | ||||
|             foreach (var i in Enumerable.Range(0, 10)) | ||||
|             { | ||||
|                 var newNode = new TreeNode(new Text(i.ToString())); | ||||
|                 currentNode.AddChild(newNode); | ||||
|                 currentNode = newNode; | ||||
|             } | ||||
|  | ||||
|             var tree = new Tree(root); | ||||
|  | ||||
|             // When | ||||
|             var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); | ||||
|  | ||||
|             // Then | ||||
|             measurement.Min.ShouldBe(41); | ||||
|             measurement.Max.ShouldBe(41); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public Task Should_Render_Tree_Correctly() | ||||
|         public Task Should_Render_Tree_With_Single_Root_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new FakeConsole(width: 80); | ||||
| @@ -101,7 +28,34 @@ namespace Spectre.Console.Tests.Unit | ||||
|             child3.AddChild(child3Child); | ||||
|             var children = new List<TreeNode> { new(new Text("child1"), nestedChildren), child2, child3 }; | ||||
|             var root = new TreeNode(new Text("Root node"), children); | ||||
|             var tree = new Tree(root); | ||||
|             var tree = new Tree().AddChild(root); | ||||
|  | ||||
|             // When | ||||
|             console.Render(tree); | ||||
|  | ||||
|             // Then | ||||
|             return Verifier.Verify(console.Output); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public Task Should_Render_Tree_With_Multiple_Roots_Correctly() | ||||
|         { | ||||
|             // Given | ||||
|             var console = new FakeConsole(width: 80); | ||||
|             var nestedChildren = | ||||
|                 Enumerable.Range(0, 10) | ||||
|                     .Select(x => new TreeNode(new Text($"multiple \n line {x}"))); | ||||
|             var child2 = new TreeNode(new Text("child2")); | ||||
|             var child2Child = new TreeNode(new Text("child2Child")); | ||||
|             child2.AddChild(child2Child); | ||||
|             child2Child.AddChild(new TreeNode(new Text("Child 2 child\n child"))); | ||||
|             var child3 = new TreeNode(new Text("child3")); | ||||
|             var child3Child = new TreeNode(new Text("single leaf\n multiline")); | ||||
|             child3Child.AddChild(new TreeNode(new Calendar(2020, 01))); | ||||
|             child3.AddChild(child3Child); | ||||
|             var children = new List<TreeNode> { new(new Text("child1"), nestedChildren), child2, child3 }; | ||||
|             var root = new TreeNode(new Text("Root node"), children); | ||||
|             var tree = new Tree().AddChild(root).AddChild(child2Child); | ||||
|  | ||||
|             // When | ||||
|             console.Render(tree); | ||||
| @@ -116,7 +70,7 @@ namespace Spectre.Console.Tests.Unit | ||||
|             // Given | ||||
|             var console = new FakeConsole(width: 80); | ||||
|             var root = new TreeNode(new Text("Root node"), Enumerable.Empty<TreeNode>()); | ||||
|             var tree = new Tree(root); | ||||
|             var tree = new Tree().AddChild(root); | ||||
|  | ||||
|             // When | ||||
|             console.Render(tree); | ||||
|   | ||||
| @@ -9,10 +9,8 @@ namespace Spectre.Console | ||||
|     /// <summary> | ||||
|     ///     Representation of tree data. | ||||
|     /// </summary> | ||||
|     public sealed class Tree : Renderable | ||||
|     public sealed class Tree : Renderable, IHasTreeNodes | ||||
|     { | ||||
|         private readonly TreeNode _rootNode; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the tree style. | ||||
|         /// </summary> | ||||
| @@ -23,13 +21,20 @@ namespace Spectre.Console | ||||
|         /// </summary> | ||||
|         public TreeAppearance Appearance { get; set; } = TreeAppearance.Ascii; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the tree nodes. | ||||
|         /// </summary> | ||||
|         public List<TreeNode> Nodes { get; } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         List<TreeNode> IHasTreeNodes.Children => Nodes; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Tree"/> class. | ||||
|         /// </summary> | ||||
|         /// <param name="rootNode">Root node of the tree to be rendered.</param> | ||||
|         public Tree(TreeNode rootNode) | ||||
|         public Tree() | ||||
|         { | ||||
|             _rootNode = rootNode ?? throw new ArgumentNullException(nameof(rootNode)); | ||||
|             Nodes = new List<TreeNode>(); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
| @@ -59,20 +64,52 @@ namespace Spectre.Console | ||||
|                 return new Measurement(currentMin, Math.Min(currentMax, maxWidth)); | ||||
|             } | ||||
|  | ||||
|             return MeasureAtDepth(context, maxWidth, _rootNode, depth: 0); | ||||
|             if (Nodes.Count == 1) | ||||
|             { | ||||
|                 return MeasureAtDepth(context, maxWidth, Nodes[0], depth: 0); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var root = new TreeNode(Text.Empty); | ||||
|                 foreach (var node in Nodes) | ||||
|                 { | ||||
|                     root.AddChild(node); | ||||
|                 } | ||||
|  | ||||
|                 return MeasureAtDepth(context, maxWidth, root, depth: 0); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             return _rootNode | ||||
|                 .Render(context, maxWidth) | ||||
|                 .Concat(new List<Segment> { Segment.LineBreak }) | ||||
|                 .Concat(RenderChildren(context, maxWidth - Appearance.PartSize, _rootNode, depth: 0)); | ||||
|             if (Nodes.Count == 1) | ||||
|             { | ||||
|                 // Single root | ||||
|                 return Nodes[0] | ||||
|                     .Render(context, maxWidth) | ||||
|                     .Concat(new List<Segment> { Segment.LineBreak }) | ||||
|                     .Concat(RenderChildren(context, maxWidth - Appearance.PartSize, Nodes[0], depth: 0)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // Multiple roots | ||||
|                 var root = new TreeNode(Text.Empty); | ||||
|                 foreach (var node in Nodes) | ||||
|                 { | ||||
|                     root.AddChild(node); | ||||
|                 } | ||||
|  | ||||
|                 return Enumerable.Empty<Segment>() | ||||
|                     .Concat(RenderChildren( | ||||
|                         context, maxWidth - Appearance.PartSize, root, | ||||
|                         depth: 0)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private IEnumerable<Segment> RenderChildren(RenderContext context, int maxWidth, TreeNode node, int depth, | ||||
|             int? trailingStarted = null) | ||||
|         private IEnumerable<Segment> RenderChildren( | ||||
|             RenderContext context, int maxWidth, TreeNode node, | ||||
|             int depth, int? trailingStarted = null) | ||||
|         { | ||||
|             var result = new List<Segment>(); | ||||
|             foreach (var (_, _, lastChild, childNode) in node.Children.Enumerate()) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user