Skip to content

Commit b83bcc2

Browse files
authored
Fixes #4259. Our wcwidth library is out of date (#4281)
* Update package versions and remove hack code from RuneExtensions Updated several package versions in `Directory.Packages.props`, including `JetBrains.Annotations`, `Microsoft.Extensions.Logging.Abstractions`, `System.IO.Abstractions`, and `Wcwidth`. Refactored methods in `RuneExtensions.cs`: - Simplified `GetColumns` by removing special Unicode handling. - Renamed constants to follow naming conventions. - Improved logic and readability in `DecodeSurrogatePair`, `Encode`, and `GetEncodingLength`. - Streamlined `IsSurrogatePair` and `MakePrintable` for clarity and efficiency. * Update package version ranges for flexibility Updated the `JetBrains.Annotations` package to use a version range starting from `2025.2.2` to allow future updates. Adjusted the `Microsoft.Extensions.Logging.Abstractions` package to a version range `[9.0.0,10)` for compatibility. Changed `System.IO.Abstractions` to a range `[22.0.16,23)` and `Wcwidth` to `[3.0.0,)` to enable future updates within specified ranges.
1 parent c814741 commit b83bcc2

File tree

2 files changed

+34
-37
lines changed

2 files changed

+34
-37
lines changed

Directory.Packages.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.11.0" />
1212
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="[8,9)" />
1313
<PackageVersion Include="ColorHelper" Version="[1.8.1,2)" />
14-
<PackageVersion Include="JetBrains.Annotations" Version="[2024.3.0,)" />
14+
<PackageVersion Include="JetBrains.Annotations" Version="[2025.2.2,)" />
1515
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.11.0" />
1616
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" />
1717
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
18-
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.2,10)" />
18+
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.0,10)" />
1919
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
20-
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.11,23)" />
21-
<PackageVersion Include="Wcwidth" Version="[2,3)" />
20+
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.16,23)" />
21+
<PackageVersion Include="Wcwidth" Version="[3.0.0,)" />
2222
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21.2,2)" />
2323
<PackageVersion Include="Serilog" Version="4.2.0" />
2424
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />

Terminal.Gui/Text/RuneExtensions.cs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,27 @@ public static bool CanBeEncodedAsRune (byte [] buffer)
3737
public static bool DecodeSurrogatePair (this Rune rune, out char []? chars)
3838
{
3939
bool isSingleUtf16CodeUnit = rune.IsBmp;
40+
4041
if (isSingleUtf16CodeUnit)
4142
{
4243
chars = null;
44+
4345
return false;
4446
}
4547

46-
const int maxCharsPerRune = 2;
47-
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
48+
const int MAX_CHARS_PER_RUNE = 2;
49+
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
4850
int charsWritten = rune.EncodeToUtf16 (charBuffer);
51+
4952
if (charsWritten >= 2 && char.IsSurrogatePair (charBuffer [0], charBuffer [1]))
5053
{
5154
chars = charBuffer [..charsWritten].ToArray ();
55+
5256
return true;
5357
}
5458

5559
chars = null;
60+
5661
return false;
5762
}
5863

@@ -65,23 +70,26 @@ public static bool DecodeSurrogatePair (this Rune rune, out char []? chars)
6570
/// <returns>he number of bytes written into the destination buffer.</returns>
6671
public static int Encode (this Rune rune, byte [] dest, int start = 0, int count = -1)
6772
{
68-
const int maxUtf8BytesPerRune = 4;
69-
Span<byte> bytes = stackalloc byte[maxUtf8BytesPerRune];
73+
const int MAX_UTF8_BYTES_PER_RUNE = 4;
74+
Span<byte> bytes = stackalloc byte [MAX_UTF8_BYTES_PER_RUNE];
7075
int writtenBytes = rune.EncodeToUtf8 (bytes);
7176

7277
int bytesToCopy = count == -1
73-
? writtenBytes
74-
: Math.Min (count, writtenBytes);
75-
int bytesWritten = 0;
76-
for (int i = 0; i < bytesToCopy; i++)
78+
? writtenBytes
79+
: Math.Min (count, writtenBytes);
80+
var bytesWritten = 0;
81+
82+
for (var i = 0; i < bytesToCopy; i++)
7783
{
7884
if (bytes [i] == '\0')
7985
{
8086
break;
8187
}
88+
8289
dest [start + i] = bytes [i];
8390
bytesWritten++;
8491
}
92+
8593
return bytesWritten;
8694
}
8795

@@ -111,22 +119,7 @@ public static bool EncodeSurrogatePair (char highSurrogate, char lowSurrogate, o
111119
/// The number of columns required to fit the rune, 0 if the argument is the null character, or -1 if the value is
112120
/// not printable, otherwise the number of columns that the rune occupies.
113121
/// </returns>
114-
public static int GetColumns (this Rune rune)
115-
{
116-
int value = rune.Value;
117-
118-
// TODO: Remove this code when #4259 is fixed
119-
// TODO: See https://github.com/gui-cs/Terminal.Gui/issues/4259
120-
if (value is >= 0x2630 and <= 0x2637 || // Trigrams
121-
value is >= 0x268A and <= 0x268F || // Monograms/Digrams
122-
value is >= 0x4DC0 and <= 0x4DFF) // Hexagrams
123-
{
124-
return 2; // Assume double-width due to Windows Terminal font rendering
125-
}
126-
127-
// Fallback to original GetWidth for other code points
128-
return UnicodeCalculator.GetWidth (rune);
129-
}
122+
public static int GetColumns (this Rune rune) { return UnicodeCalculator.GetWidth (rune); }
130123

131124
/// <summary>Get number of bytes required to encode the rune, based on the provided encoding.</summary>
132125
/// <remarks>This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.</remarks>
@@ -137,21 +130,23 @@ public static int GetEncodingLength (this Rune rune, Encoding? encoding = null)
137130
{
138131
encoding ??= Encoding.UTF8;
139132

140-
const int maxCharsPerRune = 2;
133+
const int MAX_CHARS_PER_RUNE = 2;
134+
141135
// Get characters with UTF16 to keep that part independent of selected encoding.
142-
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
143-
int charsWritten = rune.EncodeToUtf16(charBuffer);
144-
Span<char> chars = charBuffer[..charsWritten];
136+
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
137+
int charsWritten = rune.EncodeToUtf16 (charBuffer);
138+
Span<char> chars = charBuffer [..charsWritten];
145139

146140
int maxEncodedLength = encoding.GetMaxByteCount (charsWritten);
147-
Span<byte> byteBuffer = stackalloc byte[maxEncodedLength];
141+
Span<byte> byteBuffer = stackalloc byte [maxEncodedLength];
148142
int bytesEncoded = encoding.GetBytes (chars, byteBuffer);
149-
ReadOnlySpan<byte> encodedBytes = byteBuffer[..bytesEncoded];
143+
ReadOnlySpan<byte> encodedBytes = byteBuffer [..bytesEncoded];
150144

151145
if (encodedBytes [^1] == '\0')
152146
{
153147
return encodedBytes.Length - 1;
154148
}
149+
155150
return encodedBytes.Length;
156151
}
157152

@@ -175,14 +170,16 @@ public static bool IsCombiningMark (this Rune rune)
175170
public static bool IsSurrogatePair (this Rune rune)
176171
{
177172
bool isSingleUtf16CodeUnit = rune.IsBmp;
173+
178174
if (isSingleUtf16CodeUnit)
179175
{
180176
return false;
181177
}
182178

183-
const int maxCharsPerRune = 2;
184-
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
179+
const int MAX_CHARS_PER_RUNE = 2;
180+
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
185181
int charsWritten = rune.EncodeToUtf16 (charBuffer);
182+
186183
return charsWritten >= 2 && char.IsSurrogatePair (charBuffer [0], charBuffer [1]);
187184
}
188185

@@ -193,5 +190,5 @@ public static bool IsSurrogatePair (this Rune rune)
193190
/// <remarks>This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.</remarks>
194191
/// <param name="rune"></param>
195192
/// <returns></returns>
196-
public static Rune MakePrintable (this Rune rune) { return Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune; }
193+
public static Rune MakePrintable (this Rune rune) { return Rune.IsControl (rune) ? new (rune.Value + 0x2400) : rune; }
197194
}

0 commit comments

Comments
 (0)