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.
This commit is contained in:
Tig
2025-10-15 11:54:21 -06:00
committed by GitHub
parent c8147416b2
commit b83bcc2fdb
2 changed files with 34 additions and 37 deletions

View File

@@ -11,14 +11,14 @@
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.11.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="[8,9)" />
<PackageVersion Include="ColorHelper" Version="[1.8.1,2)" />
<PackageVersion Include="JetBrains.Annotations" Version="[2024.3.0,)" />
<PackageVersion Include="JetBrains.Annotations" Version="[2025.2.2,)" />
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.2,10)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.0,10)" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.11,23)" />
<PackageVersion Include="Wcwidth" Version="[2,3)" />
<PackageVersion Include="System.IO.Abstractions" Version="[22.0.16,23)" />
<PackageVersion Include="Wcwidth" Version="[3.0.0,)" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21.2,2)" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />

View File

@@ -37,22 +37,27 @@ public static class RuneExtensions
public static bool DecodeSurrogatePair (this Rune rune, out char []? chars)
{
bool isSingleUtf16CodeUnit = rune.IsBmp;
if (isSingleUtf16CodeUnit)
{
chars = null;
return false;
}
const int maxCharsPerRune = 2;
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
const int MAX_CHARS_PER_RUNE = 2;
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
int charsWritten = rune.EncodeToUtf16 (charBuffer);
if (charsWritten >= 2 && char.IsSurrogatePair (charBuffer [0], charBuffer [1]))
{
chars = charBuffer [..charsWritten].ToArray ();
return true;
}
chars = null;
return false;
}
@@ -65,23 +70,26 @@ public static class RuneExtensions
/// <returns>he number of bytes written into the destination buffer.</returns>
public static int Encode (this Rune rune, byte [] dest, int start = 0, int count = -1)
{
const int maxUtf8BytesPerRune = 4;
Span<byte> bytes = stackalloc byte[maxUtf8BytesPerRune];
const int MAX_UTF8_BYTES_PER_RUNE = 4;
Span<byte> bytes = stackalloc byte [MAX_UTF8_BYTES_PER_RUNE];
int writtenBytes = rune.EncodeToUtf8 (bytes);
int bytesToCopy = count == -1
? writtenBytes
: Math.Min (count, writtenBytes);
int bytesWritten = 0;
for (int i = 0; i < bytesToCopy; i++)
? writtenBytes
: Math.Min (count, writtenBytes);
var bytesWritten = 0;
for (var i = 0; i < bytesToCopy; i++)
{
if (bytes [i] == '\0')
{
break;
}
dest [start + i] = bytes [i];
bytesWritten++;
}
return bytesWritten;
}
@@ -111,22 +119,7 @@ public static class RuneExtensions
/// The number of columns required to fit the rune, 0 if the argument is the null character, or -1 if the value is
/// not printable, otherwise the number of columns that the rune occupies.
/// </returns>
public static int GetColumns (this Rune rune)
{
int value = rune.Value;
// TODO: Remove this code when #4259 is fixed
// TODO: See https://github.com/gui-cs/Terminal.Gui/issues/4259
if (value is >= 0x2630 and <= 0x2637 || // Trigrams
value is >= 0x268A and <= 0x268F || // Monograms/Digrams
value is >= 0x4DC0 and <= 0x4DFF) // Hexagrams
{
return 2; // Assume double-width due to Windows Terminal font rendering
}
// Fallback to original GetWidth for other code points
return UnicodeCalculator.GetWidth (rune);
}
public static int GetColumns (this Rune rune) { return UnicodeCalculator.GetWidth (rune); }
/// <summary>Get number of bytes required to encode the rune, based on the provided encoding.</summary>
/// <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 class RuneExtensions
{
encoding ??= Encoding.UTF8;
const int maxCharsPerRune = 2;
const int MAX_CHARS_PER_RUNE = 2;
// Get characters with UTF16 to keep that part independent of selected encoding.
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
int charsWritten = rune.EncodeToUtf16(charBuffer);
Span<char> chars = charBuffer[..charsWritten];
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
int charsWritten = rune.EncodeToUtf16 (charBuffer);
Span<char> chars = charBuffer [..charsWritten];
int maxEncodedLength = encoding.GetMaxByteCount (charsWritten);
Span<byte> byteBuffer = stackalloc byte[maxEncodedLength];
Span<byte> byteBuffer = stackalloc byte [maxEncodedLength];
int bytesEncoded = encoding.GetBytes (chars, byteBuffer);
ReadOnlySpan<byte> encodedBytes = byteBuffer[..bytesEncoded];
ReadOnlySpan<byte> encodedBytes = byteBuffer [..bytesEncoded];
if (encodedBytes [^1] == '\0')
{
return encodedBytes.Length - 1;
}
return encodedBytes.Length;
}
@@ -175,14 +170,16 @@ public static class RuneExtensions
public static bool IsSurrogatePair (this Rune rune)
{
bool isSingleUtf16CodeUnit = rune.IsBmp;
if (isSingleUtf16CodeUnit)
{
return false;
}
const int maxCharsPerRune = 2;
Span<char> charBuffer = stackalloc char[maxCharsPerRune];
const int MAX_CHARS_PER_RUNE = 2;
Span<char> charBuffer = stackalloc char [MAX_CHARS_PER_RUNE];
int charsWritten = rune.EncodeToUtf16 (charBuffer);
return charsWritten >= 2 && char.IsSurrogatePair (charBuffer [0], charBuffer [1]);
}
@@ -193,5 +190,5 @@ public static class RuneExtensions
/// <remarks>This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.</remarks>
/// <param name="rune"></param>
/// <returns></returns>
public static Rune MakePrintable (this Rune rune) { return Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune; }
public static Rune MakePrintable (this Rune rune) { return Rune.IsControl (rune) ? new (rune.Value + 0x2400) : rune; }
}