Skip to content

Commit fbeae03

Browse files
committed
added support for variation selectors, ZWJ sequences, and surrogate pairs in length calculation ( fixes #1847 )
1 parent 58e7d49 commit fbeae03

3 files changed

Lines changed: 30 additions & 15 deletions

File tree

src/Spectre.Console.Tests/Unit/CellTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ public sealed class CellTests
1111
[InlineData("❤️‍🔥", 2)] // U+2764 + FE0F + ZWJ + U+1F525 (heart on fire)
1212
[InlineData("🇩🇪", 2)] // Regional Indicator pair (flag)
1313
[InlineData("", 0)] // empty string
14+
[InlineData("Hello World", 11)]
1415
public void GetCellLength_Returns_Correct_Display_Width(string text, int expectedWidth)
1516
{
1617
Cell.GetCellLength(text).ShouldBe(expectedWidth);
1718
}
19+
1820
}

src/Spectre.Console/Internal/Cell.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,24 @@ static Cell()
2323
#endif
2424
}
2525

26-
public static int GetCellLength(string text) => GetCellLength(text.AsSpan());
27-
28-
public static int GetCellLength(ReadOnlySpan<char> text)
26+
public static int GetCellLength(string text)
2927
{
28+
#if !NETSTANDARD2_0
29+
return UnicodeCalculator.GetWidth(text);
30+
#else
3031
var sum = 0;
3132
foreach (var rune in text)
3233
{
3334
sum += GetCellLength(rune);
3435
}
3536

3637
return sum;
38+
#endif
3739
}
3840

41+
public static int GetCellLength(ReadOnlySpan<char> text)
42+
=> GetCellLength(text.ToString());
43+
3944
public static int GetCellLength(char rune)
4045
{
4146
// TODO: We need to figure out why Segment.SplitLines fails

src/Spectre.Console/Rendering/Segment.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public int CellCount()
108108
{
109109
return 0;
110110
}
111+
if (Text == "\n") return 1;
111112

112113
return Cell.GetCellLength(Text);
113114
}
@@ -160,10 +161,12 @@ public Segment StripLineEndings()
160161
if (offset > 0)
161162
{
162163
var accumulated = 0;
163-
foreach (var character in Text)
164+
var enumerator = StringInfo.GetTextElementEnumerator(Text);
165+
while (enumerator.MoveNext())
164166
{
165-
index++;
166-
accumulated += Cell.GetCellLength(character);
167+
var cluster = enumerator.GetTextElement();
168+
accumulated += Cell.GetCellLength(cluster);
169+
index += cluster.Length;
167170
if (accumulated >= offset)
168171
{
169172
break;
@@ -429,16 +432,18 @@ public static List<Segment> Truncate(IEnumerable<Segment> segments, int maxWidth
429432

430433
var builder = new StringBuilder();
431434
var accumulatedCellWidth = 0;
432-
foreach (var character in segment.Text)
435+
var truncateEnumerator = StringInfo.GetTextElementEnumerator(segment.Text);
436+
while (truncateEnumerator.MoveNext())
433437
{
434-
var characterWidth = UnicodeCalculator.GetWidth(character);
435-
if (accumulatedCellWidth + characterWidth > maxWidth)
438+
var cluster = truncateEnumerator.GetTextElement();
439+
var clusterWidth = Cell.GetCellLength(cluster);
440+
if (accumulatedCellWidth + clusterWidth > maxWidth)
436441
{
437442
break;
438443
}
439444

440-
builder.Append(character);
441-
accumulatedCellWidth += characterWidth;
445+
builder.Append(cluster);
446+
accumulatedCellWidth += clusterWidth;
442447
}
443448

444449
if (builder.Length == 0)
@@ -571,17 +576,20 @@ internal static List<string> SplitSegment(string text, int maxCellLength)
571576

572577
var length = 0;
573578
var sb = new StringBuilder();
574-
foreach (var ch in text)
579+
var splitEnumerator = StringInfo.GetTextElementEnumerator(text);
580+
while (splitEnumerator.MoveNext())
575581
{
576-
if (length + UnicodeCalculator.GetWidth(ch) > maxCellLength)
582+
var cluster = splitEnumerator.GetTextElement();
583+
var clusterWidth = Cell.GetCellLength(cluster);
584+
if (length + clusterWidth > maxCellLength)
577585
{
578586
list.Add(sb.ToString());
579587
sb.Clear();
580588
length = 0;
581589
}
582590

583-
length += UnicodeCalculator.GetWidth(ch);
584-
sb.Append(ch);
591+
length += clusterWidth;
592+
sb.Append(cluster);
585593
}
586594

587595
list.Add(sb.ToString());

0 commit comments

Comments
 (0)