2016-03-30 14:20:52 -07:00
|
|
|
/********************************************************************++
|
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
--********************************************************************/
|
|
|
|
|
|
|
|
|
|
// NOTE: define this if you want to test the output on US machine and ASCII
|
|
|
|
|
// characters
|
|
|
|
|
//#define TEST_MULTICELL_ON_SINGLE_CELL_LOCALE
|
|
|
|
|
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.Management.Automation;
|
|
|
|
|
using System.Management.Automation.Internal;
|
|
|
|
|
using System.Management.Automation.Host;
|
|
|
|
|
|
|
|
|
|
using Dbg = System.Management.Automation.Diagnostics;
|
|
|
|
|
|
|
|
|
|
// interfaces for host interaction
|
2016-07-29 13:02:49 -07:00
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
namespace Microsoft.PowerShell.Commands.Internal.Format
|
|
|
|
|
{
|
|
|
|
|
#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// test class to provide easily overridable behavior for testing on US machines
|
|
|
|
|
/// using US data.
|
|
|
|
|
/// NOTE: the class just forces any uppercase letter [A-Z] to be prepended
|
|
|
|
|
/// with an underscore (e.g. "A" becomes "_A", but "a" stays the same)
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class DisplayCellsTest : DisplayCells
|
|
|
|
|
{
|
|
|
|
|
internal override int Length(string str, int offset)
|
|
|
|
|
{
|
|
|
|
|
int len = 0;
|
|
|
|
|
for (int k = offset; k < str.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
len += this.Length(str[k]);
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override int Length(char character)
|
|
|
|
|
{
|
|
|
|
|
if (character >= 'A' && character <= 'Z')
|
|
|
|
|
return 2;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override int GetHeadSplitLength(string str, int offset, int displayCells)
|
|
|
|
|
{
|
|
|
|
|
return GetSplitLengthInternalHelper(str, offset, displayCells, true);
|
|
|
|
|
}
|
|
|
|
|
internal override int GetTailSplitLength(string str, int offset, int displayCells)
|
|
|
|
|
{
|
|
|
|
|
return GetSplitLengthInternalHelper(str, offset, displayCells, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal string GenerateTestString(string str)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
for (int k = 0; k < str.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
char ch = str[k];
|
|
|
|
|
if (this.Length(ch) == 2)
|
|
|
|
|
{
|
|
|
|
|
sb.Append('_');
|
|
|
|
|
}
|
|
|
|
|
sb.Append(ch);
|
|
|
|
|
}
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Tear off class
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class DisplayCellsPSHost : DisplayCells
|
|
|
|
|
{
|
|
|
|
|
internal DisplayCellsPSHost(PSHostRawUserInterface rawUserInterface)
|
|
|
|
|
{
|
|
|
|
|
_rawUserInterface = rawUserInterface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override int Length(string str, int offset)
|
|
|
|
|
{
|
|
|
|
|
Dbg.Assert(offset >= 0, "offset >= 0");
|
|
|
|
|
Dbg.Assert(string.IsNullOrEmpty(str) || (offset < str.Length), "offset < str.Length");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return _rawUserInterface.LengthInBufferCells(str, offset);
|
|
|
|
|
}
|
|
|
|
|
catch (HostException)
|
|
|
|
|
{
|
|
|
|
|
//thrown when external host rawui is not implemented, in which case
|
|
|
|
|
//we will fallback to the default value.
|
|
|
|
|
}
|
|
|
|
|
return string.IsNullOrEmpty(str) ? 0 : str.Length - offset;
|
|
|
|
|
}
|
|
|
|
|
internal override int Length(string str)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return _rawUserInterface.LengthInBufferCells(str);
|
|
|
|
|
}
|
|
|
|
|
catch (HostException)
|
|
|
|
|
{
|
|
|
|
|
//thrown when external host rawui is not implemented, in which case
|
|
|
|
|
//we will fallback to the default value.
|
|
|
|
|
}
|
|
|
|
|
return string.IsNullOrEmpty(str) ? 0 : str.Length;
|
|
|
|
|
}
|
|
|
|
|
internal override int Length(char character)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return _rawUserInterface.LengthInBufferCells(character);
|
|
|
|
|
}
|
|
|
|
|
catch (HostException)
|
|
|
|
|
{
|
|
|
|
|
//thrown when external host rawui is not implemented, in which case
|
|
|
|
|
//we will fallback to the default value.
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override int GetHeadSplitLength(string str, int offset, int displayCells)
|
|
|
|
|
{
|
|
|
|
|
return GetSplitLengthInternalHelper(str, offset, displayCells, true);
|
|
|
|
|
}
|
|
|
|
|
internal override int GetTailSplitLength(string str, int offset, int displayCells)
|
|
|
|
|
{
|
|
|
|
|
return GetSplitLengthInternalHelper(str, offset, displayCells, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private PSHostRawUserInterface _rawUserInterface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Implementation of the LineOutput interface on top of Console and RawConsole
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal sealed class ConsoleLineOutput : LineOutput
|
|
|
|
|
{
|
|
|
|
|
#region tracer
|
2016-07-29 13:02:49 -07:00
|
|
|
[TraceSource("ConsoleLineOutput", "ConsoleLineOutput")]
|
|
|
|
|
internal static PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleLineOutput", "ConsoleLineOutput");
|
2016-03-30 14:20:52 -07:00
|
|
|
#endregion tracer
|
|
|
|
|
|
|
|
|
|
#region LineOutput implementation
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// the # of columns is just the width of the screen buffer (not the
|
|
|
|
|
/// width of the window)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value></value>
|
|
|
|
|
internal override int ColumnNumber
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
CheckStopProcessing();
|
2016-07-29 13:02:49 -07:00
|
|
|
PSHostRawUserInterface raw = _console.RawUI;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
// IMPORTANT NOTE: we subtract one because
|
|
|
|
|
// we want to make sure the console's last column
|
|
|
|
|
// is never considered written. This causes the writing
|
|
|
|
|
// logic to always call WriteLine(), making sure a CR
|
|
|
|
|
// is inserted.
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
return _forceNewLine ? raw.BufferSize.Width - 1 : raw.BufferSize.Width;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
catch (HostException)
|
|
|
|
|
{
|
|
|
|
|
//thrown when external host rawui is not implemented, in which case
|
|
|
|
|
//we will fallback to the default value.
|
|
|
|
|
}
|
2016-07-29 13:02:49 -07:00
|
|
|
return _forceNewLine ? _fallbackRawConsoleColumnNumber - 1 : _fallbackRawConsoleColumnNumber;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// the # of rows is the # of rows visible in the window (and not the # of
|
|
|
|
|
/// rows in the screen buffer)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value></value>
|
|
|
|
|
internal override int RowNumber
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
CheckStopProcessing();
|
2016-07-29 13:02:49 -07:00
|
|
|
PSHostRawUserInterface raw = _console.RawUI;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return raw.WindowSize.Height;
|
|
|
|
|
}
|
|
|
|
|
catch (HostException)
|
|
|
|
|
{
|
|
|
|
|
//thrown when external host rawui is not implemented, in which case
|
|
|
|
|
//we will fallback to the default value.
|
|
|
|
|
}
|
2016-07-29 13:02:49 -07:00
|
|
|
return _fallbackRawConsoleRowNumber;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// write a line to the output device
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s">line to write</param>
|
2016-07-29 13:02:49 -07:00
|
|
|
internal override void WriteLine(string s)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
CheckStopProcessing();
|
|
|
|
|
// delegate the action to the helper,
|
|
|
|
|
// that will properly break the string into
|
|
|
|
|
// screen lines
|
2016-07-29 13:02:49 -07:00
|
|
|
_writeLineHelper.WriteLine(s, this.ColumnNumber);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal override DisplayCells DisplayCells
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
CheckStopProcessing();
|
|
|
|
|
if (_displayCellsPSHost != null)
|
|
|
|
|
{
|
|
|
|
|
return _displayCellsPSHost;
|
|
|
|
|
}
|
|
|
|
|
// fall back if we do not have a Msh host specific instance
|
|
|
|
|
return _displayCellsPSHost;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// constructor for the ConsoleLineOutput
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="hostConsole">PSHostUserInterface to wrap</param>
|
|
|
|
|
/// <param name="paging">true if we require prompting for page breaks</param>
|
|
|
|
|
/// <param name="errorContext">error context to throw exceptions</param>
|
2016-07-29 13:02:49 -07:00
|
|
|
internal ConsoleLineOutput(PSHostUserInterface hostConsole, bool paging, TerminatingErrorContext errorContext)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
if (hostConsole == null)
|
2016-07-29 13:02:49 -07:00
|
|
|
throw PSTraceSource.NewArgumentNullException("hostConsole");
|
2016-03-30 14:20:52 -07:00
|
|
|
if (errorContext == null)
|
2016-07-29 13:02:49 -07:00
|
|
|
throw PSTraceSource.NewArgumentNullException("errorContext");
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
_console = hostConsole;
|
|
|
|
|
_errorContext = errorContext;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
if (paging)
|
|
|
|
|
{
|
|
|
|
|
tracer.WriteLine("paging is needed");
|
|
|
|
|
// if we need to do paging, instantiate a prompt handler
|
|
|
|
|
// that will take care of the screen interaction
|
|
|
|
|
string promptString = StringUtil.Format(FormatAndOut_out_xxx.ConsoleLineOutput_PagingPrompt);
|
2016-07-29 13:02:49 -07:00
|
|
|
_prompt = new PromptHandler(promptString, this);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
PSHostRawUserInterface raw = _console.RawUI;
|
2016-03-30 14:20:52 -07:00
|
|
|
if (raw != null)
|
|
|
|
|
{
|
|
|
|
|
tracer.WriteLine("there is a valid raw interface");
|
|
|
|
|
#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE
|
|
|
|
|
// create a test instance with fake behavior
|
|
|
|
|
this._displayCellsPSHost = new DisplayCellsTest();
|
|
|
|
|
#else
|
|
|
|
|
// set only if we have a valid raw interface
|
2016-07-29 13:02:49 -07:00
|
|
|
_displayCellsPSHost = new DisplayCellsPSHost(raw);
|
2016-03-30 14:20:52 -07:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// instantiate the helper to do the line processing when ILineOutput.WriteXXX() is called
|
2016-07-29 13:02:49 -07:00
|
|
|
WriteLineHelper.WriteCallback wl = new WriteLineHelper.WriteCallback(this.OnWriteLine);
|
|
|
|
|
WriteLineHelper.WriteCallback w = new WriteLineHelper.WriteCallback(this.OnWrite);
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_forceNewLine)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_writeLineHelper = new WriteLineHelper(/*lineWrap*/false, wl, null, this.DisplayCells);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_writeLineHelper = new WriteLineHelper(/*lineWrap*/false, wl, w, this.DisplayCells);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// callback to be called when ILineOutput.WriteLine() is called by WriteLineHelper
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s">string to write</param>
|
2016-07-29 13:02:49 -07:00
|
|
|
private void OnWriteLine(string s)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE
|
|
|
|
|
s = ((DisplayCellsTest)this._displayCellsPSHost).GenerateTestString(s);
|
|
|
|
|
#endif
|
|
|
|
|
// Do any default transcription.
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.TranscribeResult(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
switch (this.WriteStream)
|
|
|
|
|
{
|
|
|
|
|
case WriteStreamType.Error:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteErrorLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Warning:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteWarningLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Verbose:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteVerboseLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Debug:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteDebugLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// If the host is in "transcribe only"
|
|
|
|
|
// mode (due to an implicitly added call to Out-Default -Transcribe),
|
|
|
|
|
// then don't call the actual host API.
|
2016-07-29 13:02:49 -07:00
|
|
|
if (!_console.TranscribeOnly)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
2016-07-29 13:02:49 -07:00
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
LineWrittenEvent();
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// callback to be called when ILineOutput.Write() is called by WriteLineHelper
|
|
|
|
|
/// This is called when the WriteLineHelper needs to write a line whose length
|
|
|
|
|
/// is the same as the width of the screen buffer
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s">string to write</param>
|
2016-07-29 13:02:49 -07:00
|
|
|
private void OnWrite(string s)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE
|
|
|
|
|
s = ((DisplayCellsTest)this._displayCellsPSHost).GenerateTestString(s);
|
|
|
|
|
#endif
|
|
|
|
|
switch (this.WriteStream)
|
|
|
|
|
{
|
|
|
|
|
case WriteStreamType.Error:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteErrorLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Warning:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteWarningLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Verbose:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteVerboseLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WriteStreamType.Debug:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.WriteDebugLine(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2016-07-29 13:02:49 -07:00
|
|
|
_console.Write(s);
|
2016-03-30 14:20:52 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
LineWrittenEvent();
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// called when a line was written to console
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private void LineWrittenEvent()
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
// check to avoid reeentrancy from the prompt handler
|
|
|
|
|
// writing during the PropmtUser() call
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_disableLineWrittenEvent)
|
2016-03-30 14:20:52 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// if there is no promting, we are done
|
2016-07-29 13:02:49 -07:00
|
|
|
if (_prompt == null)
|
2016-03-30 14:20:52 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// increment the count of lines written to the screen
|
2016-07-29 13:02:49 -07:00
|
|
|
_linesWritten++;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
// check if we need to put out a prompt
|
|
|
|
|
if (this.NeedToPrompt)
|
|
|
|
|
{
|
|
|
|
|
// put out the prompt
|
2016-07-29 13:02:49 -07:00
|
|
|
_disableLineWrittenEvent = true;
|
|
|
|
|
PromptHandler.PromptResponse response = _prompt.PromptUser(_console);
|
|
|
|
|
_disableLineWrittenEvent = false;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
switch (response)
|
|
|
|
|
{
|
|
|
|
|
case PromptHandler.PromptResponse.NextPage:
|
|
|
|
|
{
|
|
|
|
|
// reset the counter, since we are starting a new page
|
2016-07-29 13:02:49 -07:00
|
|
|
_linesWritten = 0;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PromptHandler.PromptResponse.NextLine:
|
|
|
|
|
{
|
|
|
|
|
// roll back the counter by one, since we allow one more line
|
2016-07-29 13:02:49 -07:00
|
|
|
_linesWritten--;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PromptHandler.PromptResponse.Quit:
|
|
|
|
|
// 1021203-2005/05/09-JonN
|
|
|
|
|
// HaltCommandException will cause the command
|
|
|
|
|
// to stop, but not be reported as an error.
|
|
|
|
|
throw new HaltCommandException();
|
2016-07-29 13:02:49 -07:00
|
|
|
}
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// check if we need to put out a prompt
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>true if we need to promp</value>
|
|
|
|
|
private bool NeedToPrompt
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
// NOTE: we recompute all the time to take into account screen resizing
|
|
|
|
|
int rawRowNumber = this.RowNumber;
|
|
|
|
|
|
|
|
|
|
if (rawRowNumber <= 0)
|
|
|
|
|
{
|
|
|
|
|
// something is wrong, there is no real estate, we suppress prompting
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the prompt will occupy some lines, so we need to subtract them form the total
|
|
|
|
|
// screen line count
|
2016-07-29 13:02:49 -07:00
|
|
|
int computedPromptLines = _prompt.ComputePromptLines(this.DisplayCells, this.ColumnNumber);
|
2016-03-30 14:20:52 -07:00
|
|
|
int availableLines = this.RowNumber - computedPromptLines;
|
|
|
|
|
|
|
|
|
|
if (availableLines <= 0)
|
|
|
|
|
{
|
|
|
|
|
tracer.WriteLine("No available Lines; suppress prompting");
|
|
|
|
|
// something is wrong, there is no real estate, we suppress prompting
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
return _linesWritten >= availableLines;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
#region Private Members
|
2016-03-30 14:20:52 -07:00
|
|
|
/// <summary>
|
|
|
|
|
/// object to manage prompting
|
|
|
|
|
/// </summary>
|
|
|
|
|
private class PromptHandler
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// prompt handler with the given prompt
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s">prompt string to be used</param>
|
|
|
|
|
/// <param name="cmdlet">the Cmdlet using this prompt handler</param>
|
|
|
|
|
internal PromptHandler(string s, ConsoleLineOutput cmdlet)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(s))
|
|
|
|
|
throw PSTraceSource.NewArgumentNullException("s");
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
_promptString = s;
|
|
|
|
|
_callingCmdlet = cmdlet;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// detemine how many rows the prompt should take.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cols">current number of colums on the screen</param>
|
|
|
|
|
/// <param name="displayCells">string manipupation helper</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
internal int ComputePromptLines(DisplayCells displayCells, int cols)
|
|
|
|
|
{
|
|
|
|
|
// split the prompt string into lines
|
2016-07-29 13:02:49 -07:00
|
|
|
_actualPrompt = StringManipulationHelper.GenerateLines(displayCells, _promptString, cols, cols);
|
|
|
|
|
return _actualPrompt.Count;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// options returned by the PromptUser() call
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal enum PromptResponse
|
|
|
|
|
{
|
|
|
|
|
NextPage,
|
|
|
|
|
NextLine,
|
|
|
|
|
Quit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// do the actual prompting
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="console">PSHostUserInterface instance to prompt to</param>
|
|
|
|
|
internal PromptResponse PromptUser(PSHostUserInterface console)
|
|
|
|
|
{
|
|
|
|
|
// NOTE: assume the values passed to ComputePromptLines are still valid
|
|
|
|
|
|
|
|
|
|
// write out the prompt line(s). The last one will not have a new line
|
|
|
|
|
// at the end because we leave the prompt at the end of the line
|
2016-07-29 13:02:49 -07:00
|
|
|
for (int k = 0; k < _actualPrompt.Count; k++)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
if (k < (_actualPrompt.Count - 1))
|
|
|
|
|
console.WriteLine(_actualPrompt[k]); // intermediate line(s)
|
2016-03-30 14:20:52 -07:00
|
|
|
else
|
2016-07-29 13:02:49 -07:00
|
|
|
console.Write(_actualPrompt[k]); // last line
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_callingCmdlet.CheckStopProcessing();
|
2016-03-30 14:20:52 -07:00
|
|
|
KeyInfo ki = console.RawUI.ReadKey(ReadKeyOptions.IncludeKeyUp | ReadKeyOptions.NoEcho);
|
|
|
|
|
char key = ki.Character;
|
|
|
|
|
if (key == 'q' || key == 'Q')
|
|
|
|
|
{
|
|
|
|
|
// need to move to the next line since we accepted input, add a newline
|
|
|
|
|
console.WriteLine();
|
|
|
|
|
return PromptResponse.Quit;
|
|
|
|
|
}
|
|
|
|
|
else if (key == ' ')
|
|
|
|
|
{
|
|
|
|
|
// need to move to the next line since we accepted input, add a newline
|
|
|
|
|
console.WriteLine();
|
|
|
|
|
return PromptResponse.NextPage;
|
|
|
|
|
}
|
|
|
|
|
else if (key == '\r')
|
|
|
|
|
{
|
|
|
|
|
// need to move to the next line since we accepted input, add a newline
|
|
|
|
|
console.WriteLine();
|
|
|
|
|
return PromptResponse.NextLine;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// cached string(s) valid during a sequence of ComputePromptLines()/PromptUser()
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private StringCollection _actualPrompt;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// prompt string as passed at initialization
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private string _promptString;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The cmdlet that uses this prompt helper
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private ConsoleLineOutput _callingCmdlet = null;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// flag to force new lines in CMD.EXE by limiting the
|
|
|
|
|
/// usable width to N-1 (e.g. 80-1) and forcing a call
|
|
|
|
|
/// to WriteLine()
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private bool _forceNewLine = true;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// use this if IRawConsole is null;
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private int _fallbackRawConsoleColumnNumber = 80;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// use this if IRawConsole is null;
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private int _fallbackRawConsoleRowNumber = 40;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
private WriteLineHelper _writeLineHelper;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// handler to prompt the user for page breaks
|
|
|
|
|
/// if this handler is not null, we have promting
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private PromptHandler _prompt = null;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// conter for the # of lines written when prompting is on
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private long _linesWritten = 0;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// flag to avoid renetrancy on promting
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private bool _disableLineWrittenEvent = false;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// refecence to the PSHostUserInterface interface we use
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private PSHostUserInterface _console = null;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Msh host specific string manipulation helper
|
|
|
|
|
/// </summary>
|
|
|
|
|
private DisplayCells _displayCellsPSHost;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// reference to error context to throw Msh exceptions
|
|
|
|
|
/// </summary>
|
2016-07-29 13:02:49 -07:00
|
|
|
private TerminatingErrorContext _errorContext = null;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
#endregion
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|