diff --git a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs index 8c2f37004..514eed8ed 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Cmdlets.cs @@ -12,6 +12,7 @@ using System.Reflection; using System.Linq; #if CORECLR using System.Runtime.InteropServices; +using Microsoft.PowerShell.Internal; #endif namespace Microsoft.PowerShell @@ -72,33 +73,24 @@ namespace Microsoft.PowerShell public class PSConsoleReadlineOptions { -#if CORECLR +#if LINUX // TODO: why different schemes for LINUX? public const ConsoleColor DefaultCommentForegroundColor = ConsoleColor.Magenta; - public const ConsoleColor DefaultKeywordForegroundColor = ConsoleColor.Green; - public const ConsoleColor DefaultStringForegroundColor = ConsoleColor.DarkCyan; public const ConsoleColor DefaultOperatorForegroundColor = ConsoleColor.Gray; - public const ConsoleColor DefaultVariableForegroundColor = ConsoleColor.Green; - public const ConsoleColor DefaultCommandForegroundColor = ConsoleColor.Yellow; public const ConsoleColor DefaultParameterForegroundColor = ConsoleColor.Green; - public const ConsoleColor DefaultTypeForegroundColor = ConsoleColor.Gray; - public const ConsoleColor DefaultNumberForegroundColor = ConsoleColor.White; - public const ConsoleColor DefaultMemberForegroundColor = ConsoleColor.Gray; - public const ConsoleColor DefaultEmphasisForegroundColor = ConsoleColor.Cyan; - public const ConsoleColor DefaultErrorForegroundColor = ConsoleColor.Red; #else public const ConsoleColor DefaultCommentForegroundColor = ConsoleColor.DarkGreen; + public const ConsoleColor DefaultOperatorForegroundColor = ConsoleColor.DarkGray; + public const ConsoleColor DefaultParameterForegroundColor = ConsoleColor.DarkGray; +#endif public const ConsoleColor DefaultKeywordForegroundColor = ConsoleColor.Green; public const ConsoleColor DefaultStringForegroundColor = ConsoleColor.DarkCyan; - public const ConsoleColor DefaultOperatorForegroundColor = ConsoleColor.DarkGray; public const ConsoleColor DefaultVariableForegroundColor = ConsoleColor.Green; public const ConsoleColor DefaultCommandForegroundColor = ConsoleColor.Yellow; - public const ConsoleColor DefaultParameterForegroundColor = ConsoleColor.DarkGray; public const ConsoleColor DefaultTypeForegroundColor = ConsoleColor.Gray; public const ConsoleColor DefaultNumberForegroundColor = ConsoleColor.White; public const ConsoleColor DefaultMemberForegroundColor = ConsoleColor.Gray; public const ConsoleColor DefaultEmphasisForegroundColor = ConsoleColor.Cyan; public const ConsoleColor DefaultErrorForegroundColor = ConsoleColor.Red; -#endif public const EditMode DefaultEditMode = EditMode.Windows; @@ -171,37 +163,29 @@ namespace Microsoft.PowerShell HistorySaveStyle = DefaultHistorySaveStyle; string historyFileName = hostName + "_history.txt"; -#if CORECLR - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // MS Windows +#if LINUX + // PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path separately + string historyPath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + + if (!String.IsNullOrEmpty(historyPath)) { - HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), - @"Microsoft\Windows\PowerShell\PSReadline\", - historyFileName); + historyPath = System.IO.Path.Combine(historyPath, "powershell", "PSReadLine", historyFileName); + HistorySavePath = historyPath; } else { - // PSReadline does not have access to Utils.CorePSPlatform. Must set PSReadline path separately - string historyPath = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - - if (!String.IsNullOrEmpty(historyPath)) - { - historyPath = System.IO.Path.Combine(historyPath, "powershell", "PSReadLine", historyFileName); - HistorySavePath = historyPath; - } - else - { - // History is data, so it goes into .local/share/powershell folder - HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), - ".local", - "share", - "powershell", - "PSReadLine", - historyFileName); - } + // History is data, so it goes into .local/share/powershell folder + HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), + ".local", + "share", + "powershell", + "PSReadLine", + historyFileName); } #else - HistorySavePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) - + @"\Microsoft\Windows\PowerShell\PSReadline\" + historyFileName; + HistorySavePath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), + @"Microsoft\Windows\PowerShell\PSReadline\", + historyFileName); #endif CommandValidationHandler = null; CommandsToValidateScriptBlockArguments = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -396,9 +380,7 @@ namespace Microsoft.PowerShell [OutputType(typeof(PSConsoleReadlineOptions))] public class GetPSReadlineOption : PSCmdlet { -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected override void EndProcessing() { WriteObject(PSConsoleReadLine.GetOptions()); @@ -641,9 +623,7 @@ namespace Microsoft.PowerShell } internal ConsoleColor? _backgroundColor; -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected override void EndProcessing() { PSConsoleReadLine.SetOptions(this); @@ -661,9 +641,7 @@ namespace Microsoft.PowerShell [Parameter] public ViMode ViMode { get; set; } -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected IDisposable UseRequestedDispatchTables() { bool inViMode = PSConsoleReadLine.GetOptions().EditMode == EditMode.Vi; @@ -706,9 +684,7 @@ namespace Microsoft.PowerShell private const string FunctionParameter = "Function"; private const string FunctionParameterSet = "Function"; -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected override void EndProcessing() { using (UseRequestedDispatchTables()) @@ -716,15 +692,9 @@ namespace Microsoft.PowerShell if (ParameterSetName.Equals(FunctionParameterSet)) { var function = (string)_dynamicParameters.Value[FunctionParameter].Value; -#if CORECLR MethodInfo mi = typeof (PSConsoleReadLine).GetMethod(function); var keyHandler = (Action) mi.CreateDelegate(typeof (Action)); -#else - var keyHandler = (Action) - Delegate.CreateDelegate(typeof (Action), - typeof (PSConsoleReadLine).GetMethod(function)); -#endif BriefDescription = function; PSConsoleReadLine.SetKeyHandler(Chord, keyHandler, BriefDescription, Description); } @@ -792,9 +762,7 @@ namespace Microsoft.PowerShell } private SwitchParameter? _unbound; -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected override void EndProcessing() { bool bound = true; @@ -821,9 +789,7 @@ namespace Microsoft.PowerShell [Cmdlet("Remove", "PSReadlineKeyHandler", HelpUri = "http://go.microsoft.com/fwlink/?LinkId=528809")] public class RemoveKeyHandlerCommand : ChangePSReadlineKeyHandlerCommandBase { -#if !CORECLR [ExcludeFromCodeCoverage] -#endif protected override void EndProcessing() { using (UseRequestedDispatchTables()) diff --git a/src/Microsoft.PowerShell.PSReadLine/Completion.cs b/src/Microsoft.PowerShell.PSReadLine/Completion.cs index 9dd13a6a6..3daa6b948 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Completion.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Completion.cs @@ -27,9 +27,7 @@ namespace Microsoft.PowerShell private static string DirectorySeparatorString = System.IO.Path.DirectorySeparatorChar.ToString(); // Stub helper method so completion can be mocked -#if !CORECLR [ExcludeFromCodeCoverage] -#endif CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell) { return CalloutUsingDefaultConsoleMode( @@ -300,24 +298,14 @@ namespace Microsoft.PowerShell return replacementText; } - private static void InvertSelectedCompletion(CHAR_INFO[] buffer, int selectedItem, int menuColumnWidth, int menuRows) + private static void InvertSelectedCompletion(BufferChar[] buffer, int selectedItem, int menuColumnWidth, int menuRows) { var selectedX = selectedItem / menuRows; var selectedY = selectedItem - (selectedX * menuRows); var start = selectedY * _singleton._console.BufferWidth + selectedX * menuColumnWidth; for (int i = 0; i < menuColumnWidth; i++) { - int j = i + start; -#if CORECLR - ConsoleColor tempColor = (int)buffer[j].ForegroundColor == -1 - ? ConsoleColor.White : buffer[j].ForegroundColor; - buffer[j].ForegroundColor = (int)buffer[j].BackgroundColor == -1 - ? ConsoleColor.Black : buffer[j].BackgroundColor; - buffer[j].BackgroundColor = tempColor; -#else - buffer[j].ForegroundColor = (ConsoleColor)((int)buffer[j].ForegroundColor ^ 7); - buffer[j].BackgroundColor = (ConsoleColor)((int)buffer[j].BackgroundColor ^ 7); -#endif + buffer[i + start].Inverse = true; } } diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs index d839e4ed9..fad206fcc 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleBufferBuilder.cs @@ -10,18 +10,18 @@ namespace Microsoft.PowerShell { internal class ConsoleBufferBuilder { - private List buffer; + private List buffer; private IConsole _console; public ConsoleBufferBuilder(int capacity, IConsole console) { - buffer = new List(capacity); + buffer = new List(capacity); _console = console; } - CHAR_INFO NewCharInfo(char c) + BufferChar NewCharInfo(char c) { - return new CHAR_INFO + return new BufferChar { UnicodeChar = c, BackgroundColor = _console.BackgroundColor, @@ -45,7 +45,7 @@ namespace Microsoft.PowerShell } } - public CHAR_INFO[] ToArray() + public BufferChar[] ToArray() { return buffer.ToArray(); } diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs index 7ab95732b..32722153d 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -#if CORECLR +#if LINUX using System.Reflection; #endif using System.Runtime.InteropServices; @@ -214,7 +214,7 @@ namespace Microsoft.PowerShell { // default for unprintables and unhandled char keyChar = '\u0000'; -#if CORECLR +#if LINUX Type keyType = typeof (Keys); FieldInfo[] keyFields = keyType.GetFields(); diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs index deadf12e3..e985f4d43 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs @@ -227,9 +227,7 @@ namespace Microsoft.PowerShell.Internal public short Right; public short Bottom; -#if !CORECLR [ExcludeFromCodeCoverage] -#endif public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", Left, Top, Right, Bottom); @@ -241,9 +239,7 @@ namespace Microsoft.PowerShell.Internal public short X; public short Y; -#if !CORECLR [ExcludeFromCodeCoverage] -#endif public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{0},{1}", X, Y); @@ -313,75 +309,65 @@ namespace Microsoft.PowerShell.Internal internal string FontFace; } - public struct CHAR_INFO + public struct BufferChar { -#if CORECLR public char UnicodeChar; public ConsoleColor ForegroundColor; public ConsoleColor BackgroundColor; +#if !LINUX + public bool IsLeadByte; + public bool IsTrailByte; +#endif + public bool Inverse; - public CHAR_INFO(char c, ConsoleColor foreground, ConsoleColor background) +#if !LINUX + public CHAR_INFO ToCharInfo() { - UnicodeChar = c; - ForegroundColor = foreground; - BackgroundColor = background; - } -#else - public ushort UnicodeChar; - public ushort Attributes; + int fg = (int) ForegroundColor; + int bg = (int) BackgroundColor; - public CHAR_INFO(char c, ConsoleColor foreground, ConsoleColor background) - { - UnicodeChar = c; - Attributes = (ushort)(((int)background << 4) | (int)foreground); - } + if (fg < 0 || fg > 0xf || bg < 0 || bg > 0xf) + throw new InvalidOperationException(); - [ExcludeFromCodeCoverage] - public ConsoleColor ForegroundColor - { - get { return (ConsoleColor)(Attributes & 0xf); } - set { Attributes = (ushort)((Attributes & 0xfff0) | ((int)value & 0xf)); } - } - - [ExcludeFromCodeCoverage] - public ConsoleColor BackgroundColor - { - get { return (ConsoleColor)((Attributes & 0xf0) >> 4); } - set { Attributes = (ushort)((Attributes & 0xff0f) | (((int)value & 0xf) << 4)); } - } - - [ExcludeFromCodeCoverage] - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append((char)UnicodeChar); - if (ForegroundColor != Console.ForegroundColor) - sb.AppendFormat(" fg: {0}", ForegroundColor); - if (BackgroundColor != Console.BackgroundColor) - sb.AppendFormat(" bg: {0}", BackgroundColor); - return sb.ToString(); - } - - [ExcludeFromCodeCoverage] - public override bool Equals(object obj) - { - if (!(obj is CHAR_INFO)) + if (Inverse) { - return false; + // TODO: check $host.UI.SupportsVirtualTerminal and maybe set Inverse instead (it does look weird) + fg = fg ^ 7; + bg = bg ^ 7; } + ushort attrs = (ushort)(fg | (bg << 4)); + if (IsLeadByte) + attrs |= (ushort)CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE; + if (IsTrailByte) + attrs |= (ushort)CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE; + CHAR_INFO result = new CHAR_INFO + { + UnicodeChar = UnicodeChar, + Attributes = attrs, + }; - var other = (CHAR_INFO)obj; - return this.UnicodeChar == other.UnicodeChar && this.Attributes == other.Attributes; + return result; } - [ExcludeFromCodeCoverage] - public override int GetHashCode() + public static BufferChar FromCharInfo(CHAR_INFO charInfo) { - return UnicodeChar.GetHashCode() + Attributes.GetHashCode(); + BufferChar result = new BufferChar + { + UnicodeChar = (char) charInfo.UnicodeChar, + ForegroundColor = (ConsoleColor)(charInfo.Attributes & 0xf), + BackgroundColor = (ConsoleColor)((charInfo.Attributes & 0x00f0) >> 4), + }; + return result; } #endif } + public struct CHAR_INFO + { + public ushort UnicodeChar; + public ushort Attributes; + } + internal static class ConsoleKeyInfoExtension { public static string ToGestureString(this ConsoleKeyInfo key) @@ -400,9 +386,10 @@ namespace Microsoft.PowerShell.Internal sb.Append("Alt"); } -#if CORECLR +#if LINUX if (sb.Length > 0) sb.Append("+"); + // TODO: find better way to map these characters to something more friendly if ((key.Key >= ConsoleKey.D0 && key.Key <= ConsoleKey.D9) || (key.Key >= ConsoleKey.Oem1 && key.Key <= ConsoleKey.Oem8)) { @@ -442,6 +429,7 @@ namespace Microsoft.PowerShell.Internal } } +#if !LINUX internal class ConhostConsole : IConsole { [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] @@ -498,18 +486,42 @@ namespace Microsoft.PowerShell.Internal return new SafeFileHandle(handle, true); }); - public uint GetConsoleInputMode() + public object GetConsoleInputMode() { + uint mode; var handle = _inputHandle.Value.DangerousGetHandle(); - uint result; - NativeMethods.GetConsoleMode(handle, out result); - return result; + NativeMethods.GetConsoleMode(handle, out mode); + return mode; } - public void SetConsoleInputMode(uint mode) + public void SetConsoleInputMode(object modeObj) { - var handle = _inputHandle.Value.DangerousGetHandle(); - NativeMethods.SetConsoleMode(handle, mode); + if (modeObj is uint) + { + // Clear a couple flags so we can actually receive certain keys: + // ENABLE_PROCESSED_INPUT - enables Ctrl+C + // ENABLE_LINE_INPUT - enables Ctrl+S + // Also clear a couple flags so we don't mask the input that we ignore: + // ENABLE_MOUSE_INPUT - mouse events + // ENABLE_WINDOW_INPUT - window resize events + var mode = (uint)modeObj & + ~(NativeMethods.ENABLE_PROCESSED_INPUT | + NativeMethods.ENABLE_LINE_INPUT | + NativeMethods.ENABLE_WINDOW_INPUT | + NativeMethods.ENABLE_MOUSE_INPUT); + + var handle = _inputHandle.Value.DangerousGetHandle(); + NativeMethods.SetConsoleMode(handle, mode); + } + } + + public void RestoreConsoleInputMode(object modeObj) + { + if (modeObj is uint) + { + var handle = _inputHandle.Value.DangerousGetHandle(); + NativeMethods.SetConsoleMode(handle, (uint)modeObj); + } } public ConsoleKeyInfo ReadKey() @@ -602,16 +614,13 @@ namespace Microsoft.PowerShell.Internal Console.WriteLine(value); } - public void WriteBufferLines(CHAR_INFO[] buffer, ref int top) + public void WriteBufferLines(BufferChar[] buffer, ref int top) { WriteBufferLines(buffer, ref top, true); } - public void WriteBufferLines(CHAR_INFO[] buffer, ref int top, bool ensureBottomLineVisible) + public void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible) { -#if !CORECLR - var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); -#endif int bufferWidth = Console.BufferWidth; int bufferLineCount = buffer.Length / bufferWidth; if ((top + bufferLineCount) > Console.BufferHeight) @@ -620,23 +629,8 @@ namespace Microsoft.PowerShell.Internal ScrollBuffer(scrollCount); top -= scrollCount; } -#if CORECLR - ConsoleColor foregroundColor = Console.ForegroundColor; - ConsoleColor backgroundColor = Console.BackgroundColor; - Console.SetCursorPosition(0, (top>=0) ? top : 0); - - for (int i = 0; i < buffer.Length; ++i) - { - Console.ForegroundColor = buffer[i].ForegroundColor; - Console.BackgroundColor = buffer[i].BackgroundColor; - - Console.Write((char)buffer[i].UnicodeChar); - } - - Console.BackgroundColor = backgroundColor; - Console.ForegroundColor = foregroundColor; -#else + var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); var bufferSize = new COORD { X = (short) bufferWidth, @@ -651,7 +645,7 @@ namespace Microsoft.PowerShell.Internal Bottom = (short) bottom, Right = (short) (bufferWidth - 1) }; - NativeMethods.WriteConsoleOutput(handle, buffer, + NativeMethods.WriteConsoleOutput(handle, ToCharInfoBuffer(buffer), bufferSize, bufferCoord, ref writeRegion); // Now make sure the bottom line is visible @@ -660,18 +654,10 @@ namespace Microsoft.PowerShell.Internal { Console.CursorTop = bottom; } -#endif } public void ScrollBuffer(int lines) { -#if CORECLR - for (int i=0; i Console.BufferHeight) + { + var scrollCount = (top + bufferLineCount) - Console.BufferHeight; + ScrollBuffer(scrollCount); + top -= scrollCount; + } + + ConsoleColor foregroundColor = Console.ForegroundColor; + ConsoleColor backgroundColor = Console.BackgroundColor; + + Console.SetCursorPosition(0, (top>=0) ? top : 0); + + for (int i = 0; i < buffer.Length; ++i) + { + // TODO: use escape sequences for better perf + Console.ForegroundColor = buffer[i].ForegroundColor; + Console.BackgroundColor = buffer[i].BackgroundColor; + + Console.Write(buffer[i].UnicodeChar); + } + + Console.BackgroundColor = backgroundColor; + Console.ForegroundColor = foregroundColor; + } + + public void ScrollBuffer(int lines) + { + for (int i=0; i { private readonly HistoryQueue _queue; @@ -116,9 +117,7 @@ namespace Microsoft.PowerShell return result; } -#if !CORECLR [ExcludeFromCodeCoverage] -#endif public T this[int index] { get diff --git a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs index 207f5cae1..aae97e503 100644 --- a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs +++ b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.cs @@ -142,7 +142,7 @@ namespace Microsoft.PowerShell { Keys.CtrlSpace, MakeKeyHandler(MenuComplete, "MenuComplete") }, { Keys.Tab, MakeKeyHandler(TabCompleteNext, "TabCompleteNext") }, { Keys.ShiftTab, MakeKeyHandler(TabCompletePrevious, "TabCompletePrevious") }, -#if !CORECLR +#if !LINUX { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") }, @@ -181,7 +181,7 @@ namespace Microsoft.PowerShell { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward, "CharacterSearchBackward") }, { Keys.F8, MakeKeyHandler(HistorySearchBackward, "HistorySearchBackward") }, { Keys.ShiftF8, MakeKeyHandler(HistorySearchForward, "HistorySearchForward") }, -#if !CORECLR +#if !LINUX { Keys.PageUp, MakeKeyHandler(ScrollDisplayUp, "ScrollDisplayUp") }, { Keys.PageDown, MakeKeyHandler(ScrollDisplayDown, "ScrollDisplayDown") }, { Keys.CtrlPageUp, MakeKeyHandler(ScrollDisplayUpLine, "ScrollDisplayUpLine") }, @@ -265,7 +265,7 @@ namespace Microsoft.PowerShell { Keys.AltPeriod, MakeKeyHandler(YankLastArg, "YankLastArg") }, { Keys.AltUnderbar, MakeKeyHandler(YankLastArg, "YankLastArg") }, { Keys.AltCtrlY, MakeKeyHandler(YankNthArg, "YankNthArg") }, -#if !CORECLR +#if !LINUX { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") }, diff --git a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs index d31cba72d..29cfa61b3 100644 --- a/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs +++ b/src/Microsoft.PowerShell.PSReadLine/KeyBindings.vi.cs @@ -70,7 +70,7 @@ namespace Microsoft.PowerShell { Keys.Tab, MakeKeyHandler(ViTabCompleteNext, "ViTabCompleteNext") }, { Keys.ShiftTab, MakeKeyHandler(ViTabCompletePrevious, "ViTabCompletePrevious") }, { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") }, -#if !CORECLR +#if !LINUX { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") }, @@ -109,7 +109,7 @@ namespace Microsoft.PowerShell { Keys.Tab, MakeKeyHandler(TabCompleteNext, "TabCompleteNext") }, { Keys.ShiftTab, MakeKeyHandler(TabCompletePrevious, "TabCompletePrevious") }, { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") }, -#if !CORECLR +#if !LINUX { Keys.VolumeDown, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeUp, MakeKeyHandler(Ignore, "Ignore") }, { Keys.VolumeMute, MakeKeyHandler(Ignore, "Ignore") }, diff --git a/src/Microsoft.PowerShell.PSReadLine/Keys.cs b/src/Microsoft.PowerShell.PSReadLine/Keys.cs index 07166088d..94c566716 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Keys.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Keys.cs @@ -290,10 +290,10 @@ namespace Microsoft.PowerShell public static ConsoleKeyInfo ShiftF8 = new ConsoleKeyInfo((char)0, ConsoleKey.F8, true, false, false); // Keys to ignore -#if !CORECLR - public static ConsoleKeyInfo VolumeUp = new ConsoleKeyInfo((char)0, ConsoleKey.VolumeUp, false, false, false); - public static ConsoleKeyInfo VolumeDown = new ConsoleKeyInfo((char)0, ConsoleKey.VolumeDown, false, false, false); - public static ConsoleKeyInfo VolumeMute = new ConsoleKeyInfo((char)0, ConsoleKey.VolumeMute, false, false, false); +#if !LINUX + public static ConsoleKeyInfo VolumeUp = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeUp*/(ConsoleKey)175, false, false, false); + public static ConsoleKeyInfo VolumeDown = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeDown*/(ConsoleKey)174, false, false, false); + public static ConsoleKeyInfo VolumeMute = new ConsoleKeyInfo((char)0, /*ConsoleKey.VolumeMute*/(ConsoleKey)173, false, false, false); #endif } } diff --git a/src/Microsoft.PowerShell.PSReadLine/KillYank.cs b/src/Microsoft.PowerShell.PSReadLine/KillYank.cs index 651364c78..ba6a87b22 100644 --- a/src/Microsoft.PowerShell.PSReadLine/KillYank.cs +++ b/src/Microsoft.PowerShell.PSReadLine/KillYank.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Management.Automation.Language; -#if !CORECLR +#if !CORECLR // TODO: clipboard using System.Windows.Forms; #endif @@ -478,7 +478,7 @@ namespace Microsoft.PowerShell [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] public static void Paste(ConsoleKeyInfo? key = null, object arg = null) { -#if !CORECLR +#if !CORECLR // TODO: clipboard string textToPaste = null; ExecuteOnSTAThread(() => { if (Clipboard.ContainsText()) @@ -510,7 +510,7 @@ namespace Microsoft.PowerShell [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] public static void Copy(ConsoleKeyInfo? key = null, object arg = null) { -#if !CORECLR +#if !CORECLR // TODO: clipboard string textToSet; if (_singleton._visualSelectionCommandCount > 0) { @@ -551,15 +551,15 @@ namespace Microsoft.PowerShell [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] public static void Cut(ConsoleKeyInfo? key = null, object arg = null) { -#if !CORECLR if (_singleton._visualSelectionCommandCount > 0) { int start, length; _singleton.GetRegion(out start, out length); +#if !CORECLR // TODO: clipboard ExecuteOnSTAThread(() => Clipboard.SetText(_singleton._buffer.ToString(start, length))); +#endif Delete(start, length); } -#endif } } } diff --git a/src/Microsoft.PowerShell.PSReadLine/Movement.cs b/src/Microsoft.PowerShell.PSReadLine/Movement.cs index 610ee0462..52c1983f0 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Movement.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Movement.cs @@ -434,7 +434,7 @@ namespace Microsoft.PowerShell public static void ClearScreen(ConsoleKeyInfo? key = null, object arg = null) { var console = _singleton._console; -#if CORECLR +#if LINUX // TODO: this is not correct, it should only scroll console.Clear(); _singleton._initialY = 0; _singleton.Render(); diff --git a/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs b/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs index 8f38d5bf1..03f1d6707 100644 --- a/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs +++ b/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs @@ -28,8 +28,9 @@ namespace Microsoft.PowerShell [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] public interface IConsole { - uint GetConsoleInputMode(); - void SetConsoleInputMode(uint mode); + object GetConsoleInputMode(); + void SetConsoleInputMode(object mode); + void RestoreConsoleInputMode(object mode); ConsoleKeyInfo ReadKey(); bool KeyAvailable { get; } int CursorLeft { get; set; } @@ -46,15 +47,15 @@ namespace Microsoft.PowerShell void SetCursorPosition(int left, int top); void WriteLine(string s); void Write(string s); - void WriteBufferLines(CHAR_INFO[] buffer, ref int top); - void WriteBufferLines(CHAR_INFO[] buffer, ref int top, bool ensureBottomLineVisible); + void WriteBufferLines(BufferChar[] buffer, ref int top); + void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible); void ScrollBuffer(int lines); - CHAR_INFO[] ReadBufferLines(int top, int count); + BufferChar[] ReadBufferLines(int top, int count); void StartRender(); int LengthInBufferCells(char c); void EndRender(); -#if CORECLR +#if LINUX void Clear(); #endif } diff --git a/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs b/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs index aabf5a46a..c2848f054 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs @@ -34,7 +34,7 @@ namespace Microsoft.PowerShell private IConsole _console; private EngineIntrinsics _engineIntrinsics; -#if !CORECLR +#if !LINUX private static GCHandle _breakHandlerGcHandle; #endif private Thread _readKeyThread; @@ -43,11 +43,7 @@ namespace Microsoft.PowerShell private ManualResetEvent _closingWaitHandle; private WaitHandle[] _threadProcWaitHandles; private WaitHandle[] _requestKeyWaitHandles; -#if CORECLR - private bool _prePSReadlineControlCMode; -#else - private uint _prePSReadlineConsoleMode; -#endif + private object _savedConsoleInputMode; private readonly StringBuilder _buffer; private readonly StringBuilder _statusBuffer; @@ -266,32 +262,13 @@ namespace Microsoft.PowerShell throw new NotSupportedException(); } -#if CORECLR - _singleton._prePSReadlineControlCMode = Console.TreatControlCAsInput; -#else - _singleton._prePSReadlineConsoleMode = console.GetConsoleInputMode(); -#endif + _singleton._savedConsoleInputMode = _singleton._console.GetConsoleInputMode(); bool firstTime = true; while (true) { try { -#if CORECLR - Console.TreatControlCAsInput = true; -#else - // Clear a couple flags so we can actually receive certain keys: - // ENABLE_PROCESSED_INPUT - enables Ctrl+C - // ENABLE_LINE_INPUT - enables Ctrl+S - // Also clear a couple flags so we don't mask the input that we ignore: - // ENABLE_MOUSE_INPUT - mouse events - // ENABLE_WINDOW_INPUT - window resize events - var mode = _singleton._prePSReadlineConsoleMode & - ~(NativeMethods.ENABLE_PROCESSED_INPUT | - NativeMethods.ENABLE_LINE_INPUT | - NativeMethods.ENABLE_WINDOW_INPUT | - NativeMethods.ENABLE_MOUSE_INPUT); - console.SetConsoleInputMode(mode); -#endif + _singleton._console.SetConsoleInputMode(_singleton._savedConsoleInputMode); if (firstTime) { @@ -362,11 +339,7 @@ namespace Microsoft.PowerShell } finally { -#if CORECLR - Console.TreatControlCAsInput = _singleton._prePSReadlineControlCMode; -#else - console.SetConsoleInputMode(_singleton._prePSReadlineConsoleMode); -#endif + _singleton._console.RestoreConsoleInputMode(_singleton._savedConsoleInputMode); } } } @@ -454,29 +427,16 @@ namespace Microsoft.PowerShell T CalloutUsingDefaultConsoleMode(Func func) { -#if CORECLR - bool psReadlineControlCMode = Console.TreatControlCAsInput; + var currentMode = _console.GetConsoleInputMode(); try { - Console.TreatControlCAsInput = _prePSReadlineControlCMode; + _console.RestoreConsoleInputMode(_savedConsoleInputMode); return func(); } finally { - Console.TreatControlCAsInput = psReadlineControlCMode; + _console.RestoreConsoleInputMode(currentMode); } -#else - uint psReadlineConsoleMode = _console.GetConsoleInputMode(); - try - { - _console.SetConsoleInputMode(_prePSReadlineConsoleMode); - return func(); - } - finally - { - _console.SetConsoleInputMode(psReadlineConsoleMode); - } -#endif } void CalloutUsingDefaultConsoleMode(Action action) @@ -519,7 +479,11 @@ namespace Microsoft.PowerShell private PSConsoleReadLine() { _mockableMethods = this; +#if LINUX + _console = new TTYConsole(); +#else _console = new ConhostConsole(); +#endif SetDefaultWindowsBindings(); @@ -580,7 +544,12 @@ namespace Microsoft.PowerShell _initialY = _console.CursorTop - Options.ExtraPromptLineCount; _initialBackgroundColor = _console.BackgroundColor; _initialForegroundColor = _console.ForegroundColor; - _space = new CHAR_INFO(' ', _initialForegroundColor, _initialBackgroundColor); + _space = new BufferChar + { + UnicodeChar = ' ', + BackgroundColor = _initialBackgroundColor, + ForegroundColor = _initialForegroundColor + }; _bufferWidth = _console.BufferWidth; _killCommandCount = 0; _yankCommandCount = 0; @@ -590,7 +559,7 @@ namespace Microsoft.PowerShell _statusIsErrorMessage = false; _consoleBuffer = ReadBufferLines(_initialY, 1 + Options.ExtraPromptLineCount); -#if CORECLR +#if LINUX // TODO: not necessary if ReadBufferLines worked, or if rendering worked on spans instead of complete lines string newPrompt = GetPrompt(); for (int i=0; i(Options.MaximumKillRingCount); -#if !CORECLR +#if !LINUX _breakHandlerGcHandle = GCHandle.Alloc(new BreakHandler(_singleton.BreakHandler)); NativeMethods.SetConsoleCtrlHandler((BreakHandler)_breakHandlerGcHandle.Target, true); #endif @@ -691,11 +660,11 @@ namespace Microsoft.PowerShell _singleton._requestKeyWaitHandles = new WaitHandle[] {_singleton._keyReadWaitHandle, _singleton._closingWaitHandle}; _singleton._threadProcWaitHandles = new WaitHandle[] {_singleton._readKeyWaitHandle, _singleton._closingWaitHandle}; +#if !CORECLR // This is for a "being hosted in an alternate appdomain scenario" (the // DomainUnload event is not raised for the default appdomain). It allows us // to exit cleanly when the appdomain is unloaded but the process is not going // away. -#if !CORECLR if (!AppDomain.CurrentDomain.IsDefaultAppDomain()) { AppDomain.CurrentDomain.DomainUnload += (x, y) => @@ -733,13 +702,14 @@ namespace Microsoft.PowerShell private static void ExecuteOnSTAThread(Action action) { -#if !CORECLR +#if CORECLR + action(); +#else if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { action(); return; } -#endif Exception exception = null; var thread = new Thread(() => @@ -754,9 +724,7 @@ namespace Microsoft.PowerShell } }); -#if !CORECLR thread.SetApartmentState(ApartmentState.STA); -#endif thread.Start(); thread.Join(); @@ -764,6 +732,7 @@ namespace Microsoft.PowerShell { throw exception; } +#endif } #region Miscellaneous bindable functions @@ -788,13 +757,11 @@ namespace Microsoft.PowerShell return; } - #region VI special case if (_singleton._options.EditMode == EditMode.Vi && key.Value.KeyChar == '0') { BeginningOfLine(); return; } - #endregion VI special case bool sawDigit = false; _singleton._statusLinePrompt = "digit-argument: "; @@ -899,7 +866,7 @@ namespace Microsoft.PowerShell _singleton._initialX = _singleton._console.CursorLeft; _singleton._consoleBuffer = ReadBufferLines(_singleton._initialY, 1 + _singleton.Options.ExtraPromptLineCount); -#if CORECLR +#if LINUX // TODO: ReadBufferLines only needed for extra line, but doesn't work on Linux for (int i=0; i bufferLineCount * bufferWidth) { @@ -228,21 +228,17 @@ namespace Microsoft.PowerShell MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } +#if !LINUX else if (size > 1) { _consoleBuffer[j].UnicodeChar = charToRender; -#if !CORECLR - _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | - (uint)CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE); -#endif + _consoleBuffer[j].IsLeadByte = true; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); _consoleBuffer[j].UnicodeChar = charToRender; -#if !CORECLR - _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | - (uint)CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE); -#endif + _consoleBuffer[j].IsTrailByte = true; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } +#endif else { _consoleBuffer[j].UnicodeChar = charToRender; @@ -253,9 +249,7 @@ namespace Microsoft.PowerShell } finally { -#if !CORECLR _console.EndRender(); -#endif } for (; j < (_consoleBuffer.Length - (statusLineCount * _bufferWidth)); j++) @@ -294,7 +288,7 @@ namespace Microsoft.PowerShell while (promptChar >= 0) { - var c = (char)_consoleBuffer[promptChar].UnicodeChar; + var c = _consoleBuffer[promptChar].UnicodeChar; if (char.IsWhiteSpace(c)) { promptChar -= 1; @@ -319,7 +313,7 @@ namespace Microsoft.PowerShell if ((_initialY + bufferLineCount) > (_console.WindowTop + _console.WindowHeight)) { -#if !CORECLR +#if !LINUX // TODO: verify this isn't necessary for LINUX _console.WindowTop = _initialY + bufferLineCount - _console.WindowHeight; #endif } @@ -340,7 +334,7 @@ namespace Microsoft.PowerShell private static void WriteBlankLines(int count, int top) { var console = _singleton._console; - var blanks = new CHAR_INFO[count * console.BufferWidth]; + var blanks = new BufferChar[count * console.BufferWidth]; for (int i = 0; i < blanks.Length; i++) { blanks[i].BackgroundColor = console.BackgroundColor; @@ -350,7 +344,7 @@ namespace Microsoft.PowerShell console.WriteBufferLines(blanks, ref top); } - private static CHAR_INFO[] ReadBufferLines(int top, int count) + private static BufferChar[] ReadBufferLines(int top, int count) { return _singleton._console.ReadBufferLines(top, count); } @@ -458,7 +452,7 @@ namespace Microsoft.PowerShell return i >= start && i < end; } - private void MaybeEmphasize(ref CHAR_INFO charInfo, int i, ConsoleColor foregroundColor, ConsoleColor backgroundColor) + private void MaybeEmphasize(ref BufferChar charInfo, int i, ConsoleColor foregroundColor, ConsoleColor backgroundColor) { if (i >= _emphasisStart && i < (_emphasisStart + _emphasisLength)) { @@ -472,9 +466,9 @@ namespace Microsoft.PowerShell // to invert only the lower 3 bits to change the color is somewhat // but looks best with the 2 default color schemes - starting PowerShell // from it's shortcut or from a cmd shortcut. -#if CORECLR - ConsoleColor tempColor = ((int)foregroundColor == -1) ? ConsoleColor.White : foregroundColor; - foregroundColor = ((int)backgroundColor == -1) ? ConsoleColor.Black : backgroundColor; +#if LINUX // TODO: set Inverse attribute and let render choose what to do. + ConsoleColor tempColor = (foregroundColor == UnknownColor) ? ConsoleColor.White : foregroundColor; + foregroundColor = (backgroundColor == UnknownColor) ? ConsoleColor.Black : backgroundColor; backgroundColor = tempColor; #else foregroundColor = (ConsoleColor)((int)foregroundColor ^ 7); @@ -654,24 +648,24 @@ namespace Microsoft.PowerShell return (_statusLinePrompt.Length + _statusBuffer.Length) / _console.BufferWidth + 1; } -#if !CORECLR [ExcludeFromCodeCoverage] -#endif void IPSConsoleReadLineMockableMethods.Ding() { -#if !CORECLR switch (Options.BellStyle) { case BellStyle.None: break; case BellStyle.Audible: +#if LINUX + Console.Beep(); +#else Console.Beep(Options.DingTone, Options.DingDuration); +#endif break; case BellStyle.Visual: // TODO: flash prompt? command line? break; } -#endif } /// @@ -694,9 +688,9 @@ namespace Microsoft.PowerShell return key.Key == ConsoleKey.Y; } - #region Screen scrolling +#region Screen scrolling -#if !CORECLR +#if !LINUX /// /// Scroll the display up one screen. /// @@ -816,6 +810,6 @@ namespace Microsoft.PowerShell } #endif - #endregion Screen scrolling +#endregion Screen scrolling } } diff --git a/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs b/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs index 2659fec97..85bfcc42b 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs @@ -2,12 +2,11 @@ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ -#if !CORECLR +#if !LINUX using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; -using System.Windows.Forms; using Microsoft.PowerShell.Internal; namespace Microsoft.PowerShell @@ -19,16 +18,8 @@ namespace Microsoft.PowerShell var buffer = ReadBufferLines(start, count); for (int i = 0; i < buffer.Length; i++) { -#if CORECLR - ConsoleColor tempColor = (int)buffer[i].ForegroundColor == -1 - ? ConsoleColor.White : buffer[i].ForegroundColor; - buffer[i].ForegroundColor = (int)buffer[i].BackgroundColor == -1 - ? ConsoleColor.Black : buffer[i].BackgroundColor; - buffer[i].BackgroundColor = tempColor; -#else buffer[i].ForegroundColor = (ConsoleColor)((int)buffer[i].ForegroundColor ^ 7); buffer[i].BackgroundColor = (ConsoleColor)((int)buffer[i].BackgroundColor ^ 7); -#endif } _singleton._console.WriteBufferLines(buffer, ref start, false); } @@ -237,7 +228,6 @@ namespace Microsoft.PowerShell var buffer = ReadBufferLines(top, count); var bufferWidth = _singleton._console.BufferWidth; - var dataObject = new DataObject(); var textBuffer = new StringBuilder(buffer.Length + count); var rtfBuffer = new StringBuilder(); @@ -307,9 +297,12 @@ namespace Microsoft.PowerShell } rtfBuffer.Append("}}"); - dataObject.SetData(DataFormats.Text, textBuffer.ToString()); - dataObject.SetData(DataFormats.Rtf, rtfBuffer.ToString()); - ExecuteOnSTAThread(() => Clipboard.SetDataObject(dataObject, copy: true)); +#if !CORECLR // TODO: break dependency on Window.Forms w/ p/invokes to clipboard directly, for now, just silently skip the copy. + var dataObject = new System.Windows.Forms.DataObject(); + dataObject.SetData(System.Windows.Forms.DataFormats.Text, textBuffer.ToString()); + dataObject.SetData(System.Windows.Forms.DataFormats.Rtf, rtfBuffer.ToString()); + ExecuteOnSTAThread(() => System.Windows.Forms.Clipboard.SetDataObject(dataObject, copy: true)); +#endif } } } diff --git a/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs b/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs index a8e8573e7..59a14053e 100644 --- a/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs +++ b/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs @@ -96,7 +96,6 @@ namespace Microsoft.PowerShell private static string GetPreferredEditor() { string[] names = {"VISUAL", "EDITOR"}; -#if CORECLR foreach (string name in names) { string editor = Environment.GetEnvironmentVariable(name); @@ -105,24 +104,7 @@ namespace Microsoft.PowerShell return editor; } } -#else - EnvironmentVariableTarget[] targets = { - EnvironmentVariableTarget.Machine, - EnvironmentVariableTarget.Process, - EnvironmentVariableTarget.User - }; - foreach (string name in names) - { - foreach (EnvironmentVariableTarget target in targets) - { - string editor = Environment.GetEnvironmentVariable(name, target); - if (!string.IsNullOrWhiteSpace(editor)) - { - return editor; - } - } - } -#endif + return null; } } diff --git a/src/Microsoft.PowerShell.PSReadLine/project.json b/src/Microsoft.PowerShell.PSReadLine/project.json index 9973dd8c5..f8f9ba1f1 100644 --- a/src/Microsoft.PowerShell.PSReadLine/project.json +++ b/src/Microsoft.PowerShell.PSReadLine/project.json @@ -9,6 +9,14 @@ "warningsAsErrors": true }, + "configurations": { + "Linux": { + "buildOptions": { + "define": [ "LINUX" ] + } + } + }, + "dependencies": { "System.Management.Automation": "1.0.0-*" },