mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 08:52:50 +08:00

The bug might occur if there are wide characters such as emojis at the end of a line. The SplitLines method mixed cell width and text length, which might give incorrect results. This commit makes sure that comparison and calculation is done using cell width where it's appropriate.
128 lines
3.7 KiB
C#
128 lines
3.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Spectre.Console.Rendering;
|
|
|
|
namespace Spectre.Console.Internal
|
|
{
|
|
internal static class StringExtensions
|
|
{
|
|
// Cache whether or not internally normalized line endings
|
|
// already are normalized. No reason to do yet another replace if it is.
|
|
private static readonly bool _alreadyNormalized
|
|
= Environment.NewLine.Equals("\n", StringComparison.OrdinalIgnoreCase);
|
|
|
|
public static int CellLength(this string text, RenderContext context)
|
|
{
|
|
if (context is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
return Cell.GetCellLength(context, text);
|
|
}
|
|
|
|
public static string Capitalize(this string text, CultureInfo? culture = null)
|
|
{
|
|
if (text == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
culture ??= CultureInfo.InvariantCulture;
|
|
|
|
if (text.Length > 0 && char.IsLower(text[0]))
|
|
{
|
|
text = string.Format(culture, "{0}{1}", char.ToUpper(text[0], culture), text.Substring(1));
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
public static string NormalizeLineEndings(this string text, bool native = false)
|
|
{
|
|
text ??= string.Empty;
|
|
|
|
var normalized = text?.Replace("\r\n", "\n")?.Replace("\r", string.Empty) ?? string.Empty;
|
|
if (native && !_alreadyNormalized)
|
|
{
|
|
normalized = normalized.Replace("\n", Environment.NewLine);
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
public static string[] SplitLines(this string text)
|
|
{
|
|
var result = text?.NormalizeLineEndings()?.Split(new[] { '\n' }, StringSplitOptions.None);
|
|
return result ?? Array.Empty<string>();
|
|
}
|
|
|
|
public static string[] SplitWords(this string word, StringSplitOptions options = StringSplitOptions.None)
|
|
{
|
|
var result = new List<string>();
|
|
|
|
static string Read(StringBuffer reader, Func<char, bool> criteria)
|
|
{
|
|
var buffer = new StringBuilder();
|
|
while (!reader.Eof)
|
|
{
|
|
var current = reader.Peek();
|
|
if (!criteria(current))
|
|
{
|
|
break;
|
|
}
|
|
|
|
buffer.Append(reader.Read());
|
|
}
|
|
|
|
return buffer.ToString();
|
|
}
|
|
|
|
using (var reader = new StringBuffer(word))
|
|
{
|
|
while (!reader.Eof)
|
|
{
|
|
var current = reader.Peek();
|
|
if (char.IsWhiteSpace(current))
|
|
{
|
|
var x = Read(reader, c => char.IsWhiteSpace(c));
|
|
if (options != StringSplitOptions.RemoveEmptyEntries)
|
|
{
|
|
result.Add(x);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.Add(Read(reader, c => !char.IsWhiteSpace(c)));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result.ToArray();
|
|
}
|
|
|
|
public static string Repeat(this string text, int count)
|
|
{
|
|
if (text is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(text));
|
|
}
|
|
|
|
if (count <= 0)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (count == 1)
|
|
{
|
|
return text;
|
|
}
|
|
|
|
return string.Concat(Enumerable.Repeat(text, count));
|
|
}
|
|
}
|
|
}
|