Add link support for supported terminals

Also refactors the code quite a bit, to make it a bit more
easier to add features like this in the future.

Closes #75
This commit is contained in:
Patrik Svensson 2020-09-11 00:34:45 +02:00 committed by Patrik Svensson
parent 1601ef24b3
commit 504746c5dc
33 changed files with 574 additions and 1539 deletions

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Description>Demonstrates how to render links in a console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

22
examples/Links/Program.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using Spectre.Console;
namespace Links
{
public static class Program
{
public static void Main()
{
if (AnsiConsole.Capabilities.SupportLinks)
{
AnsiConsole.MarkupLine("[link=https://patriksvensson.se]Click to visit my blog[/]!");
}
else
{
AnsiConsole.MarkupLine("[red]It looks like your terminal doesn't support links[/]");
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[yellow](╯°□°)╯[/]︵ [blue]┻━┻[/]");
}
}
}
}

View File

@ -1,24 +0,0 @@
using System;
namespace Spectre.Console.Tests
{
public static class ConsoleExtensions
{
public static void SetColor(this IAnsiConsole console, Color color, bool foreground)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (foreground)
{
console.Foreground = color;
}
else
{
console.Background = color;
}
}
}
}

View File

@ -0,0 +1,15 @@
namespace Spectre.Console.Tests
{
internal static class StyleExtensions
{
public static Style SetColor(this Style style, Color color, bool foreground)
{
if (foreground)
{
return style.WithForeground(color);
}
return style.WithBackground(color);
}
}
}

View File

@ -1,4 +1,4 @@
using System.Text;
using System.Text;
namespace Spectre.Console.Tests
{
@ -13,19 +13,15 @@ namespace Spectre.Console.Tests
public Encoding Encoding => _console.Encoding;
public Decoration Decoration { get => _console.Decoration; set => _console.Decoration = value; }
public Color Foreground { get => _console.Foreground; set => _console.Foreground = value; }
public Color Background { get => _console.Background; set => _console.Background = value; }
public ConsoleWithWidth(IAnsiConsole console, int width)
{
_console = console;
Width = width;
}
public void Write(string text)
public void Write(string text, Style style)
{
_console.Write(text);
_console.Write(text, style);
}
}
}

View File

@ -16,6 +16,7 @@ namespace Spectre.Console.Tests
public Decoration Decoration { get; set; }
public Color Foreground { get; set; }
public Color Background { get; set; }
public string Link { get; set; }
public StringWriter Writer { get; }
public string RawOutput => Writer.ToString();
@ -39,7 +40,7 @@ namespace Spectre.Console.Tests
Writer.Dispose();
}
public void Write(string text)
public void Write(string text, Style style)
{
Writer.Write(text);
}

View File

@ -14,10 +14,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
fixture.Console.SetColor(new Color(128, 0, 128), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(128, 0, 128), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -30,10 +29,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
fixture.Console.SetColor(Color.Purple, foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(Color.Purple, foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -49,10 +47,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit);
fixture.Console.SetColor(Color.Olive, foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -65,10 +62,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit);
fixture.Console.SetColor(new Color(128, 128, 0), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(128, 128, 0), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -81,10 +77,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.EightBit);
fixture.Console.SetColor(new Color(126, 127, 0), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(126, 127, 0), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -100,10 +95,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.SetColor(Color.Olive, foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -121,10 +115,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.SetColor(new Color(r, g, b), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -142,10 +135,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.SetColor(new Color(r, g, b), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -161,10 +153,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy);
fixture.Console.SetColor(Color.Olive, foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(Color.Olive, foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -182,10 +173,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy);
fixture.Console.SetColor(new Color(r, g, b), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
fixture.Output.ShouldBe(expected);
@ -203,10 +193,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Legacy);
fixture.Console.SetColor(new Color(r, g, b), foreground);
// When
fixture.Console.Write("Hello");
fixture.Console.Write("Hello", new Style().SetColor(new Color(r, g, b), foreground));
// Then
fixture.Output.ShouldBe(expected);

View File

@ -13,6 +13,8 @@ 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;;\\")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{
// Given

View File

@ -19,10 +19,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
fixture.Console.Decoration = decoration;
// When
fixture.Console.Write("Hello World");
fixture.Console.Write("Hello World", Style.WithDecoration(decoration));
// Then
fixture.Output.ShouldBe(expected);
@ -35,10 +34,9 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
fixture.Console.Decoration = decoration;
// When
fixture.Console.Write("Hello World");
fixture.Console.Write("Hello World", Style.WithDecoration(decoration));
// Then
fixture.Output.ShouldBe(expected);

View File

@ -1,5 +1,4 @@
using System;
using System.Globalization;
using Shouldly;
using Xunit;
@ -12,12 +11,13 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.Foreground = Color.RoyalBlue1;
fixture.Console.Background = Color.NavajoWhite1;
fixture.Console.Decoration = Decoration.Italic;
// When
fixture.Console.Write("Hello");
fixture.Console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic));
// Then
fixture.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m");
@ -28,12 +28,13 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.Foreground = Color.Default;
fixture.Console.Background = Color.NavajoWhite1;
fixture.Console.Decoration = Decoration.Italic;
// When
fixture.Console.Write("Hello");
fixture.Console.Write(
"Hello",
Style.WithForeground(Color.Default)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.Italic));
// Then
fixture.Output.ShouldBe("\u001b[3;47mHello\u001b[0m");
@ -44,12 +45,13 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.Foreground = Color.RoyalBlue1;
fixture.Console.Background = Color.Default;
fixture.Console.Decoration = Decoration.Italic;
// When
fixture.Console.Write("Hello");
fixture.Console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.Default)
.WithDecoration(Decoration.Italic));
// Then
fixture.Output.ShouldBe("\u001b[3;90mHello\u001b[0m");
@ -60,190 +62,18 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
fixture.Console.Foreground = Color.RoyalBlue1;
fixture.Console.Background = Color.NavajoWhite1;
fixture.Console.Decoration = Decoration.None;
// When
fixture.Console.Write("Hello");
fixture.Console.Write(
"Hello",
Style.WithForeground(Color.RoyalBlue1)
.WithBackground(Color.NavajoWhite1)
.WithDecoration(Decoration.None));
// Then
fixture.Output.ShouldBe("\u001b[90;47mHello\u001b[0m");
}
public sealed class Write
{
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Int32_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32);
// Then
fixture.Output.ShouldBe("32");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_UInt32_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32U);
// Then
fixture.Output.ShouldBe("32");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Int64_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32L);
// Then
fixture.Output.ShouldBe("32");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_UInt64_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32UL);
// Then
fixture.Output.ShouldBe("32");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Single_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32.432F);
// Then
fixture.Output.ShouldBe("32.432");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Double_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, (double)32.432);
// Then
fixture.Output.ShouldBe("32.432");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Decimal_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 32.432M);
// Then
fixture.Output.ShouldBe("32.432");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Boolean_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, true);
// Then
fixture.Output.ShouldBe("True");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Char_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(CultureInfo.InvariantCulture, 'P');
// Then
fixture.Output.ShouldBe("P");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Char_Array_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(
CultureInfo.InvariantCulture,
new[] { 'P', 'a', 't', 'r', 'i', 'k' });
// Then
fixture.Output.ShouldBe("Patrik");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Formatted_String_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.Write(
CultureInfo.InvariantCulture,
"Hello {0}! {1}",
"World", 32);
// Then
fixture.Output.ShouldBe("Hello World! 32");
}
}
public sealed class WriteLine
{
[Fact]
@ -253,10 +83,8 @@ namespace Spectre.Console.Tests.Unit
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
// When
fixture.Console.Background = ConsoleColor.Red;
fixture.Console.WriteLine("Hello");
fixture.Console.Background = ConsoleColor.Green;
fixture.Console.WriteLine("World");
fixture.Console.WriteLine("Hello", Style.WithBackground(ConsoleColor.Red));
fixture.Console.WriteLine("World", Style.WithBackground(ConsoleColor.Green));
// Then
fixture.Output.NormalizeLineEndings()
@ -270,183 +98,12 @@ namespace Spectre.Console.Tests.Unit
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
// When
fixture.Console.Background = ConsoleColor.Red;
fixture.Console.WriteLine("Hello\nWorld");
fixture.Console.WriteLine("Hello\nWorld", Style.WithBackground(ConsoleColor.Red));
// Then
fixture.Output.NormalizeLineEndings()
.ShouldBe("Hello\nWorld\n");
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Int32_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32);
// Then
fixture.Output.ShouldBe("32" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_UInt32_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32U);
// Then
fixture.Output.ShouldBe("32" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Int64_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32L);
// Then
fixture.Output.ShouldBe("32" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_UInt64_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32UL);
// Then
fixture.Output.ShouldBe("32" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Single_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32.432F);
// Then
fixture.Output.ShouldBe("32.432" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Double_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, (double)32.432);
// Then
fixture.Output.ShouldBe("32.432" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Decimal_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 32.432M);
// Then
fixture.Output.ShouldBe("32.432" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Boolean_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, true);
// Then
fixture.Output.ShouldBe("True" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Char_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(CultureInfo.InvariantCulture, 'P');
// Then
fixture.Output.ShouldBe("P" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Char_Array_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(
CultureInfo.InvariantCulture,
new[] { 'P', 'a', 't', 'r', 'i', 'k' });
// Then
fixture.Output.ShouldBe("Patrik" + Environment.NewLine);
}
[Theory]
[InlineData(AnsiSupport.Yes)]
[InlineData(AnsiSupport.No)]
public void Should_Write_Formatted_String_With_Format_Provider(AnsiSupport ansi)
{
// Given
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, ansi);
// When
fixture.Console.WriteLine(
CultureInfo.InvariantCulture,
"Hello {0}! {1}",
"World", 32);
// Then
fixture.Output.ShouldBe("Hello World! 32" + Environment.NewLine);
}
}
}
}

View File

@ -11,7 +11,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic);
var other = new Style(Color.Green, Color.Silver, Decoration.Underline);
var other = new Style(Color.Green, Color.Silver, Decoration.Underline, "https://example.com");
// When
var result = first.Combine(other);
@ -20,6 +20,77 @@ namespace Spectre.Console.Tests.Unit
result.Foreground.ShouldBe(Color.Green);
result.Background.ShouldBe(Color.Silver);
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Italic | Decoration.Underline);
result.Link.ShouldBe("https://example.com");
}
[Fact]
public void Should_Consider_Two_Identical_Styles_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeTrue();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Foreground_Colors_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.Blue, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Background_Colors_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Blue, Decoration.Bold | Decoration.Italic, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Decorations_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold, "http://example.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
[Fact]
public void Should_Not_Consider_Two_Styles_With_Different_Links_Equal()
{
// Given
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://example.com");
var second = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic, "http://foo.com");
// When
var result = first.Equals(second);
// Then
result.ShouldBeFalse();
}
public sealed class TheParseMethod
@ -62,16 +133,36 @@ namespace Spectre.Console.Tests.Unit
}
[Fact]
public void Should_Parse_Text_And_Decoration()
public void Should_Parse_Link_Without_Address()
{
// Given, When
var result = Style.Parse("bold underline blue on green");
var result = Style.Parse("link");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline);
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.Green);
result.Link.ShouldBe("https://emptylink");
}
[Fact]
public void Should_Parse_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com");
// Then
result.ShouldNotBeNull();
result.Link.ShouldBe("https://example.com");
}
[Fact]
public void Should_Throw_If_Link_Is_Set_Twice()
{
// Given, When
var result = Record.Exception(() => Style.Parse("link=https://example.com link=https://example.com"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("A link has already been set.");
}
[Fact]
@ -131,6 +222,20 @@ namespace Spectre.Console.Tests.Unit
result.Message.ShouldBe("Could not find color 'lol'.");
}
[Fact]
public void Should_Parse_Colors_And_Decoration_And_Link()
{
// Given, When
var result = Style.Parse("link=https://example.com bold underline blue on green");
// Then
result.ShouldNotBeNull();
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline);
result.Foreground.ShouldBe(Color.Blue);
result.Background.ShouldBe(Color.Green);
result.Link.ShouldBe("https://example.com");
}
[Theory]
[InlineData("#FF0000 on #0000FF")]
[InlineData("#F00 on #00F")]

View File

@ -29,7 +29,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Columns", "..\examples\Colu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Info", "..\examples\Info\Info.csproj", "{225CE0D4-06AB-411A-8D29-707504FE53B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Borders", "..\examples\Borders\Borders.csproj", "{094245E6-4C94-485D-B5AC-3153E878B112}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Borders", "..\examples\Borders\Borders.csproj", "{094245E6-4C94-485D-B5AC-3153E878B112}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Links", "..\examples\Links\Links.csproj", "{6AF8C93B-AA41-4F44-8B1B-B8D166576174}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -149,6 +151,18 @@ Global
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x64.Build.0 = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x86.ActiveCfg = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x86.Build.0 = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|x64.ActiveCfg = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|x64.Build.0 = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|x86.ActiveCfg = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Debug|x86.Build.0 = Debug|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|Any CPU.Build.0 = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|x64.ActiveCfg = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|x64.Build.0 = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|x86.ActiveCfg = Release|Any CPU
{6AF8C93B-AA41-4F44-8B1B-B8D166576174}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -161,6 +175,7 @@ Global
{33357599-C79D-4299-888F-634E2C3EACEF} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{225CE0D4-06AB-411A-8D29-707504FE53B3} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{094245E6-4C94-485D-B5AC-3153E878B112} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{6AF8C93B-AA41-4F44-8B1B-B8D166576174} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@ -0,0 +1,85 @@
using System;
using System.IO;
using Spectre.Console.Internal;
namespace Spectre.Console
{
/// <summary>
/// A console capable of writing ANSI escape sequences.
/// </summary>
public static partial class AnsiConsole
{
private static ConsoleColor _defaultForeground;
private static ConsoleColor _defaultBackground;
internal static Style CurrentStyle { get; private set; } = Style.Plain;
internal static bool Created { get; private set; }
/// <summary>
/// Gets or sets the foreground color.
/// </summary>
public static Color Foreground
{
get => CurrentStyle.Foreground;
set => CurrentStyle = CurrentStyle.WithForeground(value);
}
/// <summary>
/// Gets or sets the background color.
/// </summary>
public static Color Background
{
get => CurrentStyle.Background;
set => CurrentStyle = CurrentStyle.WithBackground(value);
}
/// <summary>
/// Gets or sets the text decoration.
/// </summary>
public static Decoration Decoration
{
get => CurrentStyle.Decoration;
set => CurrentStyle = CurrentStyle.WithDecoration(value);
}
internal static void Initialize(TextWriter? @out)
{
if (@out?.IsStandardOut() ?? false)
{
Foreground = _defaultForeground = System.Console.ForegroundColor;
Background = _defaultBackground = System.Console.BackgroundColor;
}
else
{
Foreground = _defaultForeground = Color.Silver;
Background = _defaultBackground = Color.Black;
}
}
/// <summary>
/// Resets colors and text decorations.
/// </summary>
public static void Reset()
{
ResetColors();
ResetDecoration();
}
/// <summary>
/// Resets the current applied text decorations.
/// </summary>
public static void ResetDecoration()
{
Decoration = Decoration.None;
}
/// <summary>
/// Resets the current applied foreground and background colors.
/// </summary>
public static void ResetColors()
{
Foreground = _defaultForeground;
Background = _defaultBackground;
}
}
}

View File

@ -14,7 +14,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(string value)
{
Console.Write(value);
Console.Write(value, CurrentStyle);
}
/// <summary>
@ -24,7 +24,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(int value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -35,7 +35,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, int value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -45,7 +45,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(uint value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -56,7 +56,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, uint value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -66,7 +66,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(long value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -77,7 +77,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, long value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -87,7 +87,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(ulong value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -98,7 +98,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, ulong value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -108,7 +108,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(float value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -119,7 +119,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, float value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -129,7 +129,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(double value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -140,7 +140,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, double value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -149,7 +149,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(decimal value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -159,7 +159,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, decimal value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -168,7 +168,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(bool value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -178,7 +178,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, bool value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -187,7 +187,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(char value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -197,7 +197,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, char value)
{
Console.Write(value.ToString(provider));
Console.Write(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -206,7 +206,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(char[] value)
{
Console.Write(CultureInfo.CurrentCulture, value);
Write(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -216,7 +216,15 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void Write(IFormatProvider provider, char[] value)
{
Console.Write(provider, value);
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
for (var index = 0; index < value.Length; index++)
{
Console.Write(value[index].ToString(provider), CurrentStyle);
}
}
/// <summary>
@ -227,7 +235,7 @@ namespace Spectre.Console
/// <param name="args">An array of objects to write.</param>
public static void Write(string format, params object[] args)
{
Console.Write(CultureInfo.CurrentCulture, format, args);
Write(CultureInfo.CurrentCulture, format, args);
}
/// <summary>
@ -239,7 +247,7 @@ namespace Spectre.Console
/// <param name="args">An array of objects to write.</param>
public static void Write(IFormatProvider provider, string format, params object[] args)
{
Console.Write(string.Format(provider, format, args));
Console.Write(string.Format(provider, format, args), CurrentStyle);
}
}
}

View File

@ -22,7 +22,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(string value)
{
Console.WriteLine(value);
Console.WriteLine(value, CurrentStyle);
}
/// <summary>
@ -32,7 +32,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(int value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -43,7 +43,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, int value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -53,7 +53,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(uint value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -64,7 +64,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, uint value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -74,7 +74,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(long value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -85,7 +85,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, long value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -95,7 +95,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(ulong value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -106,7 +106,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, ulong value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -116,7 +116,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(float value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -127,7 +127,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, float value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -137,7 +137,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(double value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -148,7 +148,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, double value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -158,7 +158,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(decimal value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -169,7 +169,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, decimal value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -179,7 +179,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(bool value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -190,7 +190,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, bool value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -200,7 +200,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(char value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -211,7 +211,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, char value)
{
Console.WriteLine(value.ToString(provider));
Console.WriteLine(value.ToString(provider), CurrentStyle);
}
/// <summary>
@ -221,7 +221,7 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(char[] value)
{
Console.WriteLine(CultureInfo.CurrentCulture, value);
WriteLine(CultureInfo.CurrentCulture, value);
}
/// <summary>
@ -232,7 +232,17 @@ namespace Spectre.Console
/// <param name="value">The value to write.</param>
public static void WriteLine(IFormatProvider provider, char[] value)
{
Console.WriteLine(provider, value);
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
for (var index = 0; index < value.Length; index++)
{
Console.Write(value[index].ToString(provider), CurrentStyle);
}
Console.WriteLine();
}
/// <summary>
@ -244,7 +254,7 @@ namespace Spectre.Console
/// <param name="args">An array of objects to write.</param>
public static void WriteLine(string format, params object[] args)
{
Console.WriteLine(CultureInfo.CurrentCulture, format, args);
WriteLine(CultureInfo.CurrentCulture, format, args);
}
/// <summary>
@ -257,7 +267,7 @@ namespace Spectre.Console
/// <param name="args">An array of objects to write.</param>
public static void WriteLine(IFormatProvider provider, string format, params object[] args)
{
Console.WriteLine(string.Format(provider, format, args));
Console.WriteLine(string.Format(provider, format, args), CurrentStyle);
}
}
}

View File

@ -16,12 +16,13 @@ namespace Spectre.Console
ColorSystem = ColorSystemSupport.Detect,
Out = System.Console.Out,
});
Initialize(System.Console.Out);
Created = true;
return console;
});
/// <summary>
/// Gets the current renderer.
/// Gets the underlying <see cref="IAnsiConsole"/>.
/// </summary>
public static IAnsiConsole Console => _console.Value;
@ -30,8 +31,6 @@ namespace Spectre.Console
/// </summary>
public static Capabilities Capabilities => Console.Capabilities;
internal static bool Created { get; private set; }
/// <summary>
/// Gets the buffer width of the console.
/// </summary>
@ -48,33 +47,6 @@ namespace Spectre.Console
get => Console.Height;
}
/// <summary>
/// Gets or sets the foreground color.
/// </summary>
public static Color Foreground
{
get => Console.Foreground;
set => Console.SetColor(value, true);
}
/// <summary>
/// Gets or sets the background color.
/// </summary>
public static Color Background
{
get => Console.Background;
set => Console.SetColor(value, false);
}
/// <summary>
/// Gets or sets the text decoration.
/// </summary>
public static Decoration Decoration
{
get => Console.Decoration;
set => Console.Decoration = value;
}
/// <summary>
/// Creates a new <see cref="IAnsiConsole"/> instance
/// from the provided settings.
@ -85,29 +57,5 @@ namespace Spectre.Console
{
return ConsoleBuilder.Build(settings);
}
/// <summary>
/// Resets colors and text decorations.
/// </summary>
public static void Reset()
{
Console.Reset();
}
/// <summary>
/// Resets the current applied text decorations.
/// </summary>
public static void ResetDecoration()
{
Console.ResetDecoration();
}
/// <summary>
/// Resets the current applied foreground and background colors.
/// </summary>
public static void ResetColors()
{
Console.ResetColors();
}
}
}

View File

@ -11,6 +11,17 @@ namespace Spectre.Console
/// </summary>
public bool SupportsAnsi { get; }
/// <summary>
/// Gets a value indicating whether or not
/// the console support links.
/// </summary>
/// <remarks>
/// There is probably a lot of room for improvement here
/// once we have more information about the terminal
/// we're running inside.
/// </remarks>
public bool SupportLinks => SupportsAnsi && !LegacyConsole;
/// <summary>
/// Gets the color system.
/// </summary>

View File

@ -1,6 +1,5 @@
using System;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
@ -28,30 +27,18 @@ namespace Spectre.Console
}
var options = new RenderContext(console.Encoding, console.Capabilities.LegacyConsole);
var segments = renderable.Render(options, console.Width).Where(x => !(x.Text.Length == 0 && !x.IsLineBreak)).ToArray();
segments = Segment.Merge(segments).ToArray();
using (console.PushStyle(Style.Plain))
var current = Style.Plain;
foreach (var segment in segments)
{
var segments = renderable.Render(options, console.Width).Where(x => !(x.Text.Length == 0 && !x.IsLineBreak)).ToArray();
segments = Segment.Merge(segments).ToArray();
var current = Style.Plain;
foreach (var segment in segments)
if (string.IsNullOrEmpty(segment.Text))
{
if (string.IsNullOrEmpty(segment.Text))
{
continue;
}
if (!segment.Style.Equals(current))
{
console.Foreground = segment.Style.Foreground;
console.Background = segment.Style.Background;
console.Decoration = segment.Style.Decoration;
current = segment.Style;
}
console.Write(segment.Text);
continue;
}
console.Write(segment.Text, segment.Style);
}
}
}

View File

@ -1,339 +0,0 @@
using System;
using System.Globalization;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary>
public static partial class AnsiConsoleExtensions
{
/// <summary>
/// Writes the specified string value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, string value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (value != null)
{
console.Write(value);
}
}
/// <summary>
/// Writes the text representation of the specified 32-bit
/// signed integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, int value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 32-bit
/// signed integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, int value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 32-bit
/// unsigned integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, uint value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 32-bit
/// unsigned integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, uint value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 64-bit
/// signed integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, long value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 64-bit
/// signed integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, long value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 64-bit
/// unsigned integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, ulong value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 64-bit
/// unsigned integer value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, ulong value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified single-precision
/// floating-point value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, float value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified single-precision
/// floating-point value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, float value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified double-precision
/// floating-point value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, double value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified double-precision
/// floating-point value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, double value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified decimal value, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, decimal value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified decimal value, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, decimal value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
Write(console, value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified boolean value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, bool value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified boolean value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, bool value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
Write(console, value.ToString(provider));
}
/// <summary>
/// Writes the specified Unicode character to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, char value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the specified Unicode character to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, char value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
Write(console, value.ToString(provider));
}
/// <summary>
/// Writes the specified array of Unicode characters to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, char[] value)
{
Write(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the specified array of Unicode characters to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, char[] value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
for (var index = 0; index < value.Length; index++)
{
console.Write(value[index].ToString(provider));
}
}
/// <summary>
/// Writes the text representation of the specified array of objects,
/// to the console using the specified format information.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="format">A composite format string.</param>
/// <param name="args">An array of objects to write.</param>
public static void Write(this IAnsiConsole console, string format, params object[] args)
{
Write(console, CultureInfo.CurrentCulture, format, args);
}
/// <summary>
/// Writes the text representation of the specified array of objects,
/// to the console using the specified format information.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="format">A composite format string.</param>
/// <param name="args">An array of objects to write.</param>
public static void Write(this IAnsiConsole console, IFormatProvider provider, string format, params object[] args)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
Write(console, string.Format(provider, format, args));
}
}
}

View File

@ -1,368 +0,0 @@
using System;
using System.Globalization;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary>
public static partial class AnsiConsoleExtensions
{
/// <summary>
/// Writes an empty line to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
public static void WriteLine(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(Environment.NewLine);
}
/// <summary>
/// Writes the specified string value, followed by the
/// current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, string value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (value != null)
{
console.Write(value);
}
console.WriteLine();
}
/// <summary>
/// Writes the text representation of the specified 32-bit signed integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, int value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 32-bit signed integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, int value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 32-bit unsigned integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, uint value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 32-bit unsigned integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, uint value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 64-bit signed integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, long value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 64-bit signed integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, long value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified 64-bit unsigned integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, ulong value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified 64-bit unsigned integer value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, ulong value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified single-precision floating-point
/// value, followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, float value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified single-precision floating-point
/// value, followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, float value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified double-precision floating-point
/// value, followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, double value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified double-precision floating-point
/// value, followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, double value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified decimal value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, decimal value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified decimal value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, decimal value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
WriteLine(console, value.ToString(provider));
}
/// <summary>
/// Writes the text representation of the specified boolean value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, bool value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the text representation of the specified boolean value,
/// followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, bool value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
WriteLine(console, value.ToString(provider));
}
/// <summary>
/// Writes the specified Unicode character, followed by the current
/// line terminator, value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, char value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the specified Unicode character, followed by the current
/// line terminator, value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, char value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
WriteLine(console, value.ToString(provider));
}
/// <summary>
/// Writes the specified array of Unicode characters, followed by the current
/// line terminator, value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, char[] value)
{
WriteLine(console, CultureInfo.CurrentCulture, value);
}
/// <summary>
/// Writes the specified array of Unicode characters, followed by the current
/// line terminator, value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="value">The value to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, char[] value)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
for (var index = 0; index < value.Length; index++)
{
console.Write(value[index].ToString(provider));
}
console.WriteLine();
}
/// <summary>
/// Writes the text representation of the specified array of objects,
/// followed by the current line terminator, to the console
/// using the specified format information.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="format">A composite format string.</param>
/// <param name="args">An array of objects to write.</param>
public static void WriteLine(this IAnsiConsole console, string format, params object[] args)
{
WriteLine(console, CultureInfo.CurrentCulture, format, args);
}
/// <summary>
/// Writes the text representation of the specified array of objects,
/// followed by the current line terminator, to the console
/// using the specified format information.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <param name="format">A composite format string.</param>
/// <param name="args">An array of objects to write.</param>
public static void WriteLine(this IAnsiConsole console, IFormatProvider provider, string format, params object[] args)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
WriteLine(console, string.Format(provider, format, args));
}
}
}

View File

@ -8,47 +8,34 @@ namespace Spectre.Console
public static partial class AnsiConsoleExtensions
{
/// <summary>
/// Resets colors and text decorations.
/// Writes an empty line to the console.
/// </summary>
/// <param name="console">The console to reset.</param>
public static void Reset(this IAnsiConsole console)
/// <param name="console">The console to write to.</param>
public static void WriteLine(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.ResetColors();
console.ResetDecoration();
console.Write(Environment.NewLine, Style.Plain);
}
/// <summary>
/// Resets the current applied text decorations.
/// Writes the specified string value, followed by the current line terminator, to the console.
/// </summary>
/// <param name="console">The console to reset the text decorations for.</param>
public static void ResetDecoration(this IAnsiConsole console)
/// <param name="console">The console to write to.</param>
/// <param name="text">The text to write.</param>
/// <param name="style">The text style.</param>
public static void WriteLine(this IAnsiConsole console, string text, Style style)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Decoration = Decoration.None;
}
/// <summary>
/// Resets the current applied foreground and background colors.
/// </summary>
/// <param name="console">The console to reset colors for.</param>
public static void ResetColors(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Foreground = Color.Default;
console.Background = Color.Default;
console.Write(text, style);
console.WriteLine();
}
}
}

View File

@ -66,5 +66,26 @@ namespace Spectre.Console
background: style.Background,
decoration: decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified link.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="link">The link.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithLink(this Style style, string link)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: style.Background,
decoration: style.Decoration,
link: link);
}
}
}

View File

@ -27,25 +27,11 @@ namespace Spectre.Console
/// </summary>
Encoding Encoding { get; }
/// <summary>
/// Gets or sets the current text decoration.
/// </summary>
Decoration Decoration { get; set; }
/// <summary>
/// Gets or sets the current foreground.
/// </summary>
Color Foreground { get; set; }
/// <summary>
/// Gets or sets the current background.
/// </summary>
Color Background { get; set; }
/// <summary>
/// Writes a string followed by a line terminator to the console.
/// </summary>
/// <param name="text">The string to write.</param>
void Write(string text);
/// <param name="style">The style to use.</param>
void Write(string text, Style style);
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Linq;
namespace Spectre.Console.Internal
@ -5,40 +6,59 @@ namespace Spectre.Console.Internal
internal static class AnsiBuilder
{
public static string GetAnsi(
ColorSystem system,
Capabilities capabilities,
string text,
Decoration decoration,
Color foreground,
Color background)
Color background,
string? link)
{
var codes = AnsiDecorationBuilder.GetAnsiCodes(decoration);
// Got foreground?
if (foreground != Color.Default)
{
codes = codes.Concat(AnsiColorBuilder.GetAnsiCodes(system, foreground, foreground: true));
codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes(
capabilities.ColorSystem,
foreground,
true));
}
// Got background?
if (background != Color.Default)
{
codes = codes.Concat(AnsiColorBuilder.GetAnsiCodes(system, background, foreground: false));
codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes(
capabilities.ColorSystem,
background,
false));
}
var result = codes.ToArray();
if (result.Length == 0)
if (result.Length == 0 && link == null)
{
return text;
}
var lol = string.Concat(
"\u001b[",
string.Join(";", result),
"m",
text,
"\u001b[0m");
var ansiCodes = string.Join(";", result);
var ansi = result.Length > 0
? $"\u001b[{ansiCodes}m{text}\u001b[0m"
: text;
return lol;
if (link != null && !capabilities.LegacyConsole)
{
// Empty links means we should take the URL from the text.
if (link.Equals(Constants.EmptyLink, StringComparison.Ordinal))
{
link = text;
}
var linkId = Math.Abs(link.GetDeterministicHashCode());
ansi = $"\u001b]8;id={linkId};{link}\u001b\\{ansi}\u001b]8;;\u001b\\";
}
return ansi;
}
}
}

View File

@ -7,13 +7,9 @@ namespace Spectre.Console.Internal
internal sealed class AnsiConsoleRenderer : IAnsiConsole
{
private readonly TextWriter _out;
private readonly ColorSystem _system;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public Decoration Decoration { get; set; }
public Color Foreground { get; set; }
public Color Background { get; set; }
public int Width
{
@ -44,28 +40,26 @@ namespace Spectre.Console.Internal
public AnsiConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole)
{
_out = @out ?? throw new ArgumentNullException(nameof(@out));
_system = system;
Capabilities = new Capabilities(true, system, legacyConsole);
Encoding = @out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8;
Foreground = Color.Default;
Background = Color.Default;
Decoration = Decoration.None;
}
public void Write(string text)
public void Write(string text, Style style)
{
if (string.IsNullOrEmpty(text))
{
return;
}
style ??= Style.Plain;
var parts = text.NormalizeLineEndings().Split(new[] { '\n' });
foreach (var (_, _, last, part) in parts.Enumerate())
{
if (!string.IsNullOrEmpty(part))
{
_out.Write(AnsiBuilder.GetAnsi(_system, part, Decoration, Foreground, Background));
_out.Write(AnsiBuilder.GetAnsi(Capabilities, part, style.Decoration, style.Foreground, style.Background, style.Link));
}
if (!last)

View File

@ -56,10 +56,7 @@ namespace Spectre.Console.Internal
if (supportsAnsi)
{
return new AnsiConsoleRenderer(buffer, colorSystem, legacyConsole)
{
Decoration = Decoration.None,
};
return new AnsiConsoleRenderer(buffer, colorSystem, legacyConsole);
}
return new FallbackConsoleRenderer(buffer, colorSystem, legacyConsole);

View File

@ -4,5 +4,7 @@ namespace Spectre.Console.Internal
{
public const int DefaultBufferWidth = 80;
public const int DefaultBufferHeight = 9001;
public const string EmptyLink = "https://emptylink";
}
}

View File

@ -1,147 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Internal
{
internal static class AnsiConsoleExtensions
{
public static IDisposable PushStyle(this IAnsiConsole console, Style style)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
var current = new Style(console.Foreground, console.Background, console.Decoration);
console.SetColor(style.Foreground, true);
console.SetColor(style.Background, false);
console.Decoration = style.Decoration;
return new StyleScope(console, current);
}
public static IDisposable PushColor(this IAnsiConsole console, Color color, bool foreground)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
var current = foreground ? console.Foreground : console.Background;
console.SetColor(color, foreground);
return new ColorScope(console, current, foreground);
}
public static IDisposable PushDecoration(this IAnsiConsole console, Decoration decoration)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
var current = console.Decoration;
console.Decoration = decoration;
return new DecorationScope(console, current);
}
public static void SetColor(this IAnsiConsole console, Color color, bool foreground)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
if (foreground)
{
console.Foreground = color;
}
else
{
console.Background = color;
}
}
}
internal sealed class StyleScope : IDisposable
{
private readonly IAnsiConsole _console;
private readonly Style _style;
public StyleScope(IAnsiConsole console, Style style)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
_style = style ?? throw new ArgumentNullException(nameof(style));
}
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
~StyleScope()
{
throw new InvalidOperationException("Style scope was not disposed.");
}
public void Dispose()
{
GC.SuppressFinalize(this);
_console.SetColor(_style.Foreground, true);
_console.SetColor(_style.Background, false);
_console.Decoration = _style.Decoration;
}
}
internal sealed class ColorScope : IDisposable
{
private readonly IAnsiConsole _console;
private readonly Color _color;
private readonly bool _foreground;
public ColorScope(IAnsiConsole console, Color color, bool foreground)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
_color = color;
_foreground = foreground;
}
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
~ColorScope()
{
throw new InvalidOperationException("Color scope was not disposed.");
}
public void Dispose()
{
GC.SuppressFinalize(this);
_console.SetColor(_color, _foreground);
}
}
internal sealed class DecorationScope : IDisposable
{
private readonly IAnsiConsole _console;
private readonly Decoration _decoration;
public DecorationScope(IAnsiConsole console, Decoration decoration)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
_decoration = decoration;
}
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
~DecorationScope()
{
throw new InvalidOperationException("Decoration scope was not disposed.");
}
public void Dispose()
{
GC.SuppressFinalize(this);
_console.Decoration = _decoration;
}
}
}

View File

@ -78,5 +78,28 @@ namespace Spectre.Console.Internal
return result.ToArray();
}
// https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/
public static int GetDeterministicHashCode(this string str)
{
unchecked
{
var hash1 = (5381 << 16) + 5381;
var hash2 = hash1;
for (var i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
{
break;
}
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return hash1 + (hash2 * 1566083941);
}
}
}
}

View File

@ -1,4 +1,3 @@
using System;
using System.IO;
using System.Text;
@ -6,16 +5,11 @@ namespace Spectre.Console.Internal
{
internal sealed class FallbackConsoleRenderer : IAnsiConsole
{
private readonly ConsoleColor _defaultForeground;
private readonly ConsoleColor _defaultBackground;
private readonly TextWriter _out;
private readonly ColorSystem _system;
private ConsoleColor _foreground;
private ConsoleColor _background;
private Style? _lastStyle;
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public int Width
@ -44,47 +38,6 @@ namespace Spectre.Console.Internal
}
}
public Decoration Decoration { get; set; }
public Color Foreground
{
get => _foreground;
set
{
_foreground = Color.ToConsoleColor(value);
if (_system != ColorSystem.NoColors && _out.IsStandardOut())
{
if ((int)_foreground == -1)
{
_foreground = _defaultForeground;
}
System.Console.ForegroundColor = _foreground;
}
}
}
public Color Background
{
get => _background;
set
{
_background = Color.ToConsoleColor(value);
if (_system != ColorSystem.NoColors && _out.IsStandardOut())
{
if ((int)_background == -1)
{
_background = _defaultBackground;
}
if (_system != ColorSystem.NoColors)
{
System.Console.BackgroundColor = _background;
}
}
}
}
public FallbackConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole)
{
_out = @out;
@ -92,25 +45,46 @@ namespace Spectre.Console.Internal
if (_out.IsStandardOut())
{
_defaultForeground = System.Console.ForegroundColor;
_defaultBackground = System.Console.BackgroundColor;
Encoding = System.Console.OutputEncoding;
}
else
{
_defaultForeground = ConsoleColor.Gray;
_defaultBackground = ConsoleColor.Black;
Encoding = Encoding.UTF8;
}
Capabilities = new Capabilities(false, _system, legacyConsole);
}
public void Write(string text)
public void Write(string text, Style style)
{
if (_lastStyle?.Equals(style) != true)
{
SetStyle(style);
}
_out.Write(text.NormalizeLineEndings(native: true));
}
private void SetStyle(Style style)
{
_lastStyle = style;
if (_out.IsStandardOut())
{
System.Console.ResetColor();
var background = Color.ToConsoleColor(style.Background);
if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)background != -1)
{
System.Console.BackgroundColor = background;
}
var foreground = Color.ToConsoleColor(style.Foreground);
if (_system != ColorSystem.NoColors && _out.IsStandardOut() && (int)foreground != -1)
{
System.Console.ForegroundColor = foreground;
}
}
}
}
}

View File

@ -35,6 +35,7 @@ namespace Spectre.Console.Internal
var effectiveDecoration = (Decoration?)null;
var effectiveForeground = (Color?)null;
var effectiveBackground = (Color?)null;
var effectiveLink = (string?)null;
var parts = text.Split(new[] { ' ' });
var foreground = true;
@ -51,6 +52,23 @@ namespace Spectre.Console.Internal
continue;
}
if (part.StartsWith("link=", StringComparison.OrdinalIgnoreCase))
{
if (effectiveLink != null)
{
error = "A link has already been set.";
return null;
}
effectiveLink = part.Substring(5);
continue;
}
else if (part.StartsWith("link", StringComparison.OrdinalIgnoreCase))
{
effectiveLink = Constants.EmptyLink;
continue;
}
var decoration = DecorationTable.GetDecoration(part);
if (decoration != null)
{
@ -116,7 +134,11 @@ namespace Spectre.Console.Internal
}
error = null;
return new Style(effectiveForeground, effectiveBackground, effectiveDecoration);
return new Style(
effectiveForeground,
effectiveBackground,
effectiveDecoration,
effectiveLink);
}
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]

View File

@ -41,12 +41,6 @@
<Compile Update="Extensions\AnsiConsoleExtensions.Rendering.cs">
<DependentUpon>AnsiConsoleExtensions.cs</DependentUpon>
</Compile>
<Compile Update="Extensions\AnsiConsoleExtensions.Write.cs">
<DependentUpon>AnsiConsoleExtensions.cs</DependentUpon>
</Compile>
<Compile Update="Extensions\AnsiConsoleExtensions.WriteLine.cs">
<DependentUpon>AnsiConsoleExtensions.cs</DependentUpon>
</Compile>
</ItemGroup>
<PropertyGroup>
<AnnotatedReferenceAssemblyVersion>3.0.0</AnnotatedReferenceAssemblyVersion>

View File

@ -24,6 +24,11 @@ namespace Spectre.Console
/// </summary>
public Decoration Decoration { get; }
/// <summary>
/// Gets the link.
/// </summary>
public string? Link { get; }
/// <summary>
/// Gets an <see cref="Style"/> with the
/// default colors and without text decoration.
@ -31,7 +36,7 @@ namespace Spectre.Console
public static Style Plain { get; } = new Style();
private Style()
: this(null, null, null)
: this(null, null, null, null)
{
}
@ -41,11 +46,13 @@ namespace Spectre.Console
/// <param name="foreground">The foreground color.</param>
/// <param name="background">The background color.</param>
/// <param name="decoration">The text decoration.</param>
public Style(Color? foreground = null, Color? background = null, Decoration? decoration = null)
/// <param name="link">The link.</param>
public Style(Color? foreground = null, Color? background = null, Decoration? decoration = null, string? link = null)
{
Foreground = foreground ?? Color.Default;
Background = background ?? Color.Default;
Decoration = decoration ?? Decoration.None;
Link = link;
}
/// <summary>
@ -78,13 +85,23 @@ namespace Spectre.Console
return new Style(decoration: decoration);
}
/// <summary>
/// Creates a new style from the specified link.
/// </summary>
/// <param name="link">The link.</param>
/// <returns>A new <see cref="Style"/> with the specified link.</returns>
public static Style WithLink(string link)
{
return new Style(link: link);
}
/// <summary>
/// Creates a copy of the current <see cref="Style"/>.
/// </summary>
/// <returns>A copy of the current <see cref="Style"/>.</returns>
public Style Clone()
{
return new Style(Foreground, Background, Decoration);
return new Style(Foreground, Background, Decoration, Link);
}
/// <summary>
@ -111,7 +128,13 @@ namespace Spectre.Console
background = other.Background;
}
return new Style(foreground, background, Decoration | other.Decoration);
var link = Link;
if (!string.IsNullOrWhiteSpace(other.Link))
{
link = other.Link;
}
return new Style(foreground, background, Decoration | other.Decoration, link);
}
/// <summary>
@ -158,6 +181,12 @@ namespace Spectre.Console
hash = (hash * 16777619) ^ Foreground.GetHashCode();
hash = (hash * 16777619) ^ Background.GetHashCode();
hash = (hash * 16777619) ^ Decoration.GetHashCode();
if (Link != null)
{
hash = (hash * 16777619) ^ Link?.GetHashCode() ?? 0;
}
return hash;
}
}
@ -178,7 +207,8 @@ namespace Spectre.Console
return Foreground.Equals(other.Foreground) &&
Background.Equals(other.Background) &&
Decoration == other.Decoration;
Decoration == other.Decoration &&
string.Equals(Link, other.Link, StringComparison.Ordinal);
}
}
}