Fixes #4387. Runes should not be used on a cell, but rather should use a single grapheme rendering 1 or 2 columns (#4388)

* 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>

* Resolving merge conflicts

* Prevents Runes throwing if Grapheme is null

* Add unit test to prove that null and empty string doesn't not throws anything.

* Fix unit test failure

* Fix IsValidLocation for wide graphemes

* Add more combining

* Prevent set invalid graphemes

* Fix unit tests

* Grapheme doesn't support invalid code points like lone surrogates

* Fixes more unit tests

* Fix unit test

* Seems all test are fixed now

* Adjust CharMap scenario with graphemes

* Upgrade Wcwidth to version 4.0.0

* Reformat

* Trying fix CheckDefaultState assertion

* Revert "Trying fix CheckDefaultState assertion"

This reverts commit c9b46b796a.

* Forgot to include driver.End in the test

* Reapply "Trying fix CheckDefaultState assertion"

This reverts commit 1060ac9b63.

* Remove ToString

* Fix merge errors

* Change to conditional expression

* Assertion to prove that no exception throws during cell initialization.

* Remove unnecessary assignment

* Remove assignment to end

* Replace string concatenation with 'StringBuilder'.

* Replace more string concatenation with 'StringBuilder'

* Remove redundant call to 'ToString' because Rune cast to a String object.

* Replace foreach loop with Sum linq

---------

Co-authored-by: Tig <tig@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
BDisp
2025-11-20 18:45:13 +00:00
committed by GitHub
parent 726b15dd28
commit cd75a20c60
53 changed files with 2654 additions and 2145 deletions

View File

@@ -88,7 +88,7 @@ public class RuneTests
1
)] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right)
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 2, 8)] // Man, Woman and Girl emoji.
[InlineData ("\u0915\u093f", "कि", 2, 1, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
//[InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
[InlineData (
"\u0e4d\u0e32",
"ํา",
@@ -213,7 +213,7 @@ public class RuneTests
[InlineData (
'\u1161',
"ᅡ",
1,
0,
1,
3
)] // ᅡ Hangul Jungseong A - Unicode Hangul Jamo for join with column width equal to 0 alone.
@@ -231,7 +231,7 @@ public class RuneTests
)] // ䷀Hexagram For The Creative Heaven - U+4dc0 - https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
// See https://github.com/microsoft/terminal/issues/19389
[InlineData ('\ud7b0', "ힰ", 1, 1, 3)] // ힰ ┤Hangul Jungseong O-Yeo - ힰ U+d7b0')]
[InlineData ('\ud7b0', "ힰ", 0, 1, 3)] // ힰ ┤Hangul Jungseong O-Yeo - ힰ U+d7b0')]
[InlineData ('\uf61e', "", 1, 1, 3)] // Private Use Area
[InlineData ('\u23f0', "⏰", 2, 1, 3)] // Alarm Clock - ⏰ U+23f0
[InlineData ('\u1100', "ᄀ", 2, 1, 3)] // ᄀ Hangul Choseong Kiyeok
@@ -365,6 +365,42 @@ public class RuneTests
[InlineData ('\ud801')]
public void Rune_Exceptions_Integers (int code) { Assert.Throws<ArgumentOutOfRangeException> (() => new Rune (code)); }
[Theory]
// Control characters (should be mapped to Control Pictures)
[InlineData ('\u0000', 0x2400)] // NULL → ␀
[InlineData ('\u0009', 0x2409)] // TAB → ␉
[InlineData ('\u000A', 0x240A)] // LF → ␊
[InlineData ('\u000D', 0x240D)] // CR → ␍
// Printable characters (should remain unchanged)
[InlineData ('A', 'A')]
[InlineData (' ', ' ')]
[InlineData ('~', '~')]
public void MakePrintable_ReturnsExpected (char inputChar, int expectedCodePoint)
{
// Arrange
Rune input = new Rune (inputChar);
// Act
Rune result = input.MakePrintable ();
// Assert
Assert.Equal (expectedCodePoint, result.Value);
}
[Fact]
public void MakePrintable_SupplementaryRune_RemainsUnchanged ()
{
// Arrange: supplementary character outside BMP (not a control)
Rune input = new Rune (0x1F600); // 😀 grinning face emoji
// Act
Rune result = input.MakePrintable ();
// Assert
Assert.Equal (input.Value, result.Value);
}
[Theory]
[InlineData (new [] { '\ud799', '\udc21' })]
public void Rune_Exceptions_Utf16_Encode (char [] code)
@@ -954,11 +990,9 @@ public class RuneTests
Assert.Equal (runeCount, us.GetRuneCount ());
Assert.Equal (stringCount, s.Length);
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (s);
var textElementCount = 0;
while (enumerator.MoveNext ())
foreach (string _ in GraphemeHelper.GetGraphemes (s))
{
textElementCount++; // For versions prior to Net5.0 the StringInfo class might handle some grapheme clusters incorrectly.
}
@@ -1064,4 +1098,95 @@ public class RuneTests
return true;
}
[Theory]
[InlineData (0x0041, new byte [] { 0x41 })] // 'A', ASCII
[InlineData (0x00E9, new byte [] { 0xC3, 0xA9 })] // 'é', 2-byte UTF-8
[InlineData (0x20AC, new byte [] { 0xE2, 0x82, 0xAC })] // '€', 3-byte UTF-8
[InlineData (0x1F600, new byte [] { 0xF0, 0x9F, 0x98, 0x80 })] // 😀 emoji, 4-byte UTF-8
public void Encode_WritesExpectedBytes (int codePoint, byte [] expectedBytes)
{
// Arrange
Rune rune = new Rune (codePoint);
byte [] buffer = new byte [10]; // extra space
for (int i = 0; i < buffer.Length; i++)
{
buffer [i] = 0xFF;
}
// Act
int written = rune.Encode (buffer);
// Assert
Assert.Equal (expectedBytes.Length, written);
for (int i = 0; i < written; i++)
{
Assert.Equal (expectedBytes [i], buffer [i]);
}
}
[Fact]
public void Encode_WithStartAndCount_WritesPartialBytes ()
{
// Arrange: U+1F600 😀 (4 bytes)
Rune rune = new Rune (0x1F600);
byte [] buffer = new byte [10];
for (int i = 0; i < buffer.Length; i++)
{
buffer [i] = 0xFF;
}
// Act: write starting at index 2, limit count to 2 bytes
int written = rune.Encode (buffer, start: 2, count: 2);
// Assert
Assert.Equal (2, written);
// Original UTF-8 bytes: F0 9F 98 80
Assert.Equal (0xF0, buffer [2]);
Assert.Equal (0x9F, buffer [3]);
// Remaining buffer untouched
Assert.Equal (0xFF, buffer [0]);
Assert.Equal (0xFF, buffer [1]);
Assert.Equal (0xFF, buffer [4]);
}
[Fact]
public void Encode_WithCountGreaterThanRuneBytes_WritesAllBytes ()
{
// Arrange: é → C3 A9
Rune rune = new Rune ('é');
byte [] buffer = new byte [10];
for (int i = 0; i < buffer.Length; i++)
{
buffer [i] = 0xFF;
}
// Act: count larger than needed
int written = rune.Encode (buffer, start: 1, count: 10);
// Assert
Assert.Equal (2, written);
Assert.Equal (0xC3, buffer [1]);
Assert.Equal (0xA9, buffer [2]);
Assert.Equal (0xFF, buffer [3]); // next byte untouched
}
[Fact]
public void Encode_ZeroCount_WritesNothing ()
{
Rune rune = new Rune ('A');
byte [] buffer = new byte [5];
for (int i = 0; i < buffer.Length; i++)
{
buffer [i] = 0xFF;
}
int written = rune.Encode (buffer, start: 0, count: 0);
Assert.Equal (0, written);
foreach (var b in buffer)
{
Assert.Equal (0xFF, b); // buffer untouched
}
}
}

View File

@@ -4,6 +4,13 @@
public class StringTests
{
[Fact]
public void TestGetColumns_Null ()
{
string? str = null;
Assert.Equal (0, str!.GetColumns ());
}
[Fact]
public void TestGetColumns_Empty ()
{
@@ -11,6 +18,20 @@ public class StringTests
Assert.Equal (0, str.GetColumns ());
}
[Fact]
public void TestGetColumns_SingleRune ()
{
var str = "a";
Assert.Equal (1, str.GetColumns ());
}
[Fact]
public void TestGetColumns_Zero_Width ()
{
var str = "\u200D";
Assert.Equal (0, str.GetColumns ());
}
[Theory]
[InlineData ("a", 1)]
[InlineData ("á", 1)]
@@ -30,39 +51,37 @@ public class StringTests
// Test known wide codepoints
[Theory]
[InlineData ("🙂", 2)]
[InlineData ("a🙂", 3)]
[InlineData ("🙂a", 3)]
[InlineData ("👨‍👩‍👦‍👦", 2)]
[InlineData ("👨‍👩‍👦‍👦🙂", 4)]
[InlineData ("👨👩👦👦🙂a", 5)]
[InlineData ("👨👩👦👦a🙂", 5)]
[InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 4)]
[InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
[InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
//[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
// // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
public void TestGetColumns_MultiRune_WideBMP (string str, int expected) { Assert.Equal (expected, str.GetColumns ()); }
[Fact]
public void TestGetColumns_Null ()
[InlineData ("🙂", 2, 1, 2)]
[InlineData ("a🙂", 3, 2, 3)]
[InlineData ("🙂a", 3, 2, 3)]
[InlineData ("👨‍👩‍👦‍👦", 8, 1, 2)]
[InlineData ("👨‍👩‍👦‍👦🙂", 10, 2, 4)]
[InlineData ("👨👩👦👦🙂a", 11, 3, 5)]
[InlineData ("👨👩👦👦a🙂", 11, 3, 5)]
[InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 16, 2, 4)]
[InlineData ("าำ", 2, 1, 2)] // า U+0E32 - THAI CHARACTER SARA AA with ำ U+0E33 - THAI CHARACTER SARA AM
[InlineData ("山", 2, 1, 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
[InlineData ("山🙂", 4, 2, 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
[InlineData ("a\ufe20e\ufe21", 2, 2, 2)] // Combining Ligature Left Half - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
// Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
//[InlineData ("क", 1, 1, 1)] // क U+0915 Devanagari Letter Ka
//[InlineData ("ि", 1, 1, 1)] // U+093F Devanagari Vowel Sign I ि (i-kar).
//[InlineData ("कि", 2, 1, 2)] // "कि" is U+0915 for the base consonant "क" with U+093F for the vowel sign "ि" (i-kar).
[InlineData ("ᄀ", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant)
[InlineData ("ᅡ", 0, 1, 0)] // ᅡ U+1161 HANGUL JUNGSEONG A (vowel)
[InlineData ("가", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant) with ᅡ U+1161 HANGUL JUNGSEONG A (vowel)
[InlineData ("ᄒ", 2, 1, 2)] // ᄒ U+1112 Hangul Choseong Hieuh
[InlineData ("ᅵ", 0, 1, 0)] // ᅵ U+1175 Hangul Jungseong I
[InlineData ("ᇂ", 0, 1, 0)] // ᇂ U+11C2 Hangul Jongseong Hieuh
[InlineData ("힣", 2, 1, 2)] // ᄒ (choseong h) + ᅵ (jungseong i) + ᇂ (jongseong h)
[InlineData ("ힰ", 0, 1, 0)] // U+D7B0 ힰ Hangul Jungseong O-Yeo
[InlineData ("ᄀힰ", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant) with U+D7B0 ힰ Hangul Jungseong O-Yeo
//[InlineData ("षि", 2, 1, 2)] // U+0937 ष DEVANAGARI LETTER SSA with U+093F ि COMBINING DEVANAGARI VOWEL SIGN I
public void TestGetColumns_MultiRune_WideBMP_Graphemes (string str, int expectedRunesWidth, int expectedGraphemesCount, int expectedWidth)
{
string? str = null;
Assert.Equal (0, str!.GetColumns ());
}
[Fact]
public void TestGetColumns_SingleRune ()
{
var str = "a";
Assert.Equal (1, str.GetColumns ());
}
[Fact]
public void TestGetColumns_Zero_Width ()
{
var str = "\u200D";
Assert.Equal (0, str.GetColumns ());
Assert.Equal (expectedRunesWidth, str.EnumerateRunes ().Sum (r => r.GetColumns ()));
Assert.Equal (expectedGraphemesCount, GraphemeHelper.GetGraphemes (str).ToArray ().Length);
Assert.Equal (expectedWidth, str.GetColumns ());
}
[Theory]
@@ -70,13 +89,124 @@ public class StringTests
[InlineData ("")]
public void TestGetColumns_Does_Not_Throws_With_Null_And_Empty_String (string? text)
{
if (text is null)
// ReSharper disable once InvokeAsExtensionMethod
Assert.Equal (0, StringExtensions.GetColumns (text!));
}
public class ReadOnlySpanExtensionsTests
{
[Theory]
[InlineData ("12345", true)] // all ASCII digits
[InlineData ("0", true)] // single ASCII digit
[InlineData ("", false)] // empty span
[InlineData ("12a45", false)] // contains a letter
[InlineData ("", false)] // full-width Unicode digits (not ASCII)
[InlineData ("12 34", false)] // contains space
[InlineData ("١٢٣", false)] // Arabic-Indic digits
public void IsAllAsciiDigits_WorksAsExpected (string input, bool expected)
{
Assert.Equal (0, StringExtensions.GetColumns (text!));
}
else
{
Assert.Equal (0, text.GetColumns ());
// Arrange
ReadOnlySpan<char> span = input.AsSpan ();
// Act
bool result = span.IsAllAsciiDigits ();
// Assert
Assert.Equal (expected, result);
}
}
[Theory]
[InlineData ("0", true)]
[InlineData ("9", true)]
[InlineData ("A", true)]
[InlineData ("F", true)]
[InlineData ("a", true)]
[InlineData ("f", true)]
[InlineData ("123ABC", true)]
[InlineData ("abcdef", true)]
[InlineData ("G", false)] // 'G' not hex
[InlineData ("Z9", false)] // 'Z' not hex
[InlineData ("12 34", false)] // space not hex
[InlineData ("", false)] // empty string
[InlineData ("", false)] // full-width digits, not ASCII
[InlineData ("0xFF", false)] // includes 'x'
public void IsAllAsciiHexDigits_ReturnsExpected (string input, bool expected)
{
// Arrange
ReadOnlySpan<char> span = input.AsSpan ();
// Act
bool result = span.IsAllAsciiHexDigits ();
// Assert
Assert.Equal (expected, result);
}
[Theory]
[MemberData (nameof (GetStringConcatCases))]
public void ToString_ReturnsExpected (IEnumerable<string> input, string expected)
{
// Act
string result = StringExtensions.ToString (input);
// Assert
Assert.Equal (expected, result);
}
public static IEnumerable<object []> GetStringConcatCases ()
{
yield return [new string [] { }, string.Empty]; // Empty sequence
yield return [new [] { "" }, string.Empty]; // Single empty string
yield return [new [] { "A" }, "A"]; // Single element
yield return [new [] { "A", "B" }, "AB"]; // Simple concatenation
yield return [new [] { "Hello", " ", "World" }, "Hello World"]; // Multiple parts
yield return [new [] { "123", "456", "789" }, "123456789"]; // Numeric strings
yield return [new [] { "👩‍", "🧒" }, "👩‍🧒"]; // Grapheme sequence
yield return [new [] { "α", "β", "γ" }, "αβγ"]; // Unicode letters
yield return [new [] { "A", null, "B" }, "AB"]; // Null ignored by string.Concat
}
[Theory]
[InlineData ("", false)] // Empty string
[InlineData ("A", false)] // Single BMP character
[InlineData ("AB", false)] // Two BMP chars, not a surrogate pair
[InlineData ("👩", true)] // Single emoji surrogate pair (U+1F469)
[InlineData ("🧒", true)] // Another emoji surrogate pair (U+1F9D2)
[InlineData ("𐍈", true)] // Gothic letter hwair (U+10348)
[InlineData ("A👩", false)] // One BMP + one surrogate half
[InlineData ("👩‍", false)] // Surrogate pair + ZWJ (length != 2)
public void IsSurrogatePair_ReturnsExpected (string input, bool expected)
{
// Act
bool result = input.IsSurrogatePair ();
// Assert
Assert.Equal (expected, result);
}
[Theory]
// Control characters (should be replaced with the "Control Pictures" block)
[InlineData ("\u0000", "\u2400")] // NULL → ␀
[InlineData ("\u0009", "\u2409")] // TAB → ␉
[InlineData ("\u000A", "\u240A")] // LF → ␊
[InlineData ("\u000D", "\u240D")] // CR → ␍
// Printable characters (should remain unchanged)
[InlineData ("A", "A")]
[InlineData (" ", " ")]
[InlineData ("~", "~")]
// Multi-character string (should return unchanged)
[InlineData ("AB", "AB")]
[InlineData ("Hello", "Hello")]
[InlineData ("\u0009A", "\u0009A")] // includes a control char, but length > 1
public void MakePrintable_ReturnsExpected (string input, string expected)
{
// Act
string result = input.MakePrintable ();
// Assert
Assert.Equal (expected, result);
}
}

View File

@@ -658,5 +658,32 @@ Nice Work")]
DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver);
}
[Theory]
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.LeftRight_TopBottom, "👨‍👩‍👧‍👦")]
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.TopBottom_LeftRight, "👨‍👩‍👧‍👦")]
public void Draw_Emojis_With_Zero_Width_Joiner (
string text,
int width,
int height,
TextDirection direction,
string expectedDraw
)
{
IDriver driver = CreateFakeDriver ();
TextFormatter tf = new ()
{
Direction = direction,
ConstrainToSize = new (width, height),
Text = text,
WordWrap = false
};
Assert.Equal (width, text.GetColumns ());
tf.Draw (driver, new (0, 0, width, height), Attribute.Default, Attribute.Default);
DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver);
}
#endregion
}

View File

@@ -792,19 +792,16 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
[MemberData (nameof (CMGlyphs))]
public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
{
List<Rune> runes = text.ToRuneList ();
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
}
[Theory]
[InlineData ("test", 3, 3)]
[InlineData ("test", 4, 4)]
[InlineData ("test", 10, 4)]
public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength)
public void GetLengthThatFits_For_String (string text, int columns, int expectedLength)
{
List<Rune> runes = text.ToRuneList ();
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
}
[Theory]
@@ -833,7 +830,8 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
public void GetLengthThatFits_With_Combining_Runes ()
{
var text = "Les Mise\u0328\u0301rables";
Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
Assert.Equal (14, TextFormatter.GetLengthThatFits (text, 14));
Assert.Equal ("Les Misę́rables", text);
}
[Fact]
@@ -841,14 +839,18 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
{
List<string> text = new () { "Les Mis", "e\u0328\u0301", "rables" };
Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
Assert.Equal ("Les Mis", text [0]);
Assert.Equal ("ę́", text [1]);
Assert.Equal ("rables", text [^1]);
}
//[Fact]
//public void GetWidestLineLength_With_Combining_Runes ()
//{
// var text = "Les Mise\u0328\u0301rables";
// Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));
//}
[Fact]
public void GetWidestLineLength_With_Combining_Runes ()
{
var text = "Les Mise\u0328\u0301rables";
Assert.Equal (14, TextFormatter.GetWidestLineLength (text, 1));
Assert.Equal ("Les Misę́rables", text);
}
[Fact]
public void Internal_Tests ()
@@ -2451,6 +2453,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
Assert.Equal (expected, breakLines);
// Double space Complex example - this is how VS 2022 does it
// which I think is not correct.
//text = "A sentence has words. ";
//breakLines = "";
//wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true);
@@ -2762,8 +2765,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
"ฮ",
"ฯ",
"ะั",
"า",
"ำ"
"า"
}
)]
public void WordWrap_Unicode_SingleWordLine (
@@ -2798,7 +2800,17 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
Assert.True (
expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
);
Assert.Equal (resultLines, wrappedLines);
if (maxWidth == 1)
{
List<string> newResultLines = resultLines.ToList ();
newResultLines [^1] = "";
Assert.Equal (newResultLines, wrappedLines);
}
else
{
Assert.Equal (resultLines, wrappedLines);
}
}
/// <summary>WordWrap strips CRLF</summary>
@@ -3075,8 +3087,8 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
}
[Theory]
[InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")]
[InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")]
[InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misę́rables")]
[InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę́\nr\na\nb\nl\ne\ns")]
[InlineData (
4,
4,
@@ -3085,7 +3097,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
LMre
eias
ssb
ęl "
ę́l "
)]
public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
{
@@ -3111,7 +3123,6 @@ ssb
driver.End ();
}
[Theory]
[InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
[InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
@@ -3187,7 +3198,6 @@ ssb
driver.End ();
}
[Theory]
[InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
[InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
@@ -3224,5 +3234,4 @@ ssb
driver.End ();
}
}