Change IAnsiConsole to render IRenderable

This makes it possible for encoders to output better representation
of the actual objects instead of working with chopped up segments.

* IAnsiConsole.Write now takes an IRenderable instead of segments
* Calculating cell width does no longer require a render context
* Removed RenderContext.LegacyConsole
* Removed RenderContext.Encoding
* Added Capabilities.Unicode
This commit is contained in:
Patrik Svensson
2021-03-24 23:09:24 +01:00
committed by Phil Scott
parent 2ba6da3514
commit 20650f1e7e
75 changed files with 492 additions and 553 deletions

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Rendering;
@ -8,21 +7,21 @@ namespace Spectre.Console
internal sealed class AnsiConsoleBackend : IAnsiConsoleBackend
{
private readonly AnsiBuilder _builder;
private readonly Profile _profile;
private readonly IAnsiConsole _console;
public IAnsiConsoleCursor Cursor { get; }
public AnsiConsoleBackend(Profile profile)
public AnsiConsoleBackend(IAnsiConsole console)
{
_profile = profile ?? throw new ArgumentNullException(nameof(profile));
_builder = new AnsiBuilder(profile);
_console = console ?? throw new ArgumentNullException(nameof(console));
_builder = new AnsiBuilder(_console.Profile);
Cursor = new AnsiConsoleCursor(this);
}
public void Clear(bool home)
{
Render(new[] { Segment.Control("\u001b[2J") });
Write(new ControlSequence("\u001b[2J"));
if (home)
{
@ -30,10 +29,10 @@ namespace Spectre.Console
}
}
public void Render(IEnumerable<Segment> segments)
public void Write(IRenderable renderable)
{
var builder = new StringBuilder();
foreach (var segment in segments)
foreach (var segment in renderable.GetSegments(_console))
{
if (segment.IsControlCode)
{
@ -58,8 +57,8 @@ namespace Spectre.Console
if (builder.Length > 0)
{
_profile.Out.Write(builder.ToString());
_profile.Out.Flush();
_console.Profile.Out.Write(builder.ToString());
_console.Profile.Out.Flush();
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
@ -16,11 +15,11 @@ namespace Spectre.Console
{
if (show)
{
_backend.Render(new[] { Segment.Control("\u001b[?25h") });
_backend.Write(new ControlSequence("\u001b[?25h"));
}
else
{
_backend.Render(new[] { Segment.Control("\u001b[?25l") });
_backend.Write(new ControlSequence("\u001b[?25l"));
}
}
@ -34,23 +33,23 @@ namespace Spectre.Console
switch (direction)
{
case CursorDirection.Up:
_backend.Render(new[] { Segment.Control($"\u001b[{steps}A") });
_backend.Write(new ControlSequence($"\u001b[{steps}A"));
break;
case CursorDirection.Down:
_backend.Render(new[] { Segment.Control($"\u001b[{steps}B") });
_backend.Write(new ControlSequence($"\u001b[{steps}B"));
break;
case CursorDirection.Right:
_backend.Render(new[] { Segment.Control($"\u001b[{steps}C") });
_backend.Write(new ControlSequence($"\u001b[{steps}C"));
break;
case CursorDirection.Left:
_backend.Render(new[] { Segment.Control($"\u001b[{steps}D") });
_backend.Write(new ControlSequence($"\u001b[{steps}D"));
break;
}
}
public void SetPosition(int column, int line)
{
_backend.Render(new[] { Segment.Control($"\u001b[{line};{column}H") });
_backend.Write(new ControlSequence($"\u001b[{line};{column}H"));
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console
@ -19,13 +18,14 @@ namespace Spectre.Console
public AnsiConsoleFacade(Profile profile, IExclusivityMode exclusivityMode)
{
_renderLock = new object();
_ansiBackend = new AnsiConsoleBackend(profile);
_legacyBackend = new LegacyConsoleBackend(profile);
Profile = profile ?? throw new ArgumentNullException(nameof(profile));
Input = new DefaultInput(Profile);
ExclusivityMode = exclusivityMode ?? throw new ArgumentNullException(nameof(exclusivityMode));
Pipeline = new RenderPipeline();
_ansiBackend = new AnsiConsoleBackend(this);
_legacyBackend = new LegacyConsoleBackend(this);
}
public void Clear(bool home)
@ -36,11 +36,11 @@ namespace Spectre.Console
}
}
public void Write(IEnumerable<Segment> segments)
public void Write(IRenderable renderable)
{
lock (_renderLock)
{
GetBackend().Render(segments);
GetBackend().Write(renderable);
}
}

View File

@ -1,4 +1,3 @@
using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console
@ -20,9 +19,9 @@ namespace Spectre.Console
void Clear(bool home);
/// <summary>
/// Renders segments to the console.
/// Writes a <see cref="IRenderable"/> to the console backend.
/// </summary>
/// <param name="segments">The segments to render.</param>
void Render(IEnumerable<Segment> segments);
/// <param name="renderable">The <see cref="IRenderable"/> to write.</param>
void Write(IRenderable renderable);
}
}

View File

@ -1,18 +1,17 @@
using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
internal sealed class LegacyConsoleBackend : IAnsiConsoleBackend
{
private readonly Profile _profile;
private readonly IAnsiConsole _console;
private Style _lastStyle;
public IAnsiConsoleCursor Cursor { get; }
public LegacyConsoleBackend(Profile profile)
public LegacyConsoleBackend(IAnsiConsole console)
{
_profile = profile ?? throw new System.ArgumentNullException(nameof(profile));
_console = console ?? throw new System.ArgumentNullException(nameof(console));
_lastStyle = Style.Plain;
Cursor = new LegacyConsoleCursor();
@ -31,9 +30,9 @@ namespace Spectre.Console
}
}
public void Render(IEnumerable<Segment> segments)
public void Write(IRenderable renderable)
{
foreach (var segment in segments)
foreach (var segment in renderable.GetSegments(_console))
{
if (segment.IsControlCode)
{
@ -45,7 +44,7 @@ namespace Spectre.Console
SetStyle(segment.Style);
}
_profile.Out.Write(segment.Text.NormalizeNewLines(native: true));
_console.Profile.Out.Write(segment.Text.NormalizeNewLines(native: true));
}
}
@ -56,13 +55,13 @@ namespace Spectre.Console
System.Console.ResetColor();
var background = Color.ToConsoleColor(style.Background);
if (_profile.ColorSystem != ColorSystem.NoColors && (int)background != -1)
if (_console.Profile.ColorSystem != ColorSystem.NoColors && (int)background != -1)
{
System.Console.BackgroundColor = background;
}
var foreground = Color.ToConsoleColor(style.Foreground);
if (_profile.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
if (_console.Profile.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
{
System.Console.ForegroundColor = foreground;
}