Add link identity generator

This commit is contained in:
Patrik Svensson 2020-09-12 14:47:32 +02:00
parent 101e244059
commit ce670a7ca9
20 changed files with 242 additions and 174 deletions

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Tools
{
public sealed class TestLinkIdentityGenerator : ILinkIdentityGenerator
{
public int GenerateId(string link, string text)
{
return 1024;
}
}
}

View File

@ -13,13 +13,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Code(bool foreground, string expected) public void Should_Return_Correct_Code(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When // 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 // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [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) public void Should_Return_Eight_Bit_Ansi_Code_For_Known_Colors(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When // When
fixture.Console.Write("Hello", new Style().SetColor(Color.Purple, foreground)); console.Write("Hello", new Style().SetColor(Color.Purple, foreground));
// Then // 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) public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); var console = new TestableAnsiConsole(ColorSystem.EightBit);
// When // When
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [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) public void Should_Map_TrueColor_To_Nearest_Eight_Bit_Color_If_Possible(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); var console = new TestableAnsiConsole(ColorSystem.EightBit);
// When // 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 // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -76,13 +76,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected) public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); var console = new TestableAnsiConsole(ColorSystem.EightBit);
// When // 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 // 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) public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // When
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -114,13 +114,13 @@ namespace Spectre.Console.Tests.Unit
string expected) string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // 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 // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -134,13 +134,13 @@ namespace Spectre.Console.Tests.Unit
string expected) string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // 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 // 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) public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); var console = new TestableAnsiConsole(ColorSystem.Legacy);
// When // When
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground)); console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -172,13 +172,13 @@ namespace Spectre.Console.Tests.Unit
string expected) string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); var console = new TestableAnsiConsole(ColorSystem.Legacy);
// When // 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 // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -192,13 +192,13 @@ namespace Spectre.Console.Tests.Unit
string expected) string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); var console = new TestableAnsiConsole(ColorSystem.Legacy);
// When // 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 // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
} }
} }

View File

@ -13,18 +13,18 @@ namespace Spectre.Console.Tests.Unit
[Theory] [Theory]
[InlineData("[yellow]Hello[/]", "Hello")] [InlineData("[yellow]Hello[/]", "Hello")]
[InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")] [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]Click to visit my blog[/]", "]8;id=1024;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[/]", "]8;id=1024;https://patriksvensson.se\\https://patriksvensson.se]8;;\\")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected) public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
fixture.Console.Markup(markup); console.Markup(markup);
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -32,13 +32,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Be_Able_To_Escape_Tags(string markup, string expected) public void Should_Be_Able_To_Escape_Tags(string markup, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
fixture.Console.Markup(markup); console.Markup(markup);
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -49,10 +49,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected) public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
var result = Record.Exception(() => fixture.Console.Markup(markup)); var result = Record.Exception(() => console.Markup(markup));
// Then // Then
result.ShouldBeOfType<InvalidOperationException>() result.ShouldBeOfType<InvalidOperationException>()
@ -63,10 +63,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Throw_If_Tags_Are_Unbalanced() public void Should_Throw_If_Tags_Are_Unbalanced()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
var result = Record.Exception(() => fixture.Console.Markup("[yellow][blue]Hello[/]")); var result = Record.Exception(() => console.Markup("[yellow][blue]Hello[/]"));
// Then // Then
result.ShouldBeOfType<InvalidOperationException>() result.ShouldBeOfType<InvalidOperationException>()
@ -77,10 +77,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Throw_If_Encounters_Closing_Tag() public void Should_Throw_If_Encounters_Closing_Tag()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
var result = Record.Exception(() => fixture.Console.Markup("Hello[/]World")); var result = Record.Exception(() => console.Markup("Hello[/]World"));
// Then // Then
result.ShouldBeOfType<InvalidOperationException>() result.ShouldBeOfType<InvalidOperationException>()

View File

@ -18,13 +18,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected) public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When // When
fixture.Console.Write("Hello World", Style.WithDecoration(decoration)); console.Write("Hello World", Style.WithDecoration(decoration));
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
[Theory] [Theory]
@ -33,13 +33,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected) public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected)
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); var console = new TestableAnsiConsole(ColorSystem.TrueColor);
// When // When
fixture.Console.Write("Hello World", Style.WithDecoration(decoration)); console.Write("Hello World", Style.WithDecoration(decoration));
// Then // Then
fixture.Output.ShouldBe(expected); console.Output.ShouldBe(expected);
} }
} }
} }

View File

@ -10,68 +10,68 @@ namespace Spectre.Console.Tests.Unit
public void Should_Combine_Decoration_And_Colors() public void Should_Combine_Decoration_And_Colors()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // When
fixture.Console.Write( console.Write(
"Hello", "Hello",
Style.WithForeground(Color.RoyalBlue1) Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1) .WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic)); .WithDecoration(Decoration.Italic));
// Then // Then
fixture.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); console.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m");
} }
[Fact] [Fact]
public void Should_Not_Include_Foreground_If_Set_To_Default_Color() public void Should_Not_Include_Foreground_If_Set_To_Default_Color()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // When
fixture.Console.Write( console.Write(
"Hello", "Hello",
Style.WithForeground(Color.Default) Style.WithForeground(Color.Default)
.WithBackground(Color.NavajoWhite1) .WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic)); .WithDecoration(Decoration.Italic));
// Then // Then
fixture.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); console.Output.ShouldBe("\u001b[3;47mHello\u001b[0m");
} }
[Fact] [Fact]
public void Should_Not_Include_Background_If_Set_To_Default_Color() public void Should_Not_Include_Background_If_Set_To_Default_Color()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // When
fixture.Console.Write( console.Write(
"Hello", "Hello",
Style.WithForeground(Color.RoyalBlue1) Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.Default) .WithBackground(Color.Default)
.WithDecoration(Decoration.Italic)); .WithDecoration(Decoration.Italic));
// Then // Then
fixture.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); console.Output.ShouldBe("\u001b[3;90mHello\u001b[0m");
} }
[Fact] [Fact]
public void Should_Not_Include_Decoration_If_Set_To_None() public void Should_Not_Include_Decoration_If_Set_To_None()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var console = new TestableAnsiConsole(ColorSystem.Standard);
// When // When
fixture.Console.Write( console.Write(
"Hello", "Hello",
Style.WithForeground(Color.RoyalBlue1) Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1) .WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.None)); .WithDecoration(Decoration.None));
// Then // Then
fixture.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); console.Output.ShouldBe("\u001b[90;47mHello\u001b[0m");
} }
public sealed class WriteLine public sealed class WriteLine
@ -80,14 +80,14 @@ namespace Spectre.Console.Tests.Unit
public void Should_Reset_Colors_Correctly_After_Line_Break() public void Should_Reset_Colors_Correctly_After_Line_Break()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
fixture.Console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red)); console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red));
fixture.Console.WriteLine("World", Style.WithBackground(ConsoleColor.Green)); console.WriteLine("World", Style.WithBackground(ConsoleColor.Green));
// Then // Then
fixture.Output.NormalizeLineEndings() console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n"); .ShouldBe("Hello\nWorld\n");
} }
@ -95,13 +95,13 @@ namespace Spectre.Console.Tests.Unit
public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text() public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text()
{ {
// Given // Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes); var console = new TestableAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When // When
fixture.Console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red)); console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red));
// Then // Then
fixture.Output.NormalizeLineEndings() console.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n"); .ShouldBe("Hello\nWorld\n");
} }
} }

View File

@ -12,7 +12,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input) public void Should_Throw_If_Closing_Tag_Is_Not_Properly_Escaped(string input)
{ {
// Given // Given
var fixture = new PlainConsole(); var console = new PlainConsole();
// When // When
var result = Record.Exception(() => new Markup(input)); var result = Record.Exception(() => new Markup(input));
@ -27,14 +27,14 @@ namespace Spectre.Console.Tests.Unit
public void Should_Escape_Markup_Blocks_As_Expected() public void Should_Escape_Markup_Blocks_As_Expected()
{ {
// Given // Given
var fixture = new PlainConsole(); var console = new PlainConsole();
var markup = new Markup("Hello [[ World ]] !"); var markup = new Markup("Hello [[ World ]] !");
// When // When
fixture.Render(markup); console.Render(markup);
// Then // Then
fixture.Output.ShouldBe("Hello [ World ] !"); console.Output.ShouldBe("Hello [ World ] !");
} }
} }
} }

View File

@ -37,14 +37,14 @@ namespace Spectre.Console.Tests.Unit
public void Should_Render_Unstyled_Text_As_Expected() public void Should_Render_Unstyled_Text_As_Expected()
{ {
// Given // Given
var fixture = new PlainConsole(width: 80); var console = new PlainConsole(width: 80);
var text = new Text("Hello World"); var text = new Text("Hello World");
// When // When
fixture.Render(text); console.Render(text);
// Then // Then
fixture.Output console.Output
.NormalizeLineEndings() .NormalizeLineEndings()
.ShouldBe("Hello World"); .ShouldBe("Hello World");
} }
@ -55,14 +55,14 @@ namespace Spectre.Console.Tests.Unit
public void Should_Write_Line_Breaks(string input) public void Should_Write_Line_Breaks(string input)
{ {
// Given // Given
var fixture = new PlainConsole(width: 5); var console = new PlainConsole(width: 5);
var text = new Text(input); var text = new Text(input);
// When // When
fixture.Render(text); console.Render(text);
// Then // Then
fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n"); console.RawOutput.ShouldBe("Hello\n\nWorld\n\n");
} }
[Fact] [Fact]
@ -87,14 +87,14 @@ namespace Spectre.Console.Tests.Unit
int width, string input, string expected) int width, string input, string expected)
{ {
// Given // Given
var fixture = new PlainConsole(width); var console = new PlainConsole(width);
var text = new Text(input); var text = new Text(input);
// When // When
fixture.Render(text); console.Render(text);
// Then // Then
fixture.Output console.Output
.NormalizeLineEndings() .NormalizeLineEndings()
.ShouldBe(expected); .ShouldBe(expected);
} }
@ -106,15 +106,15 @@ namespace Spectre.Console.Tests.Unit
public void Should_Overflow_Text_Correctly(Overflow overflow, string expected) public void Should_Overflow_Text_Correctly(Overflow overflow, string expected)
{ {
// Given // Given
var fixture = new PlainConsole(14); var console = new PlainConsole(14);
var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux") var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux")
.SetOverflow(overflow); .SetOverflow(overflow);
// When // When
fixture.Render(text); console.Render(text);
// Then // Then
fixture.Output console.Output
.NormalizeLineEndings() .NormalizeLineEndings()
.ShouldBe(expected); .ShouldBe(expected);
} }

View File

@ -55,7 +55,7 @@ namespace Spectre.Console
/// <returns>An <see cref="IAnsiConsole"/> instance.</returns> /// <returns>An <see cref="IAnsiConsole"/> instance.</returns>
public static IAnsiConsole Create(AnsiConsoleSettings settings) public static IAnsiConsole Create(AnsiConsoleSettings settings)
{ {
return ConsoleBuilder.Build(settings); return AnsiConsoleBuilder.Build(settings);
} }
} }
} }

View File

@ -18,6 +18,11 @@ namespace Spectre.Console
/// </summary> /// </summary>
public ColorSystemSupport ColorSystem { get; set; } public ColorSystemSupport ColorSystem { get; set; }
/// <summary>
/// Gets or sets the link identity generator.
/// </summary>
public ILinkIdentityGenerator? LinkIdentityGenerator { get; set; }
/// <summary> /// <summary>
/// Gets or sets the out buffer. /// Gets or sets the out buffer.
/// </summary> /// </summary>

View File

@ -12,6 +12,11 @@ namespace Spectre.Console
/// </summary> /// </summary>
Capabilities Capabilities { get; } Capabilities Capabilities { get; }
/// <summary>
/// Gets the console output encoding.
/// </summary>
Encoding Encoding { get; }
/// <summary> /// <summary>
/// Gets the buffer width of the console. /// Gets the buffer width of the console.
/// </summary> /// </summary>
@ -22,11 +27,6 @@ namespace Spectre.Console
/// </summary> /// </summary>
int Height { get; } int Height { get; }
/// <summary>
/// Gets the console output encoding.
/// </summary>
Encoding Encoding { get; }
/// <summary> /// <summary>
/// Writes a string followed by a line terminator to the console. /// Writes a string followed by a line terminator to the console.
/// </summary> /// </summary>

View File

@ -0,0 +1,16 @@
namespace Spectre.Console
{
/// <summary>
/// Represents a link identity generator.
/// </summary>
public interface ILinkIdentityGenerator
{
/// <summary>
/// Generates an ID for the given link.
/// </summary>
/// <param name="link">The link.</param>
/// <param name="text">The link text.</param>
/// <returns>A unique ID for the link.</returns>
public int GenerateId(string link, string text);
}
}

View File

@ -3,12 +3,18 @@ using System.Linq;
namespace Spectre.Console.Internal namespace Spectre.Console.Internal
{ {
internal static class AnsiBuilder internal sealed class AnsiBuilder
{ {
public static string GetAnsi( private readonly Capabilities _capabilities;
Capabilities capabilities, private readonly ILinkIdentityGenerator _linkHasher;
string text,
Style style) 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) if (style is null)
{ {
@ -22,7 +28,7 @@ namespace Spectre.Console.Internal
{ {
codes = codes.Concat( codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes( AnsiColorBuilder.GetAnsiCodes(
capabilities.ColorSystem, _capabilities.ColorSystem,
style.Foreground, style.Foreground,
true)); true));
} }
@ -32,7 +38,7 @@ namespace Spectre.Console.Internal
{ {
codes = codes.Concat( codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes( AnsiColorBuilder.GetAnsiCodes(
capabilities.ColorSystem, _capabilities.ColorSystem,
style.Background, style.Background,
false)); false));
} }
@ -48,7 +54,7 @@ namespace Spectre.Console.Internal
? $"\u001b[{ansiCodes}m{text}\u001b[0m" ? $"\u001b[{ansiCodes}m{text}\u001b[0m"
: text; : text;
if (style.Link != null && !capabilities.LegacyConsole) if (style.Link != null && !_capabilities.LegacyConsole)
{ {
var link = style.Link; var link = style.Link;
@ -58,7 +64,7 @@ namespace Spectre.Console.Internal
link = text; 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\\"; ansi = $"\u001b]8;id={linkId};{link}\u001b\\{ansi}\u001b]8;;\u001b\\";
} }

View File

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace Spectre.Console.Internal namespace Spectre.Console.Internal
{ {
internal static class ConsoleBuilder internal static class AnsiConsoleBuilder
{ {
public static IAnsiConsole Build(AnsiConsoleSettings settings) public static IAnsiConsole Build(AnsiConsoleSettings settings)
{ {
@ -54,12 +54,18 @@ namespace Spectre.Console.Internal
? ColorSystemDetector.Detect(supportsAnsi) ? ColorSystemDetector.Detect(supportsAnsi)
: (ColorSystem)settings.ColorSystem; : (ColorSystem)settings.ColorSystem;
// Get the capabilities
var capabilities = new Capabilities(supportsAnsi, colorSystem, legacyConsole);
// Create the renderer
if (supportsAnsi) 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);
} }
} }
} }

View File

@ -7,6 +7,7 @@ namespace Spectre.Console.Internal
internal sealed class AnsiConsoleRenderer : IAnsiConsole internal sealed class AnsiConsoleRenderer : IAnsiConsole
{ {
private readonly TextWriter _out; private readonly TextWriter _out;
private readonly AnsiBuilder _ansiBuilder;
public Capabilities Capabilities { get; } public Capabilities Capabilities { get; }
public Encoding Encoding { 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)); _out = @out ?? throw new ArgumentNullException(nameof(@out));
Capabilities = new Capabilities(true, system, legacyConsole); Capabilities = capabilities ?? throw new ArgumentNullException(nameof(capabilities));
Encoding = @out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8; Encoding = _out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8;
_ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
} }
public void Write(string text, Style style) public void Write(string text, Style style)
@ -59,7 +62,7 @@ namespace Spectre.Console.Internal
{ {
if (!string.IsNullOrEmpty(part)) if (!string.IsNullOrEmpty(part))
{ {
_out.Write(AnsiBuilder.GetAnsi(Capabilities, part, style)); _out.Write(_ansiBuilder.GetAnsi(part, style));
} }
if (!last) if (!last)

View File

@ -1,3 +1,4 @@
using System;
using System.IO; using System.IO;
using System.Text; 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; if (capabilities == null)
_system = system; {
throw new ArgumentNullException(nameof(capabilities));
}
_out = @out ?? throw new ArgumentNullException(nameof(@out));
_system = capabilities.ColorSystem;
if (_out.IsStandardOut()) if (_out.IsStandardOut())
{ {
@ -52,7 +58,7 @@ namespace Spectre.Console.Internal
Encoding = Encoding.UTF8; Encoding = Encoding.UTF8;
} }
Capabilities = new Capabilities(false, _system, legacyConsole); Capabilities = capabilities;
} }
public void Write(string text, Style style) public void Write(string text, Style style)

View File

@ -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));
}
}
}
}