Fix three issues with the menu completion (#1946)

1. Fix menu completion to work with the predictive list view (fix #1935 -- regression introduced in 2.2.0-beta1)
2. Fix menu completion to clear lines properly when the menu top changes on re-drawing (regression introduced in 2.2.0-beta1)
3. Fix how `RightArrow`, `LeftArrow`, `UpArrow`, and `DownArrow` navigate in the menu view (enhancement)
This commit is contained in:
Dongbo Wang
2020-11-13 11:20:51 -08:00
committed by GitHub
parent 726e7c6f29
commit 9f786b3397
4 changed files with 819 additions and 33 deletions
+112 -31
View File
@@ -491,11 +491,18 @@ namespace Microsoft.PowerShell
}
}
bool extraPreRowsCleared = false;
if (previousMenu != null)
{
if (Rows < previousMenu.Rows + previousMenu.ToolTipLines)
{
// Rest of the current line was erased, but the cursor was not moved to the next line.
// If the last menu row took the whole buffer width, then the cursor could be pushed to the
// beginning of the next line in the legacy console host (NOT in modern terminals such as
// Windows Terminal, VSCode Terminal, or virtual-terminal-enabled console host). In such a
// case, there is no need to move the cursor to the next line.
//
// If that is not the case, namely 'CursorLeft != 0', then the rest of the last menu row was
// erased, but the cursor was not moved to the next line, so we will move the cursor.
if (console.CursorLeft != 0)
{
// There are lines from the previous rendering that need to be cleared,
@@ -504,13 +511,25 @@ namespace Microsoft.PowerShell
}
Singleton.WriteBlankLines(previousMenu.Rows + previousMenu.ToolTipLines - Rows);
extraPreRowsCleared = true;
}
}
// if the menu has moved, we need to clear the lines under it
if (bufferEndPoint.Y < PreviousTop)
{
console.BlankRestOfLine();
// 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
// but the cursor was not moved to the next line.
// - if 'CursorLeft != 0', then the rest of the last menu row was erased, but the cursor
// was not moved to the next line.
if (extraPreRowsCleared || console.CursorLeft != 0)
{
// There are lines from the previous rendering that need to be cleared,
// so we are sure there is no need to scroll.
MoveCursorDown(1);
}
Singleton.WriteBlankLines(PreviousTop - bufferEndPoint.Y);
}
@@ -614,10 +633,62 @@ namespace Microsoft.PowerShell
RestoreCursor();
}
public void MoveRight() => CurrentSelection = Math.Min(CurrentSelection + Rows, MenuItems.Count - 1);
public void MoveLeft() => CurrentSelection = Math.Max(CurrentSelection - Rows, 0);
public void MoveUp() => CurrentSelection = Math.Max(CurrentSelection - 1, 0);
public void MoveDown() => CurrentSelection = Math.Min(CurrentSelection + 1, MenuItems.Count - 1);
public void MoveRight()
{
int nextInSameRow = CurrentSelection + Rows;
if (nextInSameRow <= MenuItems.Count - 1)
{
CurrentSelection = nextInSameRow;
return;
}
// Index of the column where 'CurrentSelection' is at, assuming columns start from left at index 0.
int columnIndex = CurrentSelection / Rows;
int leftmostItemInSameRow = CurrentSelection - columnIndex * Rows;
// Index of the row where 'leftMostItemAtSameRow' is at, assuming rows start from top at index 0.
int rowIndex = leftmostItemInSameRow % Rows;
// If 'rowIndex == Rows - 1', then 'CurrentSelection' is at the rightmost position in the last row,
// so moving-to-right again should move to the item at index 0.
CurrentSelection = rowIndex == Rows - 1 ? 0 : leftmostItemInSameRow + 1;
}
public void MoveLeft()
{
int previousInSameRow = CurrentSelection - Rows;
if (previousInSameRow >= 0)
{
CurrentSelection = previousInSameRow;
return;
}
// Index of the row where 'CurrentSelection' is at, assuming rows start from top at index 0.
int rowIndex = CurrentSelection % Rows;
int leftmostItemInPreviousRow = rowIndex == 0 ? Rows - 1 : rowIndex - 1;
int lastItemIndex = MenuItems.Count - 1;
// Index of the column where the last item is at, assuming columns start from left at index 0.
int lastItemColumnIndex = lastItemIndex / Rows;
// Get the rightmost item in the previous row.
CurrentSelection = leftmostItemInPreviousRow + lastItemColumnIndex * Rows;
if (CurrentSelection > lastItemIndex)
{
CurrentSelection = leftmostItemInPreviousRow + (lastItemColumnIndex - 1) * Rows;
}
}
public void MoveUp()
{
CurrentSelection = CurrentSelection > 0 ? CurrentSelection - 1 : MenuItems.Count - 1;
}
public void MoveDown()
{
CurrentSelection = CurrentSelection < (MenuItems.Count - 1) ? CurrentSelection + 1 : 0;
}
public void MovePageDown() => CurrentSelection = Math.Min(CurrentSelection + Rows - (CurrentSelection % Rows) - 1,
MenuItems.Count - 1);
public void MovePageUp() => CurrentSelection = Math.Max(CurrentSelection - (CurrentSelection % Rows), 0);
@@ -808,9 +879,6 @@ namespace Microsoft.PowerShell
var userInitialCompletionLength = userCompletionText.Length;
completions.CurrentMatchIndex = 0;
menu.DrawMenu(null, menuSelect:true);
bool processingKeys = true;
int previousSelection = -1;
@@ -832,38 +900,51 @@ namespace Microsoft.PowerShell
ExchangePointAndMark();
// After replacement, the menu might be misplaced from the command line
// getting shorter or longer.
var endOfCommandLine = ConvertOffsetToPoint(_buffer.Length);
var topAdjustment = (endOfCommandLine.Y + 1) - menu.Top;
if (topAdjustment != 0)
if (previousSelection == -1)
{
menu.Top += topAdjustment;
menu.DrawMenu(null, menuSelect:true);
completions.CurrentMatchIndex = 0;
menu.DrawMenu(null, menuSelect: true);
}
if (topAdjustment > 0)
else
{
// Render did not clear the rest of the command line which flowed
// into the menu, so we must do that here.
menu.SaveCursor();
_console.SetCursorPosition(endOfCommandLine.X, endOfCommandLine.Y);
_console.Write(Spaces(_console.BufferWidth - endOfCommandLine.X));
menu.RestoreCursor();
}
// After replacement, the menu might be misplaced from the command line
// getting shorter or longer.
var endOfCommandLine = ConvertOffsetToPoint(_buffer.Length);
var topAdjustment = (endOfCommandLine.Y + 1) - menu.Top;
if (topAdjustment != 0)
{
menu.Top += topAdjustment;
menu.DrawMenu(null, menuSelect: true);
}
if (topAdjustment > 0)
{
// Render did not clear the rest of the command line which flowed
// into the menu, so we must do that here.
menu.SaveCursor();
_console.SetCursorPosition(endOfCommandLine.X, endOfCommandLine.Y);
_console.Write(Spaces(_console.BufferWidth - endOfCommandLine.X));
menu.RestoreCursor();
}
if (previousSelection != -1)
{
if (menu.ToolTipLines > 0)
{
// Erase previous tooltip, taking into account if the menu moved up/down.
WriteBlankLines(menu.Top + menu.Rows, -topAdjustment + menu.ToolTipLines);
}
menu.UpdateMenuSelection(previousSelection, /*select*/ false,
/*showToolTips*/false, Options._emphasisColor);
menu.UpdateMenuSelection(
previousSelection,
select: false,
showTooltips: false,
Options._emphasisColor);
}
menu.UpdateMenuSelection(menu.CurrentSelection, /*select*/ true,
Options.ShowToolTips, Options._emphasisColor);
menu.UpdateMenuSelection(
menu.CurrentSelection,
select: true,
Options.ShowToolTips,
Options._emphasisColor);
previousSelection = menu.CurrentSelection;
}
+2
View File
@@ -601,6 +601,8 @@ static class PlatformWindows
var y = CursorTop;
for (int i = 0; i < BufferWidth - x; i++) Console.Write(' ');
// Last step may result in scrolling.
if (CursorTop != y+1) y -= 1;
SetCursorPosition(x, y);
+700 -2
View File
@@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
@@ -147,7 +148,7 @@ namespace Test
}
[SkippableFact]
public void MenuCompletions()
public void MenuCompletions_FilterByTyping()
{
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
@@ -173,6 +174,678 @@ namespace Test
));
}
[SkippableFact]
public void MenuCompletions_Navigation1()
{
// Test 'RightArrow' and 'LeftArrow' with the following menu:
// Get-Many0 Get-Many3 Get-Many6 Get-Many9 Get-Many12
// Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13
// Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-Many0", Keys(
"Get-Many", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Many0 ",
TokenClassification.None,
"Get-Many3 Get-Many6 Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "3", NextLine,
TokenClassification.None, "Get-Many0 ",
TokenClassification.Selection, "Get-Many3 ",
TokenClassification.None,
"Get-Many6 Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "6", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 ",
TokenClassification.Selection, "Get-Many6 ",
TokenClassification.None, "Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "9", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 Get-Many6 ",
TokenClassification.Selection, "Get-Many9 ",
TokenClassification.None, "Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "12", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 Get-Many6 Get-Many9 ",
TokenClassification.Selection, "Get-Many12 ", NextLine,
TokenClassification.None,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "1", NextLine,
TokenClassification.None,
"Get-Many0 Get-Many3 Get-Many6 Get-Many9 Get-Many12", NextLine,
TokenClassification.Selection, "Get-Many1 ",
TokenClassification.None,
"Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "12", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 Get-Many6 Get-Many9 ",
TokenClassification.Selection, "Get-Many12 ", NextLine,
TokenClassification.None,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "9", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 Get-Many6 ",
TokenClassification.Selection, "Get-Many9 ",
TokenClassification.None, "Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "6", NextLine,
TokenClassification.None, "Get-Many0 Get-Many3 ",
TokenClassification.Selection, "Get-Many6 ",
TokenClassification.None, "Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.LeftArrow, _.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Many0 ",
TokenClassification.None,
"Get-Many3 Get-Many6 Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "14", NextLine,
TokenClassification.None,
"Get-Many0 Get-Many3 Get-Many6 Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 ",
TokenClassification.Selection, "Get-Many14 ", NextLine)),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Many",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Many0 ",
TokenClassification.None,
"Get-Many3 Get-Many6 Get-Many9 Get-Many12", NextLine,
"Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13", NextLine,
"Get-Many2 Get-Many5 Get-Many8 Get-Many11 Get-Many14")),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_Navigation2()
{
// Test 'RightArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
// Get-Less2 Get-Less5 Get-Less8 Get-Less11
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-Less0", Keys(
"Get-Less", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.RightArrow, _.RightArrow, _.RightArrow, _.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "12", NextLine,
TokenClassification.None, "Get-Less0 Get-Less3 Get-Less6 Get-Less9 ",
TokenClassification.Selection, "Get-Less12 ", NextLine,
TokenClassification.None,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "1", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
TokenClassification.Selection, "Get-Less1 ",
TokenClassification.None,
"Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.RightArrow, _.RightArrow, _.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "10", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 ",
TokenClassification.Selection, "Get-Less10 ", NextLine,
TokenClassification.None,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "2", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
TokenClassification.Selection, "Get-Less2 ",
TokenClassification.None,
"Get-Less5 Get-Less8 Get-Less11")),
_.RightArrow, _.RightArrow, _.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "11", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 ",
TokenClassification.Selection, "Get-Less11 ")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_Navigation3()
{
// Test 'LeftArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
// Get-Less2 Get-Less5 Get-Less8 Get-Less11
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-Less6", Keys(
"Get-Less", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "11", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 ",
TokenClassification.Selection, "Get-Less11 ")),
_.LeftArrow, _.LeftArrow, _.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "2", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
TokenClassification.Selection, "Get-Less2 ",
TokenClassification.None,
"Get-Less5 Get-Less8 Get-Less11")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "10", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 ",
TokenClassification.Selection, "Get-Less10 ", NextLine,
TokenClassification.None,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.LeftArrow, _.LeftArrow, _.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "1", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
TokenClassification.Selection, "Get-Less1 ",
TokenClassification.None,
"Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "12", NextLine,
TokenClassification.None, "Get-Less0 Get-Less3 Get-Less6 Get-Less9 ",
TokenClassification.Selection, "Get-Less12 ", NextLine,
TokenClassification.None,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.LeftArrow, _.LeftArrow,
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_Navigation4()
{
// Test 'UpArrow' and 'DownArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
// Get-Less2 Get-Less5 Get-Less8 Get-Less11
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-Less0", Keys(
"Get-Less", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.DownArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "1", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
TokenClassification.Selection, "Get-Less1 ",
TokenClassification.None,
"Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.DownArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "2", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
TokenClassification.Selection, "Get-Less2 ",
TokenClassification.None,
"Get-Less5 Get-Less8 Get-Less11")),
_.DownArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "3", NextLine,
TokenClassification.None, "Get-Less0 ",
TokenClassification.Selection, "Get-Less3 ",
TokenClassification.None,
"Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.UpArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "2", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
TokenClassification.Selection, "Get-Less2 ",
TokenClassification.None,
"Get-Less5 Get-Less8 Get-Less11")),
_.UpArrow, _.UpArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.UpArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "12", NextLine,
TokenClassification.None, "Get-Less0 Get-Less3 Get-Less6 Get-Less9 ",
TokenClassification.Selection, "Get-Less12 ", NextLine,
TokenClassification.None,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.UpArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "11", NextLine,
TokenClassification.None,
"Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 ",
TokenClassification.Selection, "Get-Less11 ")),
_.DownArrow, _.DownArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Less",
TokenClassification.Selection, "0", NextLine,
TokenClassification.Selection, "Get-Less0 ",
TokenClassification.None,
"Get-Less3 Get-Less6 Get-Less9 Get-Less12", NextLine,
"Get-Less1 Get-Less4 Get-Less7 Get-Less10", NextLine,
"Get-Less2 Get-Less5 Get-Less8 Get-Less11")),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_Navigation5()
{
// Test 'UpArrow', 'DownArrow', 'LeftArrow', and 'RightArrow' with the following menu:
// Get-MockDynamicParameters Get-Module
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-MockDynamicParameters", Keys(
"Get-Mo", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module")),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ")),
_.RightArrow,
CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module")),
_.UpArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ")),
_.UpArrow,
CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module")),
_.DownArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ")),
_.DownArrow,
CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module")),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_Navigation6()
{
// Test 'UpArrow', 'DownArrow', 'LeftArrow', and 'RightArrow' with the following menu:
// Get-NewDynamicParameters Get-NewStyle
// Get-NewIdea
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
Test("Get-NewDynamicParameters", Keys(
"Get-New", _.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "DynamicParameters", NextLine,
TokenClassification.Selection, "Get-NewDynamicParameters ",
TokenClassification.None, "Get-NewStyle", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.RightArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Style", NextLine,
TokenClassification.None, "Get-NewDynamicParameters ",
TokenClassification.Selection, "Get-NewStyle ", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.RightArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Idea", NextLine,
TokenClassification.None, "Get-NewDynamicParameters Get-NewStyle", NextLine,
TokenClassification.Selection, "Get-NewIdea ")),
_.RightArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "DynamicParameters", NextLine,
TokenClassification.Selection, "Get-NewDynamicParameters ",
TokenClassification.None, "Get-NewStyle", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Idea", NextLine,
TokenClassification.None, "Get-NewDynamicParameters Get-NewStyle", NextLine,
TokenClassification.Selection, "Get-NewIdea ")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Style", NextLine,
TokenClassification.None, "Get-NewDynamicParameters ",
TokenClassification.Selection, "Get-NewStyle ", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.LeftArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "DynamicParameters", NextLine,
TokenClassification.Selection, "Get-NewDynamicParameters ",
TokenClassification.None, "Get-NewStyle", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.DownArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Idea", NextLine,
TokenClassification.None, "Get-NewDynamicParameters Get-NewStyle", NextLine,
TokenClassification.Selection, "Get-NewIdea ")),
_.DownArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Style", NextLine,
TokenClassification.None, "Get-NewDynamicParameters ",
TokenClassification.Selection, "Get-NewStyle ", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.DownArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "DynamicParameters", NextLine,
TokenClassification.Selection, "Get-NewDynamicParameters ",
TokenClassification.None, "Get-NewStyle", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.UpArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Style", NextLine,
TokenClassification.None, "Get-NewDynamicParameters ",
TokenClassification.Selection, "Get-NewStyle ", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.UpArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "Idea", NextLine,
TokenClassification.None, "Get-NewDynamicParameters Get-NewStyle", NextLine,
TokenClassification.Selection, "Get-NewIdea ")),
_.UpArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-New",
TokenClassification.Selection, "DynamicParameters", NextLine,
TokenClassification.Selection, "Get-NewDynamicParameters ",
TokenClassification.None, "Get-NewStyle", NextLine,
TokenClassification.None, "Get-NewIdea")),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_ClearProperly()
{
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
_console.Clear();
int width = _console.BufferWidth;
string placeholderCommand = new string('A', width - 12); // 12 = "Get-Module".Length + 2
string emptyLine = new string(' ', width);
Test($"{placeholderCommand};Get-Module", Keys(
placeholderCommand, ';',
"Get-Mo", _.Ctrl_Spacebar,
// At this point, the editing line buffer takes 2 physical lines.
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, placeholderCommand,
TokenClassification.None, ';',
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module ", NextLine,
TokenClassification.None, emptyLine)),
_.RightArrow,
// Navigating to the next item will cause the editing line to fit in
// one physical line, so the new menu is moved up and lines from the
// previous menu need to be properly cleared.
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, placeholderCommand,
TokenClassification.None, ';',
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ", NextLine,
TokenClassification.None, emptyLine)),
_.LeftArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, placeholderCommand,
TokenClassification.None, ';',
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module ", NextLine,
TokenClassification.None, emptyLine)),
_.DownArrow,
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, placeholderCommand,
TokenClassification.None, ';',
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ", NextLine,
TokenClassification.None, emptyLine)),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void MenuCompletions_WorkWithListView()
{
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
var (listWidth, windowWidth) = CheckWindowSize();
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
string emptyLine = new string(' ', windowWidth);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
_console.Clear();
SetHistory("Get-Mocha -AddMilk -AddSugur -ExtraCup", "Get-MoreBook -Kind Fiction -FlatCover");
Test("Get-Module", Keys(
"Get-Mo",
CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "Get-Mo",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "Get-Mo",
TokenClassification.None, "reBook -Kind Fiction -FlatCover",
TokenClassification.None, new string(' ', listWidth - 48), // 48 is the length of '> Get-MoreBook -Kind Fiction -FlatCover' plus '[History]'.
TokenClassification.None, '[',
TokenClassification.ListPrediction, "History",
TokenClassification.None, ']',
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "Get-Mo",
TokenClassification.None, "cha -AddMilk -AddSugur -ExtraCup",
TokenClassification.None, new string(' ', listWidth - 49), // 49 is the length of '> Get-Mocha -AddMilk -AddSugur -ExtraCup' plus '[History]'.
TokenClassification.None, '[',
TokenClassification.ListPrediction, "History",
TokenClassification.None, ']')),
_.Ctrl_Spacebar,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "ckDynamicParameters", NextLine,
TokenClassification.Selection, "Get-MockDynamicParameters ",
TokenClassification.None, "Get-Module", NextLine,
TokenClassification.None, emptyLine,
TokenClassification.None, emptyLine)),
_.RightArrow,
CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
TokenClassification.Selection, "dule", NextLine,
TokenClassification.None, "Get-MockDynamicParameters ",
TokenClassification.Selection, "Get-Module ", NextLine,
TokenClassification.None, emptyLine,
TokenClassification.None, emptyLine)),
_.Enter,
_.Enter
));
}
[SkippableFact]
public void ShowTooltips()
{
@@ -249,6 +922,21 @@ namespace Test
completions.Add(new CompletionResult("Get-Many" + i));
}
break;
case "Get-Less":
replacementIndex = 0;
replacementLength = 8;
for (int i = 0; i < 13; i++)
{
completions.Add(new CompletionResult("Get-Less" + i));
}
break;
case "Get-New":
replacementIndex = 0;
replacementLength = 7;
completions.Add(new CompletionResult("Get-NewDynamicParameters"));
completions.Add(new CompletionResult("Get-NewIdea"));
completions.Add(new CompletionResult("Get-NewStyle"));
break;
case "Get-Tooltips":
replacementIndex = 0;
replacementLength = 12;
@@ -290,6 +978,16 @@ namespace Test
break;
case "none":
break;
default:
if (input.EndsWith("Get-Mo", StringComparison.OrdinalIgnoreCase))
{
replacementIndex = input.IndexOf("Get-Mo", StringComparison.OrdinalIgnoreCase);
replacementLength = 6;
completions.Add(new CompletionResult("Get-MockDynamicParameters"));
completions.Add(new CompletionResult("Get-Module"));
}
break;
}
return (CommandCompletion)ctor.Invoke(
+5
View File
@@ -182,6 +182,11 @@ namespace Test
public void SetCursorPosition(int left, int top)
{
if (left != CursorLeft || top != CursorTop)
{
_ignoreNextNewline = false;
}
CursorLeft = left;
CursorTop = top;
}