GraphemeHelper.cs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. using System.Globalization;
  2. namespace Terminal.Gui.Drawing;
  3. /// <summary>
  4. /// Provides utility methods for enumerating Unicode grapheme clusters (user-perceived characters)
  5. /// in a string. A grapheme cluster may consist of one or more <see cref="Rune"/> values,
  6. /// including combining marks or zero-width joiner (ZWJ) sequences such as emoji family groups.
  7. /// </summary>
  8. /// <remarks>
  9. /// <para>
  10. /// This helper uses <see cref="StringInfo.GetTextElementEnumerator(string)"/> to enumerate
  11. /// text elements according to the Unicode Standard Annex #29 (UAX #29) rules for
  12. /// extended grapheme clusters.
  13. /// </para>
  14. /// <para>
  15. /// On legacy Windows consoles (e.g., <c>cmd.exe</c>, <c>conhost.exe</c>), complex grapheme
  16. /// sequences such as ZWJ emoji or combining marks may not render correctly, even though
  17. /// the underlying string data is valid.
  18. /// </para>
  19. /// <para>
  20. /// For most accurate visual rendering, prefer modern terminals such as Windows Terminal
  21. /// or Linux-based terminals with full Unicode and font support.
  22. /// </para>
  23. /// </remarks>
  24. public static class GraphemeHelper
  25. {
  26. /// <summary>
  27. /// Enumerates extended grapheme clusters from a string.
  28. /// Handles surrogate pairs, combining marks, and basic ZWJ sequences.
  29. /// Safe for legacy consoles; memory representation is correct.
  30. /// </summary>
  31. public static IEnumerable<string> GetGraphemes (string text)
  32. {
  33. if (string.IsNullOrEmpty (text))
  34. {
  35. yield break;
  36. }
  37. TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (text);
  38. while (enumerator.MoveNext ())
  39. {
  40. string element = enumerator.GetTextElement ();
  41. yield return element;
  42. }
  43. }
  44. /// <summary>
  45. /// Counts the number of grapheme clusters in a string without allocating intermediate collections.
  46. /// </summary>
  47. /// <param name="text">The string to count graphemes in.</param>
  48. /// <returns>The number of grapheme clusters, or 0 if the string is null or empty.</returns>
  49. public static int GetGraphemeCount (string text)
  50. {
  51. if (string.IsNullOrEmpty (text))
  52. {
  53. return 0;
  54. }
  55. TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (text);
  56. var count = 0;
  57. while (enumerator.MoveNext ())
  58. {
  59. count++;
  60. }
  61. return count;
  62. }
  63. }