diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs
index 3d23c64..b086bfb 100644
--- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs
+++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Advanced.cs
@@ -5,7 +5,7 @@ using Xunit;
namespace Spectre.Console.Tests.Unit
{
- public partial class AnsiConsoleTests
+ public sealed partial class AnsiConsoleTests
{
public sealed class Advanced
{
@@ -42,6 +42,20 @@ namespace Spectre.Console.Tests.Unit
console.Output.NormalizeLineEndings()
.ShouldBeEmpty();
}
+
+ [Fact]
+ public void Should_Return_Ansi_For_Renderable()
+ {
+ // Given
+ var console = new TestConsole().Colors(ColorSystem.TrueColor);
+ var markup = new Console.Markup("[yellow]Hello [blue]World[/]![/]");
+
+ // When
+ var result = console.ToAnsi(markup);
+
+ // Then
+ result.ShouldBe("[38;5;11mHello [0m[38;5;12mWorld[0m[38;5;11m![0m");
+ }
}
}
}
diff --git a/src/Spectre.Console/Extensions/Advanced/AnsiConsoleExtensions.cs b/src/Spectre.Console/Extensions/Advanced/AnsiConsoleExtensions.cs
index cf54473..1962088 100644
--- a/src/Spectre.Console/Extensions/Advanced/AnsiConsoleExtensions.cs
+++ b/src/Spectre.Console/Extensions/Advanced/AnsiConsoleExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using Spectre.Console.Rendering;
namespace Spectre.Console.Advanced
{
@@ -24,5 +25,16 @@ namespace Spectre.Console.Advanced
console.Write(new ControlCode(sequence));
}
}
+
+ ///
+ /// Gets the VT/ANSI control code sequence for a .
+ ///
+ /// The console.
+ /// The renderable to the VT/ANSI control code sequence for.
+ /// The VT/ANSI control code sequence.
+ public static string ToAnsi(this IAnsiConsole console, IRenderable renderable)
+ {
+ return AnsiBuilder.Build(console, renderable);
+ }
}
}
diff --git a/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
index de64b7b..e30923a 100644
--- a/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
@@ -1,23 +1,60 @@
using System;
using System.Linq;
+using System.Text;
+using Spectre.Console.Rendering;
using static Spectre.Console.AnsiSequences;
namespace Spectre.Console
{
- internal sealed class AnsiBuilder
+ internal static class AnsiBuilder
{
- private readonly Profile _profile;
- private readonly AnsiLinkHasher _linkHasher;
+ private static readonly AnsiLinkHasher _linkHasher;
- public AnsiBuilder(Profile profile)
+ static AnsiBuilder()
{
- _profile = profile ?? throw new ArgumentNullException(nameof(profile));
_linkHasher = new AnsiLinkHasher();
}
- public string GetAnsi(string text, Style style)
+ public static string Build(IAnsiConsole console, IRenderable renderable)
{
- if (style is null)
+ var builder = new StringBuilder();
+ foreach (var segment in renderable.GetSegments(console))
+ {
+ if (segment.IsControlCode)
+ {
+ builder.Append(segment.Text);
+ continue;
+ }
+
+ var parts = segment.Text.NormalizeNewLines().Split(new[] { '\n' });
+ foreach (var (_, _, last, part) in parts.Enumerate())
+ {
+ if (!string.IsNullOrEmpty(part))
+ {
+ builder.Append(Build(console.Profile, part, segment.Style));
+ }
+
+ if (!last)
+ {
+ builder.Append(Environment.NewLine);
+ }
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ private static string Build(Profile profile, string text, Style style)
+ {
+ if (profile is null)
+ {
+ throw new ArgumentNullException(nameof(profile));
+ }
+ else if (text is null)
+ {
+ throw new ArgumentNullException(nameof(text));
+ }
+ else if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
@@ -29,7 +66,7 @@ namespace Spectre.Console
{
codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes(
- _profile.Capabilities.ColorSystem,
+ profile.Capabilities.ColorSystem,
style.Foreground,
true));
}
@@ -39,7 +76,7 @@ namespace Spectre.Console
{
codes = codes.Concat(
AnsiColorBuilder.GetAnsiCodes(
- _profile.Capabilities.ColorSystem,
+ profile.Capabilities.ColorSystem,
style.Background,
false));
}
@@ -54,7 +91,7 @@ namespace Spectre.Console
? $"{SGR(result)}{text}{SGR(0)}"
: text;
- if (style.Link != null && !_profile.Capabilities.Legacy)
+ if (style.Link != null && !profile.Capabilities.Legacy)
{
var link = style.Link;
diff --git a/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
index e278393..071db9a 100644
--- a/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
@@ -7,7 +7,6 @@ namespace Spectre.Console
{
internal sealed class AnsiConsoleBackend : IAnsiConsoleBackend
{
- private readonly AnsiBuilder _builder;
private readonly IAnsiConsole _console;
public IAnsiConsoleCursor Cursor { get; }
@@ -15,8 +14,6 @@ namespace Spectre.Console
public AnsiConsoleBackend(IAnsiConsole console)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
- _builder = new AnsiBuilder(_console.Profile);
-
Cursor = new AnsiConsoleCursor(this);
}
@@ -33,33 +30,10 @@ namespace Spectre.Console
public void Write(IRenderable renderable)
{
- var builder = new StringBuilder();
- foreach (var segment in renderable.GetSegments(_console))
+ var result = AnsiBuilder.Build(_console, renderable);
+ if (result?.Length > 0)
{
- if (segment.IsControlCode)
- {
- builder.Append(segment.Text);
- continue;
- }
-
- var parts = segment.Text.NormalizeNewLines().Split(new[] { '\n' });
- foreach (var (_, _, last, part) in parts.Enumerate())
- {
- if (!string.IsNullOrEmpty(part))
- {
- builder.Append(_builder.GetAnsi(part, segment.Style));
- }
-
- if (!last)
- {
- builder.Append(Environment.NewLine);
- }
- }
- }
-
- if (builder.Length > 0)
- {
- _console.Profile.Out.Writer.Write(builder.ToString());
+ _console.Profile.Out.Writer.Write(result);
_console.Profile.Out.Writer.Flush();
}
}