Implemented readline, readkey, special keys
This commit is contained in:
parent
536dc41859
commit
65b2cb9831
2 changed files with 205 additions and 46 deletions
|
@ -419,7 +419,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
string prompt = Prompt(this.myHost.Runspace);
|
string prompt = Prompt(this.myHost.Runspace);
|
||||||
|
|
||||||
this.myHost.UI.Write(ConsoleColor.White, ConsoleColor.Black, prompt);
|
this.myHost.UI.Write(ConsoleColor.White, ConsoleColor.Black, prompt);
|
||||||
string cmd = this.myHost.UI.ReadLine();
|
string cmd = consoleReadLine.Read(this.myHost.Runspace);
|
||||||
this.Execute(cmd);
|
this.Execute(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
|
using System.Management.Automation.Runspaces;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -12,17 +13,62 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ConsoleReadLine
|
internal class ConsoleReadLine
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds a reference to the runspace (for history access).
|
||||||
|
/// </summary>
|
||||||
|
private Runspace runspace;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The buffer used to edit.
|
/// The buffer used to edit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private StringBuilder buffer = new StringBuilder();
|
private StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// integeger for tracking up and down arrow history
|
||||||
|
/// </summary>
|
||||||
|
private int historyIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for storing tab completion
|
||||||
|
/// </summary>
|
||||||
|
private CommandCompletion cmdCompleteOpt;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the cursor within the buffer.
|
/// The position of the cursor within the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int current;
|
private int current;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detects previously pressed key for TabCompletion
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
private ConsoleKeyInfo previousKeyPress;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retains TabCompletion position
|
||||||
|
/// </summary>
|
||||||
|
private int tabCompletionPos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// History Queue
|
||||||
|
/// </summary>
|
||||||
|
private Collection<PSObject> historyResult;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hashtable for command completion options
|
||||||
|
/// </summary>
|
||||||
|
private System.Collections.Hashtable options = new System.Collections.Hashtable();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retain original buffer for TabCompletion
|
||||||
|
/// </summary>
|
||||||
|
private StringBuilder tabBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tabbuffer for storing result of tabcompletion
|
||||||
|
/// </summary>
|
||||||
|
private string tabResult;
|
||||||
|
|
||||||
/// The count of characters in buffer rendered.
|
/// The count of characters in buffer rendered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int rendered;
|
private int rendered;
|
||||||
|
@ -30,7 +76,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Store the anchor and handle cursor movement
|
/// Store the anchor and handle cursor movement
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//private Cursor cursor;
|
private Cursor cursor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The array of colors for tokens, indexed by PSTokenType
|
/// The array of colors for tokens, indexed by PSTokenType
|
||||||
|
@ -77,30 +123,26 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
/// Read a line of text, colorizing while typing.
|
/// Read a line of text, colorizing while typing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The command line read</returns>
|
/// <returns>The command line read</returns>
|
||||||
public string Read()
|
public string Read(Runspace runspace)
|
||||||
{
|
{
|
||||||
|
this.runspace = runspace;
|
||||||
this.Initialize();
|
this.Initialize();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// no support for Console.ReadKey in CoreCLR yet
|
|
||||||
int key = Console.Read();
|
|
||||||
char keyChar = Convert.ToChar(key);
|
|
||||||
this.Insert(keyChar);
|
|
||||||
/*
|
|
||||||
ConsoleKeyInfo key = Console.ReadKey(true);
|
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||||
|
|
||||||
switch (key.Key)
|
switch (key.Key)
|
||||||
{
|
{
|
||||||
case ConsoleKey.Backspace:
|
case ConsoleKey.Backspace:
|
||||||
this.OnBackspace();
|
this.OnBackspace();
|
||||||
break;
|
break;
|
||||||
case ConsoleKey.Delete:
|
case ConsoleKey.Delete:
|
||||||
this.OnDelete();
|
this.OnDelete();
|
||||||
break;
|
break;
|
||||||
case ConsoleKey.Enter:
|
case ConsoleKey.Enter:
|
||||||
return this.OnEnter();
|
return this.OnEnter();
|
||||||
case ConsoleKey.RightArrow:
|
case ConsoleKey.RightArrow:
|
||||||
this.OnRight(key.Modifiers);
|
this.OnRight(key.Modifiers);
|
||||||
break;
|
break;
|
||||||
case ConsoleKey.LeftArrow:
|
case ConsoleKey.LeftArrow:
|
||||||
|
@ -115,14 +157,21 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
case ConsoleKey.End:
|
case ConsoleKey.End:
|
||||||
this.OnEnd();
|
this.OnEnd();
|
||||||
break;
|
break;
|
||||||
|
case ConsoleKey.Tab:
|
||||||
|
this.OnTab();
|
||||||
|
break;
|
||||||
case ConsoleKey.UpArrow:
|
case ConsoleKey.UpArrow:
|
||||||
|
this.OnUpArrow();
|
||||||
|
break;
|
||||||
case ConsoleKey.DownArrow:
|
case ConsoleKey.DownArrow:
|
||||||
case ConsoleKey.LeftWindows:
|
// TODO: this.OnDownArrow();
|
||||||
case ConsoleKey.RightWindows:
|
continue;
|
||||||
// ignore these
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
// TODO: case ConsoleKey.LeftWindows: not available in linux
|
||||||
|
// TODO: case ConsoleKey.RightWindows: not available in linux
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if (key.KeyChar == '\x0D')
|
if (key.KeyChar == '\x0D')
|
||||||
{
|
{
|
||||||
goto case ConsoleKey.Enter; // Ctrl-M
|
goto case ConsoleKey.Enter; // Ctrl-M
|
||||||
|
@ -133,9 +182,12 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
goto case ConsoleKey.Backspace; // Ctrl-H
|
goto case ConsoleKey.Backspace; // Ctrl-H
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Insert(key);
|
this.Insert(key.KeyChar);
|
||||||
break;
|
|
||||||
}*/
|
this.Render();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previousKeyPress = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,10 +196,12 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
this.buffer.Length = 0;
|
this.tabCompletionPos = 0;
|
||||||
|
this.historyIndex = 0;
|
||||||
|
this.buffer.Length = 0;
|
||||||
this.current = 0;
|
this.current = 0;
|
||||||
this.rendered = 0;
|
this.rendered = 0;
|
||||||
//this.cursor = new Cursor();
|
this.cursor = new Cursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -162,9 +216,8 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.Render();
|
this.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The End key was enetered..
|
/// The End key was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnEnd()
|
private void OnEnd()
|
||||||
{
|
{
|
||||||
|
@ -172,15 +225,84 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.cursor.Place(this.rendered);
|
this.cursor.Place(this.rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Tab key was entered
|
||||||
|
/// </summary>
|
||||||
|
private void OnTab()
|
||||||
|
{
|
||||||
|
if (previousKeyPress.Key != ConsoleKey.Tab || previousKeyPress.Key == ConsoleKey.Enter)
|
||||||
|
{
|
||||||
|
tabBuffer = this.buffer;
|
||||||
|
using (PowerShell powershell = PowerShell.Create())
|
||||||
|
{
|
||||||
|
cmdCompleteOpt = CommandCompletion.CompleteInput(this.tabBuffer.ToString(), this.current, options, powershell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tabResult = cmdCompleteOpt.CompletionMatches[tabCompletionPos].CompletionText;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//todo continue nicely
|
||||||
|
}
|
||||||
|
|
||||||
|
tabCompletionPos++;
|
||||||
|
|
||||||
|
//if there is a command for the user before the uncompleted option
|
||||||
|
if (!String.IsNullOrEmpty(tabResult))
|
||||||
|
{
|
||||||
|
//handle file path slashes
|
||||||
|
if (tabResult.Contains(".\\"))
|
||||||
|
{
|
||||||
|
tabResult = tabResult.Replace(".\\", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.buffer.ToString().Contains(" "))
|
||||||
|
{
|
||||||
|
var replaceIndex = cmdCompleteOpt.ReplacementIndex;
|
||||||
|
string replaceBuffer = this.buffer.ToString();
|
||||||
|
|
||||||
|
replaceBuffer = replaceBuffer.Remove(replaceIndex);
|
||||||
|
tabResult = replaceBuffer + tabResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnEscape();
|
||||||
|
|
||||||
|
BufferFromString(tabResult);
|
||||||
|
|
||||||
|
//re-render
|
||||||
|
this.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end of OnTab()
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set buffer to a string rather than inserting char by char
|
||||||
|
/// </summary>
|
||||||
|
private void BufferFromString(string endResult)
|
||||||
|
{
|
||||||
|
//reset prompt and buffer
|
||||||
|
OnEscape();
|
||||||
|
|
||||||
|
//set the buffer to the string
|
||||||
|
for (int i = 0; i < endResult.Length; i++)
|
||||||
|
{
|
||||||
|
this.Insert(endResult[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Home key was eneterd.
|
/// The Home key was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnHome()
|
private void OnHome()
|
||||||
{
|
{
|
||||||
this.current = 0;
|
this.current = 0;
|
||||||
this.cursor.Reset();
|
this.cursor.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Escape key was enetered.
|
/// The Escape key was enetered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -191,6 +313,44 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.Render();
|
this.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The up arrow was pressed to retrieve history
|
||||||
|
/// </summary>
|
||||||
|
private void OnUpArrow() {
|
||||||
|
if (previousKeyPress.Key != ConsoleKey.UpArrow || previousKeyPress.Key == ConsoleKey.Enter)
|
||||||
|
{
|
||||||
|
//first time getting the history
|
||||||
|
using (Pipeline pipeline = this.runspace.CreatePipeline("Get-History"))
|
||||||
|
{
|
||||||
|
historyResult = pipeline.Invoke();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferFromString(historyResult[historyIndex].Members["CommandLine"].Value.ToString());
|
||||||
|
this.Render();
|
||||||
|
|
||||||
|
historyIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BufferFromString(historyResult[historyIndex].Members["CommandLine"].Value.ToString());
|
||||||
|
this.Render();
|
||||||
|
|
||||||
|
if (historyIndex < historyResult.Count)
|
||||||
|
{
|
||||||
|
historyIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (historyIndex == historyResult.Count)
|
||||||
|
{
|
||||||
|
historyIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves to the left of the cursor position.
|
/// Moves to the left of the cursor position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -226,7 +386,6 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.MoveLeft();
|
this.MoveLeft();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if a character is a seperator.
|
/// Determines if a character is a seperator.
|
||||||
|
@ -238,7 +397,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
{
|
{
|
||||||
return !Char.IsLetter(ch);
|
return !Char.IsLetter(ch);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves to what is to the right of the cursor position.
|
/// Moves to what is to the right of the cursor position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -291,7 +450,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
Cursor.Move(1);
|
Cursor.Move(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves the cursor one character to the left.
|
/// Moves the cursor one character to the left.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -304,7 +463,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
Cursor.Move(-1);
|
Cursor.Move(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Enter key was entered.
|
/// The Enter key was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -314,7 +473,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
Console.Out.Write("\n");
|
Console.Out.Write("\n");
|
||||||
return this.buffer.ToString();
|
return this.buffer.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delete key was entered.
|
/// The delete key was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -326,7 +485,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.Render();
|
this.Render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Backspace key was entered.
|
/// The Backspace key was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -339,25 +498,28 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
this.Render();
|
this.Render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays the line.
|
/// Displays the line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void Render()
|
private void Render()
|
||||||
{
|
{
|
||||||
string text = this.buffer.ToString();
|
this.cursor.Reset();
|
||||||
|
|
||||||
|
string text = this.buffer.ToString();
|
||||||
|
|
||||||
// The PowerShell tokenizer is used to decide how to colorize
|
// The PowerShell tokenizer is used to decide how to colorize
|
||||||
// the input. Any errors in the input are returned in 'errors',
|
// the input. Any errors in the input are returned in 'errors',
|
||||||
// but we won't be looking at those here.
|
// but we won't be looking at those here.
|
||||||
Collection<PSParseError> errors = null;
|
Collection<PSParseError> errors = null;
|
||||||
Collection<PSToken> tokens = PSParser.Tokenize(text, out errors);
|
Collection<PSToken> tokens = PSParser.Tokenize(text, out errors);
|
||||||
|
|
||||||
if (tokens.Count > 0)
|
if (tokens.Count > 0)
|
||||||
{
|
{
|
||||||
// We can skip rendering tokens that end before the cursor.
|
// We can skip rendering tokens that end before the cursor.
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < tokens.Count; ++i)
|
|
||||||
|
for (i = 0; i < tokens.Count; ++i)
|
||||||
{
|
{
|
||||||
if (this.current >= tokens[i].Start)
|
if (this.current >= tokens[i].Start)
|
||||||
{
|
{
|
||||||
|
@ -402,7 +564,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
// If tokenization completely failed, just redraw the whole line. This
|
// If tokenization completely failed, just redraw the whole line. This
|
||||||
// happens most frequently when the first token is incomplete, like a string
|
// happens most frequently when the first token is incomplete, like a string
|
||||||
// literal missing a closing quote.
|
// literal missing a closing quote.
|
||||||
//this.cursor.Reset();
|
this.cursor.Reset();
|
||||||
Console.Out.Write(text);
|
Console.Out.Write(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,10 +575,9 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rendered = text.Length;
|
this.rendered = text.Length;
|
||||||
//this.cursor.Place(this.current);
|
this.cursor.Place(this.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A helper class for maintaining the cursor while editing the command line.
|
/// A helper class for maintaining the cursor while editing the command line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -431,7 +592,7 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
/// The left anchor for repositioning the cursor.
|
/// The left anchor for repositioning the cursor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int anchorLeft;
|
private int anchorLeft;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the Cursor class.
|
/// Initializes a new instance of the Cursor class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -478,8 +639,6 @@ namespace Microsoft.PowerShell.Linux.Host
|
||||||
|
|
||||||
Console.CursorTop = cursorTop;
|
Console.CursorTop = cursorTop;
|
||||||
}
|
}
|
||||||
} // End Cursor
|
} // End Cursor
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue