StringExtensions.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. namespace Terminal.Gui;
  7. /// <summary>
  8. /// Extensions to <see cref="string"/> to support TUI text manipulation.
  9. /// </summary>
  10. public static class StringExtensions {
  11. /// <summary>
  12. /// Repeats the string <paramref name="n"/> times.
  13. /// </summary>
  14. /// <remarks>
  15. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  16. /// </remarks>
  17. /// <param name="str">The text to repeat.</param>
  18. /// <param name="n">Number of times to repeat the text.</param>
  19. /// <returns>
  20. /// The text repeated if <paramref name="n"/> is greater than zero,
  21. /// otherwise <see langword="null"/>.
  22. /// </returns>
  23. public static string? Repeat (this string str, int n)
  24. {
  25. if (n <= 0) {
  26. return null;
  27. }
  28. if (string.IsNullOrEmpty (str) || n == 1) {
  29. return str;
  30. }
  31. return new StringBuilder (str.Length * n)
  32. .Insert (0, str, n)
  33. .ToString ();
  34. }
  35. /// <summary>
  36. /// Gets the number of columns the string occupies in the terminal.
  37. /// </summary>
  38. /// <remarks>
  39. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  40. /// </remarks>
  41. /// <param name="str">The string to measure.</param>
  42. /// <returns></returns>
  43. public static int GetColumns (this string str)
  44. {
  45. return str == null ? 0 : str.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 0));
  46. }
  47. /// <summary>
  48. /// Gets the number of runes in the string.
  49. /// </summary>
  50. /// <remarks>
  51. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  52. /// </remarks>
  53. /// <param name="str">The string to count.</param>
  54. /// <returns></returns>
  55. public static int GetRuneCount (this string str) => str.EnumerateRunes ().Count ();
  56. /// <summary>
  57. /// Converts the string into a <see cref="Rune"/> array.
  58. /// </summary>
  59. /// <remarks>
  60. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  61. /// </remarks>
  62. /// <param name="str">The string to convert.</param>
  63. /// <returns></returns>
  64. public static Rune [] ToRunes (this string str) => str.EnumerateRunes ().ToArray ();
  65. /// <summary>
  66. /// Converts the string into a <see cref="List{Rune}"/>.
  67. /// </summary>
  68. /// <remarks>
  69. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  70. /// </remarks>
  71. /// <param name="str">The string to convert.</param>
  72. /// <returns></returns>
  73. public static List<Rune> ToRuneList (this string str) => str.EnumerateRunes ().ToList ();
  74. /// <summary>
  75. /// Unpacks the first UTF-8 encoding in the string and returns the rune and its width in bytes.
  76. /// </summary>
  77. /// <remarks>
  78. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  79. /// </remarks>
  80. /// <param name="str">The string to decode.</param>
  81. /// <param name="start">Starting offset.</param>
  82. /// <param name="count">Number of bytes in the buffer, or -1 to make it the length of the buffer.</param>
  83. /// <returns></returns>
  84. public static (Rune Rune, int Size) DecodeRune (this string str, int start = 0, int count = -1)
  85. {
  86. var rune = str.EnumerateRunes ().ToArray () [start];
  87. var bytes = Encoding.UTF8.GetBytes (rune.ToString ());
  88. if (count == -1) {
  89. count = bytes.Length;
  90. }
  91. var operationStatus = Rune.DecodeFromUtf8 (bytes, out rune, out int bytesConsumed);
  92. if (operationStatus == System.Buffers.OperationStatus.Done && bytesConsumed >= count) {
  93. return (rune, bytesConsumed);
  94. }
  95. return (Rune.ReplacementChar, 1);
  96. }
  97. /// <summary>
  98. /// Unpacks the last UTF-8 encoding in the string.
  99. /// </summary>
  100. /// <remarks>
  101. /// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
  102. /// </remarks>
  103. /// <param name="str">The string to decode.</param>
  104. /// <param name="end">Index in string to stop at; if -1, use the buffer length.</param>
  105. /// <returns></returns>
  106. public static (Rune rune, int size) DecodeLastRune (this string str, int end = -1)
  107. {
  108. var rune = str.EnumerateRunes ().ToArray () [end == -1 ? ^1 : end];
  109. var bytes = Encoding.UTF8.GetBytes (rune.ToString ());
  110. var operationStatus = Rune.DecodeFromUtf8 (bytes, out rune, out int bytesConsumed);
  111. if (operationStatus == System.Buffers.OperationStatus.Done) {
  112. return (rune, bytesConsumed);
  113. }
  114. return (Rune.ReplacementChar, 1);
  115. }
  116. /// <summary>
  117. /// Converts a <see cref="Rune"/> generic collection into a string.
  118. /// </summary>
  119. /// <param name="runes">The enumerable rune to convert.</param>
  120. /// <returns></returns>
  121. public static string ToString (IEnumerable<Rune> runes)
  122. {
  123. var str = string.Empty;
  124. foreach (var rune in runes) {
  125. str += rune.ToString ();
  126. }
  127. return str;
  128. }
  129. /// <summary>
  130. /// Converts a byte generic collection into a string in the provided encoding (default is UTF8)
  131. /// </summary>
  132. /// <param name="bytes">The enumerable byte to convert.</param>
  133. /// <param name="encoding">The encoding to be used.</param>
  134. /// <returns></returns>
  135. public static string ToString (IEnumerable<byte> bytes, Encoding? encoding = null)
  136. {
  137. encoding ??= Encoding.UTF8;
  138. return encoding.GetString (bytes.ToArray ());
  139. }
  140. /// <summary>
  141. /// Determines if this <see cref="ReadOnlySpan{T}" /> of <see langword="char" /> is composed entirely of ASCII digits.
  142. /// </summary>
  143. /// <param name="stringSpan">A <see cref="ReadOnlySpan{T}" /> of <see langword="char" /> to check.</param>
  144. /// <returns>
  145. /// A <see langword="bool" /> indicating if all elements of the <see cref="ReadOnlySpan{T}" /> are ASCII digits (<see langword="true" />) or
  146. /// not (<see langword="false" />
  147. /// </returns>
  148. public static bool IsAllAsciiDigits (this ReadOnlySpan<char> stringSpan)
  149. {
  150. return stringSpan.ToString ().All (char.IsAsciiDigit);
  151. }
  152. /// <summary>
  153. /// Determines if this <see cref="ReadOnlySpan{T}" /> of <see langword="char" /> is composed entirely of ASCII digits.
  154. /// </summary>
  155. /// <param name="stringSpan">A <see cref="ReadOnlySpan{T}" /> of <see langword="char" /> to check.</param>
  156. /// <returns>
  157. /// A <see langword="bool" /> indicating if all elements of the <see cref="ReadOnlySpan{T}" /> are ASCII digits (<see langword="true" />) or
  158. /// not (<see langword="false" />
  159. /// </returns>
  160. public static bool IsAllAsciiHexDigits (this ReadOnlySpan<char> stringSpan)
  161. {
  162. return stringSpan.ToString ().All (char.IsAsciiHexDigit);
  163. }
  164. }