mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 13:28:16 +08:00
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:

committed by
Phil Scott

parent
2ba6da3514
commit
20650f1e7e
@ -9,10 +9,11 @@ namespace Spectre.Console.Rendering
|
||||
public interface IAnsiConsoleEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Encodes the specified segments.
|
||||
/// Encodes the specified <see cref="IRenderable"/> enumerator.
|
||||
/// </summary>
|
||||
/// <param name="segments">The segments to encode.</param>
|
||||
/// <returns>The encoded string.</returns>
|
||||
string Encode(IEnumerable<Segment> segments);
|
||||
/// <param name="console">The console to use when encoding.</param>
|
||||
/// <param name="renderable">The renderable objects to encode.</param>
|
||||
/// <returns>A string representing the encoded result.</returns>
|
||||
string Encode(IAnsiConsole console, IEnumerable<IRenderable> renderable);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ namespace Spectre.Console.Rendering
|
||||
if (_renderable != null)
|
||||
{
|
||||
var segments = _renderable.Render(context, maxWidth);
|
||||
var lines = Segment.SplitLines(context, segments);
|
||||
var lines = Segment.SplitLines(segments);
|
||||
|
||||
var shape = SegmentShape.Calculate(context, lines);
|
||||
_shape = _shape == null ? shape : _shape.Value.Inflate(shape);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
@ -7,20 +7,12 @@ namespace Spectre.Console.Rendering
|
||||
/// </summary>
|
||||
public sealed class RenderContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the console's output encoding.
|
||||
/// </summary>
|
||||
public Encoding Encoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this a legacy console (i.e. cmd.exe).
|
||||
/// </summary>
|
||||
public bool LegacyConsole { get; }
|
||||
private readonly IReadOnlyCapabilities _capabilities;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not unicode is supported.
|
||||
/// </summary>
|
||||
public bool Unicode { get; }
|
||||
public bool Unicode => _capabilities.Unicode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current justification.
|
||||
@ -36,20 +28,18 @@ namespace Spectre.Console.Rendering
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="encoding">The console's output encoding.</param>
|
||||
/// <param name="legacyConsole">A value indicating whether or not this a legacy console (i.e. cmd.exe).</param>
|
||||
/// <param name="justification">The justification to use when rendering.</param>
|
||||
public RenderContext(Encoding encoding, bool legacyConsole, Justify? justification = null)
|
||||
: this(encoding, legacyConsole, justification, false)
|
||||
/// <param name="capabilities">The capabilities.</param>
|
||||
/// <param name="justification">The justification.</param>
|
||||
public RenderContext(IReadOnlyCapabilities capabilities, Justify? justification = null)
|
||||
: this(capabilities, justification, false)
|
||||
{
|
||||
}
|
||||
|
||||
private RenderContext(Encoding encoding, bool legacyConsole, Justify? justification = null, bool singleLine = false)
|
||||
private RenderContext(IReadOnlyCapabilities capabilities, Justify? justification = null, bool singleLine = false)
|
||||
{
|
||||
Encoding = encoding ?? throw new System.ArgumentNullException(nameof(encoding));
|
||||
LegacyConsole = legacyConsole;
|
||||
_capabilities = capabilities ?? throw new ArgumentNullException(nameof(capabilities));
|
||||
|
||||
Justification = justification;
|
||||
Unicode = Encoding.EncodingName.ContainsExact("Unicode");
|
||||
SingleLine = singleLine;
|
||||
}
|
||||
|
||||
@ -60,7 +50,7 @@ namespace Spectre.Console.Rendering
|
||||
/// <returns>A new <see cref="RenderContext"/> instance.</returns>
|
||||
public RenderContext WithJustification(Justify? justification)
|
||||
{
|
||||
return new RenderContext(Encoding, LegacyConsole, justification);
|
||||
return new RenderContext(_capabilities, justification, SingleLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -75,7 +65,7 @@ namespace Spectre.Console.Rendering
|
||||
/// <returns>A new <see cref="RenderContext"/> instance.</returns>
|
||||
internal RenderContext WithSingleLine()
|
||||
{
|
||||
return new RenderContext(Encoding, LegacyConsole, Justification, true);
|
||||
return new RenderContext(_capabilities, Justification, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,36 +100,24 @@ namespace Spectre.Console.Rendering
|
||||
/// Gets the number of cells that this segment
|
||||
/// occupies in the console.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <returns>The number of cells that this segment occupies in the console.</returns>
|
||||
public int CellCount(RenderContext context)
|
||||
public int CellCount()
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (IsControlCode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Text.CellLength(context);
|
||||
return Cell.GetCellLength(Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of cells that the segments occupies in the console.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="segments">The segments to measure.</param>
|
||||
/// <returns>The number of cells that the segments occupies in the console.</returns>
|
||||
public static int CellCount(RenderContext context, IEnumerable<Segment> segments)
|
||||
public static int CellCount(IEnumerable<Segment> segments)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segments is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segments));
|
||||
@ -138,7 +126,7 @@ namespace Spectre.Console.Rendering
|
||||
var sum = 0;
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
sum += segment.CellCount(context);
|
||||
sum += segment.CellCount();
|
||||
}
|
||||
|
||||
return sum;
|
||||
@ -158,7 +146,6 @@ namespace Spectre.Console.Rendering
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset where to split the segment.</param>
|
||||
/// <returns>One or two new segments representing the split.</returns>
|
||||
[Obsolete("Use Split(RenderContext, Int32) instead")]
|
||||
public (Segment First, Segment? Second) Split(int offset)
|
||||
{
|
||||
if (offset < 0)
|
||||
@ -166,30 +153,7 @@ namespace Spectre.Console.Rendering
|
||||
return (this, null);
|
||||
}
|
||||
|
||||
if (offset >= Text.Length)
|
||||
{
|
||||
return (this, null);
|
||||
}
|
||||
|
||||
return (
|
||||
new Segment(Text.Substring(0, offset), Style),
|
||||
new Segment(Text.Substring(offset, Text.Length - offset), Style));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the segment at the offset.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="offset">The offset where to split the segment.</param>
|
||||
/// <returns>One or two new segments representing the split.</returns>
|
||||
public (Segment First, Segment? Second) Split(RenderContext context, int offset)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
return (this, null);
|
||||
}
|
||||
|
||||
if (offset >= CellCount(context))
|
||||
if (offset >= CellCount())
|
||||
{
|
||||
return (this, null);
|
||||
}
|
||||
@ -201,7 +165,7 @@ namespace Spectre.Console.Rendering
|
||||
foreach (var character in Text)
|
||||
{
|
||||
index++;
|
||||
accumulated += Cell.GetCellLength(context, character);
|
||||
accumulated += Cell.GetCellLength(character);
|
||||
if (accumulated >= offset)
|
||||
{
|
||||
break;
|
||||
@ -226,38 +190,26 @@ namespace Spectre.Console.Rendering
|
||||
/// <summary>
|
||||
/// Splits the provided segments into lines.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="segments">The segments to split.</param>
|
||||
/// <returns>A collection of lines.</returns>
|
||||
public static List<SegmentLine> SplitLines(RenderContext context, IEnumerable<Segment> segments)
|
||||
public static List<SegmentLine> SplitLines(IEnumerable<Segment> segments)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segments is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segments));
|
||||
}
|
||||
|
||||
return SplitLines(context, segments, int.MaxValue);
|
||||
return SplitLines(segments, int.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the provided segments into lines with a maximum width.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="segments">The segments to split into lines.</param>
|
||||
/// <param name="maxWidth">The maximum width.</param>
|
||||
/// <returns>A list of lines.</returns>
|
||||
public static List<SegmentLine> SplitLines(RenderContext context, IEnumerable<Segment> segments, int maxWidth)
|
||||
public static List<SegmentLine> SplitLines(IEnumerable<Segment> segments, int maxWidth)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segments is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segments));
|
||||
@ -271,16 +223,16 @@ namespace Spectre.Console.Rendering
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var segment = stack.Pop();
|
||||
var segmentLength = segment.CellCount(context);
|
||||
var segmentLength = segment.CellCount();
|
||||
|
||||
// Does this segment make the line exceed the max width?
|
||||
var lineLength = line.CellCount(context);
|
||||
var lineLength = line.CellCount();
|
||||
if (lineLength + segmentLength > maxWidth)
|
||||
{
|
||||
var diff = -(maxWidth - (lineLength + segmentLength));
|
||||
var offset = segment.Text.Length - diff;
|
||||
|
||||
var (first, second) = segment.Split(context, offset);
|
||||
var (first, second) = segment.Split(offset);
|
||||
|
||||
line.Add(first);
|
||||
lines.Add(line);
|
||||
@ -356,22 +308,16 @@ namespace Spectre.Console.Rendering
|
||||
/// </summary>
|
||||
/// <param name="segment">The segment to split.</param>
|
||||
/// <param name="overflow">The overflow strategy to use.</param>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="maxWidth">The maximum width.</param>
|
||||
/// <returns>A list of segments that has been split.</returns>
|
||||
public static List<Segment> SplitOverflow(Segment segment, Overflow? overflow, RenderContext context, int maxWidth)
|
||||
public static List<Segment> SplitOverflow(Segment segment, Overflow? overflow, int maxWidth)
|
||||
{
|
||||
if (segment is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segment));
|
||||
}
|
||||
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segment.CellCount(context) <= maxWidth)
|
||||
if (segment.CellCount() <= maxWidth)
|
||||
{
|
||||
return new List<Segment>(1) { segment };
|
||||
}
|
||||
@ -383,7 +329,7 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
if (overflow == Overflow.Fold)
|
||||
{
|
||||
var totalLength = segment.Text.CellLength(context);
|
||||
var totalLength = segment.Text.CellLength();
|
||||
var lengthLeft = totalLength;
|
||||
while (lengthLeft > 0)
|
||||
{
|
||||
@ -431,17 +377,11 @@ namespace Spectre.Console.Rendering
|
||||
/// <summary>
|
||||
/// Truncates the segments to the specified width.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="segments">The segments to truncate.</param>
|
||||
/// <param name="maxWidth">The maximum width that the segments may occupy.</param>
|
||||
/// <returns>A list of segments that has been truncated.</returns>
|
||||
public static List<Segment> Truncate(RenderContext context, IEnumerable<Segment> segments, int maxWidth)
|
||||
public static List<Segment> Truncate(IEnumerable<Segment> segments, int maxWidth)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segments is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segments));
|
||||
@ -452,7 +392,7 @@ namespace Spectre.Console.Rendering
|
||||
var totalWidth = 0;
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
var segmentCellWidth = segment.CellCount(context);
|
||||
var segmentCellWidth = segment.CellCount();
|
||||
if (totalWidth + segmentCellWidth > maxWidth)
|
||||
{
|
||||
break;
|
||||
@ -464,7 +404,7 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
if (result.Count == 0 && segments.Any())
|
||||
{
|
||||
var segment = Truncate(context, segments.First(), maxWidth);
|
||||
var segment = Truncate(segments.First(), maxWidth);
|
||||
if (segment != null)
|
||||
{
|
||||
result.Add(segment);
|
||||
@ -477,23 +417,17 @@ namespace Spectre.Console.Rendering
|
||||
/// <summary>
|
||||
/// Truncates the segment to the specified width.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="segment">The segment to truncate.</param>
|
||||
/// <param name="maxWidth">The maximum width that the segment may occupy.</param>
|
||||
/// <returns>A new truncated segment, or <c>null</c>.</returns>
|
||||
public static Segment? Truncate(RenderContext context, Segment? segment, int maxWidth)
|
||||
public static Segment? Truncate(Segment? segment, int maxWidth)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (segment is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (segment.CellCount(context) <= maxWidth)
|
||||
if (segment.CellCount() <= maxWidth)
|
||||
{
|
||||
return segment;
|
||||
}
|
||||
@ -501,7 +435,7 @@ namespace Spectre.Console.Rendering
|
||||
var builder = new StringBuilder();
|
||||
foreach (var character in segment.Text)
|
||||
{
|
||||
var accumulatedCellWidth = builder.ToString().CellLength(context);
|
||||
var accumulatedCellWidth = builder.ToString().CellLength();
|
||||
if (accumulatedCellWidth >= maxWidth)
|
||||
{
|
||||
break;
|
||||
@ -562,24 +496,19 @@ namespace Spectre.Console.Rendering
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static List<Segment> TruncateWithEllipsis(IEnumerable<Segment> segments, RenderContext context, int maxWidth)
|
||||
internal static List<Segment> TruncateWithEllipsis(IEnumerable<Segment> segments, int maxWidth)
|
||||
{
|
||||
if (segments is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(segments));
|
||||
}
|
||||
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (CellCount(context, segments) <= maxWidth)
|
||||
if (CellCount(segments) <= maxWidth)
|
||||
{
|
||||
return new List<Segment>(segments);
|
||||
}
|
||||
|
||||
segments = TrimEnd(Truncate(context, segments, maxWidth - 1));
|
||||
segments = TrimEnd(Truncate(segments, maxWidth - 1));
|
||||
if (!segments.Any())
|
||||
{
|
||||
return new List<Segment>(1);
|
||||
|
@ -16,16 +16,10 @@ namespace Spectre.Console.Rendering
|
||||
/// <summary>
|
||||
/// Gets the number of cells the segment line occupies.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <returns>The cell width of the segment line.</returns>
|
||||
public int CellCount(RenderContext context)
|
||||
public int CellCount()
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
return Segment.CellCount(context, this);
|
||||
return Segment.CellCount(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -28,7 +28,7 @@ namespace Spectre.Console.Rendering
|
||||
}
|
||||
|
||||
var height = lines.Count;
|
||||
var width = lines.Max(l => Segment.CellCount(context, l));
|
||||
var width = lines.Max(l => Segment.CellCount(l));
|
||||
|
||||
return new SegmentShape(width, height);
|
||||
}
|
||||
@ -44,7 +44,7 @@ namespace Spectre.Console.Rendering
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var length = Segment.CellCount(context, line);
|
||||
var length = Segment.CellCount(line);
|
||||
var missing = Width - length;
|
||||
if (missing > 0)
|
||||
{
|
||||
|
Reference in New Issue
Block a user