mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Merge pull request #730 from PowerShell/rawui2
MegaPatch that includes all of #717, plus RawUI fixes
This commit is contained in:
@@ -109,6 +109,9 @@ OPTIONS
|
||||
}
|
||||
// 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);
|
||||
|
||||
@@ -118,6 +121,9 @@ OPTIONS
|
||||
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);
|
||||
@@ -129,7 +135,7 @@ OPTIONS
|
||||
/// <summary>
|
||||
/// Used to read user input.
|
||||
/// </summary>
|
||||
internal ConsoleReadLine consoleReadLine = new ConsoleReadLine();
|
||||
internal ConsoleReadLine consoleReadLine;
|
||||
|
||||
/// <summary>
|
||||
/// Holds a reference to the runspace for this interpeter.
|
||||
@@ -213,6 +219,7 @@ OPTIONS
|
||||
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)
|
||||
{
|
||||
@@ -222,7 +229,6 @@ OPTIONS
|
||||
// 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)
|
||||
@@ -360,14 +366,8 @@ OPTIONS
|
||||
{
|
||||
this.currentPowerShell.Runspace = this.myRunSpace;
|
||||
|
||||
if (incompleteLine)
|
||||
{
|
||||
this.currentPowerShell.AddScript(partialLine + cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentPowerShell.AddScript(cmd);
|
||||
}
|
||||
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
|
||||
@@ -394,11 +394,7 @@ OPTIONS
|
||||
catch (IncompleteParseException)
|
||||
{
|
||||
incompleteLine = true;
|
||||
cmd = cmd.Trim();
|
||||
partialLine += cmd;
|
||||
|
||||
partialLine += System.Environment.NewLine;
|
||||
|
||||
partialLine = $"{partialLine}{cmd}{System.Environment.NewLine}";
|
||||
}
|
||||
|
||||
finally
|
||||
@@ -526,7 +522,6 @@ OPTIONS
|
||||
this.currentPowerShell.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
e.Cancel = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -544,7 +539,8 @@ OPTIONS
|
||||
{
|
||||
// Set up the control-C handler.
|
||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(this.HandleControlC);
|
||||
//Console.TreatControlCAsInput = false;
|
||||
|
||||
string initialCommand = String.Empty;
|
||||
|
||||
// Read commands and run them until the ShouldExit flag is set by
|
||||
// the user calling "exit".
|
||||
@@ -564,9 +560,26 @@ OPTIONS
|
||||
prompt = "PS> ";
|
||||
}
|
||||
|
||||
this.myHost.UI.Write(ConsoleColor.White, Console.BackgroundColor, prompt);
|
||||
string cmd = consoleReadLine.Read(this.myHost.Runspace, false);
|
||||
this.Execute(cmd);
|
||||
this.myHost.UI.Write(prompt);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,12 +595,31 @@ OPTIONS
|
||||
|
||||
WriteDebuggerStopMessages(args);
|
||||
|
||||
string initialCommand = String.Empty;
|
||||
|
||||
// loop to process Debugger commands.
|
||||
while (resumeAction == null)
|
||||
{
|
||||
Console.Write("[DBG] PS >> ");
|
||||
string command = consoleReadLine.Read(this.myHost.Runspace, true);
|
||||
Console.WriteLine();
|
||||
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>();
|
||||
@@ -595,7 +627,7 @@ OPTIONS
|
||||
{
|
||||
foreach (var item in output.ReadAll())
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
this.myHost.UI.WriteLine(item.ToString());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -606,10 +638,30 @@ OPTIONS
|
||||
// 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();
|
||||
psCommand.AddScript(command).AddCommand("Out-String").AddParameter("Stream", true);
|
||||
DebuggerCommandResults results = debugger.ProcessCommand(psCommand, output);
|
||||
if (results.ResumeAction != null)
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -632,8 +684,8 @@ OPTIONS
|
||||
// Show help message only once.
|
||||
if (!_showHelpMessage)
|
||||
{
|
||||
Console.WriteLine("Entering debug mode. Type 'h' to get help.");
|
||||
Console.WriteLine();
|
||||
this.myHost.UI.WriteLine("Entering debug mode. Type 'h' to get help.");
|
||||
this.myHost.UI.WriteLine();
|
||||
_showHelpMessage = true;
|
||||
}
|
||||
|
||||
@@ -641,12 +693,12 @@ OPTIONS
|
||||
// pertain to this debugger execution stop point.
|
||||
if (args.Breakpoints.Count > 0)
|
||||
{
|
||||
Console.WriteLine("Debugger hit breakpoint on:");
|
||||
this.myHost.UI.WriteLine("Debugger hit breakpoint on:");
|
||||
foreach (var breakPoint in args.Breakpoints)
|
||||
{
|
||||
Console.WriteLine(breakPoint.ToString());
|
||||
this.myHost.UI.WriteLine(breakPoint.ToString());
|
||||
}
|
||||
Console.WriteLine();
|
||||
this.myHost.UI.WriteLine();
|
||||
}
|
||||
|
||||
// Script position stop information.
|
||||
@@ -654,8 +706,8 @@ OPTIONS
|
||||
// there is one.
|
||||
if (args.InvocationInfo != null)
|
||||
{
|
||||
Console.WriteLine(args.InvocationInfo.PositionMessage);
|
||||
Console.WriteLine();
|
||||
this.myHost.UI.WriteLine(args.InvocationInfo.PositionMessage);
|
||||
this.myHost.UI.WriteLine();
|
||||
}
|
||||
|
||||
Console.ForegroundColor = saveFGColor;
|
||||
|
||||
@@ -122,14 +122,40 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
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 { return Console.Title; }
|
||||
set { Console.Title = value; }
|
||||
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>
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Host;
|
||||
using System.Management.Automation.Runspaces;
|
||||
using System.Text;
|
||||
|
||||
@@ -19,6 +20,11 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
/// </summary>
|
||||
private PowerShell powershell = PowerShell.Create();
|
||||
|
||||
/// <summary>
|
||||
/// Host's UserInterface
|
||||
/// </summary>
|
||||
private PSHostUserInterface ui;
|
||||
|
||||
/// <summary>
|
||||
/// The buffer used to edit.
|
||||
/// </summary>
|
||||
@@ -78,7 +84,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
/// We do not pick different colors for every token, those tokens
|
||||
/// use this default.
|
||||
/// </summary>
|
||||
private ConsoleColor defaultColor = ConsoleColor.White;
|
||||
private ConsoleColor defaultColor = Console.ForegroundColor;
|
||||
|
||||
/// <summary>
|
||||
/// To keep track of whether we have hit "enter" key since last arrow up/down key
|
||||
@@ -90,10 +96,48 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
/// </summary>
|
||||
private string preTabBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// kill buffer used by Ctrl-K and Ctrl-Y
|
||||
/// </summary>
|
||||
private string killBuffer = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Indicator that command input is complete
|
||||
/// </summary>
|
||||
private bool commandComplete;
|
||||
|
||||
/// <summary>
|
||||
/// Indicator that current command is to be aborted
|
||||
/// </summary>
|
||||
private bool abort;
|
||||
|
||||
/// <summary>
|
||||
/// Indicator that we are to redraw current command
|
||||
/// </summary>
|
||||
private bool redraw;
|
||||
|
||||
/// <summary>
|
||||
/// What Read() outputs
|
||||
/// </summary>
|
||||
public class ReadResult
|
||||
{
|
||||
public ReadResult(State s, string cmd)
|
||||
{
|
||||
this.command = cmd;
|
||||
this.state = s;
|
||||
Console.TreatControlCAsInput = false;
|
||||
}
|
||||
|
||||
public enum State {Complete, Abort, Redraw}
|
||||
|
||||
public string command;
|
||||
public State state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ConsoleReadLine class.
|
||||
/// </summary>
|
||||
public ConsoleReadLine()
|
||||
public ConsoleReadLine(Runspace runspace, PSHostUserInterface hostUI)
|
||||
{
|
||||
this.tokenColors = new ConsoleColor[]
|
||||
{
|
||||
@@ -118,147 +162,215 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
this.defaultColor, // LineContinuation
|
||||
this.defaultColor, // Position
|
||||
};
|
||||
this.powershell.Runspace = runspace;
|
||||
this.ui = hostUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a line of text, colorizing while typing.
|
||||
/// </summary>
|
||||
/// <returns>The command line read</returns>
|
||||
public string Read(Runspace runspace, bool nested)
|
||||
public ReadResult Read(bool nested, string initialValue)
|
||||
{
|
||||
this.powershell.Runspace = runspace;
|
||||
this.Initialize();
|
||||
|
||||
if (!String.IsNullOrEmpty(initialValue))
|
||||
{
|
||||
this.BufferFromString(initialValue);
|
||||
this.Render();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||
|
||||
// Basic Emacs-style readline implementation
|
||||
if (key.Modifiers.HasFlag(ConsoleModifiers.Control))
|
||||
try
|
||||
{
|
||||
switch (key.Key)
|
||||
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||
|
||||
// Basic Emacs-style readline implementation
|
||||
if (key.Modifiers.HasFlag(ConsoleModifiers.Control))
|
||||
{
|
||||
case ConsoleKey.A:
|
||||
this.OnHome();
|
||||
break;
|
||||
case ConsoleKey.E:
|
||||
this.OnEnd();
|
||||
break;
|
||||
// TODO: save buffer, yank with Ctrl-Y
|
||||
/*
|
||||
case ConsoleKey.K:
|
||||
this.OnEscape();
|
||||
break;
|
||||
*/
|
||||
case ConsoleKey.D:
|
||||
this.OnDelete();
|
||||
break;
|
||||
case ConsoleKey.B:
|
||||
this.OnLeft(key.Modifiers);
|
||||
break;
|
||||
case ConsoleKey.F:
|
||||
this.OnRight(key.Modifiers);
|
||||
break;
|
||||
case ConsoleKey.P:
|
||||
// TODO: incremental search
|
||||
case ConsoleKey.R:
|
||||
this.OnUpArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.N:
|
||||
// TODO: incremental search
|
||||
case ConsoleKey.S:
|
||||
this.OnDownArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.J:
|
||||
this.OnEnter();
|
||||
break;
|
||||
// TODO: save and restore buffer
|
||||
/*
|
||||
case ConsoleKey.L:
|
||||
this.BufferFromString("clear");
|
||||
previousKeyPress = key;
|
||||
return this.OnEnter();
|
||||
*/
|
||||
commandComplete = ProcessControlKey(key, nested);
|
||||
}
|
||||
else if (key.Modifiers.HasFlag(ConsoleModifiers.Alt))
|
||||
{
|
||||
commandComplete = ProcessAltKey(key);
|
||||
}
|
||||
// Unmodified keys
|
||||
else
|
||||
{
|
||||
commandComplete = ProcessNormalKey(key, nested);
|
||||
}
|
||||
|
||||
previousKeyPress = key;
|
||||
}
|
||||
else if (key.Modifiers.HasFlag(ConsoleModifiers.Alt))
|
||||
catch
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
// TODO: OnDelete(key)
|
||||
// TODO: OnBackspace(key)
|
||||
case ConsoleKey.B:
|
||||
this.OnLeft(key.Modifiers);
|
||||
break;
|
||||
case ConsoleKey.F:
|
||||
this.OnRight(key.Modifiers);
|
||||
break;
|
||||
}
|
||||
// if anything goes wrong, abort
|
||||
abort = true;
|
||||
}
|
||||
// Unmodified keys
|
||||
else
|
||||
|
||||
if (abort)
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
case ConsoleKey.Backspace:
|
||||
this.OnBackspace();
|
||||
break;
|
||||
case ConsoleKey.Delete:
|
||||
this.OnDelete();
|
||||
break;
|
||||
case ConsoleKey.Enter:
|
||||
previousKeyPress = key;
|
||||
return this.OnEnter();
|
||||
case ConsoleKey.RightArrow:
|
||||
this.OnRight(key.Modifiers);
|
||||
break;
|
||||
case ConsoleKey.LeftArrow:
|
||||
this.OnLeft(key.Modifiers);
|
||||
break;
|
||||
case ConsoleKey.Escape:
|
||||
this.OnEscape();
|
||||
break;
|
||||
case ConsoleKey.Home:
|
||||
this.OnHome();
|
||||
break;
|
||||
case ConsoleKey.End:
|
||||
this.OnEnd();
|
||||
break;
|
||||
case ConsoleKey.Tab:
|
||||
this.OnTab();
|
||||
break;
|
||||
case ConsoleKey.UpArrow:
|
||||
this.OnUpArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.DownArrow:
|
||||
this.OnDownArrow(nested);
|
||||
break;
|
||||
|
||||
// TODO: case ConsoleKey.LeftWindows: not available in linux
|
||||
// TODO: case ConsoleKey.RightWindows: not available in linux
|
||||
|
||||
default:
|
||||
|
||||
if (key.KeyChar == '\x0D')
|
||||
{
|
||||
goto case ConsoleKey.Enter; // Ctrl-M
|
||||
}
|
||||
|
||||
if (key.KeyChar == '\x08')
|
||||
{
|
||||
goto case ConsoleKey.Backspace; // Ctrl-H
|
||||
}
|
||||
|
||||
this.Insert(key.KeyChar);
|
||||
|
||||
this.Render();
|
||||
break;
|
||||
}
|
||||
return new ReadResult(ReadResult.State.Abort, String.Empty);
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
{
|
||||
return new ReadResult(ReadResult.State.Redraw, this.buffer.ToString());
|
||||
}
|
||||
|
||||
if (commandComplete)
|
||||
{
|
||||
return new ReadResult(ReadResult.State.Complete, this.OnEnter());
|
||||
}
|
||||
previousKeyPress = key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process Control-Key combo
|
||||
/// </summary>
|
||||
private bool ProcessControlKey(ConsoleKeyInfo key, bool nested)
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
case ConsoleKey.A:
|
||||
this.OnHome();
|
||||
break;
|
||||
case ConsoleKey.E:
|
||||
this.OnEnd();
|
||||
break;
|
||||
case ConsoleKey.K:
|
||||
this.OnKill();
|
||||
break;
|
||||
case ConsoleKey.Y:
|
||||
this.OnYank();
|
||||
break;
|
||||
case ConsoleKey.D:
|
||||
this.OnDelete();
|
||||
break;
|
||||
case ConsoleKey.B:
|
||||
this.OnLeft(false);
|
||||
break;
|
||||
case ConsoleKey.F:
|
||||
this.OnRight(false);
|
||||
break;
|
||||
case ConsoleKey.R:
|
||||
return this.ReverseSearch(nested);
|
||||
case ConsoleKey.J:
|
||||
previousKeyPress = key;
|
||||
return true;
|
||||
case ConsoleKey.L:
|
||||
Console.Clear();
|
||||
redraw = true;
|
||||
return false;
|
||||
case ConsoleKey.C:
|
||||
this.Abort();
|
||||
return true;
|
||||
case ConsoleKey.P:
|
||||
this.OnUpArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.N:
|
||||
this.OnDownArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.RightArrow:
|
||||
this.OnRight(true);
|
||||
break;
|
||||
case ConsoleKey.LeftArrow:
|
||||
this.OnLeft(true);
|
||||
break;
|
||||
case ConsoleKey.Home:
|
||||
this.OnKillBackward();
|
||||
break;
|
||||
case ConsoleKey.End:
|
||||
this.OnKill();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process Alt-Key combo
|
||||
/// </summary>
|
||||
private bool ProcessAltKey(ConsoleKeyInfo key)
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
// TODO: OnDelete(key)
|
||||
// TODO: OnBackspace(key)
|
||||
case ConsoleKey.B:
|
||||
this.OnLeft(true);
|
||||
break;
|
||||
case ConsoleKey.F:
|
||||
this.OnRight(true);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process normal key
|
||||
/// </summary>
|
||||
private bool ProcessNormalKey(ConsoleKeyInfo key, bool nested)
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
case ConsoleKey.Backspace:
|
||||
this.OnBackspace();
|
||||
break;
|
||||
case ConsoleKey.Delete:
|
||||
this.OnDelete();
|
||||
break;
|
||||
case ConsoleKey.Enter:
|
||||
previousKeyPress = key;
|
||||
return true;
|
||||
case ConsoleKey.RightArrow:
|
||||
this.OnRight(false);
|
||||
break;
|
||||
case ConsoleKey.LeftArrow:
|
||||
this.OnLeft(false);
|
||||
break;
|
||||
case ConsoleKey.Escape:
|
||||
this.OnEscape();
|
||||
break;
|
||||
case ConsoleKey.Home:
|
||||
this.OnHome();
|
||||
break;
|
||||
case ConsoleKey.End:
|
||||
this.OnEnd();
|
||||
break;
|
||||
case ConsoleKey.Tab:
|
||||
this.OnTab();
|
||||
break;
|
||||
case ConsoleKey.UpArrow:
|
||||
this.OnUpArrow(nested);
|
||||
break;
|
||||
case ConsoleKey.DownArrow:
|
||||
this.OnDownArrow(nested);
|
||||
break;
|
||||
|
||||
// TODO: case ConsoleKey.LeftWindows: not available in linux
|
||||
// TODO: case ConsoleKey.RightWindows: not available in linux
|
||||
|
||||
default:
|
||||
|
||||
if (key.KeyChar == '\x0D')
|
||||
{
|
||||
goto case ConsoleKey.Enter; // Ctrl-M
|
||||
}
|
||||
|
||||
if (key.KeyChar == '\x08')
|
||||
{
|
||||
goto case ConsoleKey.Backspace; // Ctrl-H
|
||||
}
|
||||
|
||||
this.Insert(key.KeyChar);
|
||||
|
||||
this.Render();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the buffer.
|
||||
/// </summary>
|
||||
@@ -270,6 +382,10 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
this.current = 0;
|
||||
this.rendered = 0;
|
||||
this.cursor = new Cursor();
|
||||
this.commandComplete = false;
|
||||
this.abort = false;
|
||||
this.redraw = false;
|
||||
Console.TreatControlCAsInput = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -494,6 +610,135 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
this.newHistory = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse search through history
|
||||
/// </summary>
|
||||
private bool ReverseSearch(bool nested)
|
||||
{
|
||||
GetHistory(nested);
|
||||
|
||||
if (historyResult.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int searchPos = historyResult.Count - 1;
|
||||
|
||||
StringBuilder searchString = new StringBuilder();
|
||||
string searchResult = String.Empty;
|
||||
string failed = String.Empty;
|
||||
|
||||
while (true)
|
||||
{
|
||||
OnEscape();
|
||||
ui.Write("(");
|
||||
ui.Write(ConsoleColor.Red, Console.BackgroundColor, $"{failed}");
|
||||
ui.Write($"bck-i-search)'{searchString}");
|
||||
this.current = this.cursor.GetPosition();
|
||||
ui.Write("': ");
|
||||
ui.Write(ConsoleColor.Green, Console.BackgroundColor, $"{searchResult}");
|
||||
this.rendered = this.cursor.GetPosition();
|
||||
this.cursor.Place(this.current);
|
||||
|
||||
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||
|
||||
bool terminateSearch = false;
|
||||
if (key.Modifiers.HasFlag(ConsoleModifiers.Control))
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
case ConsoleKey.R: // repeat search
|
||||
searchPos--;
|
||||
break;
|
||||
case ConsoleKey.C:
|
||||
Abort();
|
||||
return true;
|
||||
default:
|
||||
terminateSearch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (key.Modifiers.HasFlag(ConsoleModifiers.Alt))
|
||||
{
|
||||
terminateSearch = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (key.Key)
|
||||
{
|
||||
case ConsoleKey.Backspace:
|
||||
if (searchString.Length > 0)
|
||||
{
|
||||
searchString.Length--;
|
||||
}
|
||||
searchPos = historyResult.Count - 1;
|
||||
break;
|
||||
case ConsoleKey.Enter:
|
||||
BufferFromString(searchResult);
|
||||
Render();
|
||||
return true;
|
||||
case ConsoleKey.Delete:
|
||||
case ConsoleKey.RightArrow:
|
||||
case ConsoleKey.LeftArrow:
|
||||
case ConsoleKey.Escape:
|
||||
case ConsoleKey.Home:
|
||||
case ConsoleKey.End:
|
||||
case ConsoleKey.Tab:
|
||||
case ConsoleKey.UpArrow:
|
||||
case ConsoleKey.DownArrow:
|
||||
terminateSearch = true;
|
||||
break;
|
||||
default:
|
||||
searchString.Append(key.KeyChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (terminateSearch)
|
||||
{
|
||||
BufferFromString(searchResult);
|
||||
Render();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (searchString.Length == 0)
|
||||
{
|
||||
searchResult = String.Empty;
|
||||
failed = String.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = reverseSearchHistory(searchString.ToString(), ref searchPos);
|
||||
if (String.IsNullOrEmpty(result))
|
||||
{
|
||||
failed = "failed-";
|
||||
}
|
||||
else
|
||||
{
|
||||
failed = String.Empty;
|
||||
searchResult = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse search command history for one that match pattern
|
||||
/// </summary>
|
||||
private string reverseSearchHistory(string pattern, ref int pos)
|
||||
{
|
||||
for (int i = pos; i >= 0; --i)
|
||||
{
|
||||
string command = historyResult[i].Members["CommandLine"].Value.ToString();
|
||||
if (command.Contains(pattern))
|
||||
{
|
||||
pos = i;
|
||||
return command;
|
||||
}
|
||||
}
|
||||
pos = -1;
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get command history
|
||||
/// </summary>
|
||||
@@ -510,11 +755,11 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
/// <summary>
|
||||
/// Moves to the left of the cursor position.
|
||||
/// </summary>
|
||||
/// <param name="consoleModifiers">Enumeration for Alt, Control,
|
||||
/// <param name="byWord">move by word instead of by letter
|
||||
/// and Shift keys.</param>
|
||||
private void OnLeft(ConsoleModifiers consoleModifiers)
|
||||
private void OnLeft(bool byWord)
|
||||
{
|
||||
if (consoleModifiers.HasFlag(ConsoleModifiers.Alt))
|
||||
if (byWord)
|
||||
{
|
||||
// Move back to the start of the previous word.
|
||||
if (this.buffer.Length > 0 && this.current != 0)
|
||||
@@ -557,11 +802,11 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
/// <summary>
|
||||
/// Moves to what is to the right of the cursor position.
|
||||
/// </summary>
|
||||
/// <param name="consoleModifiers">Enumeration for Alt, Control,
|
||||
/// <param name="byWord">move by word instead of by letter
|
||||
/// and Shift keys.</param>
|
||||
private void OnRight(ConsoleModifiers consoleModifiers)
|
||||
private void OnRight(bool byWord)
|
||||
{
|
||||
if (consoleModifiers.HasFlag(ConsoleModifiers.Alt))
|
||||
if (byWord)
|
||||
{
|
||||
// Move to the next word.
|
||||
if (this.buffer.Length != 0 && this.current < this.buffer.Length)
|
||||
@@ -625,7 +870,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
private string OnEnter()
|
||||
{
|
||||
this.newHistory = true;
|
||||
Console.Out.WriteLine();
|
||||
ui.WriteLine();
|
||||
return this.buffer.ToString();
|
||||
}
|
||||
|
||||
@@ -641,6 +886,54 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctrl-K or Ctrl-End was entered.
|
||||
/// </summary>
|
||||
private void OnKill()
|
||||
{
|
||||
if (this.buffer.Length > 0 && this.current < this.buffer.Length)
|
||||
{
|
||||
this.killBuffer = this.buffer.ToString().Substring(this.current);
|
||||
this.buffer.Remove(this.current, killBuffer.Length);
|
||||
this.Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.killBuffer = String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctrl-Home was entered, kill from begin-of-line to current position
|
||||
/// </summary>
|
||||
private void OnKillBackward()
|
||||
{
|
||||
if (this.current > 0)
|
||||
{
|
||||
this.killBuffer = this.buffer.ToString().Substring(0, this.current);
|
||||
this.buffer.Remove(0, this.current);
|
||||
this.current = 0;
|
||||
this.Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.killBuffer = String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctrl-Y was entered.
|
||||
/// </summary>
|
||||
private void OnYank()
|
||||
{
|
||||
if (!String.IsNullOrEmpty(killBuffer))
|
||||
{
|
||||
this.buffer.Insert(this.current, killBuffer);
|
||||
this.current += killBuffer.Length;
|
||||
this.Render();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Backspace key was entered.
|
||||
/// </summary>
|
||||
@@ -663,6 +956,10 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
|
||||
string text = this.buffer.ToString();
|
||||
|
||||
// TODO: Rendering of a multiline command on separate lines.
|
||||
// For now, just display it as a single-line command.
|
||||
text = text.Replace(System.Environment.NewLine, " ");
|
||||
|
||||
// The PowerShell tokenizer is used to decide how to colorize
|
||||
// the input. Any errors in the input are returned in 'errors',
|
||||
// but we won't be looking at those here.
|
||||
@@ -671,15 +968,12 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
|
||||
if (tokens.Count > 0)
|
||||
{
|
||||
// We can skip rendering tokens that end before the cursor.
|
||||
int i;
|
||||
ConsoleColor saveFGColor = Console.ForegroundColor;
|
||||
|
||||
for (i = 0; i < tokens.Count; ++i)
|
||||
// Print leading blanks
|
||||
if (tokens[0].Start != 0)
|
||||
{
|
||||
if (this.current >= tokens[i].Start)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ui.Write(new string(' ', tokens[0].Start));
|
||||
}
|
||||
|
||||
// Place the cursor at the start of the first token to render. The
|
||||
@@ -687,13 +981,14 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
// preceding the cursor.
|
||||
//this.cursor.Place(tokens[i].Start);
|
||||
|
||||
for (; i < tokens.Count; ++i)
|
||||
int i;
|
||||
for (i = 0; i < tokens.Count; ++i)
|
||||
{
|
||||
// Write out the token. We don't use tokens[i].Content, instead we
|
||||
// use the actual text from our input because the content sometimes
|
||||
// excludes part of the token, e.g. the quote characters of a string.
|
||||
Console.ForegroundColor = this.tokenColors[(int)tokens[i].Type];
|
||||
Console.Out.Write(text.Substring(tokens[i].Start, tokens[i].Length));
|
||||
ui.Write(text.Substring(tokens[i].Start, tokens[i].Length));
|
||||
|
||||
// Whitespace doesn't show up in the array of tokens. Write it out here.
|
||||
if (i != (tokens.Count - 1))
|
||||
@@ -701,7 +996,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
Console.ForegroundColor = this.defaultColor;
|
||||
for (int j = (tokens[i].Start + tokens[i].Length); j < tokens[i + 1].Start; ++j)
|
||||
{
|
||||
Console.Out.Write(text[j]);
|
||||
ui.Write(text[j].ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -711,8 +1006,10 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
Console.ForegroundColor = this.defaultColor;
|
||||
for (int j = tokens[i - 1].Start + tokens[i - 1].Length; j < text.Length; ++j)
|
||||
{
|
||||
Console.Out.Write(text[j]);
|
||||
ui.Write(text[j].ToString());
|
||||
}
|
||||
|
||||
Console.ForegroundColor = saveFGColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -720,19 +1017,31 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
// happens most frequently when the first token is incomplete, like a string
|
||||
// literal missing a closing quote.
|
||||
this.cursor.Reset();
|
||||
Console.Out.Write(text);
|
||||
ui.Write(text);
|
||||
}
|
||||
|
||||
// If characters were deleted, we must write over previously written characters
|
||||
if (text.Length < this.rendered)
|
||||
{
|
||||
Console.Out.Write(new string(' ', this.rendered - text.Length));
|
||||
ui.Write(new string(' ', this.rendered - text.Length));
|
||||
}
|
||||
|
||||
this.rendered = text.Length;
|
||||
this.cursor.Place(this.current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abort current command
|
||||
/// </summary>
|
||||
public void Abort()
|
||||
{
|
||||
ConsoleColor saveFGColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
ui.WriteLine("^C");
|
||||
Console.ForegroundColor = saveFGColor;
|
||||
this.abort = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper class for maintaining the cursor while editing the command line.
|
||||
/// </summary>
|
||||
@@ -796,6 +1105,15 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||
|
||||
Console.CursorTop = cursorTop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return current position, relative to anchorLeft
|
||||
/// </summary>
|
||||
internal int GetPosition()
|
||||
{
|
||||
return Console.CursorLeft - this.anchorLeft;
|
||||
}
|
||||
|
||||
} // End Cursor
|
||||
|
||||
}
|
||||
|
||||
@@ -349,10 +349,13 @@ ReadNext:
|
||||
ConsoleColor backgroundColor,
|
||||
string value)
|
||||
{
|
||||
ConsoleColor oldFg = Console.ForegroundColor;
|
||||
ConsoleColor oldBg = Console.BackgroundColor;
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.BackgroundColor = backgroundColor;
|
||||
Console.Write(value);
|
||||
Console.ResetColor();
|
||||
Console.ForegroundColor = oldFg;
|
||||
Console.BackgroundColor = oldBg;
|
||||
}
|
||||
|
||||
|
||||
@@ -368,10 +371,13 @@ ReadNext:
|
||||
ConsoleColor backgroundColor,
|
||||
string value)
|
||||
{
|
||||
ConsoleColor oldFg = Console.ForegroundColor;
|
||||
ConsoleColor oldBg = Console.BackgroundColor;
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.BackgroundColor = backgroundColor;
|
||||
Console.WriteLine(value);
|
||||
Console.ResetColor();
|
||||
Console.ForegroundColor = oldFg;
|
||||
Console.BackgroundColor = oldBg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user