From ad044c68a88b5204446d71dedee7d7d8a43fecf3 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Thu, 4 Nov 2021 10:39:08 -0700 Subject: [PATCH] Fix 2 menu completion issues that happen when we need to scroll screen buffer --- PSReadLine/Completion.cs | 24 +++++++-------------- PSReadLine/DisplayBlockBase.cs | 38 ++++++++++++++++++++++++++++++++++ PSReadLine/DynamicHelp.cs | 18 ++++------------ 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/PSReadLine/Completion.cs b/PSReadLine/Completion.cs index c7d9902af..f2721d7aa 100644 --- a/PSReadLine/Completion.cs +++ b/PSReadLine/Completion.cs @@ -445,15 +445,8 @@ public void DrawMenu(Menu previousMenu, bool menuSelect) SaveCursor(); } - // Move cursor to the start of the first line after our input. - var bufferEndPoint = Singleton.ConvertOffsetToPoint(Singleton._buffer.Length); - console.SetCursorPosition(bufferEndPoint.X, bufferEndPoint.Y); - // Top must be initialized before calling AdjustForPossibleScroll, otherwise - // on the last line of the buffer, the scroll operation causes Top to point - // past the buffer, which in turn causes the menu to be printed twice. - this.Top = bufferEndPoint.Y + 1; - AdjustForPossibleScroll(1); - MoveCursorDown(1); + PreviousTop = Top; + MoveCursorToStartDrawingPosition(console); var bufferWidth = console.BufferWidth; var columnWidth = this.ColumnWidth; @@ -514,7 +507,7 @@ public void DrawMenu(Menu previousMenu, bool menuSelect) } // if the menu has moved, we need to clear the lines under it - if (bufferEndPoint.Y < PreviousTop) + if (Top < PreviousTop) { // In either of the following two cases, we will need to move the cursor to the next line: // - if extra rows from previous menu were cleared, then we know the current line was erased @@ -528,11 +521,9 @@ public void DrawMenu(Menu previousMenu, bool menuSelect) MoveCursorDown(1); } - Singleton.WriteBlankLines(PreviousTop - bufferEndPoint.Y); + Singleton.WriteBlankLines(PreviousTop - Top); } - PreviousTop = bufferEndPoint.Y; - if (menuSelect) { RestoreCursor(); @@ -797,7 +788,7 @@ private void PossibleCompletionsImpl(CommandCompletion completions, bool menuSel } else { - menu.DrawMenu(null, menuSelect:false); + menu.DrawMenu(null, menuSelect: false); InvokePrompt(key: null, arg: _console.CursorTop); } } @@ -879,7 +870,6 @@ private void MenuCompleteImpl(Menu menu, CommandCompletion completions) if (topAdjustment != 0) { - menu.Top += topAdjustment; menu.DrawMenu(null, menuSelect: true); } if (topAdjustment > 0) @@ -963,7 +953,7 @@ private void MenuCompleteImpl(Menu menu, CommandCompletion completions) { var newMenu = menuStack.Pop(); - newMenu.DrawMenu(menu, menuSelect:true); + newMenu.DrawMenu(menu, menuSelect: true); previousSelection = -1; menu = newMenu; @@ -1025,7 +1015,7 @@ private void MenuCompleteImpl(Menu menu, CommandCompletion completions) { var newMenu = CreateCompletionMenu(newMatches); - newMenu.DrawMenu(menu, menuSelect:true); + newMenu.DrawMenu(menu, menuSelect: true); previousSelection = -1; // Remember the current menu for when we see Backspace. diff --git a/PSReadLine/DisplayBlockBase.cs b/PSReadLine/DisplayBlockBase.cs index 7f3d5a0a0..c4da53247 100644 --- a/PSReadLine/DisplayBlockBase.cs +++ b/PSReadLine/DisplayBlockBase.cs @@ -23,6 +23,16 @@ protected void MoveCursorDown(int cnt) } } + protected void AdjustForActualScroll(int scrollCnt) + { + if (scrollCnt > 0) + { + Top -= scrollCnt; + _singleton._initialY -= scrollCnt; + _savedCursorTop -= scrollCnt; + } + } + protected void AdjustForPossibleScroll(int cnt) { IConsole console = Singleton._console; @@ -35,6 +45,34 @@ protected void AdjustForPossibleScroll(int cnt) } } + protected void MoveCursorToStartDrawingPosition(IConsole console) + { + // Calculate the coord to place the cursor at the end of current input. + Point bufferEndPoint = Singleton.ConvertOffsetToPoint(Singleton._buffer.Length); + // Top must be initialized before any possible adjustion by 'AdjustForPossibleScroll' or 'AdjustForActualScroll', + // otherwise its value would be corrupted and cause rendering issue. + Top = bufferEndPoint.Y + 1; + + if (bufferEndPoint.Y == console.BufferHeight) + { + // The input happens to end at the very last cell of the current buffer, so 'bufferEndPoint' is pointing to + // the first cell at one line below the current buffer, and thus we need to scroll up the buffer. + console.SetCursorPosition(console.BufferWidth - 1, console.BufferHeight - 1); + // We scroll the buffer by 2 lines here, so the cursor is placed at the start of the first line after 'bufferEndPoint'. + MoveCursorDown(2); + bufferEndPoint.Y -= 2; + AdjustForActualScroll(2); + } + else + { + // Move the cursor to the end of our input. + console.SetCursorPosition(bufferEndPoint.X, bufferEndPoint.Y); + // Move cursor to the start of the first line after our input (after 'bufferEndPoint'). + AdjustForPossibleScroll(1); + MoveCursorDown(1); + } + } + private int _savedCursorLeft; private int _savedCursorTop; diff --git a/PSReadLine/DynamicHelp.cs b/PSReadLine/DynamicHelp.cs index bffa4ac94..814a6d0fe 100644 --- a/PSReadLine/DynamicHelp.cs +++ b/PSReadLine/DynamicHelp.cs @@ -241,21 +241,11 @@ public void DrawMultilineBlock() multilineItems = 0; - this.SaveCursor(); - - // Move cursor to the start of the first line after our input. - var bufferEndPoint = Singleton.ConvertOffsetToPoint(Singleton._buffer.Length); - console.SetCursorPosition(bufferEndPoint.X, bufferEndPoint.Y); - // Top must be initialized before calling AdjustForPossibleScroll, otherwise - // on the last line of the buffer, the scroll operation causes Top to point - // past the buffer, which in turn causes the menu to be printed twice. - this.Top = bufferEndPoint.Y + 1; - AdjustForPossibleScroll(1); - MoveCursorDown(1); + SaveCursor(); + MoveCursorToStartDrawingPosition(console); var bufferWidth = console.BufferWidth; - - var items = this.ItemsToDisplay; + var items = ItemsToDisplay; for (var index = 0; index < items.Count; index++) { @@ -282,7 +272,7 @@ public void DrawMultilineBlock() } } - this.RestoreCursor(); + RestoreCursor(); } public void Clear()