From eb4a7d3bf4e70cceab98b81499826351991dd7f5 Mon Sep 17 00:00:00 2001
From: Patrik Svensson <patrik@patriksvensson.se>
Date: Mon, 21 Feb 2022 18:35:21 +0100
Subject: [PATCH] Add support for alignment

---
 examples/Console/Paths/Paths.csproj           |  2 +-
 examples/Console/Paths/Program.cs             | 44 +++++++++++++++++--
 src/Spectre.Console.v3.ncrunchsolution        |  7 ---
 src/Spectre.Console/Widgets/TextPath.cs       | 13 +++++-
 ...ctre.Console.Tests.net48.v3.ncrunchproject |  7 ---
 ...tre.Console.Tests.net5.0.v3.ncrunchproject |  7 ---
 .../Spectre.Console.Tests.v3.ncrunchproject   |  3 --
 .../Unit/Widgets/TextPathTests.cs             | 37 ++++++++++++++--
 8 files changed, 88 insertions(+), 32 deletions(-)
 delete mode 100644 src/Spectre.Console.v3.ncrunchsolution
 delete mode 100644 test/Spectre.Console.Tests/Spectre.Console.Tests.net48.v3.ncrunchproject
 delete mode 100644 test/Spectre.Console.Tests/Spectre.Console.Tests.net5.0.v3.ncrunchproject
 delete mode 100644 test/Spectre.Console.Tests/Spectre.Console.Tests.v3.ncrunchproject

diff --git a/examples/Console/Paths/Paths.csproj b/examples/Console/Paths/Paths.csproj
index 7729021..d6d9e16 100644
--- a/examples/Console/Paths/Paths.csproj
+++ b/examples/Console/Paths/Paths.csproj
@@ -4,7 +4,7 @@
     <OutputType>Exe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
     <ExampleTitle>Paths</ExampleTitle>
-    <ExampleDescription>Demonstrates how to write paths.</ExampleDescription>
+    <ExampleDescription>Demonstrates how to render paths.</ExampleDescription>
     <ExampleGroup>Widgets</ExampleGroup>
   </PropertyGroup>
 
diff --git a/examples/Console/Paths/Program.cs b/examples/Console/Paths/Program.cs
index 41ee0f2..cb30386 100644
--- a/examples/Console/Paths/Program.cs
+++ b/examples/Console/Paths/Program.cs
@@ -2,7 +2,7 @@ using System;
 using System.Threading;
 using Spectre.Console;
 
-namespace Live;
+namespace Paths;
 
 public static class Program
 {
@@ -11,11 +11,37 @@ public static class Program
         var windowsPath = @"C:\This is\A\Super Long\Windows\Path\That\Goes\On And On\And\Never\Seems\To\Stop\But\At\Some\Point\It\Must\I\Guess.txt";
         var unixPath = @"//This is/A/Super Long/Unix/Path/That/Goes/On And On/And/Never/Seems/To/Stop/But/At/Some/Point/It/Must/I/Guess.txt";
 
-        var table = new Table().BorderColor(Color.Grey);
+        AnsiConsole.WriteLine();
+        WritePlain(windowsPath, unixPath);
+
+        AnsiConsole.WriteLine();
+        WriteColorized(windowsPath, unixPath);
+
+        AnsiConsole.WriteLine();
+        WriteAligned(windowsPath);
+    }
+
+    private static void WritePlain(string windowsPath, string unixPath)
+    {
+        var table = new Table().BorderColor(Color.Grey).Title("Plain").RoundedBorder();
+        table.AddColumns("[grey]OS[/]", "[grey]Path[/]");
+        table.AddRow(new Text("Windows"), new TextPath(windowsPath));
+        table.AddRow(new Text("Unix"), new TextPath(unixPath));
+
+        AnsiConsole.Write(table);
+    }
+
+    private static void WriteColorized(string windowsPath, string unixPath)
+    {
+        var table = new Table().BorderColor(Color.Grey).Title("Colorized").RoundedBorder();
         table.AddColumns("[grey]OS[/]", "[grey]Path[/]");
 
         table.AddRow(new Text("Windows"),
-            new TextPath(windowsPath));
+            new TextPath(windowsPath)
+                .RootColor(Color.Blue)
+                .SeparatorColor(Color.Yellow)
+                .StemStyle(Color.Red)
+                .LeafStyle(Color.Green));
 
         table.AddRow(new Text("Unix"),
             new TextPath(unixPath)
@@ -26,4 +52,16 @@ public static class Program
 
         AnsiConsole.Write(table);
     }
+
+    private static void WriteAligned(string path)
+    {
+        var table = new Table().BorderColor(Color.Grey).Title("Aligned").RoundedBorder();
+        table.AddColumns("[grey]Alignment[/]", "[grey]Path[/]");
+
+        table.AddRow(new Text("Left"), new TextPath(path).LeftAligned());
+        table.AddRow(new Text("Center"), new TextPath(path).Centered());
+        table.AddRow(new Text("Right"), new TextPath(path).RightAligned());
+
+        AnsiConsole.Write(table);
+    }
 }
diff --git a/src/Spectre.Console.v3.ncrunchsolution b/src/Spectre.Console.v3.ncrunchsolution
deleted file mode 100644
index c0d9e77..0000000
--- a/src/Spectre.Console.v3.ncrunchsolution
+++ /dev/null
@@ -1,7 +0,0 @@
-<SolutionConfiguration>
-  <Settings>
-    <AllowParallelTestExecution>True</AllowParallelTestExecution>
-    <InstrumentationMode>Optimised</InstrumentationMode>
-    <SolutionConfigured>True</SolutionConfigured>
-  </Settings>
-</SolutionConfiguration>
\ No newline at end of file
diff --git a/src/Spectre.Console/Widgets/TextPath.cs b/src/Spectre.Console/Widgets/TextPath.cs
index 869cb1a..2d03e19 100644
--- a/src/Spectre.Console/Widgets/TextPath.cs
+++ b/src/Spectre.Console/Widgets/TextPath.cs
@@ -3,7 +3,7 @@ namespace Spectre.Console;
 /// <summary>
 /// Representation of a file system path.
 /// </summary>
-public sealed class TextPath : IRenderable
+public sealed class TextPath : IRenderable, IAlignable
 {
     private const string Ellipsis = "...";
     private const string UnicodeEllipsis = "…";
@@ -32,6 +32,11 @@ public sealed class TextPath : IRenderable
     /// </summary>
     public Style? LeafStyle { get; set; }
 
+    /// <summary>
+    /// Gets or sets the alignment.
+    /// </summary>
+    public Justify? Alignment { get; set; }
+
     /// <summary>
     /// Initializes a new instance of the <see cref="TextPath"/> class.
     /// </summary>
@@ -75,6 +80,8 @@ public sealed class TextPath : IRenderable
     /// <inheritdoc/>
     public IEnumerable<Segment> Render(RenderContext context, int maxWidth)
     {
+        var alignment = Alignment ?? Justify.Left;
+
         var rootStyle = RootStyle ?? Style.Plain;
         var separatorStyle = SeparatorStyle ?? Style.Plain;
         var stemStyle = StemStyle ?? Style.Plain;
@@ -111,6 +118,10 @@ public sealed class TextPath : IRenderable
             }
         }
 
+        // Align the result
+        Aligner.Align(parts, Alignment, maxWidth);
+
+        // Insert a line break
         parts.Add(Segment.LineBreak);
 
         return parts;
diff --git a/test/Spectre.Console.Tests/Spectre.Console.Tests.net48.v3.ncrunchproject b/test/Spectre.Console.Tests/Spectre.Console.Tests.net48.v3.ncrunchproject
deleted file mode 100644
index eacd190..0000000
--- a/test/Spectre.Console.Tests/Spectre.Console.Tests.net48.v3.ncrunchproject
+++ /dev/null
@@ -1,7 +0,0 @@
-<ProjectConfiguration>
-  <Settings>
-    <IgnoredTests>
-      <AllTestsSelector />
-    </IgnoredTests>
-  </Settings>
-</ProjectConfiguration>
\ No newline at end of file
diff --git a/test/Spectre.Console.Tests/Spectre.Console.Tests.net5.0.v3.ncrunchproject b/test/Spectre.Console.Tests/Spectre.Console.Tests.net5.0.v3.ncrunchproject
deleted file mode 100644
index eacd190..0000000
--- a/test/Spectre.Console.Tests/Spectre.Console.Tests.net5.0.v3.ncrunchproject
+++ /dev/null
@@ -1,7 +0,0 @@
-<ProjectConfiguration>
-  <Settings>
-    <IgnoredTests>
-      <AllTestsSelector />
-    </IgnoredTests>
-  </Settings>
-</ProjectConfiguration>
\ No newline at end of file
diff --git a/test/Spectre.Console.Tests/Spectre.Console.Tests.v3.ncrunchproject b/test/Spectre.Console.Tests/Spectre.Console.Tests.v3.ncrunchproject
deleted file mode 100644
index 95a483b..0000000
--- a/test/Spectre.Console.Tests/Spectre.Console.Tests.v3.ncrunchproject
+++ /dev/null
@@ -1,3 +0,0 @@
-<ProjectConfiguration>
-  <Settings />
-</ProjectConfiguration>
\ No newline at end of file
diff --git a/test/Spectre.Console.Tests/Unit/Widgets/TextPathTests.cs b/test/Spectre.Console.Tests/Unit/Widgets/TextPathTests.cs
index bddd5b1..0f984be 100644
--- a/test/Spectre.Console.Tests/Unit/Widgets/TextPathTests.cs
+++ b/test/Spectre.Console.Tests/Unit/Widgets/TextPathTests.cs
@@ -14,7 +14,8 @@ public sealed class TextPathTests
         console.Write(new TextPath(input));
 
         // Then
-        console.Output.TrimEnd().ShouldBe(expected);
+        console.Output.TrimEnd()
+            .ShouldBe(expected);
     }
 
     [Theory]
@@ -30,7 +31,8 @@ public sealed class TextPathTests
         console.Write(new TextPath(input));
 
         // Then
-        console.Output.TrimEnd().ShouldBe(expected);
+        console.Output.TrimEnd()
+            .ShouldBe(expected);
     }
 
     [Theory]
@@ -46,7 +48,8 @@ public sealed class TextPathTests
         console.Write(new TextPath(input));
 
         // Then
-        console.Output.TrimEnd().ShouldBe(expected);
+        console.Output.TrimEnd()
+            .ShouldBe(expected);
     }
 
     [Theory]
@@ -66,4 +69,32 @@ public sealed class TextPathTests
         // Then
         console.Output.ShouldEndWith("\n");
     }
+
+    [Fact]
+    public void Should_Right_Align_Correctly()
+    {
+        // Given
+        var console = new TestConsole().Width(40);
+
+        // When
+        console.Write(new TextPath("C:/My documents/Bar/Baz.txt").RightAligned());
+
+        // Then
+        console.Output.TrimEnd('\n')
+            .ShouldBe("             C:/My documents/Bar/Baz.txt");
+    }
+
+    [Fact]
+    public void Should_Center_Align_Correctly()
+    {
+        // Given
+        var console = new TestConsole().Width(40);
+
+        // When
+        console.Write(new TextPath("C:/My documents/Bar/Baz.txt").Centered());
+
+        // Then
+        console.Output.TrimEnd('\n')
+            .ShouldBe("      C:/My documents/Bar/Baz.txt       ");
+    }
 }