mirror of
https://github.com/PowerShell/PSReadLine
synced 2026-06-08 12:12:13 +00:00
Improve the list view to be scrollable and auto-adjust the list view height (#3583)
This commit is contained in:
@@ -664,7 +664,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
// 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 @@ namespace Microsoft.PowerShell
|
||||
|
||||
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");
|
||||
|
||||
|
||||
+11
@@ -2070,6 +2070,17 @@ namespace Microsoft.PowerShell.PSReadLine {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to "! terminal size too small to show the list view".
|
||||
/// </summary>
|
||||
internal static string WindowSizeTooSmallWarning
|
||||
{
|
||||
get
|
||||
{
|
||||
return ResourceManager.GetString("WindowSizeTooSmallWarning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy the text from the current kill ring position to the input.
|
||||
/// </summary>
|
||||
|
||||
@@ -840,6 +840,9 @@ Or not saving history with:
|
||||
<data name="WindowSizeTooSmallForListView" xml:space="preserve">
|
||||
<value>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}'.</value>
|
||||
</data>
|
||||
<data name="WindowSizeTooSmallWarning" xml:space="preserve">
|
||||
<value>! terminal size too small to show the list view</value>
|
||||
</data>
|
||||
<data name="NeedsUpdateHelp" xml:space="preserve">
|
||||
<value>No help content available. Please use Update-Help to download the latest help content.</value>
|
||||
</data>
|
||||
|
||||
@@ -9,19 +9,52 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
public partial class PSConsoleReadLine
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a prediction source.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This type represents an individual suggestion entry.
|
||||
/// </summary>
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
PredictorSession = predictorSession;
|
||||
SuggestionText = suggestion;
|
||||
InputMatchIndex = matchIndex;
|
||||
|
||||
_listItemTextRegular = _listItemTextSelected = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,8 +92,19 @@ namespace Microsoft.PowerShell
|
||||
/// <param name="selectionHighlighting">The highlighting sequences for a selected list item.</param>
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
// 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 @@ namespace Microsoft.PowerShell
|
||||
.Append(SuggestionText, 0, input.Length)
|
||||
.EndColorSection(selectionHighlighting)
|
||||
.Append(SuggestionText, input.Length, length - input.Length)
|
||||
.Append(ellipsis);
|
||||
.Append(Ellipsis);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -149,7 +195,7 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
.Append(SuggestionText, InputMatchIndex, input.Length)
|
||||
.EndColorSection(selectionHighlighting)
|
||||
.Append(SuggestionText, rightStartindex, rightStrLen)
|
||||
.Append(ellipsis);
|
||||
.Append(Ellipsis);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -201,7 +247,7 @@ namespace Microsoft.PowerShell
|
||||
{
|
||||
// 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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+487
-49
@@ -8,6 +8,7 @@ using System.Text;
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
/// </summary>
|
||||
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<SuggestionEntry> _listItems;
|
||||
private int _listItemWidth;
|
||||
private int _listItemHeight;
|
||||
// Information about the sources of those items.
|
||||
private List<SourceInfo> _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<int> _cacheList1;
|
||||
@@ -273,6 +303,36 @@ namespace Microsoft.PowerShell
|
||||
internal override bool HasPendingUpdate => _updatePending;
|
||||
internal override bool HasActiveSuggestion => _listItems != null;
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the max width and height of the list view based on the current terminal size.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the height becomes too small for the current rendering.
|
||||
/// </summary>
|
||||
private bool HeightIsTooSmall()
|
||||
{
|
||||
int physicalLineCountForBuffer = _singleton.EndOfBufferPosition().Y - _singleton._initialY + 1;
|
||||
return _singleton._console.BufferHeight < physicalLineCountForBuffer + _maxViewHeight + 1 /* one metadata line */;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get suggestion results.
|
||||
/// </summary>
|
||||
internal override void GetSuggestion(string userInput)
|
||||
{
|
||||
if (_singleton._initialY < 0)
|
||||
@@ -295,14 +355,16 @@ namespace Microsoft.PowerShell
|
||||
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
_listItems?.Clear();
|
||||
_sources?.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -323,7 +386,11 @@ namespace Microsoft.PowerShell
|
||||
|
||||
if (UseHistory)
|
||||
{
|
||||
_listItems = GetHistorySuggestions(userInput, ListMaxCount);
|
||||
_listItems = GetHistorySuggestions(userInput, HistoryMaxCount);
|
||||
if (_listItems?.Count > 0)
|
||||
{
|
||||
_sources = new List<SourceInfo>() { new SourceInfo(SuggestionEntry.HistorySource, _listItems.Count - 1, -1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -348,6 +415,7 @@ namespace Microsoft.PowerShell
|
||||
try
|
||||
{
|
||||
_listItems ??= new List<SuggestionEntry>();
|
||||
_sources ??= new List<SourceInfo>();
|
||||
_cacheList1 ??= new List<int>(); // This list holds the total number of suggestions from each of the predictors.
|
||||
_cacheList2 ??= new List<int>(); // This list holds the final number of suggestions that will be rendered for each of the predictors.
|
||||
|
||||
@@ -412,13 +480,18 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
|
||||
// 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 @@ namespace Microsoft.PowerShell
|
||||
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the rendering text for the list view.
|
||||
/// </summary>
|
||||
internal override void RenderSuggestion(List<StringBuilder> 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 @@ namespace Microsoft.PowerShell
|
||||
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));
|
||||
|
||||
if (itemSelected)
|
||||
{
|
||||
currentLineBuffer.Append(VTColorUtils.AnsiReset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the rendering text for the warning message.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the height of the list when warning was displayed.
|
||||
/// </summary>
|
||||
private int GetPesudoListHeightForWarningRendering()
|
||||
{
|
||||
int bufferWidth = _singleton._console.BufferWidth;
|
||||
int lengthInCells = LengthInBufferCells(PSReadLineResources.WindowSizeTooSmallWarning);
|
||||
int pesudoListHeight = lengthInCells / bufferWidth;
|
||||
|
||||
if (lengthInCells % bufferWidth == 0)
|
||||
{
|
||||
pesudoListHeight--;
|
||||
}
|
||||
|
||||
return pesudoListHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the rendering text for the metadata line.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper function to avoid appending extra color VT sequences unnecessarily.
|
||||
/// </summary>
|
||||
static StringBuilder AppendColor(StringBuilder buffer, string colorToUse, ref string activeColor, out int nextCharPos)
|
||||
{
|
||||
if (activeColor is null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the feedback about a suggestion was accepted.
|
||||
/// </summary>
|
||||
internal override void OnSuggestionAccepted()
|
||||
{
|
||||
if (!UsePlugin)
|
||||
@@ -569,24 +886,40 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the list view.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all the list view states.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -595,8 +928,12 @@ namespace Microsoft.PowerShell
|
||||
/// <param name="move"></param>
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
_selectedIndex = virtualItemIndex % virtualItemCount + virtualItemCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Page up/down within the list view.
|
||||
/// Update the index of the selected item based on <paramref name="pageUp"/> and <paramref name="num"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loop up/down through the sources rendered in the list view.
|
||||
/// Update the index of the selected item based on <paramref name="jumpUp"/> and <paramref name="num"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -163,6 +163,42 @@ namespace Microsoft.PowerShell
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
||||
@@ -3,6 +3,7 @@ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
--********************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.PowerShell
|
||||
{
|
||||
@@ -70,6 +71,26 @@ namespace Microsoft.PowerShell
|
||||
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)
|
||||
|
||||
+46
-11
@@ -294,11 +294,7 @@ namespace Microsoft.PowerShell
|
||||
_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 @@ namespace Microsoft.PowerShell
|
||||
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
return currentLogicalLine + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the next logical line buffer, and create a new one if we are at the end.
|
||||
/// </summary>
|
||||
private static StringBuilder NextBufferLine(List<StringBuilder> consoleBufferLines, ref int current)
|
||||
{
|
||||
current += 1;
|
||||
if (current == consoleBufferLines.Count)
|
||||
{
|
||||
consoleBufferLines.Add(new StringBuilder(COMMON_WIDEST_CONSOLE_WIDTH));
|
||||
}
|
||||
|
||||
return consoleBufferLines[current];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flip the color on the prompt if the error state changed.
|
||||
/// </summary>
|
||||
@@ -1168,6 +1174,11 @@ namespace Microsoft.PowerShell
|
||||
_current = newCursor;
|
||||
}
|
||||
|
||||
internal Point EndOfBufferPosition()
|
||||
{
|
||||
return ConvertOffsetToPoint(_buffer.Length);
|
||||
}
|
||||
|
||||
internal Point ConvertOffsetToPoint(int offset)
|
||||
{
|
||||
int x = _initialX;
|
||||
@@ -1670,6 +1681,12 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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 @@ namespace Microsoft.PowerShell
|
||||
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;
|
||||
|
||||
@@ -971,6 +971,7 @@ namespace Test
|
||||
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 @@ namespace Test
|
||||
|
||||
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 '<History(2)>'.
|
||||
dimmedColors, "<History(2)>",
|
||||
NextLine,
|
||||
TokenClassification.ListPrediction, '>',
|
||||
TokenClassification.None, ' ',
|
||||
emphasisColors, "Get-Mo",
|
||||
|
||||
@@ -397,6 +397,7 @@ namespace Test
|
||||
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");
|
||||
|
||||
/// <summary>
|
||||
/// Mocked implementation of 'PredictInput'.
|
||||
@@ -423,13 +424,22 @@ namespace Test
|
||||
new PredictiveSuggestion($"SOME NEW TEXT"),
|
||||
};
|
||||
|
||||
return new List<PredictionResult>
|
||||
var result = new List<PredictionResult>
|
||||
{
|
||||
(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]
|
||||
|
||||
@@ -831,7 +831,7 @@
|
||||
"Key": "Ctrl+PageUp",
|
||||
"KeyChar": "\u0000",
|
||||
"ConsoleKey": "PageUp",
|
||||
"Modifiers": "0"
|
||||
"Modifiers": "Control"
|
||||
},
|
||||
{
|
||||
"Key": "Ctrl+q",
|
||||
|
||||
+365
-86
File diff suppressed because it is too large
Load Diff
@@ -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 '<History(1) TestPredictor(2) LongNamePredic…(1) …>'.
|
||||
dimmedColors, "<History(1) TestPredictor(2) LongNamePredic…(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.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 '<History(1/1) TestPredictor(2) LongNamePredic…(1) …>'.
|
||||
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 '<History(1) TestPredictor(1/2) LongNamePredic…(1) …>'.
|
||||
dimmedColors, "<History(1) ",
|
||||
TokenClassification.ListPrediction, "TestPredictor(1/2) ",
|
||||
dimmedColors, "LongNamePredic…(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.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 '<History(1/1) TestPredictor(2) LongNamePredic…(1) …>'.
|
||||
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 '<History(1) TestPredictor(1/2) LongNamePredic…(1) …>'.
|
||||
dimmedColors, "<History(1) ",
|
||||
TokenClassification.ListPrediction, "TestPredictor(1/2) ",
|
||||
dimmedColors, "LongNamePredic…(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.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))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,13 @@ namespace Test
|
||||
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 @@ namespace Test
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user