MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#nullable enable5 6using System.Collections.Generic;7using System.Text.RegularExpressions;8 9namespace System.Management.Automation.Internal10{11 /// <summary>12 /// Extensions to String type to calculate and render decorated content.13 /// </summary>14 public class StringDecorated15 {16 private readonly bool _isDecorated;17 private readonly string _text;18 private string? _plaintextcontent;19 20 private string PlainText21 {22 get23 {24 _plaintextcontent ??= ValueStringDecorated.AnsiRegex.Replace(_text, string.Empty);25 26 return _plaintextcontent;27 }28 }29 30 /// <summary>31 /// Initializes a new instance of the <see cref="StringDecorated"/> class.32 /// </summary>33 /// <param name="text">The input string.</param>34 public StringDecorated(string text)35 {36 _text = text;37 _isDecorated = text.Contains(ValueStringDecorated.ESC);38 }39 40 /// <summary>41 /// Gets a value indicating whether the string contains decoration.42 /// </summary>43 /// <returns>Boolean if the string contains decoration.</returns>44 public bool IsDecorated => _isDecorated;45 46 /// <summary>47 /// Gets the length of content sans escape sequences.48 /// </summary>49 /// <returns>Length of content sans escape sequences.</returns>50 public int ContentLength => PlainText.Length;51 52 /// <summary>53 /// Render the decorarted string using automatic output rendering.54 /// </summary>55 /// <returns>Rendered string based on automatic output rendering.</returns>56 public override string ToString() => ToString(57 PSStyle.Instance.OutputRendering == OutputRendering.PlainText58 ? OutputRendering.PlainText59 : OutputRendering.Ansi);60 61 /// <summary>62 /// Return string representation of content depending on output rendering mode.63 /// </summary>64 /// <param name="outputRendering">Specify how to render the text content.</param>65 /// <returns>Rendered string based on outputRendering.</returns>66 public string ToString(OutputRendering outputRendering)67 {68 if (outputRendering == OutputRendering.Host)69 {70 throw new ArgumentException(StringDecoratedStrings.RequireExplicitRendering);71 }72 73 if (!_isDecorated)74 {75 return _text;76 }77 78 return outputRendering == OutputRendering.PlainText ? PlainText : _text;79 }80 }81 82 internal struct ValueStringDecorated83 {84 internal const char ESC = '\x1b';85 private readonly bool _isDecorated;86 private readonly string _text;87 private string? _plaintextcontent;88 private Dictionary<int, int>? _vtRanges;89 90 private string PlainText91 {92 get93 {94 _plaintextcontent ??= AnsiRegex.Replace(_text, string.Empty);95 96 return _plaintextcontent;97 }98 }99 100 // graphics/color mode ESC[1;2;...m101 private const string GraphicsRegex = @"(\x1b\[\d*(;\d+)*m)";102 103 // CSI escape sequences104 private const string CsiRegex = @"(\x1b\[\?\d+[hl])";105 106 // Hyperlink escape sequences. Note: '.*?' makes '.*' do non-greedy match.107 private const string HyperlinkRegex = @"(\x1b\]8;;.*?\x1b\\)";108 109 // replace regex with .NET 6 API once available110 internal static readonly Regex AnsiRegex = new Regex($"{GraphicsRegex}|{CsiRegex}|{HyperlinkRegex}", RegexOptions.Compiled);111 112 /// <summary>113 /// Get the ranges of all escape sequences in the text.114 /// </summary>115 /// <returns>116 /// A dictionary with the key being the starting index of an escape sequence,117 /// and the value being the length of the escape sequence.118 /// </returns>119 internal Dictionary<int, int>? EscapeSequenceRanges120 {121 get122 {123 if (_isDecorated && _vtRanges is null)124 {125 _vtRanges = new Dictionary<int, int>();126 foreach (Match match in AnsiRegex.Matches(_text))127 {128 _vtRanges.Add(match.Index, match.Length);129 }130 }131 132 return _vtRanges;133 }134 }135 136 /// <summary>137 /// Initializes a new instance of the <see cref="ValueStringDecorated"/> struct.138 /// </summary>139 /// <param name="text">The input string.</param>140 public ValueStringDecorated(string text)141 {142 _text = text;143 _isDecorated = text.Contains(ESC);144 _plaintextcontent = _isDecorated ? null : text;145 _vtRanges = null;146 }147 148 /// <summary>149 /// Gets a value indicating whether the string contains decoration.150 /// </summary>151 /// <returns>Boolean if the string contains decoration.</returns>152 public bool IsDecorated => _isDecorated;153 154 /// <summary>155 /// Gets the length of content sans escape sequences.156 /// </summary>157 /// <returns>Length of content sans escape sequences.</returns>158 public int ContentLength => PlainText.Length;159 160 /// <summary>161 /// Render the decorarted string using automatic output rendering.162 /// </summary>163 /// <returns>Rendered string based on automatic output rendering.</returns>164 public override string ToString() => ToString(165 PSStyle.Instance.OutputRendering == OutputRendering.PlainText166 ? OutputRendering.PlainText167 : OutputRendering.Ansi);168 169 /// <summary>170 /// Return string representation of content depending on output rendering mode.171 /// </summary>172 /// <param name="outputRendering">Specify how to render the text content.</param>173 /// <returns>Rendered string based on outputRendering.</returns>174 public string ToString(OutputRendering outputRendering)175 {176 if (outputRendering == OutputRendering.Host)177 {178 throw new ArgumentException(StringDecoratedStrings.RequireExplicitRendering);179 }180 181 if (!_isDecorated)182 {183 return _text;184 }185 186 return outputRendering == OutputRendering.PlainText ? PlainText : _text;187 }188 }189}190 