From e713af87bbe7d86f792b4e2673b27ef514d9c162 Mon Sep 17 00:00:00 2001 From: George Fleming Date: Wed, 16 Mar 2016 16:17:07 -0700 Subject: [PATCH] Adding support for Ctrl-K, Ctrl-Y, and Ctrl-L --- src/Microsoft.PowerShell.Linux.Host/main.cs | 52 +++++--- .../readline.cs | 115 ++++++++++++++---- 2 files changed, 121 insertions(+), 46 deletions(-) diff --git a/src/Microsoft.PowerShell.Linux.Host/main.cs b/src/Microsoft.PowerShell.Linux.Host/main.cs index 2965c93869..19abe349b2 100644 --- a/src/Microsoft.PowerShell.Linux.Host/main.cs +++ b/src/Microsoft.PowerShell.Linux.Host/main.cs @@ -535,6 +535,8 @@ OPTIONS 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". while (!this.ShouldExit && this.myHost.Runspace != null) @@ -555,19 +557,24 @@ OPTIONS this.myHost.UI.Write(ConsoleColor.White, Console.BackgroundColor, prompt); - bool abort; - string cmd = consoleReadLine.Read(this.myHost.Runspace, false, out abort); - if (string.IsNullOrEmpty(cmd)) + ConsoleReadLine.ReadResult result = consoleReadLine.Read(this.myHost.Runspace, false, initialCommand); + + switch(result.state) { - if (abort) - { + case ConsoleReadLine.ReadResult.State.Abort: incompleteLine = false; partialLine = string.Empty; - } - continue; + 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; } - - this.Execute(cmd); } } @@ -583,23 +590,30 @@ OPTIONS WriteDebuggerStopMessages(args); + string initialCommand = String.Empty; + // loop to process Debugger commands. while (resumeAction == null) { string prompt = incompleteLine ? ">> " : "[DBG] PS >> "; Console.Write(prompt); - bool abort; - string command = consoleReadLine.Read(this.myHost.Runspace, true, out abort); - - if (string.IsNullOrEmpty(command)) + ConsoleReadLine.ReadResult result = consoleReadLine.Read(this.myHost.Runspace, true, initialCommand); + + switch(result.state) { - if (abort) - { + case ConsoleReadLine.ReadResult.State.Abort: incompleteLine = false; partialLine = string.Empty; - } - continue; + 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. @@ -622,7 +636,7 @@ OPTIONS PSCommand psCommand = new PSCommand(); - string fullCommand = incompleteLine ? (partialLine + command) : command; + string fullCommand = incompleteLine ? (partialLine + result.command) : result.command; psCommand.AddScript(fullCommand).AddCommand("Out-String").AddParameter("Stream", true); incompleteLine = false; @@ -634,7 +648,7 @@ OPTIONS catch (IncompleteParseException) { incompleteLine = true; - partialLine = $"{partialLine}{command}{System.Environment.NewLine}"; + partialLine = $"{partialLine}{result.command}{System.Environment.NewLine}"; } if (!incompleteLine) diff --git a/src/Microsoft.PowerShell.Linux.Host/readline.cs b/src/Microsoft.PowerShell.Linux.Host/readline.cs index 768f15f710..0a2a77cc52 100644 --- a/src/Microsoft.PowerShell.Linux.Host/readline.cs +++ b/src/Microsoft.PowerShell.Linux.Host/readline.cs @@ -90,6 +90,29 @@ namespace Microsoft.PowerShell.Linux.Host /// private string preTabBuffer; + /// + /// kill buffer used by Ctrl-K and Ctrl-Y + /// + private string killBuffer = String.Empty; + + /// + /// What Read() outputs + /// + public class ReadResult + { + public ReadResult(State s, string cmd) + { + this.command = cmd; + this.state = s; + Console.TreatControlCAsInput = true; + } + + public enum State {Complete, Abort, Redraw} + + public string command; + public State state; + } + /// /// Initializes a new instance of the ConsoleReadLine class. /// @@ -124,13 +147,20 @@ namespace Microsoft.PowerShell.Linux.Host /// Read a line of text, colorizing while typing. /// /// The command line read - public string Read(Runspace runspace, bool nested, out bool abort) + public ReadResult Read(Runspace runspace, bool nested, string initialValue) { this.powershell.Runspace = runspace; this.Initialize(); bool commandComplete = false; - abort = false; -// Console.WriteLine("Default value of TreatCtrlCAsInput: {0}", Console.TreatControlCAsInput); + bool abort = false; + bool redraw = false; + + if (!String.IsNullOrEmpty(initialValue)) + { + this.BufferFromString(initialValue); + this.Render(); + } + Console.TreatControlCAsInput = false; while (true) @@ -140,12 +170,7 @@ namespace Microsoft.PowerShell.Linux.Host // Basic Emacs-style readline implementation if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) { - commandComplete = ProcessControlKey(key, nested, ref abort); - if (abort) - { - Console.TreatControlCAsInput = true; - return String.Empty; - } + commandComplete = ProcessControlKey(key, nested, ref abort, ref redraw); } else if (key.Modifiers.HasFlag(ConsoleModifiers.Alt)) { @@ -159,10 +184,19 @@ namespace Microsoft.PowerShell.Linux.Host previousKeyPress = key; + if (abort) + { + return new ReadResult(ReadResult.State.Abort, String.Empty); + } + + if (redraw) + { + return new ReadResult(ReadResult.State.Redraw, this.buffer.ToString()); + } + if (commandComplete) { - Console.TreatControlCAsInput = true; - return this.OnEnter(); + return new ReadResult(ReadResult.State.Complete, this.OnEnter()); } } } @@ -170,7 +204,7 @@ namespace Microsoft.PowerShell.Linux.Host /// /// Process Control-Key combo /// - private bool ProcessControlKey(ConsoleKeyInfo key, bool nested, ref bool abort) + private bool ProcessControlKey(ConsoleKeyInfo key, bool nested, ref bool abort, ref bool redraw) { switch (key.Key) { @@ -180,12 +214,12 @@ namespace Microsoft.PowerShell.Linux.Host case ConsoleKey.E: this.OnEnd(); break; - // TODO: save buffer, yank with Ctrl-Y - /* - case ConsoleKey.K: - this.OnEscape(); - break; - */ + case ConsoleKey.K: + this.OnKill(); + break; + case ConsoleKey.Y: + this.OnYank(); + break; case ConsoleKey.D: this.OnDelete(); break; @@ -206,15 +240,12 @@ namespace Microsoft.PowerShell.Linux.Host 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(); - */ + previousKeyPress = key; + return true; + case ConsoleKey.L: + Console.Clear(); + redraw = true; + return false; case ConsoleKey.C: this.Abort(); abort = true; @@ -688,6 +719,36 @@ namespace Microsoft.PowerShell.Linux.Host } } + /// + /// Ctrl-K was entered. + /// + 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; + } + } + + /// + /// Ctrl-Y was entered. + /// + private void OnYank() + { + if (!String.IsNullOrEmpty(killBuffer)) + { + this.buffer.Insert(this.current, killBuffer); + this.current += killBuffer.Length; + this.Render(); + } + } + /// /// The Backspace key was entered. ///