diff --git a/PSReadLine/Render.cs b/PSReadLine/Render.cs index 791c2046..ac487338 100644 --- a/PSReadLine/Render.cs +++ b/PSReadLine/Render.cs @@ -50,6 +50,9 @@ class RenderData lines = new[] { new RenderedLineData{ columns = 0, line = ""}} }; private int _initialX; + + // This represents the initial Y position of the cursor. If this position has scrolled off screen, this value will be negative indicating + // the number of lines off screen. private int _initialY; private ConsoleColor _initialForeground; private ConsoleColor _initialBackground; @@ -427,15 +430,42 @@ int PhysicalLineCount(int columns, bool isFirstLogicalLine, out int lenLastPhysi var logicalLine = 0; var physicalLine = 0; var lenPrevLastLine = 0; + var suppressTopLineIfScrolled = true; for (; logicalLine < renderLines.Length; logicalLine++) { if (logicalLine != 0) _console.Write("\n"); var lineData = renderLines[logicalLine]; - _console.Write(lineData.line); + var physicalLineCount = PhysicalLineCount(lineData.columns, logicalLine == 0, out var lenLastLine); + + // If the initial position was scrolled off the screen, _initialY will be negative. + // Only output for lines still in the screen buffer. + if (_initialY + physicalLine >= 0) + { + // First time redrawing from the top + if (_initialY < 0 && suppressTopLineIfScrolled) + { + suppressTopLineIfScrolled = false; + // Handle case where top of screen buffer is a partial line + if (_console.CursorTop != 0 && _initialY + physicalLine != physicalLineCount) + { + var linesToSkip = _initialY + physicalLine; + PlaceCursor(0, linesToSkip); + } + } + _console.Write(lineData.line); + } + + physicalLine += physicalLineCount; - physicalLine += PhysicalLineCount(lineData.columns, logicalLine == 0, out var lenLastLine); + // On non-Windows, if the last line fills the width exactly, it doesn't automatically scroll, so we + // need to decrement the count to make sure the y location is correct next time + if (_console.CursorTop == _console.BufferHeight - 1 && lineData.columns % _console.BufferWidth == 0) + { + physicalLine--; + previousPhysicalLine--; + } // Find the previous logical line (if any) that would have rendered // the current physical line because we may need to clear it. @@ -671,6 +701,12 @@ private void PlaceCursor(int x, int y) _initialY -= scrollCount; y -= scrollCount; } + + // y can be less than the screen buffer when a more text than the height of the buffer is pasted at once + if (y < 0) + { + y = 0; + } _console.SetCursorPosition(x, y); } diff --git a/test/KillYankTest.cs b/test/KillYankTest.cs index 548c4d42..d64a6472 100644 --- a/test/KillYankTest.cs +++ b/test/KillYankTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Windows; using Microsoft.PowerShell; using Xunit; @@ -313,6 +314,23 @@ public void Paste() "echo foobar", _.CtrlShiftLeftArrow, _.CtrlV)); } + [Fact] + public void PasteLarge() + { + TestSetup(KeyMode.Cmd); + + StringBuilder text = new StringBuilder(); + text.Append("@{"); + for (int i = 0; i < _console.BufferHeight + 10; i++) + { + text.Append(string.Format("prop{0}={0}", i)); + } + text.Append("}"); + + Clipboard.SetText(text.ToString()); + Test(text.ToString(), Keys(_.CtrlV)); + } + [Fact] public void Cut() {