diff --git a/src/Spectre.Console.Tests/Fixtures/AnsiConsoleFixture.cs b/src/Spectre.Console.Tests/Fixtures/AnsiConsoleFixture.cs deleted file mode 100644 index 54ea64e..0000000 --- a/src/Spectre.Console.Tests/Fixtures/AnsiConsoleFixture.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.IO; - -namespace Spectre.Console.Tests -{ - public sealed class AnsiConsoleFixture : IDisposable - { - private readonly StringWriter _writer; - - public IAnsiConsole Console { get; } - - public string Output => _writer.ToString(); - - public AnsiConsoleFixture(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80) - { - _writer = new StringWriter(); - - Console = new ConsoleWithWidth( - AnsiConsole.Create(new AnsiConsoleSettings - { - Ansi = ansi, - ColorSystem = (ColorSystemSupport)system, - Out = _writer, - }), width); - } - - public void Dispose() - { - _writer?.Dispose(); - } - } -} diff --git a/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs b/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs deleted file mode 100644 index 4ba1bda..0000000 --- a/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text; - -namespace Spectre.Console.Tests -{ - public sealed class ConsoleWithWidth : IAnsiConsole - { - private readonly IAnsiConsole _console; - - public Capabilities Capabilities => _console.Capabilities; - - public int Width { get; } - public int Height => _console.Height; - - public Encoding Encoding => _console.Encoding; - - public ConsoleWithWidth(IAnsiConsole console, int width) - { - _console = console; - Width = width; - } - - public void Write(string text, Style style) - { - _console.Write(text, style); - } - } -} diff --git a/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs b/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs new file mode 100644 index 0000000..1bacf34 --- /dev/null +++ b/src/Spectre.Console.Tests/Tools/AnsiConsoleFixture.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using System.Text; +using Spectre.Console.Tests.Tools; + +namespace Spectre.Console.Tests +{ + public sealed class TestableAnsiConsole : IDisposable, IAnsiConsole + { + private readonly StringWriter _writer; + private readonly IAnsiConsole _console; + + public string Output => _writer.ToString(); + + public Capabilities Capabilities => _console.Capabilities; + public Encoding Encoding => _console.Encoding; + public int Width { get; } + public int Height => _console.Height; + + public TestableAnsiConsole(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80) + { + _writer = new StringWriter(); + _console = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = ansi, + ColorSystem = (ColorSystemSupport)system, + Out = _writer, + LinkIdentityGenerator = new TestLinkIdentityGenerator(), + }); + + Width = width; + } + + public void Dispose() + { + _writer?.Dispose(); + } + + public void Write(string text, Style style) + { + _console.Write(text, style); + } + } +} diff --git a/src/Spectre.Console.Tests/Fixtures/PlainConsole.cs b/src/Spectre.Console.Tests/Tools/PlainConsole.cs similarity index 100% rename from src/Spectre.Console.Tests/Fixtures/PlainConsole.cs rename to src/Spectre.Console.Tests/Tools/PlainConsole.cs diff --git a/src/Spectre.Console.Tests/Tools/TestLinkIdentityGenerator.cs b/src/Spectre.Console.Tests/Tools/TestLinkIdentityGenerator.cs new file mode 100644 index 0000000..4302eb6 --- /dev/null +++ b/src/Spectre.Console.Tests/Tools/TestLinkIdentityGenerator.cs @@ -0,0 +1,10 @@ +namespace Spectre.Console.Tests.Tools +{ + public sealed class TestLinkIdentityGenerator : ILinkIdentityGenerator + { + public int GenerateId(string link, string text) + { + return 1024; + } + } +} diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs index 161e58f..d7c32bf 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs @@ -13,13 +13,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Return_Correct_Code(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + var console = new TestableAnsiConsole(ColorSystem.TrueColor); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(128, 0, 128), foreground)); + console.Write("Hello", new Style().SetColor(new Color(128, 0, 128), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -28,13 +28,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Return_Eight_Bit_Ansi_Code_For_Known_Colors(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + var console = new TestableAnsiConsole(ColorSystem.TrueColor); // When - fixture.Console.Write("Hello", new Style().SetColor(Color.Purple, foreground)); + console.Write("Hello", new Style().SetColor(Color.Purple, foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } } @@ -46,13 +46,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + var console = new TestableAnsiConsole(ColorSystem.EightBit); // When - fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); + console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -61,13 +61,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Map_TrueColor_To_Nearest_Eight_Bit_Color_If_Possible(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + var console = new TestableAnsiConsole(ColorSystem.EightBit); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(128, 128, 0), foreground)); + console.Write("Hello", new Style().SetColor(new Color(128, 128, 0), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -76,13 +76,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + var console = new TestableAnsiConsole(ColorSystem.EightBit); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(126, 127, 0), foreground)); + console.Write("Hello", new Style().SetColor(new Color(126, 127, 0), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } } @@ -94,13 +94,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); + console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -114,13 +114,13 @@ namespace Spectre.Console.Tests.Unit string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); + console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -134,13 +134,13 @@ namespace Spectre.Console.Tests.Unit string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); + console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } } @@ -152,13 +152,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + var console = new TestableAnsiConsole(ColorSystem.Legacy); // When - fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); + console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -172,13 +172,13 @@ namespace Spectre.Console.Tests.Unit string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + var console = new TestableAnsiConsole(ColorSystem.Legacy); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); + console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -192,13 +192,13 @@ namespace Spectre.Console.Tests.Unit string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + var console = new TestableAnsiConsole(ColorSystem.Legacy); // When - fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); + console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } } } diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs index 5f0136e..1ed652b 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs @@ -13,18 +13,18 @@ namespace Spectre.Console.Tests.Unit [Theory] [InlineData("[yellow]Hello[/]", "Hello")] [InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")] - [InlineData("[link=https://patriksvensson.se]Click to visit my blog[/]", "]8;id=2026695893;https://patriksvensson.se\\Click to visit my blog]8;;\\")] - [InlineData("[link]https://patriksvensson.se[/]", "]8;id=2026695893;https://patriksvensson.se\\https://patriksvensson.se]8;;\\")] + [InlineData("[link=https://patriksvensson.se]Click to visit my blog[/]", "]8;id=1024;https://patriksvensson.se\\Click to visit my blog]8;;\\")] + [InlineData("[link]https://patriksvensson.se[/]", "]8;id=1024;https://patriksvensson.se\\https://patriksvensson.se]8;;\\")] public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - fixture.Console.Markup(markup); + console.Markup(markup); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -32,13 +32,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Be_Able_To_Escape_Tags(string markup, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - fixture.Console.Markup(markup); + console.Markup(markup); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -49,10 +49,10 @@ namespace Spectre.Console.Tests.Unit public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - var result = Record.Exception(() => fixture.Console.Markup(markup)); + var result = Record.Exception(() => console.Markup(markup)); // Then result.ShouldBeOfType() @@ -63,10 +63,10 @@ namespace Spectre.Console.Tests.Unit public void Should_Throw_If_Tags_Are_Unbalanced() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - var result = Record.Exception(() => fixture.Console.Markup("[yellow][blue]Hello[/]")); + var result = Record.Exception(() => console.Markup("[yellow][blue]Hello[/]")); // Then result.ShouldBeOfType() @@ -77,10 +77,10 @@ namespace Spectre.Console.Tests.Unit public void Should_Throw_If_Encounters_Closing_Tag() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - var result = Record.Exception(() => fixture.Console.Markup("Hello[/]World")); + var result = Record.Exception(() => console.Markup("Hello[/]World")); // Then result.ShouldBeOfType() diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs index 5c3fb53..8a37fb0 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs @@ -18,13 +18,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + var console = new TestableAnsiConsole(ColorSystem.TrueColor); // When - fixture.Console.Write("Hello World", Style.WithDecoration(decoration)); + console.Write("Hello World", Style.WithDecoration(decoration)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } [Theory] @@ -33,13 +33,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected) { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + var console = new TestableAnsiConsole(ColorSystem.TrueColor); // When - fixture.Console.Write("Hello World", Style.WithDecoration(decoration)); + console.Write("Hello World", Style.WithDecoration(decoration)); // Then - fixture.Output.ShouldBe(expected); + console.Output.ShouldBe(expected); } } } diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs index ebfe152..4237a22 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs @@ -10,68 +10,68 @@ namespace Spectre.Console.Tests.Unit public void Should_Combine_Decoration_And_Colors() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write( + console.Write( "Hello", Style.WithForeground(Color.RoyalBlue1) .WithBackground(Color.NavajoWhite1) .WithDecoration(Decoration.Italic)); // Then - fixture.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); + console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); } [Fact] public void Should_Not_Include_Foreground_If_Set_To_Default_Color() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write( + console.Write( "Hello", Style.WithForeground(Color.Default) .WithBackground(Color.NavajoWhite1) .WithDecoration(Decoration.Italic)); // Then - fixture.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); + console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); } [Fact] public void Should_Not_Include_Background_If_Set_To_Default_Color() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write( + console.Write( "Hello", Style.WithForeground(Color.RoyalBlue1) .WithBackground(Color.Default) .WithDecoration(Decoration.Italic)); // Then - fixture.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); + console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); } [Fact] public void Should_Not_Include_Decoration_If_Set_To_None() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + var console = new TestableAnsiConsole(ColorSystem.Standard); // When - fixture.Console.Write( + console.Write( "Hello", Style.WithForeground(Color.RoyalBlue1) .WithBackground(Color.NavajoWhite1) .WithDecoration(Decoration.None)); // Then - fixture.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); + console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); } public sealed class WriteLine @@ -80,14 +80,14 @@ namespace Spectre.Console.Tests.Unit public void Should_Reset_Colors_Correctly_After_Line_Break() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - fixture.Console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red)); - fixture.Console.WriteLine("World", Style.WithBackground(ConsoleColor.Green)); + console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red)); + console.WriteLine("World", Style.WithBackground(ConsoleColor.Green)); // Then - fixture.Output.NormalizeLineEndings() + console.Output.NormalizeLineEndings() .ShouldBe("Hello\nWorld\n"); } @@ -95,13 +95,13 @@ namespace Spectre.Console.Tests.Unit public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text() { // Given - var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); + var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes); // When - fixture.Console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red)); + console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red)); // Then - fixture.Output.NormalizeLineEndings() + console.Output.NormalizeLineEndings() .ShouldBe("Hello\nWorld\n"); } } diff --git a/src/Spectre.Console.Tests/Unit/MarkupTests.cs b/src/Spectre.Console.Tests/Unit/MarkupTests.cs index 4909570..a1355e4 100644 --- a/src/Spectre.Console.Tests/Unit/MarkupTests.cs +++ b/src/Spectre.Console.Tests/Unit/MarkupTests.cs @@ -12,7 +12,7 @@ namespace Spectre.Console.Tests.Unit public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input) { // Given - var fixture = new PlainConsole(); + var console = new PlainConsole(); // When var result = Record.Exception(() => new Markup(input)); @@ -27,14 +27,14 @@ namespace Spectre.Console.Tests.Unit public void Should_Escape_Markup_Blocks_As_Expected() { // Given - var fixture = new PlainConsole(); + var console = new PlainConsole(); var markup = new Markup("Hello [[ World ]] !"); // When - fixture.Render(markup); + console.Render(markup); // Then - fixture.Output.ShouldBe("Hello [ World ] !"); + console.Output.ShouldBe("Hello [ World ] !"); } } } diff --git a/src/Spectre.Console.Tests/Unit/TextTests.cs b/src/Spectre.Console.Tests/Unit/TextTests.cs index e2121fd..22d3377 100644 --- a/src/Spectre.Console.Tests/Unit/TextTests.cs +++ b/src/Spectre.Console.Tests/Unit/TextTests.cs @@ -37,14 +37,14 @@ namespace Spectre.Console.Tests.Unit public void Should_Render_Unstyled_Text_As_Expected() { // Given - var fixture = new PlainConsole(width: 80); + var console = new PlainConsole(width: 80); var text = new Text("Hello World"); // When - fixture.Render(text); + console.Render(text); // Then - fixture.Output + console.Output .NormalizeLineEndings() .ShouldBe("Hello World"); } @@ -55,14 +55,14 @@ namespace Spectre.Console.Tests.Unit public void Should_Write_Line_Breaks(string input) { // Given - var fixture = new PlainConsole(width: 5); + var console = new PlainConsole(width: 5); var text = new Text(input); // When - fixture.Render(text); + console.Render(text); // Then - fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n"); + console.RawOutput.ShouldBe("Hello\n\nWorld\n\n"); } [Fact] @@ -87,14 +87,14 @@ namespace Spectre.Console.Tests.Unit int width, string input, string expected) { // Given - var fixture = new PlainConsole(width); + var console = new PlainConsole(width); var text = new Text(input); // When - fixture.Render(text); + console.Render(text); // Then - fixture.Output + console.Output .NormalizeLineEndings() .ShouldBe(expected); } @@ -106,15 +106,15 @@ namespace Spectre.Console.Tests.Unit public void Should_Overflow_Text_Correctly(Overflow overflow, string expected) { // Given - var fixture = new PlainConsole(14); + var console = new PlainConsole(14); var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux") .SetOverflow(overflow); // When - fixture.Render(text); + console.Render(text); // Then - fixture.Output + console.Output .NormalizeLineEndings() .ShouldBe(expected); } diff --git a/src/Spectre.Console/AnsiConsole.cs b/src/Spectre.Console/AnsiConsole.cs index b2c607b..3bed250 100644 --- a/src/Spectre.Console/AnsiConsole.cs +++ b/src/Spectre.Console/AnsiConsole.cs @@ -55,7 +55,7 @@ namespace Spectre.Console /// An instance. public static IAnsiConsole Create(AnsiConsoleSettings settings) { - return ConsoleBuilder.Build(settings); + return AnsiConsoleBuilder.Build(settings); } } } diff --git a/src/Spectre.Console/AnsiConsoleSettings.cs b/src/Spectre.Console/AnsiConsoleSettings.cs index 6e26131..4d3dc37 100644 --- a/src/Spectre.Console/AnsiConsoleSettings.cs +++ b/src/Spectre.Console/AnsiConsoleSettings.cs @@ -18,6 +18,11 @@ namespace Spectre.Console /// public ColorSystemSupport ColorSystem { get; set; } + /// + /// Gets or sets the link identity generator. + /// + public ILinkIdentityGenerator? LinkIdentityGenerator { get; set; } + /// /// Gets or sets the out buffer. /// diff --git a/src/Spectre.Console/IAnsiConsole.cs b/src/Spectre.Console/IAnsiConsole.cs index 2b6e064..d12d885 100644 --- a/src/Spectre.Console/IAnsiConsole.cs +++ b/src/Spectre.Console/IAnsiConsole.cs @@ -12,6 +12,11 @@ namespace Spectre.Console /// Capabilities Capabilities { get; } + /// + /// Gets the console output encoding. + /// + Encoding Encoding { get; } + /// /// Gets the buffer width of the console. /// @@ -22,11 +27,6 @@ namespace Spectre.Console /// int Height { get; } - /// - /// Gets the console output encoding. - /// - Encoding Encoding { get; } - /// /// Writes a string followed by a line terminator to the console. /// diff --git a/src/Spectre.Console/ILinkIdentityGenerator.cs b/src/Spectre.Console/ILinkIdentityGenerator.cs new file mode 100644 index 0000000..d52a7f4 --- /dev/null +++ b/src/Spectre.Console/ILinkIdentityGenerator.cs @@ -0,0 +1,16 @@ +namespace Spectre.Console +{ + /// + /// Represents a link identity generator. + /// + public interface ILinkIdentityGenerator + { + /// + /// Generates an ID for the given link. + /// + /// The link. + /// The link text. + /// A unique ID for the link. + public int GenerateId(string link, string text); + } +} diff --git a/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs index 41715bb..5ee6de8 100644 --- a/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs +++ b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs @@ -3,12 +3,18 @@ using System.Linq; namespace Spectre.Console.Internal { - internal static class AnsiBuilder + internal sealed class AnsiBuilder { - public static string GetAnsi( - Capabilities capabilities, - string text, - Style style) + private readonly Capabilities _capabilities; + private readonly ILinkIdentityGenerator _linkHasher; + + public AnsiBuilder(Capabilities capabilities, ILinkIdentityGenerator? linkHasher) + { + _capabilities = capabilities ?? throw new ArgumentNullException(nameof(capabilities)); + _linkHasher = linkHasher ?? new LinkIdentityGenerator(); + } + + public string GetAnsi(string text, Style style) { if (style is null) { @@ -22,7 +28,7 @@ namespace Spectre.Console.Internal { codes = codes.Concat( AnsiColorBuilder.GetAnsiCodes( - capabilities.ColorSystem, + _capabilities.ColorSystem, style.Foreground, true)); } @@ -32,7 +38,7 @@ namespace Spectre.Console.Internal { codes = codes.Concat( AnsiColorBuilder.GetAnsiCodes( - capabilities.ColorSystem, + _capabilities.ColorSystem, style.Background, false)); } @@ -48,7 +54,7 @@ namespace Spectre.Console.Internal ? $"\u001b[{ansiCodes}m{text}\u001b[0m" : text; - if (style.Link != null && !capabilities.LegacyConsole) + if (style.Link != null && !_capabilities.LegacyConsole) { var link = style.Link; @@ -58,7 +64,7 @@ namespace Spectre.Console.Internal link = text; } - var linkId = Math.Abs(link.GetDeterministicHashCode()); + var linkId = _linkHasher.GenerateId(link, text); ansi = $"\u001b]8;id={linkId};{link}\u001b\\{ansi}\u001b]8;;\u001b\\"; } diff --git a/src/Spectre.Console/Internal/ConsoleBuilder.cs b/src/Spectre.Console/Internal/AnsiConsoleBuilder.cs similarity index 84% rename from src/Spectre.Console/Internal/ConsoleBuilder.cs rename to src/Spectre.Console/Internal/AnsiConsoleBuilder.cs index a076717..0ec2d84 100644 --- a/src/Spectre.Console/Internal/ConsoleBuilder.cs +++ b/src/Spectre.Console/Internal/AnsiConsoleBuilder.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Spectre.Console.Internal { - internal static class ConsoleBuilder + internal static class AnsiConsoleBuilder { public static IAnsiConsole Build(AnsiConsoleSettings settings) { @@ -54,12 +54,18 @@ namespace Spectre.Console.Internal ? ColorSystemDetector.Detect(supportsAnsi) : (ColorSystem)settings.ColorSystem; + // Get the capabilities + var capabilities = new Capabilities(supportsAnsi, colorSystem, legacyConsole); + + // Create the renderer if (supportsAnsi) { - return new AnsiConsoleRenderer(buffer, colorSystem, legacyConsole); + return new AnsiConsoleRenderer(buffer, capabilities, settings.LinkIdentityGenerator); + } + else + { + return new FallbackConsoleRenderer(buffer, capabilities); } - - return new FallbackConsoleRenderer(buffer, colorSystem, legacyConsole); } } } diff --git a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs b/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs index 3df80f7..1772a14 100644 --- a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs +++ b/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs @@ -7,6 +7,7 @@ namespace Spectre.Console.Internal internal sealed class AnsiConsoleRenderer : IAnsiConsole { private readonly TextWriter _out; + private readonly AnsiBuilder _ansiBuilder; public Capabilities Capabilities { get; } public Encoding Encoding { get; } @@ -37,12 +38,14 @@ namespace Spectre.Console.Internal } } - public AnsiConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole) + public AnsiConsoleRenderer(TextWriter @out, Capabilities capabilities, ILinkIdentityGenerator? linkHasher) { _out = @out ?? throw new ArgumentNullException(nameof(@out)); - Capabilities = new Capabilities(true, system, legacyConsole); - Encoding = @out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8; + Capabilities = capabilities ?? throw new ArgumentNullException(nameof(capabilities)); + Encoding = _out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8; + + _ansiBuilder = new AnsiBuilder(Capabilities, linkHasher); } public void Write(string text, Style style) @@ -59,7 +62,7 @@ namespace Spectre.Console.Internal { if (!string.IsNullOrEmpty(part)) { - _out.Write(AnsiBuilder.GetAnsi(Capabilities, part, style)); + _out.Write(_ansiBuilder.GetAnsi(part, style)); } if (!last) diff --git a/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs b/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs index a5ac093..799766f 100644 --- a/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs +++ b/src/Spectre.Console/Internal/FallbackConsoleRenderer.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Text; @@ -38,10 +39,15 @@ namespace Spectre.Console.Internal } } - public FallbackConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole) + public FallbackConsoleRenderer(TextWriter @out, Capabilities capabilities) { - _out = @out; - _system = system; + if (capabilities == null) + { + throw new ArgumentNullException(nameof(capabilities)); + } + + _out = @out ?? throw new ArgumentNullException(nameof(@out)); + _system = capabilities.ColorSystem; if (_out.IsStandardOut()) { @@ -52,7 +58,7 @@ namespace Spectre.Console.Internal Encoding = Encoding.UTF8; } - Capabilities = new Capabilities(false, _system, legacyConsole); + Capabilities = capabilities; } public void Write(string text, Style style) diff --git a/src/Spectre.Console/Internal/LinkIdentityGenerator.cs b/src/Spectre.Console/Internal/LinkIdentityGenerator.cs new file mode 100644 index 0000000..c2d3995 --- /dev/null +++ b/src/Spectre.Console/Internal/LinkIdentityGenerator.cs @@ -0,0 +1,31 @@ +using System; + +namespace Spectre.Console.Internal +{ + internal sealed class LinkIdentityGenerator : ILinkIdentityGenerator + { + private readonly Random _random; + + public LinkIdentityGenerator() + { + _random = new Random(DateTime.Now.Millisecond); + } + + public int GenerateId(string link, string text) + { + if (link is null) + { + throw new ArgumentNullException(nameof(link)); + } + + link += text ?? string.Empty; + + unchecked + { + return Math.Abs( + link.GetHashCode() + + _random.Next(0, int.MaxValue)); + } + } + } +}