Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ConsoleLineOutput.cs550 linesDownload Raw Back to out-console
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.Specialized;7using System.Management.Automation;8using System.Management.Automation.Internal;9using System.Management.Automation.Host;10 11using Dbg = System.Management.Automation.Diagnostics;12 13// interfaces for host interaction14 15namespace Microsoft.PowerShell.Commands.Internal.Format16{17    /// <summary>18    /// Tear off class.19    /// </summary>20    internal class DisplayCellsHost : DisplayCells21    {22        internal DisplayCellsHost(PSHostRawUserInterface rawUserInterface)23        {24            _rawUserInterface = rawUserInterface;25        }26 27        internal override int Length(string str, int offset)28        {29            if (string.IsNullOrEmpty(str))30            {31                return 0;32            }33 34            if (offset < 0 || offset >= str.Length)35            {36                throw PSTraceSource.NewArgumentException(nameof(offset));37            }38 39            try40            {41                var valueStrDec = new ValueStringDecorated(str);42                if (valueStrDec.IsDecorated)43                {44                    str = valueStrDec.ToString(OutputRendering.PlainText);45                }46 47                int length = 0;48                for (; offset < str.Length; offset++)49                {50                    length += _rawUserInterface.LengthInBufferCells(str[offset]);51                }52 53                return length;54            }55            catch56            {57                // thrown when external host rawui is not implemented, in which case58                // we will fallback to the default value.59                return base.Length(str, offset);60            }61        }62 63        internal override int Length(char character)64        {65            try66            {67                return _rawUserInterface.LengthInBufferCells(character);68            }69            catch70            {71                // thrown when external host rawui is not implemented, in which case72                // we will fallback to the default value.73                return base.Length(character);74            }75        }76 77        private readonly PSHostRawUserInterface _rawUserInterface;78    }79 80    /// <summary>81    /// Implementation of the LineOutput interface on top of Console and RawConsole.82    /// </summary>83    internal sealed class ConsoleLineOutput : LineOutput84    {85        #region tracer86        [TraceSource("ConsoleLineOutput", "ConsoleLineOutput")]87        internal static readonly PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleLineOutput", "ConsoleLineOutput");88        #endregion tracer89 90        /// <summary>91        /// The default buffer cell calculation already works for the PowerShell console host and Visual studio code host.92        /// </summary>93        private static readonly HashSet<string> s_psHost = new(StringComparer.Ordinal) { "ConsoleHost", "Visual Studio Code Host" };94 95        #region LineOutput implementation96        /// <summary>97        /// The # of columns is just the width of the screen buffer (not the98        /// width of the window)99        /// </summary>100        /// <value></value>101        internal override int ColumnNumber102        {103            get104            {105                CheckStopProcessing();106                PSHostRawUserInterface raw = _console.RawUI;107 108                // IMPORTANT NOTE: we subtract one because109                // we want to make sure the console's last column110                // is never considered written. This causes the writing111                // logic to always call WriteLine(), making sure a CR112                // is inserted.113 114                try115                {116                    return _forceNewLine ? raw.BufferSize.Width - 1 : raw.BufferSize.Width;117                }118                catch119                {120                    // thrown when external host rawui is not implemented, in which case121                    // we will fallback to the default value.122                }123 124                return _forceNewLine ? _fallbackRawConsoleColumnNumber - 1 : _fallbackRawConsoleColumnNumber;125            }126        }127 128        /// <summary>129        /// The # of rows is the # of rows visible in the window (and not the # of130        /// rows in the screen buffer)131        /// </summary>132        /// <value></value>133        internal override int RowNumber134        {135            get136            {137                CheckStopProcessing();138                PSHostRawUserInterface raw = _console.RawUI;139 140                try141                {142                    return raw.WindowSize.Height;143                }144                catch145                {146                    // thrown when external host rawui is not implemented, in which case147                    // we will fallback to the default value.148                }149 150                return _fallbackRawConsoleRowNumber;151            }152        }153 154        /// <summary>155        /// Write a line to the output device.156        /// </summary>157        /// <param name="s">Line to write.</param>158        internal override void WriteLine(string s)159        {160            CheckStopProcessing();161 162            // delegate the action to the helper,163            // that will properly break the string into164            // screen lines165            _writeLineHelper.WriteLine(s, this.ColumnNumber);166        }167 168        internal override DisplayCells DisplayCells169        {170            get171            {172                CheckStopProcessing();173                if (_displayCellsHost != null)174                {175                    return _displayCellsHost;176                }177 178                // fall back if we do not have a Msh host specific instance179                return _displayCellsDefault;180            }181        }182        #endregion183 184        /// <summary>185        /// Constructor for the ConsoleLineOutput.186        /// </summary>187        /// <param name="host">PSHostUserInterface to wrap.</param>188        /// <param name="paging">True if we require prompting for page breaks.</param>189        /// <param name="errorContext">Error context to throw exceptions.</param>190        internal ConsoleLineOutput(PSHost host, bool paging, TerminatingErrorContext errorContext)191        {192            if (host == null)193            {194                throw PSTraceSource.NewArgumentNullException(nameof(host));195            }196 197            if (errorContext == null)198            {199                throw PSTraceSource.NewArgumentNullException(nameof(errorContext));200            }201 202            _console = host.UI;203            _errorContext = errorContext;204 205            if (paging)206            {207                tracer.WriteLine("paging is needed");208 209                // If we need to do paging, instantiate a prompt handler that will take care of the screen interaction210                string promptString = StringUtil.Format(FormatAndOut_out_xxx.ConsoleLineOutput_PagingPrompt);211                _prompt = new PromptHandler(promptString, this);212            }213 214            if (!s_psHost.Contains(host.Name) && _console.RawUI is not null)215            {216                // set only if we have a valid raw interface217                tracer.WriteLine("there is a valid raw interface");218                _displayCellsHost = new DisplayCellsHost(_console.RawUI);219            }220 221            // instantiate the helper to do the line processing when ILineOutput.WriteXXX() is called222            WriteLineHelper.WriteCallback wl = new WriteLineHelper.WriteCallback(this.OnWriteLine);223            WriteLineHelper.WriteCallback w = new WriteLineHelper.WriteCallback(this.OnWrite);224 225            if (_forceNewLine)226            {227                _writeLineHelper = new WriteLineHelper(/*lineWrap*/false, wl, null, this.DisplayCells);228            }229            else230            {231                _writeLineHelper = new WriteLineHelper(/*lineWrap*/false, wl, w, this.DisplayCells);232            }233        }234 235        /// <summary>236        /// Callback to be called when ILineOutput.WriteLine() is called by WriteLineHelper.237        /// </summary>238        /// <param name="s">String to write.</param>239        private void OnWriteLine(string s)240        {241            // Do any default transcription.242            _console.TranscribeResult(s);243 244            switch (this.WriteStream)245            {246                case WriteStreamType.Error:247                    _console.WriteErrorLine(s);248                    break;249 250                case WriteStreamType.Warning:251                    _console.WriteWarningLine(s);252                    break;253 254                case WriteStreamType.Verbose:255                    _console.WriteVerboseLine(s);256                    break;257 258                case WriteStreamType.Debug:259                    _console.WriteDebugLine(s);260                    break;261 262                default:263                    // If the host is in "transcribe only"264                    // mode (due to an implicitly added call to Out-Default -Transcribe),265                    // then don't call the actual host API.266                    if (!_console.TranscribeOnly)267                    {268                        _console.WriteLine(s);269                    }270 271                    break;272            }273 274            LineWrittenEvent();275        }276 277        /// <summary>278        /// Callback to be called when ILineOutput.Write() is called by WriteLineHelper279        /// This is called when the WriteLineHelper needs to write a line whose length280        /// is the same as the width of the screen buffer.281        /// </summary>282        /// <param name="s">String to write.</param>283        private void OnWrite(string s)284        {285            switch (this.WriteStream)286            {287                case WriteStreamType.Error:288                    _console.WriteErrorLine(s);289                    break;290 291                case WriteStreamType.Warning:292                    _console.WriteWarningLine(s);293                    break;294 295                case WriteStreamType.Verbose:296                    _console.WriteVerboseLine(s);297                    break;298 299                case WriteStreamType.Debug:300                    _console.WriteDebugLine(s);301                    break;302 303                default:304                    _console.Write(s);305                    break;306            }307 308            LineWrittenEvent();309        }310 311        /// <summary>312        /// Called when a line was written to console.313        /// </summary>314        private void LineWrittenEvent()315        {316            // check to avoid reentrancy from the prompt handler317            // writing during the PromptUser() call318            if (_disableLineWrittenEvent)319                return;320 321            // if there is no prompting, we are done322            if (_prompt == null)323                return;324 325            // increment the count of lines written to the screen326            _linesWritten++;327 328            // check if we need to put out a prompt329            if (this.NeedToPrompt)330            {331                // put out the prompt332                _disableLineWrittenEvent = true;333                PromptHandler.PromptResponse response = _prompt.PromptUser(_console);334                _disableLineWrittenEvent = false;335 336                switch (response)337                {338                    case PromptHandler.PromptResponse.NextPage:339                        {340                            // reset the counter, since we are starting a new page341                            _linesWritten = 0;342                        }343 344                        break;345                    case PromptHandler.PromptResponse.NextLine:346                        {347                            // roll back the counter by one, since we allow one more line348                            _linesWritten--;349                        }350 351                        break;352                    case PromptHandler.PromptResponse.Quit:353                        // 1021203-2005/05/09-JonN354                        // HaltCommandException will cause the command355                        // to stop, but not be reported as an error.356                        throw new HaltCommandException();357                }358            }359        }360 361        /// <summary>362        /// Check if we need to put out a prompt.363        /// </summary>364        /// <value>true if we need to prompt</value>365        private bool NeedToPrompt366        {367            get368            {369                // NOTE: we recompute all the time to take into account screen resizing370                int rawRowNumber = this.RowNumber;371 372                if (rawRowNumber <= 0)373                {374                    // something is wrong, there is no real estate, we suppress prompting375                    return false;376                }377 378                // the prompt will occupy some lines, so we need to subtract them form the total379                // screen line count380                int computedPromptLines = _prompt.ComputePromptLines(this.DisplayCells, this.ColumnNumber);381                int availableLines = this.RowNumber - computedPromptLines;382 383                if (availableLines <= 0)384                {385                    tracer.WriteLine("No available Lines; suppress prompting");386                    // something is wrong, there is no real estate, we suppress prompting387                    return false;388                }389 390                return _linesWritten >= availableLines;391            }392        }393 394        #region Private Members395        /// <summary>396        /// Object to manage prompting.397        /// </summary>398        private sealed class PromptHandler399        {400            /// <summary>401            /// Prompt handler with the given prompt.402            /// </summary>403            /// <param name="s">Prompt string to be used.</param>404            /// <param name="cmdlet">The Cmdlet using this prompt handler.</param>405            internal PromptHandler(string s, ConsoleLineOutput cmdlet)406            {407                if (string.IsNullOrEmpty(s))408                    throw PSTraceSource.NewArgumentNullException(nameof(s));409 410                _promptString = s;411                _callingCmdlet = cmdlet;412            }413 414            /// <summary>415            /// Determine how many rows the prompt should take.416            /// </summary>417            /// <param name="cols">Current number of columns on the screen.</param>418            /// <param name="displayCells">String manipulation helper.</param>419            /// <returns></returns>420            internal int ComputePromptLines(DisplayCells displayCells, int cols)421            {422                // split the prompt string into lines423                _actualPrompt = StringManipulationHelper.GenerateLines(displayCells, _promptString, cols, cols);424                return _actualPrompt.Count;425            }426 427            /// <summary>428            /// Options returned by the PromptUser() call.429            /// </summary>430            internal enum PromptResponse431            {432                NextPage,433                NextLine,434                Quit435            }436 437            /// <summary>438            /// Do the actual prompting.439            /// </summary>440            /// <param name="console">PSHostUserInterface instance to prompt to.</param>441            internal PromptResponse PromptUser(PSHostUserInterface console)442            {443                // NOTE: assume the values passed to ComputePromptLines are still valid444 445                // write out the prompt line(s). The last one will not have a new line446                // at the end because we leave the prompt at the end of the line447                for (int k = 0; k < _actualPrompt.Count; k++)448                {449                    if (k < (_actualPrompt.Count - 1))450                        console.WriteLine(_actualPrompt[k]); // intermediate line(s)451                    else452                        console.Write(_actualPrompt[k]); // last line453                }454 455                while (true)456                {457                    _callingCmdlet.CheckStopProcessing();458                    KeyInfo ki = console.RawUI.ReadKey(ReadKeyOptions.IncludeKeyUp | ReadKeyOptions.NoEcho);459                    char key = ki.Character;460                    if (key == 'q' || key == 'Q')461                    {462                        // need to move to the next line since we accepted input, add a newline463                        console.WriteLine();464                        return PromptResponse.Quit;465                    }466                    else if (key == ' ')467                    {468                        // need to move to the next line since we accepted input, add a newline469                        console.WriteLine();470                        return PromptResponse.NextPage;471                    }472                    else if (key == '\r')473                    {474                        // need to move to the next line since we accepted input, add a newline475                        console.WriteLine();476                        return PromptResponse.NextLine;477                    }478                }479            }480 481            /// <summary>482            /// Cached string(s) valid during a sequence of ComputePromptLines()/PromptUser()483            /// </summary>484            private StringCollection _actualPrompt;485 486            /// <summary>487            /// Prompt string as passed at initialization.488            /// </summary>489            private readonly string _promptString;490 491            /// <summary>492            /// The cmdlet that uses this prompt helper.493            /// </summary>494            private readonly ConsoleLineOutput _callingCmdlet = null;495        }496 497        /// <summary>498        /// Flag to force new lines in CMD.EXE by limiting the499        /// usable width to N-1 (e.g. 80-1) and forcing a call500        /// to WriteLine()501        /// </summary>502        private readonly bool _forceNewLine = true;503 504        /// <summary>505        /// Use this if IRawConsole is null;506        /// </summary>507        private readonly int _fallbackRawConsoleColumnNumber = 80;508 509        /// <summary>510        /// Use this if IRawConsole is null;511        /// </summary>512        private readonly int _fallbackRawConsoleRowNumber = 40;513 514        private readonly WriteLineHelper _writeLineHelper;515 516        /// <summary>517        /// Handler to prompt the user for page breaks518        /// if this handler is not null, we have prompting.519        /// </summary>520        private readonly PromptHandler _prompt = null;521 522        /// <summary>523        /// Counter for the # of lines written when prompting is on.524        /// </summary>525        private long _linesWritten = 0;526 527        /// <summary>528        /// Flag to avoid reentrancy on prompting.529        /// </summary>530        private bool _disableLineWrittenEvent = false;531 532        /// <summary>533        /// Reference to the PSHostUserInterface interface we use.534        /// </summary>535        private readonly PSHostUserInterface _console = null;536 537        /// <summary>538        /// Msh host specific string manipulation helper.539        /// </summary>540        private readonly DisplayCells _displayCellsHost;541 542        /// <summary>543        /// Reference to error context to throw Msh exceptions.544        /// </summary>545        private readonly TerminatingErrorContext _errorContext = null;546 547        #endregion548    }549}550