MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Globalization;6using System.Management.Automation.Host;7using System.Text;8using System.Text.RegularExpressions;9using System.Threading;10 11using Dbg = System.Management.Automation.Diagnostics;12 13namespace System.Management.Automation.Internal14{15 internal static class StringUtil16 {17 internal static string Format(string format, object arg0)18 => string.Format(CultureInfo.CurrentCulture, format, arg0);19 20 internal static string Format(string format, object arg0, object arg1)21 => string.Format(CultureInfo.CurrentCulture, format, arg0, arg1);22 23 internal static string Format(string format, object arg0, object arg1, object arg2)24 => string.Format(CultureInfo.CurrentCulture, format, arg0, arg1, arg2);25 26 internal static string Format(string format, params object[] args)27 => string.Format(CultureInfo.CurrentCulture, format, args);28 29 internal static string TruncateToBufferCellWidth(PSHostRawUserInterface rawUI, string toTruncate, int maxWidthInBufferCells)30 {31 Dbg.Assert(rawUI != null, "need a reference");32 Dbg.Assert(maxWidthInBufferCells >= 0, "maxWidthInBufferCells must be positive");33 34 string result;35 int i = Math.Min(toTruncate.Length, maxWidthInBufferCells);36 37 while (true)38 {39 result = toTruncate.Substring(0, i);40 int cellCount = rawUI.LengthInBufferCells(result);41 if (cellCount <= maxWidthInBufferCells)42 {43 // the segment from start..i fits44 45 break;46 }47 else48 {49 // The segment does not fit, back off a tad until it does50 // We need to back off 1 by 1 because there could theoretically51 // be characters taking more 2 buffer cells52 --i;53 }54 }55 56 return result;57 }58 59 // Typical padding is at most a screen's width, any more than that and we won't bother caching.60 private const int IndentCacheMax = 120;61 62 private static readonly string[] s_indentCache = new string[IndentCacheMax];63 64 internal static string Padding(int countOfSpaces)65 {66 if (countOfSpaces >= IndentCacheMax)67 return new string(' ', countOfSpaces);68 69 var result = s_indentCache[countOfSpaces];70 71 if (result == null)72 {73 Interlocked.CompareExchange(ref s_indentCache[countOfSpaces], new string(' ', countOfSpaces), null);74 result = s_indentCache[countOfSpaces];75 }76 77 return result;78 }79 80 private const int DashCacheMax = 120;81 82 private static readonly string[] s_dashCache = new string[DashCacheMax];83 84 internal static string DashPadding(int count)85 {86 if (count >= DashCacheMax)87 return new string('-', count);88 89 var result = s_dashCache[count];90 91 if (result == null)92 {93 Interlocked.CompareExchange(ref s_dashCache[count], new string('-', count), null);94 result = s_dashCache[count];95 }96 97 return result;98 }99 100 /// <summary>101 /// Substring implementation that takes into account the VT escape sequences.102 /// </summary>103 /// <param name="str">String that may contain VT escape sequences.</param>104 /// <param name="startOffset">105 /// When the string doesn't contain VT sequences, it's the starting index.106 /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.107 /// </param>108 /// <returns>The requested substring.</returns>109 internal static string VtSubstring(this string str, int startOffset)110 {111 return VtSubstring(str, startOffset, int.MaxValue, prependStr: null, appendStr: null);112 }113 114 /// <summary>115 /// Substring implementation that takes into account the VT escape sequences.116 /// </summary>117 /// <param name="str">String that may contain VT escape sequences.</param>118 /// <param name="startOffset">119 /// When the string doesn't contain VT sequences, it's the starting index.120 /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.</param>121 /// <param name="length">Number of non-escape-sequence characters to be included in the substring.</param>122 /// <returns>The requested substring.</returns>123 internal static string VtSubstring(this string str, int startOffset, int length)124 {125 return VtSubstring(str, startOffset, length, prependStr: null, appendStr: null);126 }127 128 /// <summary>129 /// Substring implementation that takes into account the VT escape sequences.130 /// </summary>131 /// <param name="str">String that may contain VT escape sequences.</param>132 /// <param name="startOffset">133 /// When the string doesn't contain VT sequences, it's the starting index.134 /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.</param>135 /// <param name="prependStr">The string to be prepended to the substring.</param>136 /// <param name="appendStr">The string to be appended to the substring.</param>137 /// <returns>The requested substring.</returns>138 internal static string VtSubstring(this string str, int startOffset, string prependStr, string appendStr)139 {140 return VtSubstring(str, startOffset, int.MaxValue, prependStr, appendStr);141 }142 143 /// <summary>144 /// Substring implementation that takes into account the VT escape sequences.145 /// </summary>146 /// <param name="str">String that may contain VT escape sequences.</param>147 /// <param name="startOffset">148 /// When the string doesn't contain VT sequences, it's the starting index.149 /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.</param>150 /// <param name="length">Number of non-escape-sequence characters to be included in the substring.</param>151 /// <param name="prependStr">The string to be prepended to the substring.</param>152 /// <param name="appendStr">The string to be appended to the substring.</param>153 /// <returns>The requested substring.</returns>154 internal static string VtSubstring(this string str, int startOffset, int length, string prependStr, string appendStr)155 {156 var valueStrDec = new ValueStringDecorated(str);157 if (valueStrDec.IsDecorated)158 {159 // Handle strings with VT sequences.160 bool copyStarted = startOffset == 0;161 bool hasEscSeqs = false;162 bool firstNonEscChar = true;163 StringBuilder sb = new(capacity: str.Length);164 Dictionary<int, int> vtRanges = valueStrDec.EscapeSequenceRanges;165 166 for (int i = 0, offset = 0; i < str.Length; i++)167 {168 // Keep all leading ANSI escape sequences.169 if (vtRanges.TryGetValue(i, out int len))170 {171 hasEscSeqs = true;172 sb.Append(str.AsSpan(i, len));173 174 i += len - 1;175 continue;176 }177 178 // OK, now we get a non-escape-sequence character.179 if (copyStarted)180 {181 if (firstNonEscChar)182 {183 // Prepend the string before we copy the first non-escape-sequence character.184 sb.Append(prependStr);185 firstNonEscChar = false;186 }187 188 // Copy this character if we've started the copy.189 sb.Append(str[i]);190 191 // Increment 'offset' to keep track of number of non-escape-sequence characters we've copied.192 offset++;193 }194 else if (++offset == startOffset)195 {196 // We've skipped enough non-escape-sequence characters, and will be copying the next one.197 copyStarted = true;198 199 // Reset 'offset' and from now on use it to track the number of copied non-escape-sequence characters.200 offset = 0;201 continue;202 }203 204 // If the number of copied non-escape-sequence characters has reached the specified length, done copying.205 if (copyStarted && offset == length)206 {207 break;208 }209 }210 211 if (hasEscSeqs)212 {213 string resetStr = PSStyle.Instance.Reset;214 bool endsWithReset = sb.EndsWith(resetStr);215 if (endsWithReset)216 {217 // Append the given string before the reset VT sequence.218 sb.Insert(sb.Length - resetStr.Length, appendStr);219 }220 else221 {222 // Append the given string and add the reset VT sequence.223 sb.Append(appendStr).Append(resetStr);224 }225 }226 else227 {228 sb.Append(appendStr);229 }230 231 return sb.ToString();232 }233 234 // Handle strings without VT sequences.235 if (length == int.MaxValue)236 {237 length = str.Length - startOffset;238 }239 240 if (prependStr is null && appendStr is null)241 {242 return str.Substring(startOffset, length);243 }244 else245 {246 int capacity = length + prependStr?.Length ?? 0 + appendStr?.Length ?? 0;247 return new StringBuilder(prependStr, capacity)248 .Append(str, startOffset, length)249 .Append(appendStr)250 .ToString();251 }252 }253 254 internal static bool EndsWith(this StringBuilder sb, string value)255 {256 if (sb.Length < value.Length)257 {258 return false;259 }260 261 int offset = sb.Length - value.Length;262 for (int i = 0; i < value.Length; i++)263 {264 if (sb[offset + i] != value[i])265 {266 return false;267 }268 }269 270 return true;271 }272 }273}274 