diff --git a/NStack.sln b/NStack.sln
index 43ed1a6..0b4bfed 100644
--- a/NStack.sln
+++ b/NStack.sln
@@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution
.github\workflows\build.yml = .github\workflows\build.yml
.github\workflows\publish.yml = .github\workflows\publish.yml
README.md = README.md
+ testenvironments.json = testenvironments.json
EndProjectSection
EndProject
Global
diff --git a/NStack/unicode/Rune.ColumnWidth.cs b/NStack/unicode/Rune.ColumnWidth.cs
index b1df9ea..381d72b 100644
--- a/NStack/unicode/Rune.ColumnWidth.cs
+++ b/NStack/unicode/Rune.ColumnWidth.cs
@@ -4,11 +4,9 @@
//
using NStack;
-namespace System
-{
- public partial struct Rune
- {
- static uint[,] combining = new uint[,] {
+namespace System {
+ public partial struct Rune {
+ static uint [,] combining = new uint [,] {
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
@@ -48,16 +46,16 @@ public partial struct Rune
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x2e9a, 0x2e9a },
- { 0x2ef4, 0x2eff }, { 0x2fd6, 0x2fef }, { 0x2ffc, 0x2fff },
- { 0x31e4, 0x31ef }, { 0x321f, 0x321f }, { 0xA48D, 0xA48F },
+ { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x2E9A, 0x2E9A },
+ { 0x2EF4, 0x2EFF }, { 0x2FD6, 0x2FEF }, { 0x2FFC, 0x2FFF },
+ { 0x31E4, 0x31EF }, { 0x321F, 0x321F }, { 0xA48D, 0xA48F },
{ 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 },
{ 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE1A, 0xFE1F },
{ 0xFE20, 0xFE23 }, { 0xFE53, 0xFE53 }, { 0xFE67, 0xFE67 },
{ 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
};
- static uint[,] combiningWideChars = new uint[,] {
+ static uint [,] combiningWideChars = new uint [,] {
/* Hangul Jamo init. consonants - 0x1100, 0x11ff */
/* Miscellaneous Technical - 0x2300, 0x23ff */
/* Hangul Syllables - 0x11a8, 0x11c2 */
@@ -84,22 +82,21 @@ public partial struct Rune
{ 0x3131, 0x318e }, { 0x3190, 0x3247 }, { 0x3250, 0x4dbf },
{ 0x4e00, 0xa4c6 }, { 0xa960, 0xa97c }, { 0xac00 ,0xd7a3 },
{ 0xf900, 0xfaff }, { 0xfe10, 0xfe1f }, { 0xfe30 ,0xfe6b },
- { 0xff01, 0xff60 }, { 0xffe0, 0xffe6 }
+ { 0xff01, 0xff60 }, { 0xffe0, 0xffe6 }, { 0x10000, 0x10ffff }
};
- static int bisearch(uint rune, uint[,] table, int max)
+ static int bisearch (uint rune, uint [,] table, int max)
{
int min = 0;
int mid;
- if (rune < table[0, 0] || rune > table[max, 1])
+ if (rune < table [0, 0] || rune > table [max, 1])
return 0;
- while (max >= min)
- {
+ while (max >= min) {
mid = (min + max) / 2;
- if (rune > table[mid, 1])
+ if (rune > table [mid, 1])
min = mid + 1;
- else if (rune < table[mid, 0])
+ else if (rune < table [mid, 0])
max = mid - 1;
else
return 1;
@@ -127,21 +124,14 @@ static int bisearch(uint rune, uint[,] table, int max)
// return false;
//}
- static uint gethexaformat(uint rune, int length)
- {
- var hex = rune.ToString($"x{length}");
- var hexstr = hex.Substring(hex.Length - length, length);
- return (uint)int.Parse(hexstr, System.Globalization.NumberStyles.HexNumber);
- }
-
///
/// Check if the rune is a non-spacing character.
///
/// The rune.
/// True if is a non-spacing character, false otherwise.
- public static bool IsNonSpacingChar(uint rune)
+ public static bool IsNonSpacingChar (uint rune)
{
- return bisearch(rune, combining, combining.GetLength(0) - 1) != 0;
+ return bisearch (rune, combining, combining.GetLength (0) - 1) != 0;
}
///
@@ -149,60 +139,29 @@ public static bool IsNonSpacingChar(uint rune)
///
/// The rune.
/// True if is a wide character, false otherwise.
- public static bool IsWideChar(uint rune)
+ public static bool IsWideChar (uint rune)
{
- return bisearch(gethexaformat(rune, 4), combiningWideChars, combiningWideChars.GetLength(0) - 1) != 0;
+ return bisearch (rune, combiningWideChars, combiningWideChars.GetLength (0) - 1) != 0;
}
- static char firstSurrogatePairChar = '\0';
-
///
/// Number of column positions of a wide-character code. This is used to measure runes as displayed by text-based terminals.
///
/// The width in columns, 0 if the argument is the null character, -1 if the value is not printable, otherwise the number of columns that the rune occupies.
/// The rune.
- public static int ColumnWidth(Rune rune)
+ public static int ColumnWidth (Rune rune)
{
- if (firstSurrogatePairChar != '\0')
- firstSurrogatePairChar = '\0';
uint irune = (uint)rune;
if (irune < 0x20 || (irune >= 0x7f && irune < 0xa0))
return -1;
if (irune < 0x7f)
return 1;
/* binary search in table of non-spacing characters */
- if (bisearch(gethexaformat(irune, 4), combining, combining.GetLength(0) - 1) != 0)
+ if (bisearch (irune, combining, combining.GetLength (0) - 1) != 0)
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
- (bisearch(gethexaformat(irune, 4), combiningWideChars, combiningWideChars.GetLength(0) - 1) != 0 ? 1 : 0);
- }
-
- ///
- /// Number of column positions of a wide-character code. This is used to measure runes as displayed by text-based terminals.
- ///
- /// The width in columns, 0 if the argument is the null character, -1 if the value is not printable, otherwise the number of columns that the rune occupies.
- /// The char.
- public static int ColumnWidth(char c)
- {
- if (!((Rune)c).IsValid)
- {
- if (firstSurrogatePairChar == '\0')
- {
- firstSurrogatePairChar = c;
- return 0;
- }
- else if (firstSurrogatePairChar != '\0')
- {
- var r = new Rune(firstSurrogatePairChar, c);
- firstSurrogatePairChar = '\0';
- return ColumnWidth(r);
- }
- }
- if (firstSurrogatePairChar != '\0')
- firstSurrogatePairChar = '\0';
-
- return ColumnWidth((Rune)c);
+ (bisearch (irune, combiningWideChars, combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0);
}
}
}
diff --git a/NStack/unicode/Rune.cs b/NStack/unicode/Rune.cs
index b8a77d1..092756e 100644
--- a/NStack/unicode/Rune.cs
+++ b/NStack/unicode/Rune.cs
@@ -8,7 +8,7 @@ namespace System {
///
///
///
- [StructLayout(LayoutKind.Sequential)]
+ [StructLayout (LayoutKind.Sequential)]
public partial struct Rune {
// Stores the rune
uint value;
@@ -54,9 +54,8 @@ public partial struct Rune {
///
public Rune (uint rune)
{
- if (rune > maxRune)
- {
- throw new ArgumentOutOfRangeException("Value is beyond the supplementary range!");
+ if (rune > maxRune) {
+ throw new ArgumentOutOfRangeException ("Value is beyond the supplementary range!");
}
this.value = rune;
}
@@ -77,17 +76,12 @@ public Rune (char ch)
/// The low surrogate code point.
public Rune (uint highSurrogate, uint lowSurrogate)
{
- if (EncodeSurrogatePair(highSurrogate, lowSurrogate, out Rune rune))
- {
+ if (EncodeSurrogatePair (highSurrogate, lowSurrogate, out Rune rune)) {
this.value = rune;
- }
- else if (highSurrogate < highSurrogateMin || lowSurrogate > lowSurrogateMax)
- {
- throw new ArgumentOutOfRangeException($"Must be between {highSurrogateMin:x} and {lowSurrogateMax:x} inclusive!");
- }
- else
- {
- throw new ArgumentOutOfRangeException($"Resulted rune must be less or equal to {(uint)MaxRune:x}!");
+ } else if (highSurrogate < highSurrogateMin || lowSurrogate > lowSurrogateMax) {
+ throw new ArgumentOutOfRangeException ($"Must be between {highSurrogateMin:x} and {lowSurrogateMax:x} inclusive!");
+ } else {
+ throw new ArgumentOutOfRangeException ($"Resulted rune must be less or equal to {(uint)MaxRune:x}!");
}
}
@@ -95,25 +89,35 @@ public Rune (uint highSurrogate, uint lowSurrogate)
/// Gets a value indicating whether this can be encoded as UTF-8
///
/// true if is valid; otherwise, false.
- public bool IsValid => ValidRune(value);
+ public bool IsValid => ValidRune (value);
///
/// Gets a value indicating whether this is a surrogate code point.
///
/// trueIf is a surrogate code point, falseotherwise.
- public bool IsSurrogate => IsSurrogateRune(value);
+ public bool IsSurrogate => IsSurrogateRune (value);
///
/// Gets a value indicating whether this is a valid surrogate pair.
///
/// trueIf is a valid surrogate pair, falseotherwise.
- public bool IsSurrogatePair => DecodeSurrogatePair(value, out _);
+ public bool IsSurrogatePair => DecodeSurrogatePair (value, out _);
+
+ ///
+ /// Gets a value indicating whether this is a high surrogate.
+ ///
+ public bool IsHighSurrogate => value >= highSurrogateMin && value <= highSurrogateMax;
+
+ ///
+ /// Gets a value indicating whether this is a low surrogate.
+ ///
+ public bool IsLowSurrogate => value >= lowSurrogateMin && value <= lowSurrogateMax;
///
/// Check if the rune is a non-spacing character.
///
/// True if is a non-spacing character, false otherwise.
- public bool IsNonSpacing => IsNonSpacingChar(value);
+ public bool IsNonSpacing => IsNonSpacingChar (value);
// Code points in the surrogate range are not valid for UTF-8.
const uint highSurrogateMin = 0xd800;
@@ -539,8 +543,7 @@ public static int InvalidIndex (byte [] buffer)
public static bool ValidRune (Rune rune)
{
if ((0 <= (int)rune.value && rune.value < highSurrogateMin) ||
- (lowSurrogateMax < rune.value && rune.value <= MaxRune.value))
- {
+ (lowSurrogateMax < rune.value && rune.value <= MaxRune.value)) {
return true;
}
@@ -552,7 +555,7 @@ public static bool ValidRune (Rune rune)
///
/// The rune.
/// trueIf is a surrogate code point, falseotherwise.
- public static bool IsSurrogateRune(uint rune)
+ public static bool IsSurrogateRune (uint rune)
{
return rune >= highSurrogateMin && rune <= lowSurrogateMax;
}
@@ -564,12 +567,11 @@ public static bool IsSurrogateRune(uint rune)
/// The low surrogate code point.
/// The returning rune.
/// Trueif the returning rune is greater than 0 Falseotherwise.
- public static bool EncodeSurrogatePair(uint highsurrogate, uint lowSurrogate, out Rune rune)
+ public static bool EncodeSurrogatePair (uint highsurrogate, uint lowSurrogate, out Rune rune)
{
rune = 0;
if (highsurrogate >= highSurrogateMin && highsurrogate <= highSurrogateMax &&
- lowSurrogate >= lowSurrogateMin && lowSurrogate <= lowSurrogateMax)
- {
+ lowSurrogate >= lowSurrogateMin && lowSurrogate <= lowSurrogateMax) {
//return 0x10000 + ((highsurrogate - highSurrogateMin) * 0x0400) + (lowSurrogate - lowSurrogateMin);
return (rune = 0x10000 + ((highsurrogate - highSurrogateMin) << 10) + (lowSurrogate - lowSurrogateMin)) > 0;
}
@@ -582,14 +584,13 @@ public static bool EncodeSurrogatePair(uint highsurrogate, uint lowSurrogate, ou
/// The rune
/// The chars if is valid. Empty otherwise.
/// trueIf is a valid surrogate pair, falseotherwise.
- public static bool DecodeSurrogatePair(uint rune, out char [] chars)
+ public static bool DecodeSurrogatePair (uint rune, out char [] chars)
{
uint s = rune - 0x10000;
uint h = highSurrogateMin + (s >> 10);
uint l = lowSurrogateMin + (s & 0x3FF);
- if (EncodeSurrogatePair (h, l, out Rune dsp) && dsp == rune)
- {
+ if (EncodeSurrogatePair (h, l, out Rune dsp) && dsp == rune) {
chars = new char [] { (char)h, (char)l };
return true;
}
@@ -603,13 +604,11 @@ public static bool DecodeSurrogatePair(uint rune, out char [] chars)
/// The string.
/// The chars if is valid. Empty otherwise.
/// trueIf is a valid surrogate pair, falseotherwise.
- public static bool DecodeSurrogatePair(string str, out char [] chars)
+ public static bool DecodeSurrogatePair (string str, out char [] chars)
{
- if (str.Length == 2)
- {
- chars = str.ToCharArray();
- if (EncodeSurrogatePair(chars[0], chars[1], out _))
- {
+ if (str.Length == 2) {
+ chars = str.ToCharArray ();
+ if (EncodeSurrogatePair (chars [0], chars [1], out _)) {
return true;
}
}
@@ -622,9 +621,9 @@ public static bool DecodeSurrogatePair(string str, out char [] chars)
///
/// The number of UTF8 bytes expected given the first prefix.
/// Is the first byte of a UTF8 sequence.
- public static int ExpectedSizeFromFirstByte(byte firstByte)
+ public static int ExpectedSizeFromFirstByte (byte firstByte)
{
- var x = first[firstByte];
+ var x = first [firstByte];
// Invalid runes, just return 1 for byte, and let higher level pass to print
if (x == xx)
@@ -806,7 +805,7 @@ public static Rune To (Case toCase, Rune rune)
{
uint rval = rune.value;
switch (toCase) {
- case Case.Lower:
+ case Case.Lower:
return new Rune (NStack.Unicode.To (NStack.Unicode.Case.Lower, rval));
case Case.Title:
return new Rune (NStack.Unicode.To (NStack.Unicode.Case.Title, rval));
@@ -874,6 +873,20 @@ public static Rune To (Case toCase, Rune rune)
/// Rune.
public static implicit operator uint (Rune rune) => rune.value;
+ ///
+ /// Implicit operator conversion from a C# integer into a rune.
+ ///
+ /// Rune representing the C# integer
+ /// 32-bit Integer.
+ public static implicit operator Rune (int value) => new Rune ((uint)value);
+
+ ///
+ /// Implicit operator conversion from a byte to an unsigned integer
+ ///
+ /// The unsigned integer representation.
+ /// Byte.
+ public static implicit operator Rune (byte byt) => new Rune (byt);
+
///
/// Implicit operator conversion from a C# char into a rune.
///
@@ -905,7 +918,7 @@ public override string ToString ()
{
var buff = new byte [4];
var size = EncodeRune (this, buff, 0);
- return System.Text.Encoding.UTF8.GetString(buff, 0, size);
+ return System.Text.Encoding.UTF8.GetString (buff, 0, size);
}
///
diff --git a/NStackTests/NStackTests.csproj b/NStackTests/NStackTests.csproj
index 125f663..8fdb4af 100644
--- a/NStackTests/NStackTests.csproj
+++ b/NStackTests/NStackTests.csproj
@@ -10,10 +10,10 @@
0.20.0
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/NStackTests/RuneTest.cs b/NStackTests/RuneTest.cs
index cf54695..65bec72 100644
--- a/NStackTests/RuneTest.cs
+++ b/NStackTests/RuneTest.cs
@@ -94,7 +94,7 @@ public void TestColumnWidth ()
var ui = ustring.Make (i);
(var runei, var sizei) = ui.DecodeRune ();
- Assert.AreEqual (1, Rune.ColumnWidth (runei));
+ Assert.AreEqual (2, Rune.ColumnWidth (runei));
Assert.AreEqual ("๓ ฟก", i);
Assert.AreEqual (2, runei.ToString ().Length);
Assert.AreEqual (4, Rune.RuneLen (runei));
@@ -106,13 +106,13 @@ public void TestColumnWidth ()
Assert.True (Rune.Valid (ui.ToByteArray ()));
Assert.True (Rune.FullRune (ui.ToByteArray ()));
(var runeli, var sizeli) = ui.DecodeLastRune ();
- Assert.AreEqual (1, Rune.ColumnWidth (runeli));
+ Assert.AreEqual (2, Rune.ColumnWidth (runeli));
Assert.AreEqual (2, runeli.ToString ().Length);
Assert.AreEqual (4, Rune.RuneLen (runeli));
Assert.AreEqual (sizeli, Rune.RuneLen (runeli));
Assert.IsTrue (Rune.ValidRune (runeli));
- Assert.AreNotEqual (Rune.ColumnWidth (runeh), Rune.ColumnWidth (runei));
+ Assert.AreEqual (Rune.ColumnWidth (runeh), Rune.ColumnWidth (runei));
Assert.AreNotEqual (h, i);
Assert.AreEqual (runeh.ToString ().Length, runei.ToString ().Length);
Assert.AreEqual (Rune.RuneLen (runeh), Rune.RuneLen (runei));
@@ -141,16 +141,16 @@ public void TestColumnWidth ()
Assert.AreEqual (1, m.ToString ().Length);
Assert.AreEqual (3, Rune.RuneLen (m));
var rn = ustring.Make (n).DecodeRune ().rune;
- Assert.AreEqual (1, Rune.ColumnWidth (rn));
+ Assert.AreEqual (2, Rune.ColumnWidth (rn));
Assert.AreEqual ("๐", rn.ToString ());
Assert.AreEqual (2, rn.ToString ().Length);
Assert.AreEqual (4, Rune.RuneLen (rn));
- Assert.AreEqual (1, Rune.ColumnWidth (o));
+ Assert.AreEqual (2, Rune.ColumnWidth (o));
Assert.AreEqual ("๐", o.ToString ());
Assert.AreEqual (2, o.ToString ().Length);
Assert.AreEqual (4, Rune.RuneLen (o));
var rp = ustring.Make (p).DecodeRune ().rune;
- Assert.AreEqual (1, Rune.ColumnWidth (rp));
+ Assert.AreEqual (2, Rune.ColumnWidth (rp));
Assert.AreEqual ("๐", p);
Assert.AreEqual (2, p.Length);
Assert.AreEqual (4, Rune.RuneLen (rp));
@@ -254,25 +254,25 @@ public void TestRune ()
Assert.AreEqual (1, c.ToString ().Length);
Assert.AreEqual ("a", c.ToString ());
Rune d = new Rune (0x10421);
- Assert.AreEqual (1, Rune.ColumnWidth (d));
+ Assert.AreEqual (2, Rune.ColumnWidth (d));
Assert.AreEqual (2, d.ToString ().Length);
Assert.AreEqual ("๐ก", d.ToString ());
Assert.False (Rune.EncodeSurrogatePair ('\ud799', '\udc21', out _));
Assert.Throws (() => new Rune ('\ud799', '\udc21'));
Rune e = new Rune ('\ud801', '\udc21');
- Assert.AreEqual (1, Rune.ColumnWidth (e));
+ Assert.AreEqual (2, Rune.ColumnWidth (e));
Assert.AreEqual (2, e.ToString ().Length);
Assert.AreEqual ("๐ก", e.ToString ());
Assert.False (new Rune ('\ud801').IsValid);
Rune f = new Rune ('\ud83c', '\udf39');
- Assert.AreEqual (1, Rune.ColumnWidth (f));
+ Assert.AreEqual (2, Rune.ColumnWidth (f));
Assert.AreEqual (2, f.ToString ().Length);
Assert.AreEqual ("๐น", f.ToString ());
Assert.DoesNotThrow (() => new Rune (0x10ffff));
Rune g = new Rune (0x10ffff);
string s = "\U0010ffff";
- Assert.AreEqual (1, Rune.ColumnWidth (g));
- Assert.AreEqual (1, ustring.Make (s).ConsoleWidth);
+ Assert.AreEqual (2, Rune.ColumnWidth (g));
+ Assert.AreEqual (2, ustring.Make (s).ConsoleWidth);
Assert.AreEqual (2, g.ToString ().Length);
Assert.AreEqual (2, s.Length);
Assert.AreEqual ("๔ฟฟ", g.ToString ());
@@ -292,15 +292,15 @@ public void TestRune ()
Assert.AreEqual (1, j.ToString ().Length);
Assert.AreEqual ("ๅฅฝ", j.ToString ());
var k = new Rune ('\ud83d', '\udc02');
- Assert.AreEqual (1, Rune.ColumnWidth (k));
+ Assert.AreEqual (2, Rune.ColumnWidth (k));
Assert.AreEqual (2, k.ToString ().Length);
Assert.AreEqual ("๐", k.ToString ());
var l = new Rune ('\ud801', '\udcbb');
- Assert.AreEqual (1, Rune.ColumnWidth (l));
+ Assert.AreEqual (2, Rune.ColumnWidth (l));
Assert.AreEqual (2, l.ToString ().Length);
Assert.AreEqual ("๐ป", l.ToString ());
var m = new Rune ('\ud801', '\udccf');
- Assert.AreEqual (1, Rune.ColumnWidth (m));
+ Assert.AreEqual (2, Rune.ColumnWidth (m));
Assert.AreEqual (2, m.ToString ().Length);
Assert.AreEqual ("๐", m.ToString ());
var n = new Rune ('\u00e1');
@@ -308,7 +308,7 @@ public void TestRune ()
Assert.AreEqual (1, n.ToString ().Length);
Assert.AreEqual ("รก", n.ToString ());
var o = new Rune ('\ud83d', '\udd2e');
- Assert.AreEqual (1, Rune.ColumnWidth (o));
+ Assert.AreEqual (2, Rune.ColumnWidth (o));
Assert.AreEqual (2, o.ToString ().Length);
Assert.AreEqual ("๐ฎ", o.ToString ());
var p = new Rune ('\u2329');
@@ -328,11 +328,11 @@ public void TestRune ()
PrintTextElementCount (ustring.Make ('\u0061', '\u0301'), "aฬ", 1, 2, 2, 1);
PrintTextElementCount (ustring.Make ('\u0065', '\u0301'), "eฬ", 1, 2, 2, 1);
PrintTextElementCount (ustring.Make (new Rune [] { new Rune (0x1f469), new Rune (0x1f3fd), new Rune ('\u200d'), new Rune (0x1f692) }),
- "๐ฉ๐ฝโ๐", 3, 4, 7, 1);
+ "๐ฉ๐ฝโ๐", 6, 4, 7, 1);
PrintTextElementCount (ustring.Make (new Rune [] { new Rune (0x1f469), new Rune (0x1f3fd), new Rune ('\u200d'), new Rune (0x1f692) }),
- "\U0001f469\U0001f3fd\u200d\U0001f692", 3, 4, 7, 1);
+ "\U0001f469\U0001f3fd\u200d\U0001f692", 6, 4, 7, 1);
PrintTextElementCount (ustring.Make (new Rune ('\ud801', '\udccf')),
- "\ud801\udccf", 1, 1, 2, 1);
+ "\ud801\udccf", 2, 1, 2, 1);
}
void PrintTextElementCount (ustring us, string s, int consoleWidth, int runeCount, int stringCount, int txtElementCount)
@@ -631,18 +631,18 @@ public void Test_Right_To_Left_Runes ()
Rune r1b = 0x02001b;
Rune r9b = 0x02009b;
- Assert.AreEqual (1, Rune.ColumnWidth (r0));
- Assert.AreEqual (1, Rune.ColumnWidth (r7));
- Assert.AreEqual (1, Rune.ColumnWidth (r1b));
- Assert.AreEqual (1, Rune.ColumnWidth (r9b));
+ Assert.AreEqual (2, Rune.ColumnWidth (r0));
+ Assert.AreEqual (2, Rune.ColumnWidth (r7));
+ Assert.AreEqual (2, Rune.ColumnWidth (r1b));
+ Assert.AreEqual (2, Rune.ColumnWidth (r9b));
Rune.DecodeSurrogatePair ("๐จ", out char [] chars);
var rtl = new Rune (chars [0], chars [1]);
var rtlp = new Rune ('\ud802', '\ude01');
var s = "\U00010a01";
- Assert.AreEqual (0, Rune.ColumnWidth (rtl));
- Assert.AreEqual (0, Rune.ColumnWidth (rtlp));
+ Assert.AreEqual (2, Rune.ColumnWidth (rtl));
+ Assert.AreEqual (2, Rune.ColumnWidth (rtlp));
Assert.AreEqual (2, s.Length);
}
@@ -886,6 +886,22 @@ public void Rune_ColumnWidth_Versus_Ustring_ConsoleWidth ()
sumRuneWidth = us.Sum (x => Rune.ColumnWidth (x));
Assert.AreEqual (199, sumRuneWidth);
}
+
+ [Test]
+ public void Rune_IsHighSurrogate_IsLowSurrogate ()
+ {
+ Rune r = '\ud800';
+ Assert.IsTrue (r.IsHighSurrogate);
+
+ r = '\udbff';
+ Assert.IsTrue (r.IsHighSurrogate);
+
+ r = '\udc00';
+ Assert.IsTrue (r.IsLowSurrogate);
+
+ r = '\udfff';
+ Assert.IsTrue (r.IsLowSurrogate);
+ }
}
}
// A Unicode character is considered a bidirectional text control character if it falls into any of the following ranges: U+061c, U+200e-U+200f, U+202a-U+202e, U+2066-U+2069.
\ No newline at end of file
diff --git a/NStackTests/ustringTest.cs b/NStackTests/ustringTest.cs
index e5680de..3e74fd1 100644
--- a/NStackTests/ustringTest.cs
+++ b/NStackTests/ustringTest.cs
@@ -723,9 +723,9 @@ public void TestConsoleWidth ()
Assert.AreEqual (1, Rune.ColumnWidth (r));
var fr = new Rune (sc, r);
Assert.False (Rune.IsNonSpacingChar (fr));
- Assert.AreEqual (1, Rune.ColumnWidth (fr));
+ Assert.AreEqual (2, Rune.ColumnWidth (fr));
var us = ustring.Make (fr);
- Assert.AreEqual (1, us.ConsoleWidth);
+ Assert.AreEqual (2, us.ConsoleWidth);
}
[Test]
@@ -887,5 +887,31 @@ public void Operator_Not_Equal_Ustring_Versus_String ()
Assert.False (ustr != " ");
Assert.False (str != " ");
}
+
+ [Test]
+ public void Ustring_Array_Is_Not_Equal_ToRunes_Array_And_String_Array ()
+ {
+ var text = "New Test ไฝ ";
+ ustring us = text;
+ string s = text;
+ Assert.AreEqual (10, us.RuneCount);
+ Assert.AreEqual (10, s.Length);
+ // The reason is ustring index is related to byte length and not rune length
+ Assert.AreEqual (12, us.Length);
+ Assert.AreNotEqual (20320, us [9]);
+ Assert.AreEqual (20320, s [9]);
+ Assert.AreEqual (228, us [9]);
+ Assert.AreEqual ("รค", ((Rune)us [9]).ToString ());
+ Assert.AreEqual ("ไฝ ", s [9].ToString ());
+
+ // Rune array is equal to string array
+ var usToRunes = us.ToRunes ();
+ Assert.AreEqual (10, usToRunes.Length);
+ Assert.AreEqual (10, s.Length);
+ Assert.AreEqual (20320, usToRunes [9]);
+ Assert.AreEqual (20320, s [9]);
+ Assert.AreEqual ("ไฝ ", ((Rune)usToRunes [9]).ToString ());
+ Assert.AreEqual ("ไฝ ", s [9].ToString ());
+ }
}
}
diff --git a/testenvironments.json b/testenvironments.json
new file mode 100644
index 0000000..70dbd0b
--- /dev/null
+++ b/testenvironments.json
@@ -0,0 +1,15 @@
+{
+ "version": "1",
+ "environments": [
+ {
+ "name": "WSL-Ubuntu",
+ "type": "wsl",
+ "wslDistribution": "Ubuntu"
+ },
+ {
+ "name": "WSL-Debian",
+ "type": "wsl",
+ "wslDistribution": "Debian"
+ }
+ ]
+}
\ No newline at end of file