diff --git a/src/Microsoft.PowerShell.Linux.Host/main.cs b/src/Microsoft.PowerShell.Linux.Host/main.cs
index 9f2a0753b..b562d2b45 100644
--- a/src/Microsoft.PowerShell.Linux.Host/main.cs
+++ b/src/Microsoft.PowerShell.Linux.Host/main.cs
@@ -419,7 +419,7 @@ namespace Microsoft.PowerShell.Linux.Host
string prompt = Prompt(this.myHost.Runspace);
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);
}
diff --git a/src/Microsoft.PowerShell.Linux.Host/readline.cs b/src/Microsoft.PowerShell.Linux.Host/readline.cs
index 3662cb7f6..1b0892862 100644
--- a/src/Microsoft.PowerShell.Linux.Host/readline.cs
+++ b/src/Microsoft.PowerShell.Linux.Host/readline.cs
@@ -3,6 +3,7 @@ namespace Microsoft.PowerShell.Linux.Host
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
+ using System.Management.Automation.Runspaces;
using System.Text;
///
@@ -12,17 +13,62 @@ namespace Microsoft.PowerShell.Linux.Host
///
internal class ConsoleReadLine
{
+ ///
+ /// Holds a reference to the runspace (for history access).
+ ///
+ private Runspace runspace;
+
///
/// The buffer used to edit.
///
private StringBuilder buffer = new StringBuilder();
+ ///
+ /// integeger for tracking up and down arrow history
+ ///
+ private int historyIndex;
+
+ ///
+ /// Used for storing tab completion
+ ///
+ private CommandCompletion cmdCompleteOpt;
+
///
/// The position of the cursor within the buffer.
///
private int current;
+ ///
+ /// Detects previously pressed key for TabCompletion
+ ///
///
+ private ConsoleKeyInfo previousKeyPress;
+
+ ///
+ /// Retains TabCompletion position
+ ///
+ private int tabCompletionPos;
+
+ ///
+ /// History Queue
+ ///
+ private Collection historyResult;
+
+ ///
+ /// Hashtable for command completion options
+ ///
+ private System.Collections.Hashtable options = new System.Collections.Hashtable();
+
+ ///
+ /// Retain original buffer for TabCompletion
+ ///
+ private StringBuilder tabBuffer;
+
+ ///
+ /// tabbuffer for storing result of tabcompletion
+ ///
+ private string tabResult;
+
/// The count of characters in buffer rendered.
///
private int rendered;
@@ -30,7 +76,7 @@ namespace Microsoft.PowerShell.Linux.Host
///
/// Store the anchor and handle cursor movement
///
- //private Cursor cursor;
+ private Cursor cursor;
///
/// 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.
///
/// The command line read
- public string Read()
+ public string Read(Runspace runspace)
{
+ this.runspace = runspace;
this.Initialize();
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);
switch (key.Key)
{
- case ConsoleKey.Backspace:
+ case ConsoleKey.Backspace:
this.OnBackspace();
break;
- case ConsoleKey.Delete:
+ case ConsoleKey.Delete:
this.OnDelete();
- break;
- case ConsoleKey.Enter:
+ break;
+ case ConsoleKey.Enter:
return this.OnEnter();
- case ConsoleKey.RightArrow:
+ case ConsoleKey.RightArrow:
this.OnRight(key.Modifiers);
break;
case ConsoleKey.LeftArrow:
@@ -115,14 +157,21 @@ namespace Microsoft.PowerShell.Linux.Host
case ConsoleKey.End:
this.OnEnd();
break;
+ case ConsoleKey.Tab:
+ this.OnTab();
+ break;
case ConsoleKey.UpArrow:
+ this.OnUpArrow();
+ break;
case ConsoleKey.DownArrow:
- case ConsoleKey.LeftWindows:
- case ConsoleKey.RightWindows:
- // ignore these
- continue;
+ // TODO: this.OnDownArrow();
+ continue;
+ // 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
@@ -133,9 +182,12 @@ namespace Microsoft.PowerShell.Linux.Host
goto case ConsoleKey.Backspace; // Ctrl-H
}
- this.Insert(key);
- break;
- }*/
+ this.Insert(key.KeyChar);
+
+ this.Render();
+ break;
+ }
+ previousKeyPress = key;
}
}
@@ -144,10 +196,12 @@ namespace Microsoft.PowerShell.Linux.Host
///
private void Initialize()
{
- this.buffer.Length = 0;
+ this.tabCompletionPos = 0;
+ this.historyIndex = 0;
+ this.buffer.Length = 0;
this.current = 0;
this.rendered = 0;
- //this.cursor = new Cursor();
+ this.cursor = new Cursor();
}
///
@@ -162,9 +216,8 @@ namespace Microsoft.PowerShell.Linux.Host
this.Render();
}
-/*
///
- /// The End key was enetered..
+ /// The End key was entered.
///
private void OnEnd()
{
@@ -172,15 +225,84 @@ namespace Microsoft.PowerShell.Linux.Host
this.cursor.Place(this.rendered);
}
+ ///
+ /// The Tab key was entered
+ ///
+ 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()
+
+ ///
+ /// Set buffer to a string rather than inserting char by char
+ ///
+ 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]);
+ }
+
+ }
+
///
- /// The Home key was eneterd.
+ /// The Home key was entered.
///
private void OnHome()
{
- this.current = 0;
+ this.current = 0;
this.cursor.Reset();
}
-
+
///
/// The Escape key was enetered.
///
@@ -191,6 +313,44 @@ namespace Microsoft.PowerShell.Linux.Host
this.Render();
}
+ ///
+ /// The up arrow was pressed to retrieve history
+ ///
+ 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--;
+ }
+
+ }
+
+ }
+
///
/// Moves to the left of the cursor position.
///
@@ -226,7 +386,6 @@ namespace Microsoft.PowerShell.Linux.Host
this.MoveLeft();
}
}
-*/
///
/// Determines if a character is a seperator.
@@ -238,7 +397,7 @@ namespace Microsoft.PowerShell.Linux.Host
{
return !Char.IsLetter(ch);
}
-/*
+
///
/// Moves to what is to the right of the cursor position.
///
@@ -291,7 +450,7 @@ namespace Microsoft.PowerShell.Linux.Host
Cursor.Move(1);
}
}
-
+
///
/// Moves the cursor one character to the left.
///
@@ -304,7 +463,7 @@ namespace Microsoft.PowerShell.Linux.Host
Cursor.Move(-1);
}
}
-
+
///
/// The Enter key was entered.
///
@@ -314,7 +473,7 @@ namespace Microsoft.PowerShell.Linux.Host
Console.Out.Write("\n");
return this.buffer.ToString();
}
-
+
///
/// The delete key was entered.
///
@@ -326,7 +485,7 @@ namespace Microsoft.PowerShell.Linux.Host
this.Render();
}
}
-
+
///
/// The Backspace key was entered.
///
@@ -339,25 +498,28 @@ namespace Microsoft.PowerShell.Linux.Host
this.Render();
}
}
-*/
+
///
/// Displays the line.
///
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 input. Any errors in the input are returned in 'errors',
// but we won't be looking at those here.
Collection errors = null;
Collection tokens = PSParser.Tokenize(text, out errors);
-
+
if (tokens.Count > 0)
{
// We can skip rendering tokens that end before the cursor.
int i;
- for (i = 0; i < tokens.Count; ++i)
+
+ for (i = 0; i < tokens.Count; ++i)
{
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
// happens most frequently when the first token is incomplete, like a string
// literal missing a closing quote.
- //this.cursor.Reset();
+ this.cursor.Reset();
Console.Out.Write(text);
}
@@ -413,10 +575,9 @@ namespace Microsoft.PowerShell.Linux.Host
}
this.rendered = text.Length;
- //this.cursor.Place(this.current);
+ this.cursor.Place(this.current);
}
-
-/*
+
///
/// A helper class for maintaining the cursor while editing the command line.
///
@@ -431,7 +592,7 @@ namespace Microsoft.PowerShell.Linux.Host
/// The left anchor for repositioning the cursor.
///
private int anchorLeft;
-
+
///
/// Initializes a new instance of the Cursor class.
///
@@ -478,8 +639,6 @@ namespace Microsoft.PowerShell.Linux.Host
Console.CursorTop = cursorTop;
}
- } // End Cursor
- */
+ } // End Cursor
}
}
-