Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ILineOutput.cs581 linesDownload Raw Back to common
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Globalization;6using System.IO;7using System.Management.Automation;8using System.Management.Automation.Host;9using System.Management.Automation.Internal;10using System.Text;11 12// interfaces for host interaction13 14namespace Microsoft.PowerShell.Commands.Internal.Format15{16    /// <summary>17    /// Base class providing support for string manipulation.18    /// This class is a tear off class provided by the LineOutput class.19    /// </summary>20    internal class DisplayCells21    {22        /// <summary>23        /// Calculate the buffer cell length of the given string.24        /// </summary>25        /// <param name="str">String that may contain VT escape sequences.</param>26        /// <returns>Number of buffer cells the string needs to take.</returns>27        internal int Length(string str)28        {29            return Length(str, 0);30        }31 32        /// <summary>33        /// Calculate the buffer cell length of the given string.34        /// </summary>35        /// <param name="str">String that may contain VT escape sequences.</param>36        /// <param name="offset">37        /// When the string doesn't contain VT sequences, it's the starting index.38        /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.</param>39        /// <returns>Number of buffer cells the string needs to take.</returns>40        internal virtual int Length(string str, int offset)41        {42            if (string.IsNullOrEmpty(str))43            {44                return 0;45            }46 47            var valueStrDec = new ValueStringDecorated(str);48            if (valueStrDec.IsDecorated)49            {50                str = valueStrDec.ToString(OutputRendering.PlainText);51            }52 53            int length = 0;54            for (; offset < str.Length; offset++)55            {56                length += CharLengthInBufferCells(str[offset]);57            }58 59            return length;60        }61 62        /// <summary>63        /// Calculate the buffer cell length of the given character.64        /// </summary>65        /// <param name="character"></param>66        /// <returns>Number of buffer cells the character needs to take.</returns>67        internal virtual int Length(char character)68        {69            return CharLengthInBufferCells(character);70        }71 72        /// <summary>73        /// Truncate from the tail of the string.74        /// </summary>75        /// <param name="str">String that may contain VT escape sequences.</param>76        /// <param name="displayCells">Number of buffer cells to fit in.</param>77        /// <returns>Number of non-escape-sequence characters from head of the string that can fit in the space.</returns>78        internal int TruncateTail(string str, int displayCells)79        {80            return TruncateTail(str, offset: 0, displayCells);81        }82 83        /// <summary>84        /// Truncate from the tail of the string.85        /// </summary>86        /// <param name="str">String that may contain VT escape sequences.</param>87        /// <param name="offset">88        /// When the string doesn't contain VT sequences, it's the starting index.89        /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence.</param>90        /// <param name="displayCells">Number of buffer cells to fit in.</param>91        /// <returns>Number of non-escape-sequence characters from head of the string that can fit in the space.</returns>92        internal int TruncateTail(string str, int offset, int displayCells)93        {94            var valueStrDec = new ValueStringDecorated(str);95            if (valueStrDec.IsDecorated)96            {97                str = valueStrDec.ToString(OutputRendering.PlainText);98            }99 100            return GetFitLength(str, offset, displayCells, startFromHead: true);101        }102 103        /// <summary>104        /// Truncate from the head of the string.105        /// </summary>106        /// <param name="str">String that may contain VT escape sequences.</param>107        /// <param name="displayCells">Number of buffer cells to fit in.</param>108        /// <returns>Number of non-escape-sequence characters from head of the string that should be skipped.</returns>109        internal int TruncateHead(string str, int displayCells)110        {111            var valueStrDec = new ValueStringDecorated(str);112            if (valueStrDec.IsDecorated)113            {114                str = valueStrDec.ToString(OutputRendering.PlainText);115            }116 117            int tailCount = GetFitLength(str, offset: 0, displayCells, startFromHead: false);118            return str.Length - tailCount;119        }120 121        #region Helpers122 123        protected static int CharLengthInBufferCells(char c)124        {125            // The following is based on http://www.cl.cam.ac.uk/~mgk25/c/wcwidth.c126            // which is derived from https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt127            bool isWide = c >= 0x1100 &&128                (c <= 0x115f || /* Hangul Jamo init. consonants */129                 c == 0x2329 || c == 0x232a ||130                 ((uint)(c - 0x2e80) <= (0xa4cf - 0x2e80) &&131                  c != 0x303f) || /* CJK ... Yi */132                 ((uint)(c - 0xac00) <= (0xd7a3 - 0xac00)) || /* Hangul Syllables */133                 ((uint)(c - 0xf900) <= (0xfaff - 0xf900)) || /* CJK Compatibility Ideographs */134                 ((uint)(c - 0xfe10) <= (0xfe19 - 0xfe10)) || /* Vertical forms */135                 ((uint)(c - 0xfe30) <= (0xfe6f - 0xfe30)) || /* CJK Compatibility Forms */136                 ((uint)(c - 0xff00) <= (0xff60 - 0xff00)) || /* Fullwidth Forms */137                 ((uint)(c - 0xffe0) <= (0xffe6 - 0xffe0)));138 139            // We can ignore these ranges because .Net strings use surrogate pairs140            // for this range and we do not handle surrogate pairs.141            // (c >= 0x20000 && c <= 0x2fffd) ||142            // (c >= 0x30000 && c <= 0x3fffd)143            return 1 + (isWide ? 1 : 0);144        }145 146        /// <summary>147        /// Given a string and a number of display cells, it computes how many148        /// characters would fit starting from the beginning or end of the string.149        /// </summary>150        /// <param name="str">String to be displayed, which doesn't contain any VT sequences.</param>151        /// <param name="offset">Offset inside the string.</param>152        /// <param name="displayCells">Number of display cells.</param>153        /// <param name="startFromHead">If true compute from the head (i.e. k++) else from the tail (i.e. k--).</param>154        /// <returns>Number of characters that would fit.</returns>155        protected int GetFitLength(string str, int offset, int displayCells, bool startFromHead)156        {157            int filledDisplayCellsCount = 0; // number of cells that are filled in158            int charactersAdded = 0; // number of characters that fit159            int currCharDisplayLen; // scratch variable160 161            int k = startFromHead ? offset : str.Length - 1;162            int kFinal = startFromHead ? str.Length - 1 : offset;163            while (true)164            {165                if ((startFromHead && k > kFinal) || (!startFromHead && k < kFinal))166                {167                    break;168                }169 170                // compute the cell number for the current character171                currCharDisplayLen = this.Length(str[k]);172 173                if (filledDisplayCellsCount + currCharDisplayLen > displayCells)174                {175                    // if we added this character it would not fit, we cannot continue176                    break;177                }178 179                // keep adding, we fit180                filledDisplayCellsCount += currCharDisplayLen;181                charactersAdded++;182 183                // check if we fit exactly184                if (filledDisplayCellsCount == displayCells)185                {186                    // exact fit, we cannot add more187                    break;188                }189 190                k = startFromHead ? (k + 1) : (k - 1);191            }192 193            return charactersAdded;194        }195 196        #endregion197    }198 199    /// <summary>200    /// Base class providing information about the screen device capabilities201    /// and used to write the output strings to the text output device.202    /// Each device supported will have to derive from it.203    /// Examples of supported devices are:204    /// *   Screen Layout: it layers on top of Console and RawConsole205    /// *   File: it layers on top of a TextWriter206    /// *   In Memory text stream: it layers on top of an in memory buffer207    /// *   Printer: it layers on top of a memory buffer then sent to a printer device208    ///209    /// Assumptions:210    /// - Fixed pitch font: layout done in terms of character cells211    /// - character cell layout not affected by bold, reverse screen, color, etc.212    /// - returned values might change from call to call if the specific underlying213    ///   implementation allows window resizing.214    /// </summary>215    internal abstract class LineOutput216    {217        /// <summary>218        /// Whether the device requires full buffering of formatting219        /// objects before any processing.220        /// </summary>221        internal virtual bool RequiresBuffering { get { return false; } }222 223        /// <summary>224        /// Delegate the implementor of ExecuteBufferPlayBack should225        /// call to cause the playback to happen when ready to execute.226        /// </summary>227        internal delegate void DoPlayBackCall();228 229        /// <summary>230        /// If RequiresBuffering = true, this call will be made to231        /// start the playback.232        /// </summary>233        internal virtual void ExecuteBufferPlayBack(DoPlayBackCall playback) { }234 235        /// <summary>236        /// The number of columns the current device has.237        /// </summary>238        internal abstract int ColumnNumber { get; }239 240        /// <summary>241        /// The number of rows the current device has.242        /// </summary>243        internal abstract int RowNumber { get; }244 245        /// <summary>246        /// Write a line to the output device.247        /// </summary>248        /// <param name="s">249        ///     string to be written to the device250        /// </param>251        internal abstract void WriteLine(string s);252 253        /// <summary>254        /// Write a line of string as raw text to the output device, with no change to the string.255        /// For example, keeping VT escape sequences intact in it.256        /// </summary>257        /// <param name="s">The raw text to be written to the device.</param>258        internal virtual void WriteRawText(string s) => WriteLine(s);259 260        internal WriteStreamType WriteStream261        {262            get;263            set;264        }265 266        /// <summary>267        /// Handle the stop processing signal.268        /// Set a flag that will be checked during operations.269        /// </summary>270        internal void StopProcessing()271        {272            _isStopping = true;273        }274 275        private bool _isStopping;276 277        internal void CheckStopProcessing()278        {279            if (!_isStopping)280                return;281            throw new PipelineStoppedException();282        }283 284        /// <summary>285        /// Return an instance of the display helper tear off.286        /// </summary>287        /// <value></value>288        internal virtual DisplayCells DisplayCells289        {290            get291            {292                CheckStopProcessing();293                // just return the default singleton implementation294                return _displayCellsDefault;295            }296        }297 298        /// <summary>299        /// Singleton used for the default implementation.300        /// NOTE: derived classes may chose to provide a different301        /// implementation by overriding.302        /// </summary>303        protected static DisplayCells _displayCellsDefault = new DisplayCells();304    }305 306    /// <summary>307    /// Helper class to provide line breaking (based on device width)308    /// and embedded newline processing309    /// It needs to be provided with two callbacks for line processing.310    /// </summary>311    internal class WriteLineHelper312    {313        #region callbacks314 315        /// <summary>316        /// Delegate definition.317        /// </summary>318        /// <param name="s">String to write.</param>319        internal delegate void WriteCallback(string s);320 321        /// <summary>322        /// Instance of the delegate previously defined323        /// for line that has EXACTLY this.ncols characters.324        /// </summary>325        private readonly WriteCallback _writeCall = null;326 327        /// <summary>328        /// Instance of the delegate previously defined329        /// for generic line, less that this.ncols characters.330        /// </summary>331        private readonly WriteCallback _writeLineCall = null;332 333        #endregion334 335        private readonly bool _lineWrap;336 337        /// <summary>338        /// Construct an instance, given the two callbacks339        /// NOTE: if the underlying device treats the two cases as the340        /// same, the same delegate can be passed twice.341        /// </summary>342        /// <param name="lineWrap">True if we require line wrapping.</param>343        /// <param name="wlc">Delegate for WriteLine(), must ben non null.</param>344        /// <param name="wc">Delegate for Write(), if null, use the first parameter.</param>345        /// <param name="displayCells">Helper object for manipulating strings.</param>346        internal WriteLineHelper(bool lineWrap, WriteCallback wlc, WriteCallback wc, DisplayCells displayCells)347        {348            if (wlc == null)349                throw PSTraceSource.NewArgumentNullException(nameof(wlc));350            if (displayCells == null)351                throw PSTraceSource.NewArgumentNullException(nameof(displayCells));352 353            _displayCells = displayCells;354            _writeLineCall = wlc;355            _writeCall = wc ?? wlc;356            _lineWrap = lineWrap;357        }358 359        /// <summary>360        /// Main entry point to process a line.361        /// </summary>362        /// <param name="s">String to process.</param>363        /// <param name="cols">Width of the device.</param>364        internal void WriteLine(string s, int cols)365        {366            WriteLineInternal(s, cols);367        }368 369        /// <summary>370        /// Internal helper, needed because it might make recursive calls to itself.371        /// </summary>372        /// <param name="val">String to process.</param>373        /// <param name="cols">Width of the device.</param>374        private void WriteLineInternal(string val, int cols)375        {376            if (string.IsNullOrEmpty(val))377            {378                _writeLineCall(val);379                return;380            }381 382            // If the output is being redirected, then we don't break val383            if (!_lineWrap)384            {385                _writeCall(val);386                return;387            }388 389            // check for line breaks390            List<string> lines = StringManipulationHelper.SplitLines(val);391 392            // process the substrings as separate lines393            for (int k = 0; k < lines.Count; k++)394            {395                // compute the display length of the string396                int displayLength = _displayCells.Length(lines[k]);397 398                if (displayLength < cols)399                {400                    // NOTE: this is the case where where System.Console.WriteLine() would work just fine401                    _writeLineCall(lines[k]);402                    continue;403                }404 405                if (displayLength == cols)406                {407                    // NOTE: this is the corner case where System.Console.WriteLine() cannot be called408                    _writeCall(lines[k]);409                    continue;410                }411 412                // the string does not fit, so we have to wrap around on multiple lines413                string s = lines[k];414 415                while (true)416                {417                    // the string is still too long to fit, write the first cols characters418                    // and go back for more wraparound419                    int headCount = _displayCells.TruncateTail(s, cols);420                    WriteLineInternal(s.VtSubstring(0, headCount), cols);421 422                    // chop off the first fieldWidth characters, already printed423                    s = s.VtSubstring(headCount);424                    if (_displayCells.Length(s) <= cols)425                    {426                        // if we fit, print the tail of the string and we are done427                        WriteLineInternal(s, cols);428                        break;429                    }430                }431            }432        }433 434        private readonly DisplayCells _displayCells;435    }436 437    /// <summary>438    /// Implementation of the ILineOutput interface accepting an instance of a439    /// TextWriter abstract class.440    /// </summary>441    internal sealed class TextWriterLineOutput : LineOutput442    {443        #region ILineOutput methods444 445        /// <summary>446        /// Get the columns on the screen447        /// for files, it is settable at creation time.448        /// </summary>449        internal override int ColumnNumber450        {451            get452            {453                CheckStopProcessing();454                return _columns;455            }456        }457 458        /// <summary>459        /// Get the # of rows on the screen: for files460        /// we return -1, meaning infinite.461        /// </summary>462        internal override int RowNumber463        {464            get465            {466                CheckStopProcessing();467                return -1;468            }469        }470 471        /// <summary>472        /// Write a line by delegating to the writer underneath.473        /// </summary>474        /// <param name="s"></param>475        internal override void WriteLine(string s)476        {477            WriteRawText(PSHostUserInterface.GetOutputString(s, isHost: false));478        }479 480        /// <summary>481        /// Write a raw text by delegating to the writer underneath, with no change to the text.482        /// For example, keeping VT escape sequences intact in it.483        /// </summary>484        /// <param name="s">The raw text to be written to the device.</param>485        internal override void WriteRawText(string s)486        {487            CheckStopProcessing();488 489            if (_suppressNewline)490            {491                _writer.Write(s);492            }493            else494            {495                _writer.WriteLine(s);496            }497        }498 499        #endregion500 501        /// <summary>502        /// Initialization of the object. It must be called before503        /// attempting any operation.504        /// </summary>505        /// <param name="writer">TextWriter to write to.</param>506        /// <param name="columns">Max columns widths for the text.</param>507        internal TextWriterLineOutput(TextWriter writer, int columns)508        {509            _writer = writer;510            _columns = columns;511        }512 513        /// <summary>514        /// Initialization of the object. It must be called before515        /// attempting any operation.516        /// </summary>517        /// <param name="writer">TextWriter to write to.</param>518        /// <param name="columns">Max columns widths for the text.</param>519        /// <param name="suppressNewline">False to add a newline to the end of the output string, true if not.</param>520        internal TextWriterLineOutput(TextWriter writer, int columns, bool suppressNewline)521            : this(writer, columns)522        {523            _suppressNewline = suppressNewline;524        }525 526        private readonly int _columns = 0;527 528        private readonly TextWriter _writer = null;529 530        private readonly bool _suppressNewline = false;531    }532 533    /// <summary>534    /// TextWriter to generate data for the Monad pipeline in a streaming fashion:535    /// the provided callback will be called each time a line is written.536    /// </summary>537    internal class StreamingTextWriter : TextWriter538    {539        #region tracer540        [TraceSource("StreamingTextWriter", "StreamingTextWriter")]541        private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("StreamingTextWriter", "StreamingTextWriter");542        #endregion tracer543 544        /// <summary>545        /// Create an instance by passing a delegate.546        /// </summary>547        /// <param name="writeCall">Delegate to write to.</param>548        /// <param name="culture">Culture for this TextWriter.</param>549        internal StreamingTextWriter(WriteLineCallback writeCall, CultureInfo culture)550            : base(culture)551        {552            if (writeCall == null)553                throw PSTraceSource.NewArgumentNullException(nameof(writeCall));554 555            _writeCall = writeCall;556        }557 558        #region TextWriter overrides559 560        public override Encoding Encoding { get { return new UnicodeEncoding(); } }561 562        public override void WriteLine(string s)563        {564            _writeCall(s);565        }566 567        #endregion568 569        /// <summary>570        /// Delegate definition.571        /// </summary>572        /// <param name="s">String to write.</param>573        internal delegate void WriteLineCallback(string s);574 575        /// <summary>576        /// Instance of the delegate previously defined.577        /// </summary>578        private readonly WriteLineCallback _writeCall = null;579    }580}581