Skip to content

Fix crash and rendering where large content is pasted to console on non-Windows #691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions PSReadLine/Render.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}

Expand Down
18 changes: 18 additions & 0 deletions test/KillYankTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Microsoft.PowerShell;
using Xunit;
Expand Down Expand Up @@ -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()
{
Expand Down