diff --git a/src/Microsoft.PowerShell.Linux.Host/main.cs b/src/Microsoft.PowerShell.Linux.Host/main.cs index 2411e79d5c..2965c93869 100644 --- a/src/Microsoft.PowerShell.Linux.Host/main.cs +++ b/src/Microsoft.PowerShell.Linux.Host/main.cs @@ -360,14 +360,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 +388,7 @@ OPTIONS catch (IncompleteParseException) { incompleteLine = true; - cmd = cmd.Trim(); - partialLine += cmd; - - partialLine += System.Environment.NewLine; - + partialLine = $"{partialLine}{cmd}{System.Environment.NewLine}"; } finally @@ -526,7 +516,6 @@ OPTIONS this.currentPowerShell.Stop(); } } - e.Cancel = true; } catch (Exception exception) @@ -565,7 +554,19 @@ OPTIONS } this.myHost.UI.Write(ConsoleColor.White, Console.BackgroundColor, prompt); - string cmd = consoleReadLine.Read(this.myHost.Runspace, false); + + bool abort; + string cmd = consoleReadLine.Read(this.myHost.Runspace, false, out abort); + if (string.IsNullOrEmpty(cmd)) + { + if (abort) + { + incompleteLine = false; + partialLine = string.Empty; + } + continue; + } + this.Execute(cmd); } } @@ -585,9 +586,21 @@ OPTIONS // 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 >> "; + Console.Write(prompt); + + bool abort; + string command = consoleReadLine.Read(this.myHost.Runspace, true, out abort); + + if (string.IsNullOrEmpty(command)) + { + if (abort) + { + incompleteLine = false; + partialLine = string.Empty; + } + continue; + } // Stream output from command processing to console. var output = new PSDataCollection(); @@ -606,10 +619,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 + command) : 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}{command}{System.Environment.NewLine}"; + } + + if (!incompleteLine) + { + partialLine = string.Empty; + } + + if (!incompleteLine && results.ResumeAction != null) { resumeAction = results.ResumeAction; } diff --git a/src/Microsoft.PowerShell.Linux.Host/readline.cs b/src/Microsoft.PowerShell.Linux.Host/readline.cs index 7a2df6a234..7c7d8c7b14 100644 --- a/src/Microsoft.PowerShell.Linux.Host/readline.cs +++ b/src/Microsoft.PowerShell.Linux.Host/readline.cs @@ -124,10 +124,14 @@ namespace Microsoft.PowerShell.Linux.Host /// Read a line of text, colorizing while typing. /// /// The command line read - public string Read(Runspace runspace, bool nested) + public string Read(Runspace runspace, bool nested, out bool abort) { this.powershell.Runspace = runspace; this.Initialize(); + bool finishedTyping = false; + abort = false; +// Console.WriteLine("Default value of TreatCtrlCAsInput: {0}", Console.TreatControlCAsInput); +// Console.TreatControlCAsInput = false; while (true) { @@ -136,129 +140,174 @@ namespace Microsoft.PowerShell.Linux.Host // Basic Emacs-style readline implementation if (key.Modifiers.HasFlag(ConsoleModifiers.Control)) { - switch (key.Key) + finishedTyping = ProcessControlKey(key, nested, ref abort); + if (abort) { - 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(); - */ + Console.WriteLine(); +// Console.TreatControlCAsInput = true; + return String.Empty; } } else if (key.Modifiers.HasFlag(ConsoleModifiers.Alt)) { - 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; - } + finishedTyping = ProcessAltKey(key); } // Unmodified keys else { - 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; - } + finishedTyping = ProcessNormalKey(key, nested); } + previousKeyPress = key; + + if (finishedTyping) + { +// Console.TreatControlCAsInput = true; + return this.OnEnter(); + } } } + /// + /// Process Control-Key combo + /// + private bool ProcessControlKey(ConsoleKeyInfo key, bool nested, ref bool abort) + { + switch (key.Key) + { + 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(); + */ + case ConsoleKey.C: + Console.WriteLine("received Ctrl-C"); + this.Abort(); + abort = true; + break; + } + return false; + } + + /// + /// Process Alt-Key combo + /// + private bool ProcessAltKey(ConsoleKeyInfo key) + { + 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; + } + return false; + } + + /// + /// Process normal key + /// + 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(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 false; + } + /// /// Initializes the buffer. /// @@ -733,6 +782,17 @@ namespace Microsoft.PowerShell.Linux.Host this.cursor.Place(this.current); } + /// + /// Abort current command + /// + public void Abort() + { + ConsoleColor saveFGColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.Out.WriteLine("^C"); + Console.ForegroundColor = saveFGColor; + } + /// /// A helper class for maintaining the cursor while editing the command line. ///