diff --git a/src/Microsoft.PowerShell.PSReadLine/Completion.cs b/src/Microsoft.PowerShell.PSReadLine/Completion.cs index cba45cd01b..3daa6b9483 100644 --- a/src/Microsoft.PowerShell.PSReadLine/Completion.cs +++ b/src/Microsoft.PowerShell.PSReadLine/Completion.cs @@ -298,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 LINUX // TODO: use real inverse - ConsoleColor tempColor = buffer[j].ForegroundColor == UnknownColor - ? ConsoleColor.White : buffer[j].ForegroundColor; - buffer[j].ForegroundColor = buffer[j].BackgroundColor == UnknownColor - ? 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 d839e4ed98..fad206fcc7 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/ConsoleLib.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs index 03be91594d..e985f4d43e 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs @@ -309,60 +309,63 @@ namespace Microsoft.PowerShell.Internal internal string FontFace; } + public struct BufferChar + { + public char UnicodeChar; + public ConsoleColor ForegroundColor; + public ConsoleColor BackgroundColor; +#if !LINUX + public bool IsLeadByte; + public bool IsTrailByte; +#endif + public bool Inverse; + +#if !LINUX + public CHAR_INFO ToCharInfo() + { + int fg = (int) ForegroundColor; + int bg = (int) BackgroundColor; + + if (fg < 0 || fg > 0xf || bg < 0 || bg > 0xf) + throw new InvalidOperationException(); + + if (Inverse) + { + // 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, + }; + + return result; + } + + public static BufferChar FromCharInfo(CHAR_INFO charInfo) + { + 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; - - public CHAR_INFO(char c, ConsoleColor foreground, ConsoleColor background) - { - UnicodeChar = c; - Attributes = (ushort)((((int)background << 4) & 0xf) | ((int)foreground & 0xf)); - } - - [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 & 0x00f0) >> 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)) - { - return false; - } - - var other = (CHAR_INFO)obj; - return this.UnicodeChar == other.UnicodeChar && this.Attributes == other.Attributes; - } - - [ExcludeFromCodeCoverage] - public override int GetHashCode() - { - return UnicodeChar.GetHashCode() + Attributes.GetHashCode(); - } } internal static class ConsoleKeyInfoExtension @@ -611,12 +614,12 @@ 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) { int bufferWidth = Console.BufferWidth; int bufferLineCount = buffer.Length / bufferWidth; @@ -642,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 @@ -665,11 +668,15 @@ namespace Microsoft.PowerShell.Internal Right = (short)Console.BufferWidth }; var destinationOrigin = new COORD {X = 0, Y = 0}; - var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); + var fillChar = new CHAR_INFO + { + UnicodeChar = ' ', + Attributes = (ushort)((int)Console.ForegroundColor | ((int)Console.BackgroundColor << 4)) + }; NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar); } - public CHAR_INFO[] ReadBufferLines(int top, int count) + public BufferChar[] ReadBufferLines(int top, int count) { var result = new CHAR_INFO[BufferWidth * count]; var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); @@ -688,7 +695,7 @@ namespace Microsoft.PowerShell.Internal NativeMethods.ReadConsoleOutput(handle, result, readBufferSize, readBufferCoord, ref readRegion); - return result; + return ToBufferCharBuffer(result); } [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", @@ -882,6 +889,35 @@ namespace Microsoft.PowerShell.Internal NativeMethods.ReleaseDC(_hwnd, _hDC); } } + + private CHAR_INFO[] cachedBuffer; + private CHAR_INFO[] ToCharInfoBuffer(BufferChar[] buffer) + { + if (cachedBuffer == null || cachedBuffer.Length != buffer.Length) + { + cachedBuffer = new CHAR_INFO[buffer.Length]; + } + + for (int i = 0; i < buffer.Length; i++) + { + cachedBuffer[i] = buffer[i].ToCharInfo(); + } + + return cachedBuffer; + } + + private BufferChar[] ToBufferCharBuffer(CHAR_INFO[] buffer) + { + var result = new BufferChar[buffer.Length]; + + for (int i = 0; i < buffer.Length; i++) + { + result[i] = BufferChar.FromCharInfo(buffer[i]); + } + + return result; + } + } #else @@ -996,12 +1032,12 @@ 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) { int bufferWidth = Console.BufferWidth; int bufferLineCount = buffer.Length / bufferWidth; @@ -1023,7 +1059,7 @@ namespace Microsoft.PowerShell.Internal Console.ForegroundColor = buffer[i].ForegroundColor; Console.BackgroundColor = buffer[i].BackgroundColor; - Console.Write((char)buffer[i].UnicodeChar); + Console.Write(buffer[i].UnicodeChar); } Console.BackgroundColor = backgroundColor; @@ -1039,9 +1075,9 @@ namespace Microsoft.PowerShell.Internal } } - public CHAR_INFO[] ReadBufferLines(int top, int count) + public BufferChar[] ReadBufferLines(int top, int count) { - var result = new CHAR_INFO[BufferWidth * count]; + var result = new BufferChar[BufferWidth * count]; for (int i=0; i bufferLineCount * bufferWidth) { @@ -226,17 +228,17 @@ namespace Microsoft.PowerShell MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } +#if !LINUX else if (size > 1) { _consoleBuffer[j].UnicodeChar = charToRender; - _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | - (uint)CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE); + _consoleBuffer[j].IsLeadByte = true; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); _consoleBuffer[j].UnicodeChar = charToRender; - _consoleBuffer[j].Attributes = (ushort)(_consoleBuffer[j].Attributes | - (uint)CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE); + _consoleBuffer[j].IsTrailByte = true; MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); } +#endif else { _consoleBuffer[j].UnicodeChar = charToRender; @@ -286,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; @@ -332,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; @@ -342,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); } @@ -450,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)) { @@ -682,7 +684,7 @@ namespace Microsoft.PowerShell return key.Key == ConsoleKey.Y; } - #region Screen scrolling +#region Screen scrolling #if !LINUX /// @@ -804,6 +806,6 @@ namespace Microsoft.PowerShell } #endif - #endregion Screen scrolling +#endregion Screen scrolling } }