Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ListWriter.cs289 linesDownload Raw Back to common
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.Specialized;7using System.Diagnostics;8using System.Management.Automation;9using System.Management.Automation.Internal;10using System.Text;11 12namespace Microsoft.PowerShell.Commands.Internal.Format13{14    /// <summary>15    /// Class to write object properties in list form by using16    /// the host screen interfaces.17    /// </summary>18    internal class ListWriter19    {20        /// <summary>21        /// Labels already padded with blanks, separator characters, etc.22        /// </summary>23        private string[] _propertyLabels;24 25        /// <summary>26        /// Display length of the property labels in the array (all the same length)27        /// </summary>28        private int _propertyLabelsDisplayLength = 0;29 30        /// <summary>31        /// Column width of the screen.32        /// </summary>33        private int _columnWidth = 0;34 35        /// <summary>36        /// A cached string builder used within this type to reduce creation of temporary strings.37        /// </summary>38        private readonly StringBuilder _cachedBuilder = new();39 40        /// <summary>41        /// </summary>42        /// <param name="propertyNames">Names of the properties to display.</param>43        /// <param name="screenColumnWidth">Column width of the screen.</param>44        /// <param name="dc">Instance of the DisplayCells helper object.</param>45        internal void Initialize(string[] propertyNames, int screenColumnWidth, DisplayCells dc)46        {47            _columnWidth = screenColumnWidth;48            if (propertyNames == null || propertyNames.Length == 0)49            {50                // there is nothing to show51                _disabled = true;52                return;53            }54 55            _disabled = false;56 57            Debug.Assert(propertyNames != null, "propertyNames is null");58            Debug.Assert(propertyNames.Length > 0, "propertyNames has zero length");59 60            // assess the useful widths61            if ((screenColumnWidth - Separator.Length - MinFieldWidth - MinLabelWidth) < 0)62            {63                // we do not have enough space for any meaningful display64                _disabled = true;65                return;66            }67 68            // check if we have to truncate the labels69            int maxAllowableLabelLength = screenColumnWidth - Separator.Length - MinFieldWidth;70            if (InternalTestHooks.ForceFormatListFixedLabelWidth)71            {72                maxAllowableLabelLength = 10;73            }74 75            // find out the max display length (cell count) of the property names76            _propertyLabelsDisplayLength = 0; // reset max77 78            // cache the cell lengths for each property79            Span<int> propertyNameCellCounts = propertyNames.Length <= OutCommandInner.StackAllocThreshold ? stackalloc int[propertyNames.Length] : new int[propertyNames.Length];80            for (int k = 0; k < propertyNames.Length; k++)81            {82                Debug.Assert(propertyNames[k] != null, "propertyNames[k] is null");83                propertyNameCellCounts[k] = dc.Length(propertyNames[k]);84                if (propertyNameCellCounts[k] > _propertyLabelsDisplayLength)85                    _propertyLabelsDisplayLength = propertyNameCellCounts[k];86            }87 88            if (_propertyLabelsDisplayLength > maxAllowableLabelLength)89            {90                // need to truncate91                _propertyLabelsDisplayLength = maxAllowableLabelLength;92            }93 94            _propertyLabels = new string[propertyNames.Length];95 96            for (int k = 0; k < propertyNames.Length; k++)97            {98                string propertyName = propertyNames[k];99                if (propertyNameCellCounts[k] < _propertyLabelsDisplayLength)100                {101                    // shorter than the max, add padding102                    _propertyLabels[k] = propertyName + StringUtil.Padding(_propertyLabelsDisplayLength - propertyNameCellCounts[k]);103                }104                else if (propertyNameCellCounts[k] > _propertyLabelsDisplayLength)105                {106                    // longer than the max, clip107                    _propertyLabels[k] = propertyName.VtSubstring(0, dc.TruncateTail(propertyName, _propertyLabelsDisplayLength));108                }109                else110                {111                    _propertyLabels[k] = propertyName;112                }113 114                _propertyLabels[k] += Separator;115            }116 117            _propertyLabelsDisplayLength += Separator.Length;118        }119 120        /// <summary>121        /// Write the values of the properties of an object.122        /// </summary>123        /// <param name="values">Array with the values in form of formatted strings.</param>124        /// <param name="lo">LineOutput interface to write to.</param>125        internal void WriteProperties(string[] values, LineOutput lo)126        {127            if (_disabled)128                return;129 130            string[] valuesToPrint = null;131            if (values == null)132            {133                // we have nothing, but we have to create an empty array134                valuesToPrint = new string[_propertyLabels.Length];135                for (int k = 0; k < _propertyLabels.Length; k++)136                    valuesToPrint[k] = string.Empty;137            }138            else if (values.Length < _propertyLabels.Length)139            {140                // need to pad to the end of the array141                valuesToPrint = new string[_propertyLabels.Length];142                for (int k = 0; k < _propertyLabels.Length; k++)143                {144                    if (k < values.Length)145                        valuesToPrint[k] = values[k];146                    else147                        valuesToPrint[k] = string.Empty;148                }149            }150            else if (values.Length > _propertyLabels.Length)151            {152                // need to trim153                valuesToPrint = new string[_propertyLabels.Length];154                for (int k = 0; k < _propertyLabels.Length; k++)155                    valuesToPrint[k] = values[k];156            }157            else158            {159                // perfect match160                valuesToPrint = values;161            }162 163            Debug.Assert(lo != null, "LineOutput is null");164 165            for (int k = 0; k < _propertyLabels.Length; k++)166            {167                WriteProperty(k, valuesToPrint[k], lo);168            }169        }170 171        /// <summary>172        /// Helper, writing a single property to the screen.173        /// It wraps the value of the property if it is tool long to fit.174        /// </summary>175        /// <param name="k">Index of property to write.</param>176        /// <param name="propertyValue">String value of the property to write.</param>177        /// <param name="lo">LineOutput interface to write to.</param>178        private void WriteProperty(int k, string propertyValue, LineOutput lo)179        {180            propertyValue ??= string.Empty;181 182            // make sure we honor embedded newlines183            List<string> lines = StringManipulationHelper.SplitLines(propertyValue);184 185            // padding to use in the lines after the first186            string padding = null;187 188            for (int i = 0; i < lines.Count; i++)189            {190                string prependString = null;191 192                if (i == 0)193                    prependString = _propertyLabels[k];194                else195                {196                    padding ??= StringUtil.Padding(_propertyLabelsDisplayLength);197 198                    prependString = padding;199                }200 201                WriteSingleLineHelper(prependString, lines[i], lo);202            }203        }204 205        /// <summary>206        /// Internal helper to split a line that is too long to fit and pad it to the left207        /// with a given string.208        /// </summary>209        /// <param name="prependString">String to add to the left.</param>210        /// <param name="line">Line to print.</param>211        /// <param name="lo">LineOutput to write to.</param>212        private void WriteSingleLineHelper(string prependString, string line, LineOutput lo)213        {214            line ??= string.Empty;215 216            // compute the width of the field for the value string (in screen cells)217            int fieldCellCount = _columnWidth - _propertyLabelsDisplayLength;218 219            // split the lines220            StringCollection sc = StringManipulationHelper.GenerateLines(lo.DisplayCells, line, fieldCellCount, fieldCellCount);221 222            // The padding to use in the lines after the first.223            string headPadding = null;224 225            // The VT style used for the list label.226            string style = PSStyle.Instance.Formatting.FormatAccent;227            string reset = PSStyle.Instance.Reset;228 229            // display the string collection230            for (int k = 0; k < sc.Count; k++)231            {232                string str = sc[k];233                _cachedBuilder.Clear();234 235                if (k == 0)236                {237                    if (string.IsNullOrWhiteSpace(prependString) || style == string.Empty)238                    {239                        // - Sometimes 'prependString' is just padding white spaces, and we don't240                        //   need to add formatting escape sequences in such a case.241                        // - Otherwise, if the style is an empty string, then the user has chosen242                        //   to not apply a style to the list label.243                        _cachedBuilder.Append(prependString).Append(str);244                    }245                    else246                    {247                        // Apply the style to the list label.248                        _cachedBuilder249                            .Append(style)250                            .Append(prependString)251                            .Append(reset)252                            .Append(str);253                    }254                }255                else256                {257                    // Lazily calculate the padding to use for the subsequent lines as it's quite often that only the first line exists.258                    headPadding ??= StringUtil.Padding(_propertyLabelsDisplayLength);259                    _cachedBuilder.Append(headPadding).Append(str);260                }261 262                if (str.Contains(ValueStringDecorated.ESC) && !str.AsSpan().TrimEnd().EndsWith(reset, StringComparison.Ordinal))263                {264                    _cachedBuilder.Append(reset);265                }266 267                lo.WriteLine(_cachedBuilder.ToString());268            }269        }270 271        /// <summary>272        /// Set to true when the width of the screen is too small to do anything useful.273        /// </summary>274        private bool _disabled = false;275 276        private const string Separator = " : ";277 278        /// <summary>279        /// Minimum width for the property label field.280        /// </summary>281        private const int MinLabelWidth = 1;282 283        /// <summary>284        /// Minimum width for the property value field.285        /// </summary>286        private const int MinFieldWidth = 1;287    }288}289