From 99c6bd650d6b4877aeeff097daef30d207c2a3ba Mon Sep 17 00:00:00 2001 From: Jason Shirk Date: Sun, 19 May 2013 09:04:40 -0700 Subject: [PATCH] Fix some more end of buffer problems. --- PSReadLine/ConsoleLib.cs | 7 +++ PSReadLine/ReadLine.cs | 105 ++++++++++++++++++++++++--------------- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/PSReadLine/ConsoleLib.cs b/PSReadLine/ConsoleLib.cs index a61a205..12612f6 100644 --- a/PSReadLine/ConsoleLib.cs +++ b/PSReadLine/ConsoleLib.cs @@ -11,6 +11,13 @@ namespace PSConsoleUtilities [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr GetStdHandle(uint handleId); + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput, + ref SMALL_RECT lpScrollRectangle, + ref SMALL_RECT lpClipRectangle, + COORD dwDestinationOrigin, + ref CHAR_INFO lpFill); + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool WriteConsole(IntPtr hConsoleOutput, string lpBuffer, uint nNumberOfCharsToWrite, out uint lpNumberOfCharsWritten, IntPtr lpReserved); diff --git a/PSReadLine/ReadLine.cs b/PSReadLine/ReadLine.cs index 7406bc1..db06609 100644 --- a/PSReadLine/ReadLine.cs +++ b/PSReadLine/ReadLine.cs @@ -441,29 +441,11 @@ namespace PSConsoleUtilities _initialForegroundColor = Console.ForegroundColor; _space = new CHAR_INFO(' ', _initialForegroundColor, _initialBackgroundColor); _bufferWidth = Console.BufferWidth; - // Most command lines will be about 1 line - we'll reallocate this buffer - // later if necessary. - _consoleBuffer = new CHAR_INFO[_bufferWidth * (1 + _extraPromptLineCount)]; _killCommandCount = 0; _yankCommandCount = 0; _tabCommandCount = 0; - var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); - - // Read the prompt into the buffer just once - so we can avoid re-reading - // it later. - var widthToRead = _extraPromptLineCount > 0 ? _bufferWidth : _initialX; - var readBufferSize = new COORD {X = (short)widthToRead, Y = (short)(1 + _extraPromptLineCount)}; - var readBufferCoord = new COORD {X = 0, Y = 0}; - var readRegion = new SMALL_RECT - { - Top = (short)_initialY, - Left = 0, - Bottom = (short)(_initialY + _extraPromptLineCount), - Right = (short)(widthToRead - 1) - }; - NativeMethods.ReadConsoleOutput(handle, _consoleBuffer, - readBufferSize, readBufferCoord, ref readRegion); + _consoleBuffer = ReadBufferLines(_initialY, 1 + _extraPromptLineCount); } private static void EmacsMeta() @@ -935,9 +917,7 @@ namespace PSConsoleUtilities // Don't overwrite any of the line - so move to first line after the end of our buffer. var coords = _singleton.ConvertOffsetToCoordinates(_singleton._buffer.Length); - Console.CursorLeft = coords.X; - Console.CursorTop = coords.Y; - MoveCursorToNextLine(); + PlaceCursor(0, coords.Y + 1); var sb = new StringBuilder(); var minColWidth = completions.CompletionMatches.Max(c => c.ListItemText.Length); @@ -1279,7 +1259,7 @@ namespace PSConsoleUtilities { _consoleBuffer[i] = _space; } - RenderBufferToConsole(); + WriteBufferLines(_consoleBuffer, ref _initialY); } _consoleBuffer = newBuffer; } @@ -1381,7 +1361,7 @@ namespace PSConsoleUtilities { ConsoleColor prevColor = _consoleBuffer[promptChar].ForegroundColor; _consoleBuffer[promptChar].ForegroundColor = ConsoleColor.Red; - RenderBufferToConsole(); + WriteBufferLines(_consoleBuffer, ref _initialY); rendered = true; _consoleBuffer[promptChar].ForegroundColor = prevColor; break; @@ -1392,18 +1372,24 @@ namespace PSConsoleUtilities if (!rendered) { - RenderBufferToConsole(); + WriteBufferLines(_consoleBuffer, ref _initialY); } PlaceCursor(); } - private void RenderBufferToConsole() + private static void WriteBufferLines(CHAR_INFO[] buffer, ref int top) { var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); int bufferWidth = Console.BufferWidth; - int bufferLineCount = _consoleBuffer.Length / bufferWidth; + int bufferLineCount = buffer.Length / bufferWidth; + if ((top + bufferLineCount) > Console.BufferHeight) + { + var scrollCount = (top + bufferLineCount) - Console.BufferHeight; + ScrollBuffer(scrollCount); + top -= scrollCount; + } var bufferSize = new COORD { X = (short) bufferWidth, @@ -1412,15 +1398,36 @@ namespace PSConsoleUtilities var bufferCoord = new COORD {X = 0, Y = 0}; var writeRegion = new SMALL_RECT { - Top = (short) _initialY, + Top = (short) top, Left = 0, - Bottom = (short) (_initialY + bufferLineCount - 1), + Bottom = (short) (top + bufferLineCount - 1), Right = (short) bufferWidth }; - NativeMethods.WriteConsoleOutput(handle, _consoleBuffer, + NativeMethods.WriteConsoleOutput(handle, buffer, bufferSize, bufferCoord, ref writeRegion); } + private static CHAR_INFO[] ReadBufferLines(int top, int count) + { + var result = new CHAR_INFO[Console.BufferWidth * count]; + var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); + + var readBufferSize = new COORD { + X = (short)Console.BufferWidth, + Y = (short)count}; + var readBufferCoord = new COORD {X = 0, Y = 0}; + var readRegion = new SMALL_RECT + { + Top = (short)top, + Left = 0, + Bottom = (short)(top + count), + Right = (short)(Console.BufferWidth - 1) + }; + NativeMethods.ReadConsoleOutput(handle, result, + readBufferSize, readBufferCoord, ref readRegion); + return result; + } + private TokenClassification GetTokenClassification(Token token) { switch (token.Kind) @@ -1456,10 +1463,36 @@ namespace PSConsoleUtilities return TokenClassification.None; } + private static void ScrollBuffer(int lines) + { + var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); + + var scrollRectangle = new SMALL_RECT + { + Top = (short) lines, + Left = 0, + Bottom = (short) (lines + Console.BufferHeight - 1), + Right = (short)Console.BufferWidth + }; + var destinationOrigin = new COORD {X = 0, Y = 0}; + var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); + NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, ref scrollRectangle, destinationOrigin, ref fillChar); + } + + private static void PlaceCursor(int x, int y) + { + if (y >= Console.BufferHeight) + { + ScrollBuffer(y - Console.BufferHeight + 1); + y = Console.BufferHeight - 1; + } + Console.SetCursorPosition(x, y); + } + private void PlaceCursor() { var coordinates = ConvertOffsetToCoordinates(_current); - Console.SetCursorPosition(coordinates.X, coordinates.Y); + PlaceCursor(coordinates.X, coordinates.Y); } private COORD ConvertOffsetToCoordinates(int offset) @@ -1538,15 +1571,7 @@ namespace PSConsoleUtilities }; NativeMethods.WriteConsoleOutput(handle, buffer, bufferSize, bufferCoord, ref writeRegion); - Console.CursorLeft = 0; - MoveCursorToNextLine(); - } - - static void MoveCursorToNextLine() - { - // Can't set to Console.CursorTop + 1 because that might be out of bounds, so write a newline to force the console to scroll. - uint unused; - NativeMethods.WriteConsole(NativeMethods.GetStdHandle((uint)StandardHandleId.Output), "\n", 1, out unused, IntPtr.Zero); + PlaceCursor(0, Console.CursorTop + 1); } #endregion Rendering