Update calculation of char width to respect CJK chars correctly (#11262)

This commit is contained in:
Steve Lee
2019-12-05 08:09:56 +05:00
committed by Ilya
parent d0ac69e667
commit ee4eba5348
4 changed files with 100 additions and 29 deletions
@@ -2781,7 +2781,36 @@ namespace Microsoft.PowerShell
}
}
return str.Length - offset - escapeSequenceAdjustment;
int length = 0;
foreach (char c in str)
{
length += LengthInBufferCells(c);
}
return length - offset - escapeSequenceAdjustment;
}
internal static int LengthInBufferCells(char c)
{
// The following is based on http://www.cl.cam.ac.uk/~mgk25/c/wcwidth.c
// which is derived from https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
bool isWide = c >= 0x1100 &&
(c <= 0x115f || /* Hangul Jamo init. consonants */
c == 0x2329 || c == 0x232a ||
((uint)(c - 0x2e80) <= (0xa4cf - 0x2e80) &&
c != 0x303f) || /* CJK ... Yi */
((uint)(c - 0xac00) <= (0xd7a3 - 0xac00)) || /* Hangul Syllables */
((uint)(c - 0xf900) <= (0xfaff - 0xf900)) || /* CJK Compatibility Ideographs */
((uint)(c - 0xfe10) <= (0xfe19 - 0xfe10)) || /* Vertical forms */
((uint)(c - 0xfe30) <= (0xfe6f - 0xfe30)) || /* CJK Compatibility Forms */
((uint)(c - 0xff00) <= (0xff60 - 0xff00)) || /* Fullwidth Forms */
((uint)(c - 0xffe0) <= (0xffe6 - 0xffe0)));
// We can ignore these ranges because .Net strings use surrogate pairs
// for this range and we do not handle surrogage pairs.
// (c >= 0x20000 && c <= 0x2fffd) ||
// (c >= 0x30000 && c <= 0x3fffd)
return 1 + (isWide ? 1 : 0);
}
#if !UNIX
@@ -2950,33 +2979,6 @@ namespace Microsoft.PowerShell
#endregion helper
#region
internal static int LengthInBufferCells(char c)
{
// The following is based on http://www.cl.cam.ac.uk/~mgk25/c/wcwidth.c
// which is derived from https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
bool isWide = c >= 0x1100 &&
(c <= 0x115f || /* Hangul Jamo init. consonants */
c == 0x2329 || c == 0x232a ||
(c >= 0x2e80 && c <= 0xa4cf &&
c != 0x303f) || /* CJK ... Yi */
(c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
(c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
(c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
(c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
(c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
(c >= 0xffe0 && c <= 0xffe6));
// We can ignore these ranges because .Net strings use surrogate pairs
// for this range and we do not handle surrogage pairs.
// (c >= 0x20000 && c <= 0x2fffd) ||
// (c >= 0x30000 && c <= 0x3fffd)
return 1 + (isWide ? 1 : 0);
}
#endregion
#region SendInput
internal static void MimicKeyPress(INPUT[] inputs)
@@ -29,7 +29,14 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
internal virtual int Length(string str, int offset)
{
return str.Length - offset;
int length = 0;
foreach (char c in str)
{
length += LengthInBufferCells(c);
}
return length - offset;
}
internal virtual int Length(char character) { return 1; }
@@ -58,6 +65,29 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
#region Helpers
protected static int LengthInBufferCells(char c)
{
// The following is based on http://www.cl.cam.ac.uk/~mgk25/c/wcwidth.c
// which is derived from https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
bool isWide = c >= 0x1100 &&
(c <= 0x115f || /* Hangul Jamo init. consonants */
c == 0x2329 || c == 0x232a ||
((uint)(c - 0x2e80) <= (0xa4cf - 0x2e80) &&
c != 0x303f) || /* CJK ... Yi */
((uint)(c - 0xac00) <= (0xd7a3 - 0xac00)) || /* Hangul Syllables */
((uint)(c - 0xf900) <= (0xfaff - 0xf900)) || /* CJK Compatibility Ideographs */
((uint)(c - 0xfe10) <= (0xfe19 - 0xfe10)) || /* Vertical forms */
((uint)(c - 0xfe30) <= (0xfe6f - 0xfe30)) || /* CJK Compatibility Forms */
((uint)(c - 0xff00) <= (0xff60 - 0xff00)) || /* Fullwidth Forms */
((uint)(c - 0xffe0) <= (0xffe6 - 0xffe0)));
// We can ignore these ranges because .Net strings use surrogate pairs
// for this range and we do not handle surrogage pairs.
// (c >= 0x20000 && c <= 0x2fffd) ||
// (c >= 0x30000 && c <= 0x3fffd)
return 1 + (isWide ? 1 : 0);
}
/// <summary>
/// Given a string and a number of display cells, it computes how many
/// characters would fit starting from the beginning or end of the string.
@@ -159,4 +159,24 @@ Describe "Format-List DRT basic functionality" -Tags "CI" {
$result | Should -Match "Name\s*:\s*test.txt"
$result | Should -Match "Length\s*:\s*5"
}
It "Format-List should work with double byte wide chars" {
$obj = [pscustomobject]@{
"哇" = "62";
"dbda" = "KM";
"消息" = "千"
}
$expected = @"
哇 : 62
dbda : KM
消息 : 千
"@
$obj | Format-List | Out-String | Should -BeExactly $expected
}
}
@@ -812,4 +812,23 @@ A Name B
$output = [pscustomobject] @{ one = 1 } | Format-Table @{ l='one'; e='one'; width=10; alignment='center' } | Out-String
$output.Replace("`r","").Replace(" ",".").Replace("`n","^") | Should -BeExactly $expectedTable.Replace("`r","").Replace(" ",".").Replace("`n","^")
}
It "Should be formatted correctly with double byte wide chars" {
$obj = [pscustomobject]@{
"哇" = "62";
"dbda" = "KM";
"消息" = "千"
}
$expected = @"
哇 dbda 消息
-- ---- ----
62 KM 千
"@
$obj | Format-Table | Out-String | Should -BeExactly $expected
}
}