diff --git a/PSReadLine/KeyBindings.cs b/PSReadLine/KeyBindings.cs
index 7b3f0c1c..69daaa0c 100644
--- a/PSReadLine/KeyBindings.cs
+++ b/PSReadLine/KeyBindings.cs
@@ -664,7 +664,7 @@ public static void ShowKeyBindings(ConsoleKeyInfo? key = null, object arg = null
}
// Don't overwrite any of the line - so move to first line after the end of our buffer.
- var point = _singleton.ConvertOffsetToPoint(_singleton._buffer.Length);
+ var point = _singleton.EndOfBufferPosition();
console.SetCursorPosition(point.X, point.Y);
console.Write("\n");
@@ -721,7 +721,7 @@ public static void WhatIsKey(ConsoleKeyInfo? key = null, object arg = null)
var console = _singleton._console;
// Don't overwrite any of the line - so move to first line after the end of our buffer.
- var point = _singleton.ConvertOffsetToPoint(_singleton._buffer.Length);
+ var point = _singleton.EndOfBufferPosition();
console.SetCursorPosition(point.X, point.Y);
console.Write("\n");
diff --git a/PSReadLine/PSReadLineResources.Designer.cs b/PSReadLine/PSReadLineResources.Designer.cs
index 925355f7..35dd5a33 100644
--- a/PSReadLine/PSReadLineResources.Designer.cs
+++ b/PSReadLine/PSReadLineResources.Designer.cs
@@ -2070,6 +2070,17 @@ internal static string WindowSizeTooSmallForListView
}
}
+ ///
+ /// Looks up a localized string similar to "! terminal size too small to show the list view".
+ ///
+ internal static string WindowSizeTooSmallWarning
+ {
+ get
+ {
+ return ResourceManager.GetString("WindowSizeTooSmallWarning", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Copy the text from the current kill ring position to the input.
///
diff --git a/PSReadLine/PSReadLineResources.resx b/PSReadLine/PSReadLineResources.resx
index 7dc66be2..28d6e5d4 100644
--- a/PSReadLine/PSReadLineResources.resx
+++ b/PSReadLine/PSReadLineResources.resx
@@ -840,6 +840,9 @@ Or not saving history with:
The prediction 'ListView' is temporarily disabled because the current window size of the console is too small. To use the 'ListView', please make sure the 'WindowWidth' is not less than '{0}' and the 'WindowHeight' is not less than '{1}'.
+
+ ! terminal size too small to show the list view
+
No help content available. Please use Update-Help to download the latest help content.
diff --git a/PSReadLine/Prediction.Entry.cs b/PSReadLine/Prediction.Entry.cs
index a60d05e2..f576f62a 100644
--- a/PSReadLine/Prediction.Entry.cs
+++ b/PSReadLine/Prediction.Entry.cs
@@ -9,19 +9,52 @@ namespace Microsoft.PowerShell
{
public partial class PSConsoleReadLine
{
+ ///
+ /// Represents a prediction source.
+ ///
+ private readonly struct SourceInfo
+ {
+ internal readonly string SourceName;
+ internal readonly int EndIndex;
+ internal readonly int PrevSourceEndIndex;
+ internal readonly int ItemCount;
+
+ internal SourceInfo(string sourceName, int endIndex, int prevSourceEndIndex)
+ {
+ SourceName = sourceName;
+ int sourceWidth = LengthInBufferCells(SourceName);
+ if (sourceWidth > PredictionListView.SourceMaxWidth)
+ {
+ sourceWidth = PredictionListView.SourceMaxWidth - 1;
+ int sourceStrLen = SubstringLengthByCells(sourceName, sourceWidth);
+ SourceName = sourceName.Substring(0, sourceStrLen) + SuggestionEntry.Ellipsis;
+ }
+
+ EndIndex = endIndex;
+ PrevSourceEndIndex = prevSourceEndIndex;
+ ItemCount = EndIndex - PrevSourceEndIndex;
+ }
+ }
+
///
/// This type represents an individual suggestion entry.
///
private struct SuggestionEntry
{
+ internal const char Ellipsis = '\u2026';
+ internal const string HistorySource = "History";
+
internal readonly Guid PredictorId;
internal readonly uint? PredictorSession;
internal readonly string Source;
internal readonly string SuggestionText;
internal readonly int InputMatchIndex;
+ private string _listItemTextRegular;
+ private string _listItemTextSelected;
+
internal SuggestionEntry(string suggestion, int matchIndex)
- : this(source: "History", predictorId: Guid.Empty, predictorSession: null, suggestion, matchIndex)
+ : this(source: HistorySource, predictorId: Guid.Empty, predictorSession: null, suggestion, matchIndex)
{
}
@@ -32,6 +65,8 @@ internal SuggestionEntry(string source, Guid predictorId, uint? predictorSession
PredictorSession = predictorSession;
SuggestionText = suggestion;
InputMatchIndex = matchIndex;
+
+ _listItemTextRegular = _listItemTextSelected = null;
}
///
@@ -57,8 +92,19 @@ private static int DivideAndRoundUp(int dividend, int divisor)
/// The highlighting sequences for a selected list item.
internal string GetListItemText(int width, string input, string selectionHighlighting)
{
- const string ellipsis = "...";
- const int ellipsisLength = 3;
+ const int ellipsisLength = 1;
+
+ if (selectionHighlighting is null)
+ {
+ if (_listItemTextRegular is not null)
+ {
+ return _listItemTextRegular;
+ }
+ }
+ else if (_listItemTextSelected is not null)
+ {
+ return _listItemTextSelected;
+ }
// Calculate the 'SOURCE' portion to be rendered.
int sourceStrLen = Source.Length;
@@ -119,7 +165,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
// The suggestion text doesn't contain the user input.
int length = SubstringLengthByCells(SuggestionText, textWidth - ellipsisLength);
line.Append(SuggestionText, 0, length)
- .Append(ellipsis);
+ .Append(Ellipsis);
break;
}
@@ -136,7 +182,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
.Append(SuggestionText, 0, input.Length)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, input.Length, length - input.Length)
- .Append(ellipsis);
+ .Append(Ellipsis);
}
else
{
@@ -149,7 +195,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
int remainingLenInCells = textWidth - ellipsisLength - rightLenInCells;
int length = SubstringLengthByCellsFromEnd(SuggestionText, input.Length - 1, remainingLenInCells);
line.Append(_singleton._options.EmphasisColor)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, input.Length - length, length)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, input.Length, SuggestionText.Length - input.Length);
@@ -162,11 +208,11 @@ internal string GetListItemText(int width, string input, string selectionHighlig
int startIndex = input.Length - leftStrLen;
int totalStrLen = SubstringLengthByCells(SuggestionText, startIndex, textWidth - ellipsisLength * 2);
line.Append(_singleton._options.EmphasisColor)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, startIndex, leftStrLen)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, input.Length, totalStrLen - leftStrLen)
- .Append(ellipsis);
+ .Append(Ellipsis);
}
}
@@ -192,7 +238,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
.Append(SuggestionText, InputMatchIndex, input.Length)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, rightStrLen)
- .Append(ellipsis);
+ .Append(Ellipsis);
break;
}
@@ -201,7 +247,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
{
// Otherwise, if the (mid+right) portions take up to 2/3 of the text width, we just truncate the suggestion text at the beginning.
int leftStrLen = SubstringLengthByCellsFromEnd(SuggestionText, InputMatchIndex - 1, textWidth - midRightLenInCells - ellipsisLength);
- line.Append(ellipsis)
+ line.Append(Ellipsis)
.Append(SuggestionText, InputMatchIndex - leftStrLen, leftStrLen)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, input.Length)
@@ -223,13 +269,13 @@ internal string GetListItemText(int width, string input, string selectionHighlig
int leftStrLen = SubstringLengthByCellsFromEnd(SuggestionText, InputMatchIndex - 1, leftCellLen - ellipsisLength);
int rightStrLen = SubstringLengthByCells(SuggestionText, rightStartindex, rigthCellLen - ellipsisLength);
- line.Append(ellipsis)
+ line.Append(Ellipsis)
.Append(SuggestionText, InputMatchIndex - leftStrLen, leftStrLen)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, input.Length)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, rightStrLen)
- .Append(ellipsis);
+ .Append(Ellipsis);
break;
}
@@ -249,7 +295,7 @@ internal string GetListItemText(int width, string input, string selectionHighlig
line.Append(SuggestionText, 0, InputMatchIndex)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, midLeftStrLen)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, rightStartindex - midRightStrLen, midRightStrLen)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, SuggestionText.Length - rightStartindex);
@@ -277,11 +323,11 @@ internal string GetListItemText(int width, string input, string selectionHighlig
line.Append(SuggestionText, 0, InputMatchIndex)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, midLeftStrLen)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, rightStartindex - midRightStrLen, midRightStrLen)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, rightStrLen)
- .Append(ellipsis);
+ .Append(Ellipsis);
break;
}
@@ -298,11 +344,11 @@ internal string GetListItemText(int width, string input, string selectionHighlig
int midRightStrLen = SubstringLengthByCellsFromEnd(SuggestionText, rightStartindex - 1, midRightCellLen);
int leftStrLen = SubstringLengthByCellsFromEnd(SuggestionText, InputMatchIndex - 1, midRemainingLenInCells);
- line.Append(ellipsis)
+ line.Append(Ellipsis)
.Append(SuggestionText, InputMatchIndex - leftStrLen, leftStrLen)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, midLeftStrLen)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, rightStartindex - midRightStrLen, midRightStrLen)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, SuggestionText.Length - rightStartindex);
@@ -324,15 +370,15 @@ internal string GetListItemText(int width, string input, string selectionHighlig
int spacesNeeded = textWidth - midRemainingLenInCells * 3 - ellipsisLength * 3;
string spaces = spacesNeeded > 0 ? Spaces(spacesNeeded) : string.Empty;
- line.Append(ellipsis)
+ line.Append(Ellipsis)
.Append(SuggestionText, InputMatchIndex - leftStrLen, leftStrLen)
.Append(_singleton._options.EmphasisColor)
.Append(SuggestionText, InputMatchIndex, midLeftStrLen)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(SuggestionText, rightStartindex - midRightStrLen, midRightStrLen)
.EndColorSection(selectionHighlighting)
.Append(SuggestionText, rightStartindex, rightStrLen)
- .Append(ellipsis)
+ .Append(Ellipsis)
.Append(spaces);
break;
}
@@ -351,13 +397,29 @@ internal string GetListItemText(int width, string input, string selectionHighlig
else
{
line.Append(Source, 0, sourceStrLen)
- .Append(ellipsis);
+ .Append(Ellipsis);
}
line.EndColorSection(selectionHighlighting)
.Append(']');
- return line.ToString();
+ if (selectionHighlighting is not null)
+ {
+ // Need to reset at the end if the selection highlighting is being applied.
+ line.Append(VTColorUtils.AnsiReset);
+ }
+
+ string textForRendering = line.ToString();
+ if (selectionHighlighting is null)
+ {
+ _listItemTextRegular = textForRendering;
+ }
+ else
+ {
+ _listItemTextSelected = textForRendering;
+ }
+
+ return textForRendering;
}
}
}
diff --git a/PSReadLine/Prediction.Views.cs b/PSReadLine/Prediction.Views.cs
index ddfbd073..91ef8437 100644
--- a/PSReadLine/Prediction.Views.cs
+++ b/PSReadLine/Prediction.Views.cs
@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using System.Management.Automation.Subsystem.Prediction;
using Microsoft.PowerShell.Internal;
+using Microsoft.PowerShell.PSReadLine;
namespace Microsoft.PowerShell
{
@@ -207,19 +208,48 @@ protected List GetPredictionResults()
///
private class PredictionListView : PredictionViewBase
{
- internal const int ListMaxCount = 10;
- internal const int ListMaxWidth = 100;
+ // Item count constants.
+ internal const int ListMaxCount = 50;
+ internal const int HistoryMaxCount = 10;
+
+ // List view constants.
+ internal const int ListViewMaxHeight = 10;
+ internal const int ListViewMaxWidth = 100;
internal const int SourceMaxWidth = 15;
- internal const int MinWindowWidth = 54;
- internal const int MinWindowHeight = 15;
+ // Minimal window size.
+ internal const int MinWindowWidth = 50;
+ internal const int MinWindowHeight = 5;
+ // The items to be displayed in the list view.
private List _listItems;
- private int _listItemWidth;
- private int _listItemHeight;
+ // Information about the sources of those items.
+ private List _sources;
+ // The index that is currently selected by user.
private int _selectedIndex;
+ // Indicates to have the list view starts at the selected index.
+ private bool _renderFromSelected;
+ // Indicates a navigation update within the list view is pending.
private bool _updatePending;
+ // The max list height to be used for rendering, which is auto-adjusted based on terminal height.
+ private int _maxViewHeight;
+ // The actual height of the list view that is currently rendered.
+ private int _listViewHeight;
+ // The actual width of the list view that is currently rendered.
+ private int _listViewWidth;
+ // An index pointing to the item that is shown in the first slot of the list view.
+ private int _listViewTop;
+ // An index pointing to the item right AFTER the one that is shown in the last slot of the list view.
+ private int _listViewEnd;
+
+ // Indicates if we need to check on the height for each navigation in the list view.
+ private bool _checkOnHeight;
+ // To warn about that the terminal size is too small to display the list view.
+ private bool _warnAboutSize;
+ // Indicates if a warning message was displayed.
+ private bool _warningPrinted;
+
// Caches re-used when aggregating the suggestion results from predictors and history.
// Those caches help us avoid allocation on tons of short-lived collections.
private List _cacheList1;
@@ -273,6 +303,36 @@ internal PredictionListView(PSConsoleReadLine singleton)
internal override bool HasPendingUpdate => _updatePending;
internal override bool HasActiveSuggestion => _listItems != null;
+ ///
+ /// Calculate the max width and height of the list view based on the current terminal size.
+ ///
+ private (int maxWidth, int maxHeight, bool checkOnHeight) RefreshMaxViewSize()
+ {
+ var console = _singleton._console;
+ int maxWidth = Math.Min(console.BufferWidth, ListViewMaxWidth);
+
+ (int maxHeight, bool moreCheck) = console.BufferHeight switch
+ {
+ > ListViewMaxHeight * 2 => (ListViewMaxHeight, false),
+ > ListViewMaxHeight => (ListViewMaxHeight / 2, false),
+ _ => (ListViewMaxHeight / 3, true)
+ };
+
+ return (maxWidth, maxHeight, moreCheck);
+ }
+
+ ///
+ /// Check if the height becomes too small for the current rendering.
+ ///
+ private bool HeightIsTooSmall()
+ {
+ int physicalLineCountForBuffer = _singleton.EndOfBufferPosition().Y - _singleton._initialY + 1;
+ return _singleton._console.BufferHeight < physicalLineCountForBuffer + _maxViewHeight + 1 /* one metadata line */;
+ }
+
+ ///
+ /// Get suggestion results.
+ ///
internal override void GetSuggestion(string userInput)
{
if (_singleton._initialY < 0)
@@ -295,14 +355,16 @@ internal override void GetSuggestion(string userInput)
if (!WindowSizeMeetsMinRequirement)
{
- // If the window size is too small for the list view to work, we just disable the list view.
- Reset();
+ // If the window size is too small to show the list view, we disable the list view and show a warning.
+ _warnAboutSize = true;
return;
}
_inputText = userInput;
+ // Reset the list item selection.
_selectedIndex = -1;
- _listItemWidth = Math.Min(_singleton._console.BufferWidth, ListMaxWidth);
+ // Refresh the list view width and height in case the terminal was resized.
+ (_listViewWidth, _maxViewHeight, _checkOnHeight) = RefreshMaxViewSize();
if (inputUnchanged)
{
@@ -313,6 +375,7 @@ internal override void GetSuggestion(string userInput)
}
_listItems?.Clear();
+ _sources?.Clear();
try
{
@@ -323,7 +386,11 @@ internal override void GetSuggestion(string userInput)
if (UseHistory)
{
- _listItems = GetHistorySuggestions(userInput, ListMaxCount);
+ _listItems = GetHistorySuggestions(userInput, HistoryMaxCount);
+ if (_listItems?.Count > 0)
+ {
+ _sources = new List() { new SourceInfo(SuggestionEntry.HistorySource, _listItems.Count - 1, -1) };
+ }
}
}
catch
@@ -348,6 +415,7 @@ private void AggregateSuggestions()
try
{
_listItems ??= new List();
+ _sources ??= new List();
_cacheList1 ??= new List(); // This list holds the total number of suggestions from each of the predictors.
_cacheList2 ??= new List(); // This list holds the final number of suggestions that will be rendered for each of the predictors.
@@ -412,13 +480,18 @@ private void AggregateSuggestions()
break;
}
- more = _cacheList1[i] > 0;
+ more |= _cacheList1[i] > 0;
}
}
if (hCount > 0)
{
- _listItems.RemoveRange(hCount, _listItems.Count - hCount);
+ if (hCount < _listItems.Count)
+ {
+ _listItems.RemoveRange(hCount, _listItems.Count - hCount);
+ _sources.Clear();
+ _sources.Add(new SourceInfo(SuggestionEntry.HistorySource, hCount - 1, prevSourceEndIndex: -1));
+ }
if (_cachedComparer != _singleton._options.HistoryStringComparer)
{
@@ -471,22 +544,29 @@ private void AggregateSuggestions()
// Get the number of prediction results that were actually put in the list after filtering out the duplicate ones.
int count = _cacheList2[index] - num;
- if (item.Session.HasValue && count > 0)
+ if (count > 0)
{
- // Send feedback only if the mini-session id is specified and we truely have its results in the list to be rendered.
- // When the mini-session id is not specified, we consider the predictor doesn't accept feedback.
- //
- // NOTE: when any duplicate results were skipped, the 'count' passed in here won't be accurate as it still includes
- // those skipped ones. This is due to the limitation of the 'OnSuggestionDisplayed' interface method, which didn't
- // assume any prediction results from a predictor could be filtered out at the initial design time. We will have to
- // change the predictor interface to pass in accurate information, such as:
- // void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex, int[] skippedIndices)
- //
- // However, an interface change has huge impacts. At least, a newer version of PSReadLine will stop working on the
- // existing PowerShell 7+ versions. For this particular issue, the chance that it could happen is low and the impact
- // of the inaccurate feedback is also low, so we should delay this interface change until another highly-demanded
- // change to the interface is required in future (e.g. changes related to supporting OpenAI models).
- _singleton._mockableMethods.OnSuggestionDisplayed(item.Id, item.Session.Value, count + skipCount);
+ int prevEndIndex = _sources.Count > 0 ? _sources[_sources.Count - 1].EndIndex : -1;
+ int endIndex = _listItems.Count - 1;
+ _sources.Add(new SourceInfo(_listItems[endIndex].Source, endIndex, prevEndIndex));
+
+ if (item.Session.HasValue && count > 0)
+ {
+ // Send feedback only if the mini-session id is specified and we truely have its results in the list to be rendered.
+ // When the mini-session id is not specified, we consider the predictor doesn't accept feedback.
+ //
+ // NOTE: when any duplicate results were skipped, the 'count' passed in here won't be accurate as it still includes
+ // those skipped ones. This is due to the limitation of the 'OnSuggestionDisplayed' interface method, which didn't
+ // assume any prediction results from a predictor could be filtered out at the initial design time. We will have to
+ // change the predictor interface to pass in accurate information, such as:
+ // void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex, int[] skippedIndices)
+ //
+ // However, an interface change has huge impacts. At least, a newer version of PSReadLine will stop working on the
+ // existing PowerShell 7+ versions. For this particular issue, the chance that it could happen is low and the impact
+ // of the inaccurate feedback is also low, so we should delay this interface change until another highly-demanded
+ // change to the interface is required in future (e.g. changes related to supporting OpenAI models).
+ _singleton._mockableMethods.OnSuggestionDisplayed(item.Id, item.Session.Value, count + skipCount);
+ }
}
}
}
@@ -501,7 +581,10 @@ private void AggregateSuggestions()
if (_listItems?.Count > 0)
{
- _listItemHeight = Math.Min(_listItems.Count, ListMaxCount);
+ // Initialize the view window position here.
+ _listViewTop = 0;
+ _listViewEnd = Math.Min(_listItems.Count, _maxViewHeight);
+ _listViewHeight = _listViewEnd - _listViewTop;
}
else
{
@@ -509,8 +592,24 @@ private void AggregateSuggestions()
}
}
+ ///
+ /// Generate the rendering text for the list view.
+ ///
internal override void RenderSuggestion(List consoleBufferLines, ref int currentLogicalLine)
{
+ if (_warnAboutSize || (_checkOnHeight && HeightIsTooSmall()))
+ {
+ _warningPrinted = true;
+ RenderWarningLine(NextBufferLine(consoleBufferLines, ref currentLogicalLine));
+
+ Reset();
+ return;
+ }
+ else
+ {
+ _warningPrinted = false;
+ }
+
if (_updatePending)
{
_updatePending = false;
@@ -520,36 +619,254 @@ internal override void RenderSuggestion(List consoleBufferLines,
AggregateSuggestions();
}
- if (_listItems == null)
+ if (_listItems is null)
{
return;
}
- for (int i = 0; i < _listItemHeight; i++)
+ // Create the metadata line.
+ RenderMetadataLine(NextBufferLine(consoleBufferLines, ref currentLogicalLine));
+
+ if (_selectedIndex >= 0)
{
- currentLogicalLine += 1;
- if (currentLogicalLine == consoleBufferLines.Count)
+ // An item was selected, so update the view window accrodingly.
+ if (_renderFromSelected)
{
- consoleBufferLines.Add(new StringBuilder(COMMON_WIDEST_CONSOLE_WIDTH));
+ // Render from the selected index if there are enough items left for a page.
+ // If not, then render all remaining items plus a few from above the selected one, so as to render a full page.
+ _renderFromSelected = false;
+ int offset = _maxViewHeight - Math.Min(_listItems.Count - _selectedIndex, _maxViewHeight);
+ _listViewTop = offset > 0 ? Math.Max(0, _selectedIndex - offset) : _selectedIndex;
+ _listViewEnd = Math.Min(_listItems.Count, _listViewTop + _maxViewHeight);
+ }
+ else
+ {
+ // - if the selected item is within the current top/end, then no need to move the list view window.
+ // - if the selected item is before the current top, then move the top to the selected item.
+ // - if the selected item is after the current end, then move the end to one beyond the selected item.
+ if (_selectedIndex < _listViewTop)
+ {
+ _listViewTop = _selectedIndex;
+ _listViewEnd = Math.Min(_listItems.Count, _selectedIndex + _maxViewHeight);
+ }
+ else if (_selectedIndex >= _listViewEnd)
+ {
+ _listViewEnd = _selectedIndex + 1;
+ _listViewTop = Math.Max(0, _listViewEnd - _maxViewHeight);
+ }
}
- bool itemSelected = i == _selectedIndex;
- StringBuilder currentLineBuffer = consoleBufferLines[currentLogicalLine];
+ _listViewHeight = _listViewEnd - _listViewTop;
+ }
+ for (int i = _listViewTop; i < _listViewEnd; i++)
+ {
+ bool itemSelected = i == _selectedIndex;
string selectionColor = itemSelected ? _singleton._options._listPredictionSelectedColor : null;
- currentLineBuffer.Append(
- _listItems[i].GetListItemText(
- _listItemWidth,
+
+ NextBufferLine(consoleBufferLines, ref currentLogicalLine)
+ .Append(_listItems[i].GetListItemText(
+ _listViewWidth,
_inputText,
selectionColor));
+ }
+ }
+
+ ///
+ /// Generate the rendering text for the warning message.
+ ///
+ private void RenderWarningLine(StringBuilder buffer)
+ {
+ // Add italic text effect to the highlight color.
+ string highlightStyle = _singleton._options._listPredictionColor + "\x1b[3m";
+
+ buffer.Append(highlightStyle)
+ .Append(PSReadLineResources.WindowSizeTooSmallWarning)
+ .Append(VTColorUtils.AnsiReset);
+ }
- if (itemSelected)
+ ///
+ /// Calculate the height of the list when warning was displayed.
+ ///
+ private int GetPesudoListHeightForWarningRendering()
+ {
+ int bufferWidth = _singleton._console.BufferWidth;
+ int lengthInCells = LengthInBufferCells(PSReadLineResources.WindowSizeTooSmallWarning);
+ int pesudoListHeight = lengthInCells / bufferWidth;
+
+ if (lengthInCells % bufferWidth == 0)
+ {
+ pesudoListHeight--;
+ }
+
+ return pesudoListHeight;
+ }
+
+ ///
+ /// Generate the rendering text for the metadata line.
+ ///
+ private void RenderMetadataLine(StringBuilder buffer)
+ {
+ // Add italic text effect to the highlight color.
+ string highlightStyle = _singleton._options._listPredictionColor + "\x1b[3m";
+ string dimmedStyle = PSConsoleReadLineOptions.DefaultInlinePredictionColor;
+ string activeStyle = null;
+
+ // Render the quick indicator.
+ buffer.Append(highlightStyle)
+ .Append('<')
+ .Append(_selectedIndex > -1 ? _selectedIndex + 1 : "-")
+ .Append('/')
+ .Append(_listItems.Count)
+ .Append('>')
+ .Append(VTColorUtils.AnsiReset);
+
+ if (_listViewWidth < 60)
+ {
+ // We don't render the additional information about sources when the list view width is less than 60.
+ // Adjust the position of quick indicator a little bit in this case and call it done.
+ buffer.Insert(0, VTColorUtils.AnsiReset);
+ buffer.Insert(VTColorUtils.AnsiReset.Length, " ", count: 2);
+ return;
+ }
+
+ ///
+ /// A helper function to avoid appending extra color VT sequences unnecessarily.
+ ///
+ static StringBuilder AppendColor(StringBuilder buffer, string colorToUse, ref string activeColor, out int nextCharPos)
+ {
+ if (activeColor is null)
{
- currentLineBuffer.Append(VTColorUtils.AnsiReset);
+ buffer.Append(colorToUse);
}
+ else if (activeColor != colorToUse)
+ {
+ buffer.Append(VTColorUtils.AnsiReset).Append(colorToUse);
+ }
+
+ activeColor = colorToUse;
+ nextCharPos = buffer.Length;
+ return buffer;
}
+
+ // The list view width decides how to render the source information:
+ // - when width >= 80, we render upto 3 sources,
+ // - when width >= 60, we render upto 2 sources.
+ // The reason to select '80' and '60' here is because:
+ // - To render upto 3 sources, the maximum cell length that could be taken by both the total-count part and the extra-info part
+ // will be 75 (7+68), so we choose '80' as the minimal requirement for rendering 3 sources.
+ // - To render upto 2 sources, the maximum cell length that could be taken by both the total-count part and the extra-info part
+ // will be 55 (7+48), so we choose '60' as the minimal requirement for rendering 2 sources.
+ int maxSourceCount = _listViewWidth >= 80 ? 3 : 2;
+ int charPosition = buffer.Length;
+ int totalCountPartLength = buffer.Length - highlightStyle.Length - VTColorUtils.AnsiReset.Length;
+ int additionalPartLength = 0;
+
+ int selected = -1;
+ int startFrom = 0;
+
+ // If a list item was selected, calculate which source the list item belongs to and which source
+ // to start render for the additional information part.
+ if (_selectedIndex > -1)
+ {
+ for (int i = 0; i < _sources.Count; i++)
+ {
+ if (_selectedIndex <= _sources[i].EndIndex)
+ {
+ selected = i;
+ break;
+ }
+ }
+
+ if (selected == 0)
+ {
+ startFrom = 0;
+ }
+ else if (selected == _sources.Count - 1)
+ {
+ startFrom = Math.Max(0, selected - (maxSourceCount - 1));
+ }
+ else
+ {
+ startFrom = maxSourceCount == 3 ? selected - 1 : selected;
+ }
+ }
+
+ // Start the extra information about the sources -- add the opening arrow bracket.
+ AppendColor(buffer, dimmedStyle, ref activeStyle, out _).Append('<');
+ additionalPartLength++;
+
+ // Add the prefix, continue to use dimmed color.
+ if (startFrom > 0)
+ {
+ buffer.Append(SuggestionEntry.Ellipsis).Append(' ');
+ additionalPartLength += 2;
+ }
+
+ // Add the sources.
+ for (int i = 0; i < maxSourceCount; i++)
+ {
+ int index = startFrom + i;
+ if (index == _sources.Count)
+ {
+ break;
+ }
+
+ if (i > 0)
+ {
+ // Add the separator.
+ buffer.Append(' ');
+ additionalPartLength++;
+ }
+
+ int nextCharPos;
+ SourceInfo info = _sources[index];
+ if (selected == index)
+ {
+ AppendColor(buffer, highlightStyle, ref activeStyle, out nextCharPos)
+ .Append(info.SourceName)
+ .Append('(')
+ .Append(_selectedIndex - info.PrevSourceEndIndex)
+ .Append('/')
+ .Append(info.ItemCount)
+ .Append(')');
+ }
+ else
+ {
+ AppendColor(buffer, dimmedStyle, ref activeStyle, out nextCharPos)
+ .Append(info.SourceName)
+ .Append('(')
+ .Append(info.ItemCount)
+ .Append(')');
+ }
+
+ // Need to take into account multi-cell characters when calculating length.
+ additionalPartLength += LengthInBufferCells(buffer, nextCharPos, buffer.Length);
+ }
+
+ // Add the suffix.
+ if (startFrom + maxSourceCount < _sources.Count)
+ {
+ AppendColor(buffer, dimmedStyle, ref activeStyle, out _)
+ .Append(' ')
+ .Append(SuggestionEntry.Ellipsis);
+ additionalPartLength += 2;
+ }
+
+ // Add the closing arrow bracket.
+ AppendColor(buffer, dimmedStyle, ref activeStyle, out _)
+ .Append('>')
+ .Append(VTColorUtils.AnsiReset);
+ additionalPartLength++;
+
+ // Lastly, insert the padding spaces.
+ int padding = _listViewWidth - additionalPartLength - totalCountPartLength;
+ buffer.Insert(charPosition, " ", padding);
}
+ ///
+ /// Trigger the feedback about a suggestion was accepted.
+ ///
internal override void OnSuggestionAccepted()
{
if (!UsePlugin)
@@ -569,24 +886,40 @@ internal override void OnSuggestionAccepted()
}
}
+ ///
+ /// Clear the list view.
+ ///
internal override void Clear(bool cursorAtEol)
{
- if (_listItems == null) { return; }
+ if (_listItems == null && !_warningPrinted)
+ {
+ return;
+ }
+
+ int listHeight = _warningPrinted
+ ? GetPesudoListHeightForWarningRendering()
+ : _listViewHeight;
int top = cursorAtEol
? _singleton._console.CursorTop
- : _singleton.ConvertOffsetToPoint(_inputText.Length).Y;
+ : _singleton.EndOfBufferPosition().Y;
- _singleton.WriteBlankLines(top + 1, _listItemHeight);
+ _warningPrinted = false;
+ _singleton.WriteBlankLines(top + 1, listHeight + 1 /* plus 1 to include the metadata line */);
Reset();
}
+ ///
+ /// Reset all the list view states.
+ ///
internal override void Reset()
{
base.Reset();
+
+ _sources = null;
_listItems = null;
- _listItemWidth = _listItemHeight = _selectedIndex = -1;
- _updatePending = false;
+ _maxViewHeight = _listViewTop = _listViewEnd = _listViewWidth = _listViewHeight = _selectedIndex = -1;
+ _warnAboutSize = _checkOnHeight = _updatePending = _renderFromSelected = false;
}
///
@@ -595,8 +928,12 @@ internal override void Reset()
///
internal void UpdateListSelection(int move)
{
+ // While moving around the list, we want to go back to the original input when we move one down
+ // after the last item, or move one up before the first item in the list.
+ // So, we can imagine a virtual list constructed by inserting the original input at the index 0
+ // of the real list.
int virtualItemIndex = _selectedIndex + 1;
- int virtualItemCount = _listItemHeight + 1;
+ int virtualItemCount = _listItems.Count + 1;
_updatePending = true;
virtualItemIndex += move;
@@ -614,6 +951,107 @@ internal void UpdateListSelection(int move)
_selectedIndex = virtualItemIndex % virtualItemCount + virtualItemCount - 1;
}
}
+
+ ///
+ /// Page up/down within the list view.
+ /// Update the index of the selected item based on and .
+ ///
+ internal bool UpdateListByPaging(bool pageUp, int num)
+ {
+ if (_selectedIndex == -1)
+ {
+ return false;
+ }
+
+ int oldSelectedIndex = _selectedIndex;
+ int lastItemIndex = _listItems.Count - 1;
+
+ for (int i = 0; i < num; i++)
+ {
+ if (pageUp)
+ {
+ if (_selectedIndex == 0)
+ {
+ break;
+ }
+
+ // Do one page up.
+ _selectedIndex = _selectedIndex == _listViewEnd - 1
+ ? _listViewTop
+ : Math.Max(0, _selectedIndex - (_maxViewHeight - 1));
+ }
+ else
+ {
+ if (_selectedIndex == lastItemIndex)
+ {
+ break;
+ }
+
+ // Do one page down.
+ _selectedIndex = _selectedIndex == _listViewTop
+ ? _listViewEnd - 1
+ : Math.Min(lastItemIndex, _selectedIndex + (_maxViewHeight - 1));
+ }
+ }
+
+ if (_selectedIndex != oldSelectedIndex)
+ {
+ // The selected item is changed, so we need to update the rendering.
+ _updatePending = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Loop up/down through the sources rendered in the list view.
+ /// Update the index of the selected item based on and .
+ ///
+ internal bool UpdateListByLoopingSources(bool jumpUp, int num)
+ {
+ if (_selectedIndex == -1)
+ {
+ return false;
+ }
+
+ int selectedSource = -1;
+ for (int i = 0; i < _sources.Count; i++)
+ {
+ if (_selectedIndex <= _sources[i].EndIndex)
+ {
+ selectedSource = i;
+ break;
+ }
+ }
+
+ int oldSelectedIndex = _selectedIndex;
+ for (int i = 0; i < num; i++)
+ {
+ if (jumpUp)
+ {
+ _selectedIndex = selectedSource == 0
+ ? _sources[_sources.Count - 1].PrevSourceEndIndex + 1
+ : _sources[selectedSource - 1].PrevSourceEndIndex + 1;
+ }
+ else
+ {
+ _selectedIndex = selectedSource == _sources.Count - 1
+ ? 0
+ : _sources[selectedSource].EndIndex + 1;
+ }
+ }
+
+ if (_selectedIndex != oldSelectedIndex)
+ {
+ // The selected item is changed, so we need to update the rendering.
+ _updatePending = true;
+ _renderFromSelected = true;
+ return true;
+ }
+
+ return false;
+ }
}
///
diff --git a/PSReadLine/Prediction.cs b/PSReadLine/Prediction.cs
index 53f6b843..dfa1f1cc 100644
--- a/PSReadLine/Prediction.cs
+++ b/PSReadLine/Prediction.cs
@@ -163,6 +163,42 @@ private static bool UpdateListSelection(int numericArg)
return false;
}
+ private static bool UpdateListByPaging(bool pageUp, int numericArg)
+ {
+ if (_singleton._prediction.ActiveView is PredictionListView listView && listView.HasActiveSuggestion)
+ {
+ // Ignore the visual selection.
+ _singleton._visualSelectionCommandCount = 0;
+
+ if (listView.UpdateListByPaging(pageUp, numericArg))
+ {
+ ReplaceSelection(listView.SelectedItemText);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool UpdateListByLoopingSources(bool jumpUp, int numericArg)
+ {
+ if (_singleton._prediction.ActiveView is PredictionListView listView && listView.HasActiveSuggestion)
+ {
+ // Ignore the visual selection.
+ _singleton._visualSelectionCommandCount = 0;
+
+ if (listView.UpdateListByLoopingSources(jumpUp, numericArg))
+ {
+ ReplaceSelection(listView.SelectedItemText);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
///
/// Replace current buffer with the selected list item text.
/// The replacement is done in a way that allows further selection updates for the same list view
diff --git a/PSReadLine/Render.Helper.cs b/PSReadLine/Render.Helper.cs
index 0c041e73..8fe8de07 100644
--- a/PSReadLine/Render.Helper.cs
+++ b/PSReadLine/Render.Helper.cs
@@ -3,6 +3,7 @@
--********************************************************************/
using System;
+using System.Text;
namespace Microsoft.PowerShell
{
@@ -70,6 +71,26 @@ internal static int LengthInBufferCells(string str, int start, int end)
return sum;
}
+ internal static int LengthInBufferCells(StringBuilder sb, int start, int end)
+ {
+ var sum = 0;
+ for (var i = start; i < end; i++)
+ {
+ var c = sb[i];
+ if (c == 0x1b && (i + 1) < end && sb[i + 1] == '[')
+ {
+ // Simple escape sequence skipping
+ i += 2;
+ while (i < end && sb[i] != 'm')
+ i++;
+
+ continue;
+ }
+ sum += LengthInBufferCells(c);
+ }
+ return sum;
+ }
+
internal static int LengthInBufferCells(char c)
{
if (c < 256)
diff --git a/PSReadLine/Render.cs b/PSReadLine/Render.cs
index ff5649fe..d08b6597 100644
--- a/PSReadLine/Render.cs
+++ b/PSReadLine/Render.cs
@@ -294,11 +294,7 @@ void RenderOneChar(char charToRender, bool toEmphasize)
_consoleBufferLines[currentLogicalLine].Append(VTColorUtils.AnsiReset);
}
- currentLogicalLine += 1;
- if (currentLogicalLine == _consoleBufferLines.Count)
- {
- _consoleBufferLines.Add(new StringBuilder(COMMON_WIDEST_CONSOLE_WIDTH));
- }
+ NextBufferLine(_consoleBufferLines, ref currentLogicalLine);
// Reset the color for continuation prompt so the color sequence will always be explicitly
// specified for continuation prompt in the generated render strings.
@@ -458,11 +454,7 @@ void RenderOneChar(char charToRender, bool toEmphasize)
if (_statusLinePrompt != null)
{
- currentLogicalLine += 1;
- if (currentLogicalLine > _consoleBufferLines.Count - 1)
- {
- _consoleBufferLines.Add(new StringBuilder(COMMON_WIDEST_CONSOLE_WIDTH));
- }
+ NextBufferLine(_consoleBufferLines, ref currentLogicalLine);
color = _statusIsErrorMessage ? Options._errorColor : defaultColor;
UpdateColorsIfNecessary(color);
@@ -478,6 +470,20 @@ void RenderOneChar(char charToRender, bool toEmphasize)
return currentLogicalLine + 1;
}
+ ///
+ /// Return the next logical line buffer, and create a new one if we are at the end.
+ ///
+ private static StringBuilder NextBufferLine(List consoleBufferLines, ref int current)
+ {
+ current += 1;
+ if (current == consoleBufferLines.Count)
+ {
+ consoleBufferLines.Add(new StringBuilder(COMMON_WIDEST_CONSOLE_WIDTH));
+ }
+
+ return consoleBufferLines[current];
+ }
+
///
/// Flip the color on the prompt if the error state changed.
///
@@ -1168,6 +1174,11 @@ private void MoveCursor(int newCursor)
_current = newCursor;
}
+ internal Point EndOfBufferPosition()
+ {
+ return ConvertOffsetToPoint(_buffer.Length);
+ }
+
internal Point ConvertOffsetToPoint(int offset)
{
int x = _initialX;
@@ -1670,6 +1681,12 @@ private bool PromptYesOrNo(string s)
public static void ScrollDisplayUp(ConsoleKeyInfo? key = null, object arg = null)
{
TryGetArgAsInt(arg, out var numericArg, +1);
+
+ if (UpdateListByPaging(pageUp: true, numericArg))
+ {
+ return;
+ }
+
var console = _singleton._console;
var newTop = console.WindowTop - (numericArg * console.WindowHeight);
if (newTop < 0)
@@ -1685,6 +1702,12 @@ public static void ScrollDisplayUp(ConsoleKeyInfo? key = null, object arg = null
public static void ScrollDisplayUpLine(ConsoleKeyInfo? key = null, object arg = null)
{
TryGetArgAsInt(arg, out var numericArg, +1);
+
+ if (UpdateListByLoopingSources(jumpUp: true, numericArg))
+ {
+ return;
+ }
+
var console = _singleton._console;
var newTop = console.WindowTop - numericArg;
if (newTop < 0)
@@ -1700,6 +1723,12 @@ public static void ScrollDisplayUpLine(ConsoleKeyInfo? key = null, object arg =
public static void ScrollDisplayDown(ConsoleKeyInfo? key = null, object arg = null)
{
TryGetArgAsInt(arg, out var numericArg, +1);
+
+ if (UpdateListByPaging(pageUp: false, numericArg))
+ {
+ return;
+ }
+
var console = _singleton._console;
var newTop = console.WindowTop + (numericArg * console.WindowHeight);
if (newTop > (console.BufferHeight - console.WindowHeight))
@@ -1715,6 +1744,12 @@ public static void ScrollDisplayDown(ConsoleKeyInfo? key = null, object arg = nu
public static void ScrollDisplayDownLine(ConsoleKeyInfo? key = null, object arg = null)
{
TryGetArgAsInt(arg, out var numericArg, +1);
+
+ if (UpdateListByLoopingSources(jumpUp: false, numericArg))
+ {
+ return;
+ }
+
var console = _singleton._console;
var newTop = console.WindowTop + numericArg;
if (newTop > (console.BufferHeight - console.WindowHeight))
@@ -1738,7 +1773,7 @@ public static void ScrollDisplayTop(ConsoleKeyInfo? key = null, object arg = nul
public static void ScrollDisplayToCursor(ConsoleKeyInfo? key = null, object arg = null)
{
// Ideally, we'll put the last input line at the bottom of the window
- var point = _singleton.ConvertOffsetToPoint(_singleton._buffer.Length);
+ var point = _singleton.EndOfBufferPosition();
var console = _singleton._console;
var newTop = point.Y - console.WindowHeight + 1;
diff --git a/test/CompletionTest.cs b/test/CompletionTest.cs
index eeec6a16..238006c0 100644
--- a/test/CompletionTest.cs
+++ b/test/CompletionTest.cs
@@ -971,6 +971,7 @@ public void MenuCompletions_WorkWithListView()
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
@@ -979,9 +980,13 @@ public void MenuCompletions_WorkWithListView()
Test("Get-Module", Keys(
"Get-Mo",
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "Get-Mo",
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "Get-Mo",
diff --git a/test/InlinePredictionTest.cs b/test/InlinePredictionTest.cs
index 9c0edd75..7dedb2c5 100644
--- a/test/InlinePredictionTest.cs
+++ b/test/InlinePredictionTest.cs
@@ -397,6 +397,7 @@ public void ViDefect2408()
private const uint MiniSessionId = 56;
private static readonly Guid predictorId_1 = Guid.Parse("b45b5fbe-90fa-486c-9c87-e7940fdd6273");
private static readonly Guid predictorId_2 = Guid.Parse("74a86463-033b-44a3-b386-41ee191c94be");
+ private static readonly Guid predictorId_3 = Guid.Parse("19e98622-99e0-41f7-9ee0-a8bed92cde51");
///
/// Mocked implementation of 'PredictInput'.
@@ -423,13 +424,22 @@ internal static List MockedPredictInput(Ast ast, Token[] token
new PredictiveSuggestion($"SOME NEW TEXT"),
};
- return new List
+ var result = new List
{
(PredictionResult)ctor.Invoke(
new object[] { predictorId_1, "TestPredictor", MiniSessionId, suggestions_1 }),
(PredictionResult)ctor.Invoke(
new object[] { predictorId_2, "LongNamePredictor", MiniSessionId, suggestions_2 }),
};
+
+ // Return an extra source if it's for testing the metadata line.
+ if (input == "metadata-line")
+ {
+ result.Add((PredictionResult)ctor.Invoke(
+ new object[] { predictorId_3, "Metadata", MiniSessionId, suggestions_2 }));
+ }
+
+ return result;
}
[SkippableFact]
diff --git a/test/KeyInfo-en-US-windows.json b/test/KeyInfo-en-US-windows.json
index 53243ab2..c0fcc65d 100644
--- a/test/KeyInfo-en-US-windows.json
+++ b/test/KeyInfo-en-US-windows.json
@@ -831,7 +831,7 @@
"Key": "Ctrl+PageUp",
"KeyChar": "\u0000",
"ConsoleKey": "PageUp",
- "Modifiers": "0"
+ "Modifiers": "Control"
},
{
"Key": "Ctrl+q",
diff --git a/test/ListPredictionTest.cs b/test/ListPredictionTest.cs
index 3788fca1..4931867a 100644
--- a/test/ListPredictionTest.cs
+++ b/test/ListPredictionTest.cs
@@ -8,7 +8,7 @@ public partial class ReadLine
{
// The source of truth is defined in 'Microsoft.PowerShell.PSConsoleReadLine+PredictionListView'.
// Make sure the values are in sync.
- private const int MinWindowWidth = 54;
+ private const int MinWindowWidth = 50;
private const int MinWindowHeight = 15;
private const int ListMaxWidth = 100;
private const int SourceMaxWidth = 15;
@@ -19,8 +19,8 @@ private int CheckWindowSize()
// This is a precaution check, just in case that things change.
int winWidth = _console.WindowWidth;
int winHeight = _console.WindowHeight;
- Assert.True(winWidth >= 54, $"list-view prediction requires minimum window width {MinWindowWidth}. Make sure the TestConsole's width is set properly.");
- Assert.True(winHeight >= 15, $"list-view prediction requires minimum window height {MinWindowHeight}. Make sure the TestConsole's height is set properly.");
+ Assert.True(winWidth >= MinWindowWidth, $"list-view prediction requires minimum window width {MinWindowWidth}. Make sure the TestConsole's width is set properly.");
+ Assert.True(winHeight >= MinWindowHeight, $"list-view prediction requires minimum window height {MinWindowHeight}. Make sure the TestConsole's height is set properly.");
int listWidth = winWidth > ListMaxWidth ? ListMaxWidth : winWidth;
return listWidth;
@@ -98,15 +98,22 @@ public void List_RenderSuggestion_ListUpdatesWhileTyping()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ // The font effect sequences of the dimmed color used in list view metadata line
+ // are ignored in the mock console, so only the white color will be left.
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
// Different matches as more input coming
SetHistory("echo -bar", "eca -zoo");
Test("ech", Keys(
- 'e', CheckThat(() => AssertScreenIs(3,
+ 'e', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -125,9 +132,13 @@ public void List_RenderSuggestion_ListUpdatesWhileTyping()
TokenClassification.ListPrediction, "History",
TokenClassification.None, ']'
)),
- 'c', CheckThat(() => AssertScreenIs(3,
+ 'c', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "ec",
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "ec",
@@ -146,9 +157,13 @@ public void List_RenderSuggestion_ListUpdatesWhileTyping()
TokenClassification.ListPrediction, "History",
TokenClassification.None, ']'
)),
- 'h', CheckThat(() => AssertScreenIs(2,
+ 'h', CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "ech",
NextLine,
+ TokenClassification.ListPrediction, "<-/1>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/1>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "ech",
@@ -171,15 +186,20 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
// Navigate up and down in the list
SetHistory("echo -bar", "eca -zoo");
Test("e", Keys(
- 'e', CheckThat(() => AssertScreenIs(3,
+ 'e', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -199,11 +219,17 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.None, ']'
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -223,11 +249,17 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.None, ']'
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -247,9 +279,13 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.ListPredictionSelected, ']'
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -269,11 +305,17 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.None, ']'
)),
_.UpArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -293,11 +335,17 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.ListPredictionSelected, ']'
)),
_.UpArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -317,9 +365,13 @@ public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
TokenClassification.None, ']'
)),
_.UpArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -353,15 +405,20 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
new KeyHandler("Ctrl+p", PSConsoleReadLine.HistorySearchBackward),
new KeyHandler("Ctrl+l", PSConsoleReadLine.HistorySearchForward));
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
// Navigate up and down in the list
SetHistory("echo -bar", "eca -zoo");
Test("e", Keys(
- 'e', CheckThat(() => AssertScreenIs(3,
+ 'e', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -381,11 +438,17 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.None, ']'
)),
_.Ctrl_l,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -405,11 +468,17 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.None, ']'
)),
_.Ctrl_l,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -429,9 +498,13 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.ListPredictionSelected, ']'
)),
_.Ctrl_l,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -451,11 +524,17 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.None, ']'
)),
_.Ctrl_p,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -475,11 +554,17 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.ListPredictionSelected, ']'
)),
_.Ctrl_p,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -499,9 +584,13 @@ public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
TokenClassification.None, ']'
)),
_.Ctrl_p,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -533,15 +622,20 @@ public void List_RenderSuggestion_Escape()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
// Press 'Escape' without selecting an item.
SetHistory("echo -bar", "eca -zoo");
Test("echo -bar", Keys(
- 'c', CheckThat(() => AssertScreenIs(3,
+ 'c', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'c',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -568,9 +662,13 @@ public void List_RenderSuggestion_Escape()
TokenClassification.None, new string(' ', listWidth)
)),
// Keep typing will trigger the list view again
- 'h', CheckThat(() => AssertScreenIs(2,
+ 'h', CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "ch",
NextLine,
+ TokenClassification.ListPrediction, "<-/1>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/1>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, "ch",
@@ -581,11 +679,17 @@ public void List_RenderSuggestion_Escape()
TokenClassification.None, ']'
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(2,
+ CheckThat(() => AssertScreenIs(3,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<1/1>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/1>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/1)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " e",
emphasisColors, "ch",
@@ -607,9 +711,13 @@ public void List_RenderSuggestion_Escape()
// Press 'Escape' after selecting an item.
SetHistory("echo -bar", "eca -zoo");
Test("c", Keys(
- 'c', CheckThat(() => AssertScreenIs(3,
+ 'c', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'c',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -629,11 +737,17 @@ public void List_RenderSuggestion_Escape()
TokenClassification.None, ']'
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " e",
emphasisColors, 'c',
@@ -671,14 +785,19 @@ public void List_RenderSuggestion_DigitArgument()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
SetHistory("echo -bar", "eca -zoo");
Test("c", Keys(
- 'c', CheckThat(() => AssertScreenIs(3,
+ 'c', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'c',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -698,9 +817,13 @@ public void List_RenderSuggestion_DigitArgument()
TokenClassification.None, ']'
)),
_.Alt_2,
- CheckThat(() => AssertScreenIs(4,
+ CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, 'c',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -722,11 +845,17 @@ public void List_RenderSuggestion_DigitArgument()
TokenClassification.None, "digit-argument: 2"
)),
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -746,11 +875,17 @@ public void List_RenderSuggestion_DigitArgument()
TokenClassification.ListPredictionSelected, ']'
)),
_.Alt_2,
- CheckThat(() => AssertScreenIs(4,
+ CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, "echo",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-bar",
NextLine,
+ TokenClassification.ListPrediction, "<2/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<2/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(2/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -772,9 +907,13 @@ public void List_RenderSuggestion_DigitArgument()
TokenClassification.None, "digit-argument: 2"
)),
_.UpArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'c',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " e",
emphasisColors, 'c',
@@ -806,14 +945,19 @@ public void List_RenderSuggestion_CtrlZ()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
SetHistory("echo -bar", "eca -zoo");
Test("e", Keys(
- 'e', CheckThat(() => AssertScreenIs(3,
+ 'e', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -833,11 +977,17 @@ public void List_RenderSuggestion_CtrlZ()
TokenClassification.None, ']'
)),
_.UpArrow, _.UpArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -858,9 +1008,13 @@ public void List_RenderSuggestion_CtrlZ()
)),
// No matter how many navigation operations were done, 'Ctrl+z' (undo) reverts back to the initial list view state.
_.Ctrl_z,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -881,11 +1035,17 @@ public void List_RenderSuggestion_CtrlZ()
)),
// After undo, you can continue to navigate in the list.
_.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -918,14 +1078,19 @@ public void List_RenderSuggestion_Selection()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.ListView);
SetHistory("echo -bar", "eca -zoo");
Test("eca -zoo", Keys(
- 'e', CheckThat(() => AssertScreenIs(3,
+ 'e', CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, 'e',
NextLine,
+ TokenClassification.ListPrediction, "<-/2>",
+ TokenClassification.None, new string(' ', listWidth - 17), // 17 is the length of '<-/2>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, 'e',
@@ -946,11 +1111,17 @@ public void List_RenderSuggestion_Selection()
)),
_.DownArrow,
CheckThat(() => AssertCursorLeftIs(8)),
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -972,11 +1143,17 @@ public void List_RenderSuggestion_Selection()
// Moving cursor won't trigger a new prediction.
_.LeftArrow, _.LeftArrow,
CheckThat(() => AssertCursorLeftIs(6)),
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -997,11 +1174,17 @@ public void List_RenderSuggestion_Selection()
)),
_.Ctrl_LeftArrow,
CheckThat(() => AssertCursorLeftIs(5)),
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -1022,12 +1205,18 @@ public void List_RenderSuggestion_Selection()
)),
// Text selection won't trigger a new prediction.
_.Shift_LeftArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Selection, '-',
TokenClassification.Parameter, "zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -1047,10 +1236,16 @@ public void List_RenderSuggestion_Selection()
TokenClassification.None, ']'
)),
_.Ctrl_Shift_LeftArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Selection, "eca -",
TokenClassification.Parameter, "zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -1083,6 +1278,7 @@ public void List_HistorySource_NoAcceptanceCallback()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
// Using the 'History' source will not trigger 'acceptance' callbacks.
@@ -1092,11 +1288,17 @@ public void List_HistorySource_NoAcceptanceCallback()
SetHistory("echo -bar", "eca -zoo");
Test("eca -zooa", Keys(
'e', _.DownArrow,
- CheckThat(() => AssertScreenIs(3,
+ CheckThat(() => AssertScreenIs(4,
TokenClassification.Command, "eca",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/2>",
+ TokenClassification.None, new string(' ', listWidth - 19), // 19 is the length of '<1/2>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, 'e',
@@ -1140,6 +1342,7 @@ public void List_PluginSource_Acceptance()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
// Using the 'Plugin' source will make PSReadLine get prediction from the plugin only.
@@ -1148,9 +1351,13 @@ public void List_PluginSource_Acceptance()
SetHistory("echo -bar", "eca -zoo");
Test("SOME NEW TEX SOME TEXT AFTER", Keys(
- "ec", CheckThat(() => AssertScreenIs(5,
+ "ec", CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "ec",
NextLine,
+ TokenClassification.ListPrediction, "<-/3>",
+ TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "ec",
@@ -1170,9 +1377,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1183,10 +1390,16 @@ public void List_PluginSource_Acceptance()
CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.DownArrow,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " TEXT BEFORE ec",
NextLine,
+ TokenClassification.ListPrediction, "<1/3>",
+ TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "TestPredictor(1/2) ",
+ dimmedColors, "LongNamePredic�(1)>",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
emphasisColors, "ec",
@@ -1206,9 +1419,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1217,9 +1430,15 @@ public void List_PluginSource_Acceptance()
// `OnSuggestionDisplayed` should not be fired when navigating the list.
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Shift_Home,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Selection, "SOME TEXT BEFORE ec",
NextLine,
+ TokenClassification.ListPrediction, "<1/3>",
+ TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "TestPredictor(1/2) ",
+ dimmedColors, "LongNamePredic�(1)>",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
emphasisColors, "ec",
@@ -1239,9 +1458,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1250,9 +1469,13 @@ public void List_PluginSource_Acceptance()
// `OnSuggestionDisplayed` should not be fired when selecting the input.
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
"j",
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "j",
NextLine,
+ TokenClassification.ListPrediction, "<-/3>",
+ TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "j",
@@ -1272,9 +1495,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1290,10 +1513,16 @@ public void List_PluginSource_Acceptance()
_.DownArrow,
_.DownArrow,
_.DownArrow,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEXT",
NextLine,
+ TokenClassification.ListPrediction, "<3/3>",
+ TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus ''.
+ dimmedColors, "',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "j",
@@ -1313,9 +1542,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
- TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.ListPredictionSelected, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.ListPredictionSelected, ']',
// List view is done, no more list item following.
NextLine,
@@ -1324,10 +1553,14 @@ public void List_PluginSource_Acceptance()
// `OnSuggestionDisplayed` should not be fired when navigating the input.
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Backspace,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEX",
NextLine,
+ TokenClassification.ListPrediction, "<-/3>",
+ TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "SOME NEW TEX",
@@ -1349,9 +1582,9 @@ public void List_PluginSource_Acceptance()
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1366,10 +1599,16 @@ public void List_PluginSource_Acceptance()
CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.UpArrow,
_.UpArrow,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEX SOME TEXT AFTER",
NextLine,
+ TokenClassification.ListPrediction, "<2/3>",
+ TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "TestPredictor(2/2) ",
+ dimmedColors, "LongNamePredic�(1)>",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "SOME NEW TEX",
@@ -1391,9 +1630,9 @@ public void List_PluginSource_Acceptance()
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1424,6 +1663,7 @@ public void List_HistoryAndPluginSource_Acceptance()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
// Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from both history and plugin.
@@ -1432,9 +1672,13 @@ public void List_HistoryAndPluginSource_Acceptance()
SetHistory("echo -bar", "java", "eca -zoo");
Test("SOME NEW TEX SOME TEXT AFTER", Keys(
- "ec", CheckThat(() => AssertScreenIs(7,
+ "ec", CheckThat(() => AssertScreenIs(8,
TokenClassification.Command, "ec",
NextLine,
+ TokenClassification.ListPrediction, "<-/5>",
+ TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/5>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "ec",
@@ -1472,9 +1716,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1485,9 +1729,15 @@ public void List_HistoryAndPluginSource_Acceptance()
CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.DownArrow, _.Shift_Home,
- CheckThat(() => AssertScreenIs(7,
+ CheckThat(() => AssertScreenIs(8,
TokenClassification.Selection, "eca -zoo",
NextLine,
+ TokenClassification.ListPrediction, "<1/5>",
+ TokenClassification.None, new string(' ', listWidth - 38), // 38 is the length of '<1/5>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/2) ",
+ dimmedColors, "TestPredictor(2) �>",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
emphasisColors, "ec",
@@ -1525,9 +1775,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1535,9 +1785,13 @@ public void List_HistoryAndPluginSource_Acceptance()
)),
// `OnSuggestionDisplayed` should not be fired when navigating the list.
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
- 'j', CheckThat(() => AssertScreenIs(6,
+ 'j', CheckThat(() => AssertScreenIs(7,
TokenClassification.Command, "j",
NextLine,
+ TokenClassification.ListPrediction, "<-/4>",
+ TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "j",
@@ -1566,9 +1820,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1583,10 +1837,16 @@ public void List_HistoryAndPluginSource_Acceptance()
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.UpArrow,
- CheckThat(() => AssertScreenIs(6,
+ CheckThat(() => AssertScreenIs(7,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEXT",
NextLine,
+ TokenClassification.ListPrediction, "<4/4>",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '<4/4>' plus '<� TestPredictor(2) LongNamePredic�(1/1)>'.
+ dimmedColors, "<� TestPredictor(2) ",
+ TokenClassification.ListPrediction, "LongNamePredic�(1/1)",
+ dimmedColors, '>',
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "j",
@@ -1615,9 +1875,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
- TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.ListPredictionSelected, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.ListPredictionSelected, ']',
// List view is done, no more list item following.
NextLine,
@@ -1626,10 +1886,14 @@ public void List_HistoryAndPluginSource_Acceptance()
// `OnSuggestionDisplayed` should not be fired when navigating the list.
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Backspace,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEX",
NextLine,
+ TokenClassification.ListPrediction, "<-/3>",
+ TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "SOME NEW TEX",
@@ -1651,9 +1915,9 @@ public void List_HistoryAndPluginSource_Acceptance()
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1668,10 +1932,16 @@ public void List_HistoryAndPluginSource_Acceptance()
CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.UpArrow,
_.UpArrow,
- CheckThat(() => AssertScreenIs(5,
+ CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "SOME",
TokenClassification.None, " NEW TEX SOME TEXT AFTER",
NextLine,
+ TokenClassification.ListPrediction, "<2/3>",
+ TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "TestPredictor(2/2) ",
+ dimmedColors, "LongNamePredic�(1)>",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
emphasisColors, "SOME NEW TEX",
@@ -1693,9 +1963,9 @@ public void List_HistoryAndPluginSource_Acceptance()
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1726,6 +1996,7 @@ public void List_HistoryAndPluginSource_Deduplication()
{
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
// Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from both history and plugin.
@@ -1736,9 +2007,13 @@ public void List_HistoryAndPluginSource_Deduplication()
// which is the default comparison. So, that result will be filtered out due to the de-duplication logic.
SetHistory("some TEXT BEFORE de-dup", "de-dup -of");
Test("de-dup", Keys(
- "de-dup", CheckThat(() => AssertScreenIs(6,
+ "de-dup", CheckThat(() => AssertScreenIs(7,
TokenClassification.Command, "de-dup",
NextLine,
+ TokenClassification.ListPrediction, "<-/4>",
+ TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "de-dup",
@@ -1767,9 +2042,9 @@ public void List_HistoryAndPluginSource_Deduplication()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1796,9 +2071,13 @@ public void List_HistoryAndPluginSource_Deduplication()
// so, that result will be filtered out due to the de-duplication logic.
SetHistory("de-dup SOME TEXT AFTER", "some TEXT BEFORE de-dup");
Test("de-dup", Keys(
- "de-dup", CheckThat(() => AssertScreenIs(6,
+ "de-dup", CheckThat(() => AssertScreenIs(7,
TokenClassification.Command, "de-dup",
NextLine,
+ TokenClassification.ListPrediction, "<-/4>",
+ TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus ''.
+ dimmedColors, "",
+ NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
emphasisColors, "de-dup",
@@ -1826,9 +2105,9 @@ public void List_HistoryAndPluginSource_Deduplication()
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME NEW TEXT",
- TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePred...]'
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic�]'
TokenClassification.None, '[',
- TokenClassification.ListPrediction, "LongNamePred...",
+ TokenClassification.ListPrediction, "LongNamePredic�",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
diff --git a/test/ListScrollableViewTest.cs b/test/ListScrollableViewTest.cs
new file mode 100644
index 00000000..11452bb2
--- /dev/null
+++ b/test/ListScrollableViewTest.cs
@@ -0,0 +1,908 @@
+using System;
+using Microsoft.PowerShell;
+using Xunit;
+
+namespace Test
+{
+ public partial class ReadLine
+ {
+ [SkippableFact]
+ public void List_MetaLine_And_Paging_Navigation()
+ {
+ int listWidth = 100;
+ TestSetup(new TestConsole(keyboardLayout: _, width: listWidth, height: 15), KeyMode.Cmd);
+
+ // The font effect sequences of the dimmed color used in list view metadata line
+ // are ignored in the mock console, so only the white color will be left.
+ var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
+ var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
+
+ // Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from both history and plugin.
+ using var disp = SetPrediction(PredictionSource.HistoryAndPlugin, PredictionViewStyle.ListView);
+ _mockedMethods.ClearPredictionFields();
+
+ SetHistory("metadata-line -zoo");
+ Test("SOME TEXT BEFORE metadata-line", Keys(
+ "metadata-line", CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "metadata-line",
+ NextLine,
+ TokenClassification.ListPrediction, "<-/5>",
+ TokenClassification.None, new string(' ', listWidth - 55), // 55 is the length of '<-/5>' plus ''.
+ dimmedColors, "",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.DownArrow, _.PageDown,
+ CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.ListPrediction, "<5/5>",
+ TokenClassification.None, new string(' ', listWidth - 58), // 58 is the length of '<5/5>' plus '<… TestPredictor(2) LongNamePredic…(1) Metadata(1/1)>'.
+ dimmedColors, "<… TestPredictor(2) LongNamePredic…(1) ",
+ TokenClassification.ListPrediction, "Metadata(1/1)",
+ dimmedColors, '>',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.PageUp, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, ' ',
+ TokenClassification.Parameter, "-zoo",
+ NextLine,
+ TokenClassification.ListPrediction, "<1/5>",
+ TokenClassification.None, new string(' ', listWidth - 57), // 57 is the length of '<1/5>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/1) ",
+ dimmedColors, "TestPredictor(2) LongNamePredic…(1) …>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, " -zoo",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageDown, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " TEXT BEFORE metadata-line",
+ NextLine,
+ TokenClassification.ListPrediction, "<2/5>",
+ TokenClassification.None, new string(' ', listWidth - 57), // 57 is the length of '<1/5>' plus ''.
+ dimmedColors, "",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageDown, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.ListPrediction, "<4/5>",
+ TokenClassification.None, new string(' ', listWidth - 58), // 58 is the length of '<4/5>' plus '<… TestPredictor(2) LongNamePredic…(1/1) Metadata(1)>'.
+ dimmedColors, "<… TestPredictor(2) ",
+ TokenClassification.ListPrediction, "LongNamePredic…(1/1) ",
+ dimmedColors, "Metadata(1)>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageDown, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.ListPrediction, "<5/5>",
+ TokenClassification.None, new string(' ', listWidth - 58), // 58 is the length of '<5/5>' plus '<… TestPredictor(2) LongNamePredic…(1) Metadata(1/1)>'.
+ dimmedColors, "<… TestPredictor(2) LongNamePredic…(1) ",
+ TokenClassification.ListPrediction, "Metadata(1/1)",
+ dimmedColors, '>',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageDown, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, ' ',
+ TokenClassification.Parameter, "-zoo",
+ NextLine,
+ TokenClassification.ListPrediction, "<1/5>",
+ TokenClassification.None, new string(' ', listWidth - 57), // 57 is the length of '<1/5>' plus ''.
+ dimmedColors, '<',
+ TokenClassification.ListPrediction, "History(1/1) ",
+ dimmedColors, "TestPredictor(2) LongNamePredic…(1) …>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, " -zoo",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageUp, CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.ListPrediction, "<5/5>",
+ TokenClassification.None, new string(' ', listWidth - 58), // 58 is the length of '<5/5>' plus '<… TestPredictor(2) LongNamePredic…(1) Metadata(1/1)>'.
+ dimmedColors, "<… TestPredictor(2) LongNamePredic…(1) ",
+ TokenClassification.ListPrediction, "Metadata(1/1)",
+ dimmedColors, '>',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageUp, _.Ctrl_PageUp,
+ CheckThat(() => AssertScreenIs(8,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " TEXT BEFORE metadata-line",
+ NextLine,
+ TokenClassification.ListPrediction, "<2/5>",
+ TokenClassification.None, new string(' ', listWidth - 57), // 57 is the length of '<1/5>' plus ''.
+ dimmedColors, "",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+
+ // Once accepted, the list should be cleared.
+ _.Enter, CheckThat(() => AssertScreenIs(2,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " TEXT BEFORE metadata-line",
+ NextLine,
+ NextLine))
+ ));
+ }
+
+ [SkippableFact]
+ public void ListView_AdapteTo_ConsoleSize()
+ {
+ // Console size is very small (h: 6, w: 50), and thus the list view will adjust to use 3-line height,
+ // and the metadata line will be reduced to only show the (index/total) info.
+ int listWidth = 50;
+ TestSetup(new TestConsole(keyboardLayout: _, width: listWidth, height: 6), KeyMode.Cmd);
+ var emphasisColors = Tuple.Create(PSConsoleReadLineOptions.DefaultEmphasisColor, _console.BackgroundColor);
+
+ // Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from both history and plugin.
+ using var disp = SetPrediction(PredictionSource.HistoryAndPlugin, PredictionViewStyle.ListView);
+ _mockedMethods.ClearPredictionFields();
+
+ SetHistory("metadata-line -zoo");
+ Test("metadata-line -zoo", Keys(
+ "metadata-line", CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "metadata-line",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<-/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.UpArrow, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<5/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.UpArrow, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<4/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.Ctrl_PageUp, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " TEXT BEFORE metadata-line",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<2/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.PageUp, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, ' ',
+ TokenClassification.Parameter, "-zoo",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<1/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, " -zoo",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.PageDown, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<3/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " -zoo",
+ TokenClassification.None, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, " SOME TEXT AFTER",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.PageDown, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "SOME",
+ TokenClassification.None, " NEW TEXT",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<5/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, " SOME NEW TEXT",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.ListPredictionSelected, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.DownArrow, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "metadata-line",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<-/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "LongNamePredic…",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME NEW TEXT",
+ TokenClassification.None, new string(' ', listWidth - 25), // 25 is the length of '> SOME NEW TEXT' plus '[Metadata]'
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "Metadata",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ _.DownArrow, CheckThat(() => AssertScreenIs(6,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, ' ',
+ TokenClassification.Parameter, "-zoo",
+ NextLine,
+ TokenClassification.None, " ",
+ TokenClassification.ListPrediction, "<1/5>",
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.ListPredictionSelected, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.ListPredictionSelected, " -zoo",
+ TokenClassification.ListPredictionSelected, new string(' ', listWidth - 29), // 29 is the length of '> metadata-line -zoo' plus '[History]'.
+ TokenClassification.ListPredictionSelected, '[',
+ TokenClassification.ListPrediction, "History",
+ TokenClassification.ListPredictionSelected, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, " SOME TEXT BEFORE ",
+ emphasisColors, "metadata-line",
+ TokenClassification.None, new string(' ', listWidth - 47), // 47 is the length of '> SOME TEXT BEFORE metadata-line' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ NextLine,
+ TokenClassification.ListPrediction, '>',
+ TokenClassification.None, ' ',
+ emphasisColors, "metadata-line",
+ TokenClassification.None, " SOME TEXT AFTER",
+ TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '> metadata-line SOME TEXT AFTER' plus '[TestPredictor]'.
+ TokenClassification.None, '[',
+ TokenClassification.ListPrediction, "TestPredictor",
+ TokenClassification.None, ']',
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+
+ // Once accepted, the list should be cleared.
+ _.Enter, CheckThat(() => AssertScreenIs(2,
+ TokenClassification.Command, "metadata-line",
+ TokenClassification.None, ' ',
+ TokenClassification.Parameter, "-zoo",
+ NextLine,
+ NextLine))
+ ));
+ }
+
+ [SkippableFact]
+ public void ListView_TermSize_Warning()
+ {
+ // Console size is very small (h: 6, w: 50), and thus the list view will adjust to use 3-line height,
+ // and the metadata line will be reduced to only show the (index/total) info.
+ int listWidth = 40;
+ TestSetup(new TestConsole(keyboardLayout: _, width: listWidth, height: 4), KeyMode.Cmd);
+ using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
+
+ Test("git", Keys(
+ _.F2, // Switch to the list view, then test the warning message.
+ 'g', CheckThat(() => AssertScreenIs(4,
+ TokenClassification.Command, "g",
+ NextLine,
+ TokenClassification.ListPrediction, "! terminal size too small to show the li",
+ NextLine,
+ TokenClassification.ListPrediction, "st view",
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+ 'i', CheckThat(() => AssertScreenIs(4,
+ TokenClassification.Command, "gi",
+ NextLine,
+ TokenClassification.ListPrediction, "! terminal size too small to show the li",
+ NextLine,
+ TokenClassification.ListPrediction, "st view",
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+
+ // Escape should clear the warning as well.
+ _.Escape, CheckThat(() => AssertScreenIs(3,
+ NextLine,
+ NextLine,
+ NextLine
+ )),
+ "git", CheckThat(() => AssertScreenIs(4,
+ TokenClassification.Command, "git",
+ NextLine,
+ TokenClassification.ListPrediction, "! terminal size too small to show the li",
+ NextLine,
+ TokenClassification.ListPrediction, "st view",
+ // List view is done, no more list item following.
+ NextLine,
+ NextLine
+ )),
+
+ // Once accepted, the list should be cleared.
+ _.Enter, CheckThat(() => AssertScreenIs(3,
+ TokenClassification.Command, "git",
+ NextLine,
+ NextLine,
+ NextLine))
+ ));
+ }
+ }
+}
diff --git a/test/MockConsole.cs b/test/MockConsole.cs
index dc218e77..f827d469 100644
--- a/test/MockConsole.cs
+++ b/test/MockConsole.cs
@@ -235,6 +235,13 @@ public virtual void Write(string s)
var escapeSequence = s.Substring(i + 2, len);
foreach (var subsequence in escapeSequence.Split(';'))
{
+ if (subsequence is "2" or "3")
+ {
+ // Ignore the font effect sequence: 2 - dimmed color; 3 - italics
+ // They are used in the metadata line of the list view.
+ continue;
+ }
+
EscapeSequenceActions[subsequence](this);
}
i = endSequence;
@@ -444,6 +451,13 @@ public override void Write(string s)
var escapeSequence = s.Substring(i + 2, len);
foreach (var subsequence in escapeSequence.Split(';'))
{
+ if (subsequence is "2" or "3")
+ {
+ // Ignore the font effect sequence: 2 - dimmed color; 3 - italics
+ // They are used in the metadata line of the list view.
+ continue;
+ }
+
EscapeSequenceActions[subsequence](this);
}
i = endSequence;