diff --git a/src/Spectre.Console.Tests/Unit/SegmentTests.cs b/src/Spectre.Console.Tests/Unit/SegmentTests.cs
index ca8141c..f3cf5f9 100644
--- a/src/Spectre.Console.Tests/Unit/SegmentTests.cs
+++ b/src/Spectre.Console.Tests/Unit/SegmentTests.cs
@@ -1,3 +1,4 @@
+using System.Text;
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
@@ -6,6 +7,16 @@ namespace Spectre.Console.Tests.Unit
{
public sealed class SegmentTests
{
+ [Fact]
+ public void Lol()
+ {
+ var context = new RenderContext(Encoding.UTF8, false);
+
+ var result = new Segment(" ").CellCount(context);
+
+ result.ShouldBe(4);
+ }
+
public sealed class TheSplitMethod
{
[Fact]
@@ -31,7 +42,10 @@ namespace Spectre.Console.Tests.Unit
[Fact]
public void Should_Split_Segment()
{
+ var context = new RenderContext(Encoding.UTF8, false);
+
var lines = Segment.SplitLines(
+ context,
new[]
{
new Segment("Foo"),
@@ -61,7 +75,9 @@ namespace Spectre.Console.Tests.Unit
[Fact]
public void Should_Split_Segments_With_Linebreak_In_Text()
{
+ var context = new RenderContext(Encoding.UTF8, false);
var lines = Segment.SplitLines(
+ context,
new[]
{
new Segment("Foo\n"),
diff --git a/src/Spectre.Console/Internal/Aligner.cs b/src/Spectre.Console/Internal/Aligner.cs
index f51e208..4ff8432 100644
--- a/src/Spectre.Console/Internal/Aligner.cs
+++ b/src/Spectre.Console/Internal/Aligner.cs
@@ -57,7 +57,7 @@ namespace Spectre.Console.Internal
return;
}
- var width = Segment.CellLength(context, segments);
+ var width = Segment.CellCount(context, segments);
if (width >= maxWidth)
{
return;
diff --git a/src/Spectre.Console/Internal/Extensions/StringExtensions.cs b/src/Spectre.Console/Internal/Extensions/StringExtensions.cs
index 7e22b9a..28e9b43 100644
--- a/src/Spectre.Console/Internal/Extensions/StringExtensions.cs
+++ b/src/Spectre.Console/Internal/Extensions/StringExtensions.cs
@@ -16,6 +16,11 @@ namespace Spectre.Console.Internal
public static int CellLength(this string text, RenderContext context)
{
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
return Cell.GetCellLength(context, text);
}
diff --git a/src/Spectre.Console/Internal/Text/Cell.cs b/src/Spectre.Console/Internal/Text/Cell.cs
index dad3a70..2f0d3d8 100644
--- a/src/Spectre.Console/Internal/Text/Cell.cs
+++ b/src/Spectre.Console/Internal/Text/Cell.cs
@@ -21,6 +21,17 @@ namespace Spectre.Console.Internal
}
}
+ // TODO: We need to figure out why Segment.SplitLines fails
+ // if we let wcwidth (which returns -1 instead of 1)
+ // calculate the size for new line characters.
+ // That is correct from a Unicode perspective, but the
+ // algorithm was written before wcwidth was added and used
+ // to work with string length and not cell length.
+ if (rune == '\n')
+ {
+ return 1;
+ }
+
return UnicodeCalculator.GetWidth(rune);
});
}
diff --git a/src/Spectre.Console/Rendering/Segment.cs b/src/Spectre.Console/Rendering/Segment.cs
index 7b42b20..84b8e95 100644
--- a/src/Spectre.Console/Rendering/Segment.cs
+++ b/src/Spectre.Console/Rendering/Segment.cs
@@ -79,11 +79,37 @@ namespace Spectre.Console.Rendering
///
/// The render context.
/// The number of cells that this segment occupies in the console.
- public int CellLength(RenderContext context)
+ public int CellCount(RenderContext context)
{
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
return Text.CellLength(context);
}
+ ///
+ /// Gets the number of cells that the segments occupies in the console.
+ ///
+ /// The render context.
+ /// The segments to measure.
+ /// The number of cells that the segments occupies in the console.
+ public static int CellCount(RenderContext context, IEnumerable segments)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return segments.Sum(segment => segment.CellCount(context));
+ }
+
///
/// Returns a new segment without any trailing line endings.
///
@@ -124,35 +150,41 @@ namespace Spectre.Console.Rendering
return new Segment(Text, Style);
}
- ///
- /// Gets the number of cells that the segments occupies in the console.
- ///
- /// The render context.
- /// The segments to measure.
- /// The number of cells that the segments occupies in the console.
- public static int CellLength(RenderContext context, IEnumerable segments)
- {
- return segments.Sum(segment => segment.CellLength(context));
- }
-
///
/// Splits the provided segments into lines.
///
+ /// The render context.
/// The segments to split.
/// A collection of lines.
- public static List SplitLines(IEnumerable segments)
+ public static List SplitLines(RenderContext context, IEnumerable segments)
{
- return SplitLines(segments, int.MaxValue);
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return SplitLines(context, segments, int.MaxValue);
}
///
/// Splits the provided segments into lines with a maximum width.
///
+ /// The render context.
/// The segments to split into lines.
/// The maximum width.
/// A list of lines.
- public static List SplitLines(IEnumerable segments, int maxWidth)
+ public static List SplitLines(RenderContext context, IEnumerable segments, int maxWidth)
{
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
if (segments is null)
{
throw new ArgumentNullException(nameof(segments));
@@ -167,9 +199,10 @@ namespace Spectre.Console.Rendering
{
var segment = stack.Pop();
- if (line.Width + segment.Text.Length > maxWidth)
+ // Does this segment make the line exceed the max width?
+ if (line.CellCount(context) + segment.CellCount(context) > maxWidth)
{
- var diff = -(maxWidth - (line.Width + segment.Text.Length));
+ var diff = -(maxWidth - (line.Length + segment.Text.Length));
var offset = segment.Text.Length - diff;
var (first, second) = segment.Split(offset);
@@ -186,11 +219,13 @@ namespace Spectre.Console.Rendering
continue;
}
+ // Does the segment contain a newline?
if (segment.Text.Contains("\n"))
{
+ // Is it a new line?
if (segment.Text == "\n")
{
- if (line.Width > 0 || segment.IsLineBreak)
+ if (line.Length != 0 || segment.IsLineBreak)
{
lines.Add(line);
line = new SegmentLine();
@@ -213,7 +248,7 @@ namespace Spectre.Console.Rendering
if (parts.Length > 1)
{
- if (line.Width > 0)
+ if (line.Length > 0)
{
lines.Add(line);
line = new SegmentLine();
@@ -247,16 +282,21 @@ namespace Spectre.Console.Rendering
/// The segment to split.
/// The overflow strategy to use.
/// The render context.
- /// The maximum width.
+ /// The maximum width.
/// A list of segments that has been split.
- public static List SplitOverflow(Segment segment, Overflow? overflow, RenderContext context, int width)
+ public static List SplitOverflow(Segment segment, Overflow? overflow, RenderContext context, int maxWidth)
{
if (segment is null)
{
throw new ArgumentNullException(nameof(segment));
}
- if (segment.CellLength(context) <= width)
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (segment.CellCount(context) <= maxWidth)
{
return new List(1) { segment };
}
@@ -275,7 +315,7 @@ namespace Spectre.Console.Rendering
var index = totalLength - lengthLeft;
// How many characters should we take?
- var take = Math.Min(width, totalLength - index);
+ var take = Math.Min(maxWidth, totalLength - index);
if (take <= 0)
{
// This shouldn't really occur, but I don't like
@@ -289,24 +329,24 @@ namespace Spectre.Console.Rendering
}
else if (overflow == Overflow.Crop)
{
- if (Math.Max(0, width - 1) == 0)
+ if (Math.Max(0, maxWidth - 1) == 0)
{
result.Add(new Segment(string.Empty, segment.Style));
}
else
{
- result.Add(new Segment(segment.Text.Substring(0, width), segment.Style));
+ result.Add(new Segment(segment.Text.Substring(0, maxWidth), segment.Style));
}
}
else if (overflow == Overflow.Ellipsis)
{
- if (Math.Max(0, width - 1) == 0)
+ if (Math.Max(0, maxWidth - 1) == 0)
{
result.Add(new Segment("…", segment.Style));
}
else
{
- result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
+ result.Add(new Segment(segment.Text.Substring(0, maxWidth - 1) + "…", segment.Style));
}
}
@@ -337,14 +377,14 @@ namespace Spectre.Console.Rendering
var totalWidth = 0;
foreach (var segment in segments)
{
- var segmentWidth = segment.CellLength(context);
- if (totalWidth + segmentWidth > maxWidth)
+ var segmentCellWidth = segment.CellCount(context);
+ if (totalWidth + segmentCellWidth > maxWidth)
{
break;
}
result.Add(segment);
- totalWidth += segmentWidth;
+ totalWidth += segmentCellWidth;
}
if (result.Count == 0 && segments.Any())
@@ -368,12 +408,17 @@ namespace Spectre.Console.Rendering
/// A new truncated segment, or null.
public static Segment? Truncate(RenderContext context, Segment segment, int maxWidth)
{
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
if (segment is null)
{
return null;
}
- if (segment.CellLength(context) <= maxWidth)
+ if (segment.CellCount(context) <= maxWidth)
{
return segment;
}
@@ -381,7 +426,8 @@ namespace Spectre.Console.Rendering
var builder = new StringBuilder();
foreach (var character in segment.Text)
{
- if (Cell.GetCellLength(context, builder.ToString()) >= maxWidth)
+ var accumulatedCellWidth = builder.ToString().CellLength(context);
+ if (accumulatedCellWidth >= maxWidth)
{
break;
}
@@ -399,6 +445,11 @@ namespace Spectre.Console.Rendering
internal static IEnumerable Merge(IEnumerable segments)
{
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
var result = new List();
var previous = (Segment?)null;
@@ -432,6 +483,33 @@ namespace Spectre.Console.Rendering
internal static Segment TruncateWithEllipsis(string text, Style style, RenderContext context, int maxWidth)
{
+ if (text is null)
+ {
+ throw new ArgumentNullException(nameof(text));
+ }
+
+ if (style is null)
+ {
+ throw new ArgumentNullException(nameof(style));
+ }
+
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var overflow = SplitOverflow(new Segment(text, style), Overflow.Ellipsis, context, maxWidth);
+ if (overflow.Count == 0)
+ {
+ if (maxWidth > 0)
+ {
+ return new Segment(text, style);
+ }
+
+ // We got space for an ellipsis
+ return new Segment("…", style);
+ }
+
return SplitOverflow(
new Segment(text, style),
Overflow.Ellipsis,
@@ -441,7 +519,17 @@ namespace Spectre.Console.Rendering
internal static List TruncateWithEllipsis(IEnumerable segments, RenderContext context, int maxWidth)
{
- if (CellLength(context, segments) <= maxWidth)
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (CellCount(context, segments) <= maxWidth)
{
return new List(segments);
}
@@ -459,6 +547,11 @@ namespace Spectre.Console.Rendering
internal static List TrimEnd(IEnumerable segments)
{
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
var stack = new Stack();
var checkForWhitespace = true;
foreach (var segment in segments.Reverse())
@@ -481,6 +574,11 @@ namespace Spectre.Console.Rendering
internal static List> MakeSameHeight(int cellHeight, List> cells)
{
+ if (cells is null)
+ {
+ throw new ArgumentNullException(nameof(cells));
+ }
+
foreach (var cell in cells)
{
if (cell.Count < cellHeight)
diff --git a/src/Spectre.Console/Rendering/SegmentLine.cs b/src/Spectre.Console/Rendering/SegmentLine.cs
index f1483d6..f643e9d 100644
--- a/src/Spectre.Console/Rendering/SegmentLine.cs
+++ b/src/Spectre.Console/Rendering/SegmentLine.cs
@@ -13,16 +13,21 @@ namespace Spectre.Console.Rendering
///
/// Gets the width of the line.
///
- public int Width => this.Sum(line => line.Text.Length);
+ public int Length => this.Sum(line => line.Text.Length);
///
- /// Gets the cell width of the segment line.
+ /// Gets the number of cells the segment line occupies.
///
/// The render context.
/// The cell width of the segment line.
- public int CellWidth(RenderContext context)
+ public int CellCount(RenderContext context)
{
- return this.Sum(line => line.CellLength(context));
+ if (context is null)
+ {
+ throw new System.ArgumentNullException(nameof(context));
+ }
+
+ return Segment.CellCount(context, this);
}
///
@@ -31,6 +36,11 @@ namespace Spectre.Console.Rendering
/// The segment to prepend.
public void Prepend(Segment segment)
{
+ if (segment is null)
+ {
+ throw new System.ArgumentNullException(nameof(segment));
+ }
+
Insert(0, segment);
}
}
diff --git a/src/Spectre.Console/Widgets/Padder.cs b/src/Spectre.Console/Widgets/Padder.cs
index 6e6dff4..bba0a4b 100644
--- a/src/Spectre.Console/Widgets/Padder.cs
+++ b/src/Spectre.Console/Widgets/Padder.cs
@@ -66,7 +66,7 @@ namespace Spectre.Console
}
var child = _child.Render(context, maxWidth - paddingWidth);
- foreach (var (_, _, _, line) in Segment.SplitLines(child).Enumerate())
+ foreach (var (_, _, _, line) in Segment.SplitLines(context, child).Enumerate())
{
// Left padding
if (Padding.Left != 0)
@@ -83,7 +83,7 @@ namespace Spectre.Console
}
// Missing space on right side?
- var lineWidth = line.CellWidth(context);
+ var lineWidth = line.CellCount(context);
var diff = width - lineWidth - Padding.Left - Padding.Right;
if (diff > 0)
{
diff --git a/src/Spectre.Console/Widgets/Panel.cs b/src/Spectre.Console/Widgets/Panel.cs
index b5bbe59..1755085 100644
--- a/src/Spectre.Console/Widgets/Panel.cs
+++ b/src/Spectre.Console/Widgets/Panel.cs
@@ -94,7 +94,7 @@ namespace Spectre.Console
// Split the child segments into lines.
var childSegments = ((IRenderable)child).Render(context, childWidth);
- foreach (var line in Segment.SplitLines(childSegments, childWidth))
+ foreach (var line in Segment.SplitLines(context, childSegments, childWidth))
{
if (line.Count == 1 && line[0].IsWhiteSpace)
{
@@ -109,7 +109,7 @@ namespace Spectre.Console
content.AddRange(line);
// Do we need to pad the panel?
- var length = line.Sum(segment => segment.CellLength(context));
+ var length = line.Sum(segment => segment.CellCount(context));
if (length < childWidth)
{
var diff = childWidth - length;
@@ -148,7 +148,7 @@ namespace Spectre.Console
var headerWidth = panelWidth - (EdgeWidth * 2);
var header = Segment.TruncateWithEllipsis(Header.Text, Header.Style ?? borderStyle, context, headerWidth);
- var excessWidth = headerWidth - header.CellLength(context);
+ var excessWidth = headerWidth - header.CellCount(context);
if (excessWidth > 0)
{
switch (Header.Alignment ?? Justify.Left)
diff --git a/src/Spectre.Console/Widgets/Paragraph.cs b/src/Spectre.Console/Widgets/Paragraph.cs
index a78ed3a..4a6c447 100644
--- a/src/Spectre.Console/Widgets/Paragraph.cs
+++ b/src/Spectre.Console/Widgets/Paragraph.cs
@@ -119,8 +119,8 @@ namespace Spectre.Console
return new Measurement(0, 0);
}
- var min = _lines.Max(line => line.Max(segment => segment.CellLength(context)));
- var max = _lines.Max(x => x.CellWidth(context));
+ var min = _lines.Max(line => line.Max(segment => segment.CellCount(context)));
+ var max = _lines.Max(x => x.CellCount(context));
return new Measurement(min, Math.Min(max, maxWidth));
}
@@ -187,7 +187,7 @@ namespace Spectre.Console
return new List();
}
- if (_lines.Max(x => x.CellWidth(context)) <= maxWidth)
+ if (_lines.Max(x => x.CellCount(context)) <= maxWidth)
{
return Clone();
}
@@ -231,7 +231,7 @@ namespace Spectre.Console
continue;
}
- var length = current.CellLength(context);
+ var length = current.CellCount(context);
if (length > maxWidth)
{
// The current segment is longer than the width of the console,
@@ -239,7 +239,7 @@ namespace Spectre.Console
var segments = Segment.SplitOverflow(current, Overflow, context, maxWidth);
if (segments.Count > 0)
{
- if (line.CellWidth(context) + segments[0].CellLength(context) > maxWidth)
+ if (line.CellCount(context) + segments[0].CellCount(context) > maxWidth)
{
lines.Add(line);
line = new SegmentLine();
@@ -259,7 +259,7 @@ namespace Spectre.Console
}
else
{
- if (line.CellWidth(context) + length > maxWidth)
+ if (line.CellCount(context) + length > maxWidth)
{
line.Add(Segment.Empty);
lines.Add(line);
diff --git a/src/Spectre.Console/Widgets/Rule.cs b/src/Spectre.Console/Widgets/Rule.cs
index 1ac04d3..168cbb0 100644
--- a/src/Spectre.Console/Widgets/Rule.cs
+++ b/src/Spectre.Console/Widgets/Rule.cs
@@ -52,7 +52,7 @@ namespace Spectre.Console
// Get the title and make sure it fits.
var title = GetTitleSegments(context, Title, maxWidth - 6);
- if (Segment.CellLength(context, title) > maxWidth - 6)
+ if (Segment.CellCount(context, title) > maxWidth - 6)
{
// Truncate the title
title = Segment.TruncateWithEllipsis(title, context, maxWidth - 6);
@@ -88,13 +88,13 @@ namespace Spectre.Console
{
var alignment = Alignment ?? Justify.Center;
- var titleLength = Segment.CellLength(context, title);
+ var titleLength = Segment.CellCount(context, title);
if (alignment == Justify.Left)
{
var left = new Segment(new string('─', 2) + " ", Style ?? Style.Plain);
- var rightLength = maxWidth - titleLength - left.CellLength(context) - 1;
+ var rightLength = maxWidth - titleLength - left.CellCount(context) - 1;
var right = new Segment(" " + new string('─', rightLength), Style ?? Style.Plain);
return (left, right);
@@ -104,7 +104,7 @@ namespace Spectre.Console
var leftLength = ((maxWidth - titleLength) / 2) - 1;
var left = new Segment(new string('─', leftLength) + " ", Style ?? Style.Plain);
- var rightLength = maxWidth - titleLength - left.CellLength(context) - 1;
+ var rightLength = maxWidth - titleLength - left.CellCount(context) - 1;
var right = new Segment(" " + new string('─', rightLength), Style ?? Style.Plain);
return (left, right);
@@ -113,7 +113,7 @@ namespace Spectre.Console
{
var right = new Segment(" " + new string('─', 2), Style ?? Style.Plain);
- var leftLength = maxWidth - titleLength - right.CellLength(context) - 1;
+ var leftLength = maxWidth - titleLength - right.CellCount(context) - 1;
var left = new Segment(new string('─', leftLength) + " ", Style ?? Style.Plain);
return (left, right);
diff --git a/src/Spectre.Console/Widgets/Table.cs b/src/Spectre.Console/Widgets/Table.cs
index 39d29b1..03417ee 100644
--- a/src/Spectre.Console/Widgets/Table.cs
+++ b/src/Spectre.Console/Widgets/Table.cs
@@ -217,7 +217,7 @@ namespace Spectre.Console
var justification = _columns[columnIndex].Alignment;
var childContext = context.WithJustification(justification);
- var lines = Segment.SplitLines(cell.Render(childContext, rowWidth));
+ var lines = Segment.SplitLines(context, cell.Render(childContext, rowWidth));
cellHeight = Math.Max(cellHeight, lines.Count);
cells.Add(lines);
}
@@ -261,7 +261,7 @@ namespace Spectre.Console
rowResult.AddRange(cell[cellRowIndex]);
// Pad cell content right
- var length = cell[cellRowIndex].Sum(segment => segment.CellLength(context));
+ var length = cell[cellRowIndex].Sum(segment => segment.CellCount(context));
if (length < columnWidths[cellIndex])
{
rowResult.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
@@ -295,7 +295,7 @@ namespace Spectre.Console
Aligner.Align(context, rowResult, Alignment, actualMaxWidth);
// Is the row larger than the allowed max width?
- if (Segment.CellLength(context, rowResult) > actualMaxWidth)
+ if (Segment.CellCount(context, rowResult) > actualMaxWidth)
{
result.AddRange(Segment.Truncate(context, rowResult, actualMaxWidth));
}