Delete deprecated CoreConsoleHost project

This commit is contained in:
Andrew Schwartzmeyer
2016-05-17 13:28:44 -07:00
parent 97ea9308fb
commit eee564e465
8 changed files with 0 additions and 3090 deletions
@@ -1,8 +0,0 @@
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Resources;
[assembly:InternalsVisibleTo("powershell-tests")]
[assembly:AssemblyCulture("")]
[assembly:NeutralResourcesLanguage("en-US")]
@@ -1,25 +0,0 @@
# Core PowerShell Host
This host is based off the infamous `host06` example. It relies only on managed
code, and is cross-platform. It needs a fair bit of work, but it loads
profiles, has tab-completion, and can be used to debug PowerShell scripts.
## Executable
The `bin/powershell[.exe]` executable for Core PowerShell is built by this
project, as it is the top dependency of the graph, and has `emitEntryPoint:
true`, meaning a native executable is produced automatically by CLI (no need to
own a separate native host). It is also the project that deploys the `ps1xml`
types and formatting files, as well as the default profile and the included
PowerShell modules.
Note that many of these should probably live with System.Management.Automation,
but we're waiting on a bug fix from CLI to move them.
## update-content.sh
This script is used to update our current tree's files with those that live in
`src/monad`. We only need to update them when new changes are merged from
Source Depot, and the build scripts are much simpler without this logic. With
the files living here, the `content` key in the `project.json` handles the
deployment for us in a cross-platform manner.
@@ -1,218 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Globalization;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
/// <summary>
/// This is a sample implementation of the PSHost abstract class for
/// console applications. Not all members are implemented. Those that
/// are not implemented throw a NotImplementedException exception or
/// return nothing.
/// </summary>
internal class MyHost : PSHost, IHostSupportsInteractiveSession
{
public MyHost(Listener Listener)
{
this.Listener = Listener;
this.myHostUserInterface = new MyHostUserInterface(Listener.HasUI, Listener.Interactive);
}
/// <summary>
/// A reference to the PSHost implementation.
/// </summary>
private Listener Listener;
/// <summary>
/// The culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalCultureInfo = CultureInfo.CurrentCulture;
/// <summary>
/// The UI culture information of the thread that created
/// this object.
/// </summary>
private CultureInfo originalUICultureInfo = CultureInfo.CurrentCulture;
/// <summary>
/// The identifier of this PSHost implementation.
/// </summary>
private static Guid instanceId = Guid.NewGuid();
/// <summary>
/// A reference to the implementation of the PSHostUserInterface
/// class for this application.
/// </summary>
private MyHostUserInterface myHostUserInterface;
/// <summary>
/// A reference to the runspace used to start an interactive session.
/// </summary>
public Runspace pushedRunspace = null;
/// <summary>
/// Gets the culture information to use. This implementation
/// returns a snapshot of the culture information of the thread
/// that created this object.
/// </summary>
public override CultureInfo CurrentCulture
{
get { return this.originalCultureInfo; }
}
/// <summary>
/// Gets the UI culture information to use. This implementation
/// returns a snapshot of the UI culture information of the thread
/// that created this object.
/// </summary>
public override CultureInfo CurrentUICulture
{
get { return this.originalUICultureInfo; }
}
/// <summary>
/// Gets an identifier for this host. This implementation always
/// returns the GUID allocated at instantiation time.
/// </summary>
public override Guid InstanceId
{
get { return instanceId; }
}
/// <summary>
/// Gets a string that contains the name of this host implementation.
/// Keep in mind that this string may be used by script writers to
/// identify when your host is being used.
/// </summary>
public override string Name
{
get { return "CoreConsoleHost"; }
}
/// <summary>
/// Gets an instance of the implementation of the PSHostUserInterface
/// class for this application. This instance is allocated once at startup time
/// and returned every time thereafter.
/// </summary>
public override PSHostUserInterface UI
{
get { return this.myHostUserInterface; }
}
/// <summary>
/// Gets the version object for this application. Typically this
/// should match the version resource in the application.
/// </summary>
public override Version Version
{
get { return new Version(1, 0, 0, 0); }
}
#region IHostSupportsInteractiveSession Properties
/// <summary>
/// Gets a value indicating whether a request
/// to open a PSSession has been made.
/// </summary>
public bool IsRunspacePushed
{
get { return this.pushedRunspace != null; }
}
/// <summary>
/// Gets or sets the runspace used by the PSSession.
/// </summary>
public Runspace Runspace
{
get { return this.Listener.myRunSpace; }
internal set { this.Listener.myRunSpace = value; }
}
#endregion IHostSupportsInteractiveSession Properties
/// <summary>
/// This API Instructs the host to interrupt the currently running
/// pipeline and start a new nested input loop. In this example this
/// functionality is not needed so the method throws a
/// NotImplementedException exception.
/// </summary>
public override void EnterNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API instructs the host to exit the currently running input loop.
/// In this example this functionality is not needed so the method
/// throws a NotImplementedException exception.
/// </summary>
public override void ExitNestedPrompt()
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API is called before an external application process is
/// started. Typically it is used to save state so that the parent
/// can restore state that has been modified by a child process (after
/// the child exits). In this example this functionality is not
/// needed so the method returns nothing.
/// </summary>
public override void NotifyBeginApplication()
{
return;
}
/// <summary>
/// This API is called after an external application process finishes.
/// Typically it is used to restore state that a child process has
/// altered. In this example, this functionality is not needed so
/// the method returns nothing.
/// </summary>
public override void NotifyEndApplication()
{
return;
}
/// <summary>
/// Indicate to the host application that exit has
/// been requested. Pass the exit code that the host
/// application should use when exiting the process.
/// </summary>
/// <param name="exitCode">The exit code that the
/// host application should use.</param>
public override void SetShouldExit(int exitCode)
{
this.Listener.ShouldExit = true;
this.Listener.ExitCode = exitCode;
}
#region IHostSupportsInteractiveSession Methods
/// <summary>
/// Requests to close a PSSession.
/// </summary>
public void PopRunspace()
{
Runspace = this.pushedRunspace;
this.pushedRunspace = null;
}
/// <summary>
/// Requests to open a PSSession.
/// </summary>
/// <param name="runspace">Runspace to use.</param>
public void PushRunspace(Runspace runspace)
{
this.pushedRunspace = Runspace;
Runspace = runspace;
}
#endregion IHostSupportsInteractiveSession Methods
}
}
@@ -1,857 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Linq;
using PowerShell = System.Management.Automation.PowerShell;
public static class Program
{
private enum Options {Help, File, Command, NoProfile, NonInteractive, EncodedCommand, Invalid};
private class CommandOptions
{
public Options opt;
public string optString;
public CommandOptions(Options o, string s)
{
opt = o;
optString = s;
}
}
private static CommandOptions[] commandOptions = new CommandOptions[]
{
new CommandOptions(Options.Help, "help"),
new CommandOptions(Options.Help, "?"),
new CommandOptions(Options.File, "file"),
new CommandOptions(Options.Command, "command"),
new CommandOptions(Options.NoProfile, "noprofile"),
new CommandOptions(Options.NonInteractive, "noninteractive"),
new CommandOptions(Options.EncodedCommand, "encodedcommand")
};
/// <summary>
/// Creates and initiates the listener instance.
/// </summary>
public static void Main(string[] args)
{
// Setup the Assembly Load Context, which Core PowerShell uses to
// analyze the libraries for types, functions, cmdlets, etc. and
// provide the ability to load assemblies by file path. Doing this
// here eliminates the need for a custom native host.
PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext(string.Empty);
// Argument parsing
string initialScript = null;
bool loadProfiles = true;
bool interactive = true;
if (args.Length > 0)
{
for (int i = 0; i < args.Length; ++i)
{
string arg = args[i];
bool hasNext = (i+1) < args.Length;
string nextArg = hasNext ? args[i+1] : string.Empty;
// options start with "-" or "--"
string option;
if (arg.StartsWith("-"))
{
if (arg.StartsWith("--"))
{
option = arg.Remove(0, 2);
}
else
{
option = arg.Remove(0, 1);
}
Options bestOption = FindOption(option);
switch (bestOption)
{
case Options.Help:
Console.WriteLine(@"
usage: powershell[.exe] [ (--help | -h | -?) ]
[ (--file | -f) <filePath> ]
[ <script>.ps1 ]
[ (--command | -c) <string> ]
[ --noprofile | -nop ]
[ --noninteractive | -non ]
[ (--encodedcommand | -e) <base64Command> ]
SYNOPSIS
Open PowerShell console can take none or one of several arguments.
OPTIONS
No arguments
Will launch PowerShell interactively.
(--file | -f) <filePath>
Given a file path, will execute as a PowerShell script.
<script>.ps1
Given a .ps1 script, will execute without needing --flag.
(--command | -c) <string>
Will execute given string as a PowerShell script.
(--noprofile | -nop)
Disables parsing of PowerShell profiles.
(--help | -h | -?)
Prints this text.
(--noninteractive | -non)
Does not present an interactive prompt to the user.
(--encodedcommand | -e) <base64Command>
Accepts a base-64-encoded string version of a command.
");
return;
case Options.NoProfile:
loadProfiles = false;
break;
case Options.File:
initialScript = Path.GetFullPath(nextArg);
++i;
break;
case Options.Command:
if (nextArg == "-")
{
initialScript = "\"TODO: read stdin using Console.OpenStandardInput\"";
}
else
{
initialScript = nextArg;
}
++i;
break;
case Options.NonInteractive:
interactive = false;
break;
case Options.EncodedCommand:
byte[] data = Convert.FromBase64String(nextArg);
initialScript = Encoding.Unicode.GetString(data);
++i;
break;
default:
Console.WriteLine("Invalid command-line option: {0}. Use '-help' option to get a list of valid options.", arg);
return;
}
}
else // not an option parameter
{
// lone argument is a script
if (!hasNext && arg.EndsWith(".ps1"))
{
initialScript = Path.GetFullPath(arg);
}
// lone argument is an inline script
else if (!hasNext)
{
initialScript = arg;
}
}
}
}
// TODO: check for input on stdin
ConsoleColor InitialForegroundColor = Console.ForegroundColor;
ConsoleColor InitialBackgroundColor = Console.BackgroundColor;
// Create the listener and run it
Listener listener = new Listener(initialScript, loadProfiles, interactive);
// only run if there was no script file passed in
if (initialScript == null)
{
listener.Run();
}
Console.ForegroundColor = InitialForegroundColor;
Console.BackgroundColor = InitialBackgroundColor;
// Exit with the desired exit code that was set by the exit command.
// The exit code is set in the host by the MyHost.SetShouldExit() method.
System.Environment.Exit(listener.ExitCode);
}
private static Options FindOption(string option)
{
int matches = 0;
int index = -1;
for (int i=0; i<commandOptions.Length; ++i)
{
if (commandOptions[i].optString.StartsWith(option, StringComparison.OrdinalIgnoreCase))
{
matches++;
index = i;
}
}
return (matches == 1) ? commandOptions[index].opt : Options.Invalid;
}
}
internal class Listener
{
/// <summary>
/// Used to read user input.
/// </summary>
internal ConsoleReadLine consoleReadLine;
/// <summary>
/// Holds a reference to the runspace for this interpeter.
/// </summary>
internal Runspace myRunSpace;
/// <summary>
/// Indicator to tell the host application that it should exit.
/// </summary>
private bool shouldExit;
/// <summary>
/// The exit code that the host application will use to exit.
/// </summary>
private int exitCode;
/// <summary>
/// Holds a reference to the PSHost implementation for this interpreter.
/// </summary>
private MyHost myHost;
/// <summary>
/// Holds a reference to the currently executing pipeline so that it can be
/// stopped by the control-C handler.
/// </summary>
private PowerShell currentPowerShell;
/// <summary>
/// Used to serialize access to instance data.
/// </summary>
private object instanceLock = new object();
/// <summary>
/// To keep track whether we've displayed the debugger help message
/// </summary>
private bool _showHelpMessage;
/// <summary>
/// To keep track whether last entered command was complete
/// </summary>
private bool incompleteLine = false;
/// <summary>
/// To store incomplete lines
/// </summary>
private string partialLine = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether the host application
/// should exit.
/// </summary>
public bool ShouldExit
{
get { return this.shouldExit; }
set { this.shouldExit = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the host application
/// should exit.
/// </summary>
public int ExitCode
{
get { return this.exitCode; }
set { this.exitCode = value; }
}
/// <summary>
/// Gets or sets a value indicating whether UI exists (display prompt)
/// </summary>
public bool HasUI;
/// <summary>
/// Gets or sets a value indicating whether session is interactive (allow popup)
/// </summary>
public bool Interactive;
public Listener(string initialScript, bool loadProfiles, bool interactive)
{
this.HasUI = (initialScript == null) ? true : false;
this.Interactive = interactive;
// Create the host and runspace instances for this interpreter.
// Note that this application does not support console files so
// only the default snap-ins will be available.
this.myHost = new MyHost(this);
InitialSessionState iss = InitialSessionState.CreateDefault2();
this.myRunSpace = RunspaceFactory.CreateRunspace(this.myHost, iss);
this.myRunSpace.Open();
this.consoleReadLine = new ConsoleReadLine(this.myHost.Runspace, this.myHost.UI);
if (this.myRunSpace.Debugger != null)
{
this.myRunSpace.Debugger.DebuggerStop += HandleDebuggerStopEvent;
// Workflow debugging is new for PowerShell version 4 and is an opt-in
// feature. In order to debug Workflow script functions the debugger
// DebugMode must include the DebugModes.LocalScript flag.
this.myRunSpace.Debugger.SetDebugMode(DebugModes.LocalScript);
}
if (loadProfiles)
{
LoadProfiles();
}
// run the initial script
if (initialScript != null)
{
Execute(initialScript);
}
}
internal void LoadProfiles()
{
// Create a PowerShell object to run the commands used to create
// $profile and load the profiles.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
try
{
this.currentPowerShell.Runspace = this.myRunSpace;
PSCommand[] profileCommands = HostUtilities.GetProfileCommands("Microsoft.PowerShellCore");
foreach (PSCommand command in profileCommands)
{
RunCommand(command);
}
}
finally
{
// Dispose the PowerShell object and set currentPowerShell
// to null. It is locked because currentPowerShell may be
// accessed by the ctrl-C handler.
lock (this.instanceLock)
{
// The PowerShell instance will be null if it failed to
// start due to, say, a parse exception in a profile.
if (this.currentPowerShell != null) {
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
}
/// Sets the prompt equal to the output of the prompt function
public string Prompt(Runspace rs)
{
string returnVal = string.Empty;
if (this.myHost.IsRunspacePushed)
{
returnVal = string.Format($"{System.Environment.NewLine}[{this.myRunSpace.ConnectionInfo.ComputerName}] PSL> ");
return returnVal;
}
if (incompleteLine)
{
return ">> ";
}
Collection<PSObject> output;
Command promptCommand = new Command("prompt");
using (Pipeline pipeline = rs.CreatePipeline())
{
pipeline.Commands.Add(promptCommand);
output = pipeline.Invoke();
}
foreach (PSObject item in output)
{
returnVal = item.BaseObject.ToString();
}
return returnVal;
}
/// <summary>
/// Runs individual commands
/// </summary>
/// <param name="command">command to run</param>
internal void RunCommand(PSCommand command)
{
if (command == null)
{
return;
}
command.AddCommand("out-default");
command.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
this.currentPowerShell.Commands = command;
try
{
this.currentPowerShell.Invoke();
}
catch (RuntimeException e)
{
this.ReportException(e);
}
}
/// <summary>
/// A helper class that builds and executes a pipeline that writes
/// to the default output path. Any exceptions that are thrown are
/// just passed to the caller. Since all output goes to the default
/// outter, this method does not return anything.
/// </summary>
/// <param name="cmd">The script to run.</param>
/// <param name="input">Any input arguments to pass to the script.
/// If null then nothing is passed in.</param>
private void ExecuteHelper(string cmd, object input)
{
// Ignore empty command lines.
if (string.IsNullOrEmpty(cmd))
{
return;
}
// Create the pipeline object and make it available to the
// ctrl-C handle through the currentPowerShell instance
// variable.
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
// Add a script and command to the pipeline and then run the pipeline. Place
// the results in the currentPowerShell variable so that the pipeline can be
// stopped.
try
{
this.currentPowerShell.Runspace = this.myRunSpace;
string fullCommand = incompleteLine ? (partialLine + cmd) : cmd;
this.currentPowerShell.AddScript(fullCommand);
incompleteLine = false;
// Add the default outputter to the end of the pipe and then call the
// MergeMyResults method to merge the output and error streams from the
// pipeline. This will result in the output being written using the PSHost
// and PSHostUserInterface classes instead of returning objects to the host
// application.
this.currentPowerShell.AddCommand("out-default");
this.currentPowerShell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
// If there is any input pass it in, otherwise just invoke the
// the pipeline.
PSInvocationSettings settings = new PSInvocationSettings();
settings.AddToHistory = true;
if (input != null)
{
this.currentPowerShell.Invoke(new object[] { input }, settings);
}
else
{
this.currentPowerShell.Invoke(null, settings);
}
}
catch (IncompleteParseException)
{
incompleteLine = true;
partialLine = $"{partialLine}{cmd}{System.Environment.NewLine}";
}
finally
{
// Dispose the PowerShell object and set currentPowerShell to null.
// It is locked because currentPowerShell may be accessed by the
// ctrl-C handler.
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
if (!incompleteLine)
{
partialLine = string.Empty;
}
}
}
/// <summary>
/// To display an exception using the display formatter,
/// run a second pipeline passing in the error record.
/// The runtime will bind this to the $input variable,
/// which is why $input is being piped to the Out-String
/// cmdlet. The WriteErrorLine method is called to make sure
/// the error gets displayed in the correct error color.
/// </summary>
/// <param name="e">The exception to display.</param>
private void ReportException(Exception e)
{
if (e != null)
{
// Return non-zero exit code if an exception is thrown
this.ExitCode = 1;
object error;
IContainsErrorRecord icer = e as IContainsErrorRecord;
if (icer != null)
{
error = icer.ErrorRecord;
}
else
{
error = (object)new ErrorRecord(e, "Host.ReportException", ErrorCategory.NotSpecified, null);
}
lock (this.instanceLock)
{
this.currentPowerShell = PowerShell.Create();
}
this.currentPowerShell.Runspace = this.myRunSpace;
try
{
this.currentPowerShell.AddScript("$input").AddCommand("out-string");
// Do not merge errors, this function will swallow errors.
Collection<PSObject> result;
PSDataCollection<object> inputCollection = new PSDataCollection<object>();
inputCollection.Add(error);
inputCollection.Complete();
result = this.currentPowerShell.Invoke(inputCollection);
if (result.Count > 0)
{
string str = result[0].BaseObject as string;
if (!string.IsNullOrEmpty(str))
{
// Remove \r\n, which is added by the Out-String cmdlet.
this.myHost.UI.WriteErrorLine(str.Substring(0, str.Length - 2));
}
}
}
finally
{
// Dispose of the pipeline and set it to null, locking it because
// currentPowerShell may be accessed by the ctrl-C handler.
lock (this.instanceLock)
{
this.currentPowerShell.Dispose();
this.currentPowerShell = null;
}
}
}
}
/// <summary>
/// Basic script execution routine. Any runtime exceptions are
/// caught and passed back to the Windows PowerShell engine to
/// display.
/// </summary>
/// <param name="cmd">Script to run.</param>
private void Execute(string cmd)
{
try
{
// Run the command with no input.
this.ExecuteHelper(cmd, null);
}
catch (RuntimeException rte)
{
this.ReportException(rte);
}
}
/// <summary>
/// Method used to handle control-C's from the user. It calls the
/// pipeline Stop() method to stop execution. If any exceptions occur
/// they are printed to the console but otherwise ignored.
/// </summary>
/// <param name="sender">See sender property documentation of
/// ConsoleCancelEventHandler.</param>
/// <param name="e">See e property documentation of
/// ConsoleCancelEventHandler.</param>
private void HandleControlC(object sender, ConsoleCancelEventArgs e)
{
try
{
lock (this.instanceLock)
{
if (this.currentPowerShell != null && this.currentPowerShell.InvocationStateInfo.State == PSInvocationState.Running)
{
this.currentPowerShell.Stop();
}
}
e.Cancel = true;
}
catch (Exception exception)
{
this.myHost.UI.WriteErrorLine(exception.ToString());
}
}
/// <summary>
/// Implements the basic listener loop. It sets up the ctrl-C handler, then
/// reads a command from the user, executes it and repeats until the ShouldExit
/// flag is set.
/// </summary>
internal void Run()
{
// Set up the control-C handler.
Console.CancelKeyPress += new ConsoleCancelEventHandler(this.HandleControlC);
string initialCommand = String.Empty;
// Read commands and run them until the ShouldExit flag is set by
// the user calling "exit".
while (!this.ShouldExit && this.myHost.Runspace != null)
{
// Reset exit code for each command
this.ExitCode = 0;
// If the prompt function failed for any reason, use a sane default
string prompt;
try
{
prompt = Prompt(this.myHost.Runspace);
}
catch
{
prompt = "PS> ";
}
this.myHost.UI.Write(prompt);
string input;
if (TryInvokeUserDefinedReadLine(out input, true))
{
this.Execute(input);
}
else
{
ConsoleReadLine.ReadResult result = consoleReadLine.Read(false, initialCommand);
switch(result.state)
{
case ConsoleReadLine.ReadResult.State.Abort:
incompleteLine = false;
partialLine = string.Empty;
initialCommand = String.Empty;
break;
case ConsoleReadLine.ReadResult.State.Redraw:
initialCommand = result.command;
break;
case ConsoleReadLine.ReadResult.State.Complete:
default:
this.Execute(result.command);
initialCommand = String.Empty;
break;
}
}
}
}
/// <summary>
/// Helper function to test if PSReadLine or another alternate ReadLine has been loaded
/// </summary>
const string CustomReadlineCommand = "PSConsoleHostReadLine";
private bool TryInvokeUserDefinedReadLine(out string input, bool useUserDefinedCustomReadLine)
{
if (useUserDefinedCustomReadLine)
{
var runspace = this.myHost.Runspace;
if (runspace != null
&& runspace.ExecutionContext.EngineIntrinsics.InvokeCommand.GetCommands(CustomReadlineCommand, CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any())
{
try
{
PowerShell ps;
if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) && (Runspace.DefaultRunspace != null))
{
ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
}
else
{
ps = PowerShell.Create();
ps.Runspace = runspace;
}
var result = ps.AddCommand(CustomReadlineCommand).Invoke();
if (result.Count == 1)
{
input = PSObject.Base(result[0]) as string;
return true;
}
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
}
}
}
input = null;
return false;
}
/// <summary>
/// Method to handle the Debugger DebuggerStop event.
/// </summary>
/// <param name="sender"> Debugger instance
/// <param name="args"> DebuggerStop event args
private void HandleDebuggerStopEvent(object sender, DebuggerStopEventArgs args)
{
Debugger debugger = sender as Debugger;
DebuggerResumeAction? resumeAction = null;
WriteDebuggerStopMessages(args);
string initialCommand = String.Empty;
// loop to process Debugger commands.
while (resumeAction == null)
{
string prompt = incompleteLine ? ">> " : "[DBG] PS >> ";
this.myHost.UI.Write(prompt);
ConsoleReadLine.ReadResult result = consoleReadLine.Read(true, initialCommand);
switch(result.state)
{
case ConsoleReadLine.ReadResult.State.Abort:
incompleteLine = false;
partialLine = string.Empty;
initialCommand = String.Empty;
continue;
case ConsoleReadLine.ReadResult.State.Redraw:
initialCommand = result.command;
continue;
case ConsoleReadLine.ReadResult.State.Complete:
default:
initialCommand = String.Empty;
break;
}
// Stream output from command processing to console.
var output = new PSDataCollection<PSObject>();
output.DataAdded += (dSender, dArgs) =>
{
foreach (var item in output.ReadAll())
{
this.myHost.UI.WriteLine(item.ToString());
}
};
// Process command.
// The Debugger.ProcesCommand method will parse and handle debugger specific
// commands such as 'h' (help), 'list', 'stepover', etc. If the command is
// not specific to the debugger then it will be evaluated as a PowerShell
// command or script. The returned DebuggerCommandResults object will indicate
// whether the command was evaluated by the debugger and if the debugger should
// be released with a specific resume action.
PSCommand psCommand = new PSCommand();
string fullCommand = incompleteLine ? (partialLine + result.command) : result.command;
psCommand.AddScript(fullCommand).AddCommand("Out-String").AddParameter("Stream", true);
incompleteLine = false;
DebuggerCommandResults results = null;
try
{
results = debugger.ProcessCommand(psCommand, output);
}
catch (IncompleteParseException)
{
incompleteLine = true;
partialLine = $"{partialLine}{result.command}{System.Environment.NewLine}";
}
if (!incompleteLine)
{
partialLine = string.Empty;
}
if (!incompleteLine && results.ResumeAction != null)
{
resumeAction = results.ResumeAction;
}
}
// Return from event handler with user resume action.
args.ResumeAction = resumeAction.Value;
}
/// <summary>
/// Helper method to write debugger stop messages.
/// </summary>
/// <param name="args">DebuggerStopEventArgs for current debugger stop</param>
private void WriteDebuggerStopMessages(DebuggerStopEventArgs args)
{
// Write debugger stop information in yellow.
ConsoleColor saveFGColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
// Show help message only once.
if (!_showHelpMessage)
{
this.myHost.UI.WriteLine("Entering debug mode. Type 'h' to get help.");
this.myHost.UI.WriteLine();
_showHelpMessage = true;
}
// Breakpoint stop information. Writes all breakpoints that
// pertain to this debugger execution stop point.
if (args.Breakpoints.Count > 0)
{
this.myHost.UI.WriteLine("Debugger hit breakpoint on:");
foreach (var breakPoint in args.Breakpoints)
{
this.myHost.UI.WriteLine(breakPoint.ToString());
}
this.myHost.UI.WriteLine();
}
// Script position stop information.
// This writes the InvocationInfo position message if
// there is one.
if (args.InvocationInfo != null)
{
this.myHost.UI.WriteLine(args.InvocationInfo.PositionMessage);
this.myHost.UI.WriteLine();
}
Console.ForegroundColor = saveFGColor;
}
}
}
@@ -1,46 +0,0 @@
{
"name": "powershell",
"version": "1.0.0-*",
"description": "PowerShell Managed Console for .NET Core",
"authors": [ "andschwa" ],
"compilationOptions": {
"warningsAsErrors": true,
"emitEntryPoint": true
},
"dependencies": {
"Newtonsoft.Json": "8.0.2",
"System.Xml.XDocument": "4.0.11-rc2-24103",
"System.IO.MemoryMappedFiles": "4.0.0-rc2-24103",
"Microsoft.PowerShell.Commands.Management": "1.0.0-*",
"Microsoft.PowerShell.Commands.Utility": "1.0.0-*",
"Microsoft.PowerShell.PSReadLine": "1.0.0-*"
},
"content": [
"Modules",
"../Modules",
"*_profile.ps1",
"*.so",
"*.dylib"
],
"frameworks": {
"netcoreapp1.0": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"dependencies": {
"Microsoft.NETCore.App": "1.0.0-rc2-3002485"
}
}
},
"runtimes": {
"ubuntu.14.04-x64": { },
"centos.7.1-x64": { },
"win7-x64": { },
"win81-x64": { },
"win10-x64": { },
"osx.10.11-x64": { }
}
}
@@ -1,237 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.PowerShell.CoreConsoleHost
{
// this is all from https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx
internal class MyRawUserInterface : PSHostRawUserInterface
{
/// <summary>
/// Gets or sets the background color of the displayed text.
/// This maps to the corresponding Console.Background property.
/// </summary>
public override ConsoleColor BackgroundColor
{
get { return Console.BackgroundColor; }
set { Console.BackgroundColor = value; }
}
/// <summary>
/// Gets or sets the size of the host buffer. In this example the
/// buffer size is adapted from the Console buffer size members.
/// </summary>
public override Size BufferSize
{
get { return new Size(Console.BufferWidth, Console.BufferHeight); }
set { Console.SetBufferSize(value.Width, value.Height); }
}
/// <summary>
/// Gets or sets the cursor position. In this example this
/// functionality is not needed so the property throws a
/// NotImplementException exception.
/// </summary>
public override Coordinates CursorPosition
{
get { return new Coordinates(Console.CursorLeft, Console.CursorTop); }
set { Console.SetCursorPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed cursor. In this example
/// the cursor size is taken directly from the Console.CursorSize
/// property.
/// </summary>
public override int CursorSize
{
get { return Console.CursorSize; }
set { Console.CursorSize = value; }
}
/// <summary>
/// Gets or sets the foreground color of the displayed text.
/// This maps to the corresponding Console.ForgroundColor property.
/// </summary>
public override ConsoleColor ForegroundColor
{
get { return Console.ForegroundColor; }
set { Console.ForegroundColor = value; }
}
/// <summary>
/// Gets a value indicating whether the user has pressed a key. This maps
/// to the corresponding Console.KeyAvailable property.
/// </summary>
public override bool KeyAvailable
{
get { return Console.KeyAvailable; }
}
/// <summary>
/// Gets the dimensions of the largest window that could be
/// rendered in the current display, if the buffer was at the least
/// that large. This example uses the Console.LargestWindowWidth and
/// Console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxPhysicalWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets the dimentions of the largest window size that can be
/// displayed. This example uses the Console.LargestWindowWidth and
/// console.LargestWindowHeight properties to determine the returned
/// value of this property.
/// </summary>
public override Size MaxWindowSize
{
get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); }
}
/// <summary>
/// Gets or sets the position of the displayed window. This example
/// uses the Console window position APIs to determine the returned
/// value of this property.
/// </summary>
public override Coordinates WindowPosition
{
get { return new Coordinates(Console.WindowLeft, Console.WindowTop); }
set { Console.SetWindowPosition(value.X, value.Y); }
}
/// <summary>
/// Gets or sets the size of the displayed window. This example
/// uses the corresponding Console window size APIs to determine the
/// returned value of this property.
/// </summary>
public override Size WindowSize
{
get { return new Size(Console.WindowWidth, Console.WindowHeight); }
set { Console.SetWindowSize(value.Width, value.Height); }
}
/// <summary>
/// Cached Window Title, for systems that needs it
/// </summary>
private string title = String.Empty;
/// <summary>
/// Gets or sets the title of the displayed window. The example
/// maps the Console.Title property to the value of this property.
/// </summary>
public override string WindowTitle
{
get
{
// In Unix/Linux systems, Console.Title current results in a not-implemented
// exception. In that case, we return a cached copy of title that was set earlier.
// Obviously, this will not work if: 1) Title was never set in PowerShell, or 2)
// one sets the windows's title outside of PowerShell.
string result;
try
{
result = Console.Title;
}
catch (PlatformNotSupportedException)
{
return title;
}
return result;
}
set
{
Console.Title = value;
title = value;
}
}
/// <summary>
/// This API resets the input buffer. In this example this
/// functionality is not needed so the method returns nothing.
/// </summary>
public override void FlushInputBuffer()
{
}
/// <summary>
/// This API returns a rectangular region of the screen buffer. In
/// this example this functionality is not needed so the method throws
/// a NotImplementException exception.
/// </summary>
/// <param name="rectangle">Defines the size of the rectangle.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This API reads a pressed, released, or pressed and released keystroke
/// from the keyboard device, blocking processing until a keystroke is
/// typed that matches the specified keystroke options.
/// </summary>
/// <param name="options">Unused</param>
public override KeyInfo ReadKey(ReadKeyOptions options)
{
ConsoleKeyInfo key = Console.ReadKey();
return new KeyInfo((int)key.Key, key.KeyChar, new ControlKeyStates(), true);
}
/// <summary>
/// This API crops a region of the screen buffer. In this example
/// this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="source">The region of the screen to be scrolled.</param>
/// <param name="destination">The region of the screen to receive the
/// source region contents.</param>
/// <param name="clip">The region of the screen to include in the operation.</param>
/// <param name="fill">The character and attributes to be used to fill all cell.</param>
public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This method copies an array of buffer cells into the screen buffer
/// at a specified location. In this example this functionality is
/// not needed so the method throws a NotImplementedException exception.
/// </summary>
/// <param name="origin">The parameter is not used.</param>
/// <param name="contents">The parameter is not used.</param>
public override void SetBufferContents(Coordinates origin,
BufferCell[,] contents)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// This method copies a given character, foreground color, and background
/// color to a region of the screen buffer. In this example this
/// functionality is not needed so the method throws a
/// NotImplementException exception./// </summary>
/// <param name="rectangle">Defines the area to be filled. </param>
/// <param name="fill">Defines the fill character.</param>
public override void SetBufferContents(Rectangle rectangle, BufferCell fill)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,566 +0,0 @@
namespace Microsoft.PowerShell.CoreConsoleHost
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Text;
/// <summary>
/// A sample implementation of the PSHostUserInterface abstract class for
/// console applications. Not all members are implemented. Those that are
/// not implemented throw a NotImplementedException exception or return
/// nothing. Members that are implemented include those that map easily to
/// Console APIs and a basic implementation of the prompt API provided.
/// </summary>
internal class MyHostUserInterface : PSHostUserInterface, IHostUISupportsMultipleChoiceSelection
{
/// <summary>
/// Public constructor
/// </summary>
public MyHostUserInterface(bool hasUI, bool interactive)
{
myRawUi = (hasUI) ? new MyRawUserInterface() : null;
Interactive = interactive;
}
/// <summary>
/// A reference to the PSRawUserInterface implementation.
/// </summary>
private MyRawUserInterface myRawUi;
/// <summary>
/// Gets an instance of the PSRawUserInterface class for this host
/// application.
/// </summary>
public override PSHostRawUserInterface RawUI
{
get { return this.myRawUi; }
}
/// <summary>
/// Whether user pop-ups are allowed
/// </summary>
public bool Interactive;
/// <summary>
/// Prompts the user for input.
/// <param name="caption">The caption or title of the prompt.</param>
/// <param name="message">The text of the prompt.</param>
/// <param name="descriptions">A collection of FieldDescription objects
/// that describe each field of the prompt.</param>
/// <returns>A dictionary object that contains the results of the user
/// prompts.</returns>
public override Dictionary<string, PSObject> Prompt(
string caption,
string message,
Collection<FieldDescription> descriptions)
{
if (Interactive)
{
this.Write(
ConsoleColor.White,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + " ");
Dictionary<string, PSObject> results =
new Dictionary<string, PSObject>();
foreach (FieldDescription fd in descriptions)
{
string[] label = GetHotkeyAndLabel(fd.Label);
this.WriteLine(label[1]);
string userData = Console.ReadLine();
if (userData == null)
{
return null;
}
results[fd.Name] = PSObject.AsPSObject(userData);
}
return results;
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
/// <summary>
/// Provides a set of choices that enable the user to choose a
/// single option from a set of options.
/// </summary>
/// <param name="caption">Text that proceeds (a title) the choices.</param>
/// <param name="message">A message that describes the choice.</param>
/// <param name="choices">A collection of ChoiceDescription objects that
/// describ each choice.</param>
/// <param name="defaultChoice">The index of the label in the Choices
/// parameter collection. To indicate no default choice, set to -1.</param>
/// <returns>The index of the Choices parameter collection element that
/// corresponds to the option that is selected by the user.</returns>
public override int PromptForChoice(
string caption,
string message,
Collection<ChoiceDescription> choices,
int defaultChoice)
{
if (Interactive)
{
// Write the caption and message strings in Blue.
this.WriteLine(
ConsoleColor.Blue,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + System.Environment.NewLine);
// Convert the choice collection into something that is
// easier to work with. See the BuildHotkeysAndPlainLabels
// method for details.
string[,] promptData = BuildHotkeysAndPlainLabels(choices);
// Format the overall choice prompt string to display.
StringBuilder sb = new StringBuilder();
for (int element = 0; element < choices.Count; element++)
{
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"|{0}> {1} ",
promptData[0, element],
promptData[1, element]));
}
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"[Default is ({0}]",
promptData[0, defaultChoice]));
// Read prompts until a match is made, the default is
// chosen, or the loop is interrupted with ctrl-C.
while (true)
{
this.WriteLine(ConsoleColor.Cyan, Console.BackgroundColor, sb.ToString());
string data = Console.ReadLine().Trim().ToUpper();
// If the choice string was empty, use the default selection.
if (data.Length == 0)
{
return defaultChoice;
}
// See if the selection matched and return the
// corresponding index if it did.
for (int i = 0; i < choices.Count; i++)
{
if (promptData[0, i] == data)
{
return i;
}
}
this.WriteErrorLine("Invalid choice: " + data);
}
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
#region IHostUISupportsMultipleChoiceSelection Members
/// <summary>
/// Provides a set of choices that enable the user to choose a one or
/// more options from a set of options.
/// </summary>
/// <param name="caption">Text that proceeds (a title) the choices.</param>
/// <param name="message">A message that describes the choice.</param>
/// <param name="choices">A collection of ChoiceDescription objects that
/// describ each choice.</param>
/// <param name="defaultChoices">The index of the label in the Choices
/// parameter collection. To indicate no default choice, set to -1.</param>
/// <returns>The index of the Choices parameter collection element that
/// corresponds to the option that is selected by the user.</returns>
public Collection<int> PromptForChoice(
string caption,
string message,
Collection<ChoiceDescription> choices,
IEnumerable<int> defaultChoices)
{
if (Interactive)
{
// Write the caption and message strings in Blue.
this.WriteLine(
ConsoleColor.Blue,
Console.BackgroundColor,
caption + System.Environment.NewLine + message + System.Environment.NewLine);
// Convert the choice collection into something that is
// easier to work with. See the BuildHotkeysAndPlainLabels
// method for details.
string[,] promptData = BuildHotkeysAndPlainLabels(choices);
// Format the overall choice prompt string to display.
StringBuilder sb = new StringBuilder();
for (int element = 0; element < choices.Count; element++)
{
sb.Append(String.Format(
CultureInfo.CurrentCulture,
"|{0}> {1} ",
promptData[0, element],
promptData[1, element]));
}
Collection<int> defaultResults = new Collection<int>();
if (defaultChoices != null)
{
int countDefaults = 0;
foreach (int defaultChoice in defaultChoices)
{
++countDefaults;
defaultResults.Add(defaultChoice);
}
if (countDefaults != 0)
{
sb.Append(countDefaults == 1 ? "[Default choice is " : "[Default choices are ");
foreach (int defaultChoice in defaultChoices)
{
sb.AppendFormat(
CultureInfo.CurrentCulture,
"\"{0}\",",
promptData[0, defaultChoice]);
}
sb.Remove(sb.Length - 1, 1);
sb.Append("]");
}
}
this.WriteLine(
ConsoleColor.Cyan,
Console.BackgroundColor,
sb.ToString());
// Read prompts until a match is made, the default is
// chosen, or the loop is interrupted with ctrl-C.
Collection<int> results = new Collection<int>();
while (true)
{
ReadNext:
string prompt = string.Format(CultureInfo.CurrentCulture, "Choice[{0}]:", results.Count);
this.Write(ConsoleColor.Cyan, Console.BackgroundColor, prompt);
string data = Console.ReadLine().Trim().ToUpper();
// If the choice string was empty, no more choices have been made.
// If there were no choices made, return the defaults
if (data.Length == 0)
{
return (results.Count == 0) ? defaultResults : results;
}
// See if the selection matched and return the
// corresponding index if it did.
for (int i = 0; i < choices.Count; i++)
{
if (promptData[0, i] == data)
{
results.Add(i);
goto ReadNext;
}
}
this.WriteErrorLine("Invalid choice: " + data);
}
}
else
{
throw new PSInvalidOperationException("Cannot prompt user when invoked with --noninteractive option.");
}
}
#endregion
/// <summary>
/// Prompts the user for credentials with a specified prompt window
/// caption, prompt message, user name, and target name. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="caption">The caption for the message window.</param>
/// <param name="message">The text of the message.</param>
/// <param name="userName">The user name whose credential is to be
/// prompted for.</param>
/// <param name="targetName">The name of the target for which the
/// credential is collected.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Prompts the user for credentials by using a specified prompt window
/// caption, prompt message, user name and target name, credential
/// types allowed to be returned, and UI behavior options. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <param name="caption">The caption for the message window.</param>
/// <param name="message">The text of the message.</param>
/// <param name="userName">The user name whose credential is to be
/// prompted for.</param>
/// <param name="targetName">The name of the target for which the
/// credential is collected.</param>
/// <param name="allowedCredentialTypes">A PSCredentialTypes constant
/// that identifies the type of credentials that can be returned.</param>
/// <param name="options">A PSCredentialUIOptions constant that
/// identifies the UI behavior when it gathers the credentials.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName,
PSCredentialTypes allowedCredentialTypes,
PSCredentialUIOptions options)
{
throw new NotImplementedException(
"The method or operation is not implemented.");
}
/// <summary>
/// Reads characters that are entered by the user until a newline
/// (carriage return) is encountered.
/// </summary>
/// <returns>The characters that are entered by the user.</returns>
public override string ReadLine()
{
return Console.ReadLine();
}
/// <summary>
/// Reads characters entered by the user until a newline (carriage return)
/// is encountered and returns the characters as a secure string. In this
/// example this functionality is not needed so the method throws a
/// NotImplementException exception.
/// </summary>
/// <returns>Throws a NotImplemented exception.</returns>
public override System.Security.SecureString ReadLineAsSecureString()
{
throw new NotImplementedException("The method or operation is not implemented.");
}
/// <summary>
/// Writes characters to the output display of the host.
/// </summary>
/// <param name="value">The characters to be written.</param>
public override void Write(string value)
{
Console.Write(value);
}
/// <summary>
/// Writes characters to the output display of the host with possible
/// foreground and background colors.
/// </summary>
/// <param name="foregroundColor">The color of the characters.</param>
/// <param name="backgroundColor">The backgound color to use.</param>
/// <param name="value">The characters to be written.</param>
public override void Write(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
string value)
{
ConsoleColor oldFg = Console.ForegroundColor;
ConsoleColor oldBg = Console.BackgroundColor;
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
Console.Write(value);
Console.ForegroundColor = oldFg;
Console.BackgroundColor = oldBg;
}
/// <summary>
/// Writes a line of characters to the output display of the host
/// with foreground and background colors and appends a newline (carriage return).
/// </summary>
/// <param name="foregroundColor">The foreground color of the display. </param>
/// <param name="backgroundColor">The background color of the display. </param>
/// <param name="value">The line to be written.</param>
public override void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
string value)
{
ConsoleColor oldFg = Console.ForegroundColor;
ConsoleColor oldBg = Console.BackgroundColor;
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
Console.WriteLine(value);
Console.ForegroundColor = oldFg;
Console.BackgroundColor = oldBg;
}
/// <summary>
/// Writes a debug message to the output display of the host.
/// </summary>
/// <param name="message">The debug message that is displayed.</param>
public override void WriteDebugLine(string message)
{
this.WriteLine(
ConsoleColor.Yellow,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "DEBUG: {0}", message));
}
/// <summary>
/// Writes an error message to the output display of the host.
/// </summary>
/// <param name="value">The error message that is displayed.</param>
public override void WriteErrorLine(string value)
{
this.WriteLine(
ConsoleColor.Red,
Console.BackgroundColor,
value);
}
/// <summary>
/// Writes a newline character (carriage return)
/// to the output display of the host.
/// </summary>
public override void WriteLine()
{
Console.WriteLine();
}
/// <summary>
/// Writes a line of characters to the output display of the host
/// and appends a newline character(carriage return).
/// </summary>
/// <param name="value">The line to be written.</param>
public override void WriteLine(string value)
{
Console.WriteLine(value);
}
/// <summary>
/// Writes a progress report to the output display of the host.
/// </summary>
/// <param name="sourceId">Unique identifier of the source of the record. </param>
/// <param name="record">A ProgressReport object.</param>
public override void WriteProgress(long sourceId, ProgressRecord record)
{
if (record == null)
{
throw PSTraceSource.NewArgumentNullException("record");
}
string percentComplete = " [";
for (int i =0; i < record.PercentComplete; i++){
percentComplete = percentComplete + "0";
}
percentComplete = percentComplete + "]";
Console.Write ("\r{0} {1} {2} ", record.Activity, record.StatusDescription, percentComplete, "\r");
if (record.PercentComplete == 100){
Console.WriteLine(); //create a new line for the prompt
}
}
/// <summary>
/// Writes a verbose message to the output display of the host.
/// </summary>
/// <param name="message">The verbose message that is displayed.</param>
public override void WriteVerboseLine(string message)
{
this.WriteLine(
ConsoleColor.Green,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "VERBOSE: {0}", message));
}
/// <summary>
/// Writes a warning message to the output display of the host.
/// </summary>
/// <param name="message">The warning message that is displayed.</param>
public override void WriteWarningLine(string message)
{
this.WriteLine(
ConsoleColor.Yellow,
Console.BackgroundColor,
String.Format(CultureInfo.CurrentCulture, "WARNING: {0}", message));
}
/// <summary>
/// Parse a string containing a hotkey character.
/// Take a string of the form
/// Yes to &amp;all
/// and returns a two-dimensional array split out as
/// "A", "Yes to all".
/// </summary>
/// <param name="input">The string to process</param>
/// <returns>
/// A two dimensional array containing the parsed components.
/// </returns>
private static string[] GetHotkeyAndLabel(string input)
{
string[] result = new string[] { String.Empty, String.Empty };
string[] fragments = input.Split('&');
if (fragments.Length == 2)
{
if (fragments[1].Length > 0)
{
result[0] = fragments[1][0].ToString().ToUpper();
}
result[1] = (fragments[0] + fragments[1]).Trim();
}
else
{
result[1] = input;
}
return result;
}
/// <summary>
/// This is a private worker function splits out the
/// accelerator keys from the menu and builds a two
/// dimentional array with the first access containing the
/// accelerator and the second containing the label string
/// with the &amp; removed.
/// </summary>
/// <param name="choices">The choice collection to process</param>
/// <returns>
/// A two dimensional array containing the accelerator characters
/// and the cleaned-up labels</returns>
private static string[,] BuildHotkeysAndPlainLabels(
Collection<ChoiceDescription> choices)
{
// Allocate the result array
string[,] hotkeysAndPlainLabels = new string[2, choices.Count];
for (int i = 0; i < choices.Count; ++i)
{
string[] hotkeyAndLabel = GetHotkeyAndLabel(choices[i].Label);
hotkeysAndPlainLabels[0, i] = hotkeyAndLabel[0];
hotkeysAndPlainLabels[1, i] = hotkeyAndLabel[1];
}
return hotkeysAndPlainLabels;
}
}
}