Skip to content

Commit fc818b0

Browse files
BDisptigCopilot
authored
Fixes #4382. StringExtensions.GetColumns method should only return the total text width and not the sum of all runes width (#4383)
* Fixes #4382. StringExtensions.GetColumns method should only return the total text width and not the sum of all runes width * Trying to fix unit test error * Update StringExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add unit test to prove that null and empty string doesn't not throws anything. --------- Co-authored-by: Tig <tig@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent be9d193 commit fc818b0

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

Terminal.Gui/Text/StringExtensions.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System.Buffers;
3+
using System.Globalization;
34

45
namespace Terminal.Gui.Text;
56

@@ -55,7 +56,29 @@ public static (Rune Rune, int Size) DecodeRune (this string str, int start = 0,
5556
/// <remarks>This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.</remarks>
5657
/// <param name="str">The string to measure.</param>
5758
/// <returns></returns>
58-
public static int GetColumns (this string str) { return str is null ? 0 : str.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 0)); }
59+
public static int GetColumns (this string str)
60+
{
61+
if (string.IsNullOrEmpty (str))
62+
{
63+
return 0;
64+
}
65+
66+
var total = 0;
67+
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (str);
68+
69+
while (enumerator.MoveNext ())
70+
{
71+
string element = enumerator.GetTextElement ();
72+
73+
// Get the maximum rune width within this grapheme cluster
74+
int width = element
75+
.EnumerateRunes ()
76+
.Max (r => Math.Max (r.GetColumns (), 0));
77+
total += width;
78+
}
79+
80+
return total;
81+
}
5982

6083
/// <summary>Gets the number of runes in the string.</summary>
6184
/// <remarks>This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.</remarks>

Tests/UnitTests/Views/HexViewTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public void ReadOnly_Prevents_Edits ()
7272

7373
Application.Top.Dispose ();
7474
Application.ResetState (true);
75-
7675
}
7776

7877
[Fact]
@@ -321,6 +320,7 @@ public void KeyBindings_Test_Movement_LeftSide ()
321320
[Fact]
322321
public void PositionChanged_Event ()
323322
{
323+
Application.Navigation = new ApplicationNavigation ();
324324
var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 };
325325
Application.Top = new Toplevel ();
326326
Application.Top.Add (hv);
@@ -346,6 +346,7 @@ public void PositionChanged_Event ()
346346
[Fact]
347347
public void Source_Sets_Address_To_Zero_If_Greater_Than_Source_Length ()
348348
{
349+
Application.Navigation = new ApplicationNavigation ();
349350
var hv = new HexView (LoadStream (null, out _)) { Width = 10, Height = 5 };
350351
Application.Top = new Toplevel ();
351352
Application.Top.Add (hv);

Tests/UnitTestsParallelizable/Text/RuneTests.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace UnitTests_Parallelizable.TextTests;
77
public class RuneTests
88
{
99
[Fact]
10-
public void Cast_To_Char_Durrogate_Pair_Return_UTF16 ()
10+
public void Cast_To_Char_Surrogate_Pair_Return_UTF16 ()
1111
{
1212
Assert.NotEqual ("𝔹", $"{new Rune (unchecked ((char)0x1d539))}");
1313
Assert.Equal ("픹", $"{new Rune (unchecked ((char)0x1d539))}");
@@ -65,8 +65,11 @@ public void GetColumns_GetRuneCount ()
6565
PrintTextElementCount ("\u0061\u0301", "á", 1, 2, 2, 1);
6666
PrintTextElementCount ("\u0061\u0301", "á", 1, 2, 2, 1);
6767
PrintTextElementCount ("\u0065\u0301", "é", 1, 2, 2, 1);
68-
PrintTextElementCount ("\U0001f469\U0001f3fd\u200d\U0001f692", "👩🏽‍🚒", 6, 4, 7, 1);
68+
PrintTextElementCount ("\U0001f469\U0001f3fd\u200d\U0001f692", "👩🏽‍🚒", 2, 4, 7, 1);
6969
PrintTextElementCount ("\ud801\udccf", "𐓏", 1, 1, 2, 1);
70+
PrintTextElementCount ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", "👨‍👩‍👧‍👦", 2, 7, 11, 1);
71+
PrintTextElementCount ("\U0001f469\u200d\U0001f692", "👩‍🚒", 2, 3, 5, 1);
72+
PrintTextElementCount ("\u0068\u0069", "hi", 2, 2, 2, 2);
7073
}
7174

7275
[Theory]
@@ -84,8 +87,8 @@ public void GetColumns_GetRuneCount ()
8487
2,
8588
1
8689
)] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right)
87-
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 6, 8)] // Man, Woman and Girl emoji.
88-
[InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
90+
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 2, 8)] // Man, Woman and Girl emoji.
91+
[InlineData ("\u0915\u093f", "कि", 2, 1, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
8992
[InlineData (
9093
"\u0e4d\u0e32",
9194
"ํา",

Tests/UnitTestsParallelizable/Text/StringTests.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ public void TestGetColumns_Empty ()
3333
[InlineData ("🙂", 2)]
3434
[InlineData ("a🙂", 3)]
3535
[InlineData ("🙂a", 3)]
36-
[InlineData ("👨‍👩‍👦‍👦", 8)]
37-
[InlineData ("👨‍👩‍👦‍👦🙂", 10)]
38-
[InlineData ("👨‍👩‍👦‍👦🙂a", 11)]
39-
[InlineData ("👨‍👩‍👦‍👦a🙂", 11)]
40-
[InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 16)]
36+
[InlineData ("👨‍👩‍👦‍👦", 2)]
37+
[InlineData ("👨‍👩‍👦‍👦🙂", 4)]
38+
[InlineData ("👨‍👩‍👦‍👦🙂a", 5)]
39+
[InlineData ("👨‍👩‍👦‍👦a🙂", 5)]
40+
[InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 4)]
4141
[InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
4242
[InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
4343
//[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
@@ -57,4 +57,26 @@ public void TestGetColumns_SingleRune ()
5757
var str = "a";
5858
Assert.Equal (1, str.GetColumns ());
5959
}
60+
61+
[Fact]
62+
public void TestGetColumns_Zero_Width ()
63+
{
64+
var str = "\u200D";
65+
Assert.Equal (0, str.GetColumns ());
66+
}
67+
68+
[Theory]
69+
[InlineData (null)]
70+
[InlineData ("")]
71+
public void TestGetColumns_Does_Not_Throws_With_Null_And_Empty_String (string? text)
72+
{
73+
if (text is null)
74+
{
75+
Assert.Equal (0, StringExtensions.GetColumns (text!));
76+
}
77+
else
78+
{
79+
Assert.Equal (0, text.GetColumns ());
80+
}
81+
}
6082
}

0 commit comments

Comments
 (0)