Add screen reader support to PSReadLine (#4854)

This commit is contained in:
Dongbo Wang
2025-08-28 11:22:05 -07:00
committed by GitHub
23 changed files with 678 additions and 310 deletions
+73 -4
View File
@@ -2,6 +2,7 @@
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Microsoft.PowerShell.Internal
@@ -10,14 +11,82 @@ namespace Microsoft.PowerShell.Internal
{
internal static bool IsScreenReaderActive()
{
bool returnValue = false;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
PlatformWindows.SystemParametersInfo(PlatformWindows.SPI_GETSCREENREADER, 0, ref returnValue, 0);
return IsAnyWindowsScreenReaderEnabled();
}
return returnValue;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return IsVoiceOverEnabled();
}
// TODO: Support Linux per https://code.visualstudio.com/docs/configure/accessibility/accessibility
return false;
}
private static bool IsAnyWindowsScreenReaderEnabled()
{
// The supposedly official way to check for a screen reader on
// Windows is SystemParametersInfo(SPI_GETSCREENREADER, ...) but it
// doesn't detect the in-box Windows Narrator and is otherwise known
// to be problematic.
//
// Unfortunately, the alternative method used by Electron and
// Chromium, where the relevant screen reader libraries (modules)
// are checked for does not work in the context of PowerShell
// because it relies on those applications injecting themselves into
// the app. Which they do not because PowerShell is not a windowed
// app, so we're stuck using the known-to-be-buggy way.
bool spiScreenReader = false;
PlatformWindows.SystemParametersInfo(PlatformWindows.SPI_GETSCREENREADER, 0, ref spiScreenReader, 0);
if (spiScreenReader)
{
return true;
}
// At least we can correctly check for Windows Narrator using the
// NarratorRunning mutex. Windows Narrator is mostly not broken with
// PSReadLine, not in the way that NVDA and VoiceOver are.
if (PlatformWindows.IsMutexPresent("NarratorRunning"))
{
return true;
}
return false;
}
private static bool IsVoiceOverEnabled()
{
try
{
// Use the 'defaults' command to check if VoiceOver is enabled
// This checks the com.apple.universalaccess preference for voiceOverOnOffKey
ProcessStartInfo startInfo = new()
{
FileName = "defaults",
Arguments = "read com.apple.universalaccess voiceOverOnOffKey",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using Process process = Process.Start(startInfo);
process.WaitForExit(250);
if (process.HasExited && process.ExitCode == 0)
{
string output = process.StandardOutput.ReadToEnd().Trim();
// VoiceOver is enabled if the value is 1
return output == "1";
}
}
catch
{
// If we can't determine the status, assume VoiceOver is not enabled
}
return false;
}
}
}
+2 -2
View File
@@ -86,7 +86,7 @@ namespace Microsoft.PowerShell
_singleton._current = _singleton._buffer.Length;
using var _ = _singleton._prediction.DisableScoped();
_singleton.ForceRender();
_singleton.Render(force: true);
_singleton._console.Write("\x1b[91m^C\x1b[0m");
@@ -335,7 +335,7 @@ namespace Microsoft.PowerShell
if (renderNeeded)
{
ForceRender();
Render(force: true);
}
// Only run validation if we haven't before. If we have and status line shows an error,
+12 -5
View File
@@ -15,6 +15,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.PowerShell.PSReadLine;
using Microsoft.PowerShell.Internal;
using AllowNull = System.Management.Automation.AllowNullAttribute;
namespace Microsoft.PowerShell
@@ -150,11 +151,6 @@ namespace Microsoft.PowerShell
public const HistorySaveStyle DefaultHistorySaveStyle = HistorySaveStyle.SaveIncrementally;
/// <summary>
/// The predictive suggestion feature is disabled by default.
/// </summary>
public const PredictionSource DefaultPredictionSource = PredictionSource.None;
public const PredictionViewStyle DefaultPredictionViewStyle = PredictionViewStyle.InlineView;
/// <summary>
@@ -201,6 +197,7 @@ namespace Microsoft.PowerShell
{
ResetColors();
EditMode = DefaultEditMode;
ScreenReaderModeEnabled = Accessibility.IsScreenReaderActive();
ContinuationPrompt = DefaultContinuationPrompt;
ContinuationPromptColor = Console.ForegroundColor;
ExtraPromptLineCount = DefaultExtraPromptLineCount;
@@ -533,6 +530,8 @@ namespace Microsoft.PowerShell
public bool TerminateOrphanedConsoleApps { get; set; }
public bool ScreenReaderModeEnabled { get; set; }
internal string _defaultTokenColor;
internal string _commentColor;
internal string _keywordColor;
@@ -847,6 +846,14 @@ namespace Microsoft.PowerShell
}
internal SwitchParameter? _terminateOrphanedConsoleApps;
[Parameter]
public SwitchParameter EnableScreenReaderMode
{
get => _enableScreenReaderMode.GetValueOrDefault();
set => _enableScreenReaderMode = value;
}
internal SwitchParameter? _enableScreenReaderMode;
[ExcludeFromCodeCoverage]
protected override void EndProcessing()
{
+4
View File
@@ -185,6 +185,10 @@ namespace Microsoft.PowerShell
nameof(Options.TerminateOrphanedConsoleApps)));
}
}
if (options._enableScreenReaderMode.HasValue)
{
Options.ScreenReaderModeEnabled = options.EnableScreenReaderMode;
}
}
private void SetKeyHandlerInternal(string[] keys, Action<ConsoleKeyInfo?, object> handler, string briefDescription, string longDescription, ScriptBlock scriptBlock)
+15
View File
@@ -79,6 +79,21 @@ static class PlatformWindows
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr GetStdHandle(uint handleId);
internal const int ERROR_ALREADY_EXISTS = 0xB7;
internal static bool IsMutexPresent(string name)
{
try
{
using var mutex = new System.Threading.Mutex(false, name);
return Marshal.GetLastWin32Error() == ERROR_ALREADY_EXISTS;
}
catch
{
return false;
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool SetConsoleCtrlHandler(BreakHandler handlerRoutine, bool add);
+203 -34
View File
@@ -218,36 +218,197 @@ namespace Microsoft.PowerShell
Render();
}
private void Render()
private void Render(bool force = false)
{
// If there are a bunch of keys queued up, skip rendering if we've rendered very recently.
long elapsedMs = _lastRenderTime.ElapsedMilliseconds;
if (_queuedKeys.Count > 10 && elapsedMs < 50)
if (!force)
{
// We won't render, but most likely the tokens will be different, so make
// sure we don't use old tokens, also allow garbage to get collected.
_tokens = null;
_ast = null;
_parseErrors = null;
_waitingToRender = true;
return;
// If there are a bunch of keys queued up, skip rendering if we've rendered very recently.
long elapsedMs = _lastRenderTime.ElapsedMilliseconds;
if (_queuedKeys.Count > 10 && elapsedMs < 50)
{
// We won't render, but most likely the tokens will be different, so make
// sure we don't use old tokens, also allow garbage to get collected.
_tokens = null;
_ast = null;
_parseErrors = null;
_waitingToRender = true;
return;
}
// If we've rendered very recently, skip the terminal window resizing check as it's unlikely
// to happen in such a short time interval.
// We try to avoid unnecessary resizing check because it requires getting the cursor position
// which would force a network round trip in an environment where front-end xtermjs talking to
// a server-side PTY via websocket. Without querying for cursor position, content written on
// the server side could be buffered, which is much more performant.
// See the following 2 GitHub issues for more context:
// - https://github.com/PowerShell/PSReadLine/issues/3879#issuecomment-2573996070
// - https://github.com/PowerShell/PowerShell/issues/24696
if (elapsedMs < 50)
{
_handlePotentialResizing = false;
}
}
// If we've rendered very recently, skip the terminal window resizing check as it's unlikely
// to happen in such a short time interval.
// We try to avoid unnecessary resizing check because it requires getting the cursor position
// which would force a network round trip in an environment where front-end xtermjs talking to
// a server-side PTY via websocket. Without querying for cursor position, content written on
// the server side could be buffered, which is much more performant.
// See the following 2 GitHub issues for more context:
// - https://github.com/PowerShell/PSReadLine/issues/3879#issuecomment-2573996070
// - https://github.com/PowerShell/PowerShell/issues/24696
if (elapsedMs < 50)
// Use simplified rendering for screen readers
if (Options.ScreenReaderModeEnabled)
{
_handlePotentialResizing = false;
RenderForScreenReader();
}
else
{
ForceRender();
}
}
private void RenderForScreenReader()
{
int bufferWidth = _console.BufferWidth;
int bufferHeight = _console.BufferHeight;
static int FindCommonPrefixLength(string leftStr, string rightStr)
{
if (string.IsNullOrEmpty(leftStr) || string.IsNullOrEmpty(rightStr))
{
return 0;
}
int i = 0;
int minLength = Math.Min(leftStr.Length, rightStr.Length);
while (i < minLength && leftStr[i] == rightStr[i])
{
i++;
}
return i;
}
ForceRender();
// For screen readers, we are just comparing the previous and current buffer text
// (without colors) and only writing the differences.
//
// Note that we don't call QueryForSuggestion() which is the only
// entry into the prediction logic, so while it could be enabled, it
// won't do anything in this rendering implementation.
string parsedInput = ParseInput();
StringBuilder buffer = new(parsedInput);
// Really simple handling of a status line: append it!
if (!string.IsNullOrEmpty(_statusLinePrompt))
{
buffer.Append("\n");
buffer.Append(_statusLinePrompt);
buffer.Append(_statusBuffer);
}
string currentBuffer = buffer.ToString();
string previousBuffer = _previousRender.lines[0].Line;
// In case the buffer was resized.
RecomputeInitialCoords(isTextBufferUnchanged: false);
// Make cursor invisible while we're rendering.
_console.CursorVisible = false;
if (currentBuffer == previousBuffer)
{
// No-op, such as when selecting text or otherwise re-entering.
}
else if (previousBuffer.Length == 0)
{
// Previous buffer was empty so we just render the current buffer,
// and we don't need to move the cursor.
_console.Write(currentBuffer);
}
else
{
// Calculate what to render and where to start the rendering.
int commonPrefixLength = FindCommonPrefixLength(previousBuffer, currentBuffer);
// If we're scrolling through history we always want to re-render.
// Writing only the diff in this scenario is a weird UX.
if (commonPrefixLength > 0 && _anyHistoryCommandCount == 0)
{
// We need to differentially render, possibly with a partial rewrite.
if (commonPrefixLength != previousBuffer.Length)
{
// The buffers share a common prefix but the previous buffer has additional content.
// Move cursor to where the difference starts and clear so we can rewrite.
var diffPoint = ConvertOffsetToPoint(commonPrefixLength, buffer);
_console.SetCursorPosition(diffPoint.X, diffPoint.Y);
_console.Write("\x1b[0J");
} // Otherwise the previous buffer is a complete prefix and we just write.
// TODO: There is a rare edge case where the common prefix can be incorrectly
// calculated because the incoming replacement text matches the text at the current
// cursor position. Unfortunately we don't have a solution yet. For example:
//
// 1. Previous line is "abcdef" and cursor is at (before) the letter "d"
// 2. Paste "defghi" so currentBuffer is "abcdefghidef"
// 3. The diff is "ghidef" and because commonPrefixLength == previousBuffer.Length
// 4. The terminal will incorrectly display "abcghidef" instead of "abcdefghidef"
// Finally, write the diff.
var diffData = currentBuffer.Substring(commonPrefixLength);
_console.Write(diffData);
}
else
{
// The buffers are completely different so we need to rewrite from the start.
_console.SetCursorPosition(_initialX, _initialY);
_console.Write("\x1b[0J");
_console.Write(currentBuffer);
}
}
// If we had to wrap to render everything, update _initialY
var endPoint = ConvertOffsetToPoint(currentBuffer.Length, buffer);
if (endPoint.Y >= bufferHeight)
{
// We had to scroll to render everything, update _initialY.
int offset = 1; // Base case to handle zero-indexing.
if (endPoint.X == 0 && !currentBuffer.EndsWith("\n"))
{
// The line hasn't actually wrapped yet because we have exactly filled the line.
offset -= 1;
}
int scrolledLines = endPoint.Y - bufferHeight + offset;
_initialY -= scrolledLines;
}
// Calculate the coord to place the cursor for the next input.
var point = ConvertOffsetToPoint(_current, buffer);
if (point.Y == bufferHeight)
{
// The cursor top exceeds the buffer height and it hasn't already wrapped,
// (because we have exactly filled the line) so we need to scroll up the buffer by 1 line.
if (point.X == 0)
{
_console.Write("\n");
}
// Adjust the initial cursor position and the to-be-set cursor position
// after scrolling up the buffer.
_initialY -= 1;
point.Y -= 1;
}
_console.SetCursorPosition(point.X, point.Y);
_console.CursorVisible = true;
// Preserve the current render data.
var renderData = new RenderData
{
lines = new RenderedLineData[] { new(currentBuffer, isFirstLogicalLine: true) },
errorPrompt = (_parseErrors != null && _parseErrors.Length > 0) // Not yet used.
};
_previousRender = renderData;
_previousRender.UpdateConsoleInfo(bufferWidth, bufferHeight, point.X, point.Y);
_previousRender.initialY = _initialY;
_lastRenderTime.Restart();
_waitingToRender = false;
}
private void ForceRender()
@@ -261,7 +422,7 @@ namespace Microsoft.PowerShell
// and minimize writing more than necessary on the next render.)
var renderLines = new RenderedLineData[logicalLineCount];
var renderData = new RenderData {lines = renderLines};
var renderData = new RenderData { lines = renderLines };
for (var i = 0; i < logicalLineCount; i++)
{
var line = _consoleBufferLines[i].ToString();
@@ -872,9 +1033,6 @@ namespace Microsoft.PowerShell
WriteBlankLines(lineCount);
}
// Preserve the current render data.
_previousRender = renderData;
// If we counted pseudo physical lines, deduct them to get the real physical line counts
// before updating '_initialY'.
physicalLine -= pseudoPhysicalLineOffset;
@@ -950,6 +1108,8 @@ namespace Microsoft.PowerShell
_console.SetCursorPosition(point.X, point.Y);
_console.CursorVisible = true;
// Preserve the current render data.
_previousRender = renderData;
_previousRender.UpdateConsoleInfo(bufferWidth, bufferHeight, point.X, point.Y);
_previousRender.initialY = _initialY;
@@ -1201,17 +1361,23 @@ namespace Microsoft.PowerShell
return ConvertOffsetToPoint(_buffer.Length);
}
internal Point ConvertOffsetToPoint(int offset)
internal Point ConvertOffsetToPoint(int offset, StringBuilder buffer = null)
{
// This lets us re-use the logic in the screen reader rendering implementation
// where the status line is added to the buffer without modifying the local state.
buffer ??= _buffer;
int x = _initialX;
int y = _initialY;
int bufferWidth = _console.BufferWidth;
var continuationPromptLength = LengthInBufferCells(Options.ContinuationPrompt);
var continuationPromptLength = Options.ScreenReaderModeEnabled
? 0
: LengthInBufferCells(Options.ContinuationPrompt);
for (int i = 0; i < offset; i++)
{
char c = _buffer[i];
char c = buffer[i];
if (c == '\n')
{
y += 1;
@@ -1229,7 +1395,7 @@ namespace Microsoft.PowerShell
// If cursor is at column 0 and the next character is newline, let the next loop
// iteration increment y.
if (x != 0 || !(i + 1 < offset && _buffer[i + 1] == '\n'))
if (x != 0 || !(i + 1 < offset && buffer[i + 1] == '\n'))
{
y += 1;
}
@@ -1238,9 +1404,9 @@ namespace Microsoft.PowerShell
}
// If next character actually exists, and isn't newline, check if wider than the space left on the current line.
if (_buffer.Length > offset && _buffer[offset] != '\n')
if (buffer.Length > offset && buffer[offset] != '\n')
{
int size = LengthInBufferCells(_buffer[offset]);
int size = LengthInBufferCells(buffer[offset]);
if (x + size > bufferWidth)
{
// Character was wider than remaining space, so character, and cursor, appear on next line.
@@ -1259,7 +1425,10 @@ namespace Microsoft.PowerShell
int y = _initialY;
int bufferWidth = _console.BufferWidth;
var continuationPromptLength = LengthInBufferCells(Options.ContinuationPrompt);
var continuationPromptLength = Options.ScreenReaderModeEnabled
? 0
: LengthInBufferCells(Options.ContinuationPrompt);
for (offset = 0; offset < _buffer.Length; offset++)
{
// If we are on the correct line, return when we find
+16 -31
View File
@@ -494,8 +494,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\n\"", Keys(
 _.DQuote, _.Enter,
"one", _.Enter,
@@ -503,7 +501,7 @@ namespace Test
"three", _.Enter,
_.DQuote, _.Escape,
"kl", // go to the 'hree' portion of "three"
"2dk", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
"2dk", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -512,8 +510,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\none\ntwo\n\"", Keys(
 _.DQuote, _.Enter,
"one", _.Enter,
@@ -521,7 +517,7 @@ namespace Test
"three", _.Enter,
_.DQuote, _.Escape,
"dk",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
CheckThat(() => AssertCursorTopIs(2)),
CheckThat(() => AssertLineIs("\"\none\ntwo")),
// finish the buffer to close the multiline string
@@ -571,8 +567,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\no\nthree\n\"", Keys(
_.DQuote, _.Enter,
"one", _.Enter,
@@ -582,7 +576,7 @@ namespace Test
"kkkl", // go to the 'ne' portion of "one"
// delete to the end of the next line
"2d$",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -591,18 +585,16 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nsome words\n\"", Keys(
_.DQuote, _.Enter,
" this is a line with some words", _.Enter,
_.DQuote, _.Escape,
"k6W",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 23)),
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 23)),
// delete from first non blank of line
"d0",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength))
));
}
@@ -611,17 +603,15 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\n some spaces\n\"", Keys(
_.DQuote, _.Enter,
" this is a line with some spaces", _.Enter,
_.DQuote, _.Escape,
"k6W",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 23)),
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 23)),
// delete from first non blank of line
"d^",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3))
));
}
@@ -630,8 +620,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\n\"", Keys(
 _.DQuote, _.Enter,
"one", _.Enter,
@@ -639,7 +627,7 @@ namespace Test
"three", _.Enter,
_.DQuote, _.Escape,
"kkkl", // go to the 'ne' portion of "one"
"2dj", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
"2dj", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -648,8 +636,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nthree\n\"", Keys(
_.DQuote, _.Enter,
"one", _.Enter,
@@ -659,7 +645,7 @@ namespace Test
"kkl", // go to the 'wo' portion of "two"
// delete from line 2 to the current line (3)
"2dgg",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
Test("\"\none\nthree\n\"", Keys(
@@ -671,7 +657,7 @@ namespace Test
"kkl", // go to the 'wo' portion of "two"
// delete the current line (3)
"3dgg",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
Test("\"\none\n\"", Keys(
@@ -683,7 +669,7 @@ namespace Test
"kkl", // go to the 'wo' portion of "two"
// delete from the current line (3) to line 4
"4dgg",
CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -977,7 +963,6 @@ namespace Test
[SkippableFact]
public void ViInsertLine()
{
int adder = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
TestSetup(KeyMode.Vi);
Test("line1\n", Keys(
@@ -985,8 +970,8 @@ namespace Test
));
Test("\nline1", Keys(
_.Escape, "oline1", CheckThat(() => AssertCursorLeftIs(5 + adder)), CheckThat(() => AssertLineIs("\nline1")),
_.Escape, CheckThat(() => AssertCursorLeftIs(4 + adder))
_.Escape, "oline1", CheckThat(() => AssertCursorLeftIs(5 + ContinuationPromptLength)), CheckThat(() => AssertLineIs("\nline1")),
_.Escape, CheckThat(() => AssertCursorLeftIs(4 + ContinuationPromptLength))
));
Test("", Keys(
@@ -1010,10 +995,10 @@ namespace Test
));
Test("", Keys(
_.Escape, "oline4", CheckThat(() => AssertLineIs("\nline4")), CheckThat(() => AssertCursorLeftIs(5 + adder)),
_.Escape, "Oline2", CheckThat(() => AssertLineIs("\nline2\nline4")), CheckThat(() => AssertCursorLeftIs(5 + adder)),
_.Escape, "oline4", CheckThat(() => AssertLineIs("\nline4")), CheckThat(() => AssertCursorLeftIs(5 + ContinuationPromptLength)),
_.Escape, "Oline2", CheckThat(() => AssertLineIs("\nline2\nline4")), CheckThat(() => AssertCursorLeftIs(5 + ContinuationPromptLength)),
_.Escape, "oline3", CheckThat(() => AssertLineIs("\nline2\nline3\nline4")),
_.Escape, CheckThat(() => AssertLineIs("\nline2\nline3\nline4")), CheckThat(() => AssertCursorLeftIs(4 + adder)),
_.Escape, CheckThat(() => AssertLineIs("\nline2\nline3\nline4")), CheckThat(() => AssertCursorLeftIs(4 + ContinuationPromptLength)),
'u', CheckThat(() => AssertLineIs("\nline2\nline4")),
'u', CheckThat(() => AssertLineIs("\nline4")),
'u'
+17 -21
View File
@@ -340,8 +340,6 @@ namespace Test
{
TestSetup(KeyMode.Cmd);
var continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
// Test case - start with single line, cursor at end
Test("56\n1234", Keys("1234", _.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)), "56"));
@@ -356,13 +354,13 @@ namespace Test
// Test case - start with multi-line, cursor at end of second line (end of input)
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"9ABC"));
// Test case - start with multi-line, cursor at beginning of second line
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"9ABC"));
// Test case - start with multi-line, cursor at end of first line
@@ -380,15 +378,15 @@ namespace Test
// Test case - insert multiple blank lines
Test("1234\n9ABC\n\n5678", Keys("1234", _.Shift_Enter, "5678",
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"9ABC"));
// Test case - create leading blank line, cursor to stay on same line
Test("\n\n1234", Keys("1234",
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0,0)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1))));
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1))));
}
[SkippableFact]
@@ -396,51 +394,49 @@ namespace Test
{
TestSetup(KeyMode.Cmd);
var continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
// Test case - start with single line, cursor at end
Test("1234\n56", Keys("1234",
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"56"));
// Test case - start with single line, cursor in home position
Test("1234\n56", Keys("1234",
_.Home, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Home, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"56"));
// Test case - start with single line, cursor in middle
Test("1234\n56", Keys("1234",
_.LeftArrow, _.LeftArrow, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.LeftArrow, _.LeftArrow, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"56"));
// Test case - start with multi-line, cursor at end of second line (end of input)
Test("1234\n5678\n9ABC", Keys("1234", _.Shift_Enter, "5678",
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 2)),
"9ABC"));
// Test case - start with multi-line, cursor at beginning of second line
Test("1234\n5678\n9ABC", Keys("1234", _.Shift_Enter, "5678",
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 2)),
"9ABC"));
// Test case - start with multi-line, cursor at end of first line
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
_.UpArrow, _.LeftArrow, _.End, CheckThat(() => AssertCursorLeftTopIs(4, 0)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"9ABC"));
// Test case - start with multi-line, cursor at beginning of first line - temporarily having to press Home twice to
// work around bug in home handler.
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
_.UpArrow, _.LeftArrow, _.Home, _.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
"9ABC"));
// Test case - insert multiple blank lines
Test("1234\n5678\n\n9ABC", Keys("1234", _.Shift_Enter, "5678",
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 3)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 2)),
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 3)),
"9ABC"));
}
+18
View File
@@ -177,6 +177,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation1()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'RightArrow' and 'LeftArrow' with the following menu:
// Get-Many0 Get-Many3 Get-Many6 Get-Many9 Get-Many12
// Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13
@@ -304,6 +306,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation2()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'RightArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -387,6 +391,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation3()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'LeftArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -462,6 +468,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation4()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'UpArrow' and 'DownArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -633,6 +641,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation6()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'UpArrow', 'DownArrow', 'LeftArrow', and 'RightArrow' with the following menu:
// Get-NewDynamicParameters Get-NewStyle
// Get-NewIdea
@@ -736,6 +746,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation7()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Trigger the menu completion from the last line in the screen buffer, which will cause the screen
// to scroll up. Then test 'DownArrow' and 'UpArrow' with the following menu to verify if scrolling
// was handled correctly:
@@ -968,6 +980,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_WorkWithListView()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
int listWidth = CheckWindowSize();
@@ -1032,6 +1046,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_HandleScrolling1()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// This test case covers the fix to https://github.com/PowerShell/PSReadLine/issues/2928.
var basicScrollingConsole = new BasicScrollingConsole(keyboardLayout: _, width: 133, height: 10);
TestSetup(basicScrollingConsole, KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
@@ -1127,6 +1143,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_HandleScrolling2()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// This test case covers the fix to https://github.com/PowerShell/PSReadLine/issues/2948.
var basicScrollingConsole = new BasicScrollingConsole(keyboardLayout: _, width: 133, height: 10);
TestSetup(basicScrollingConsole, KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
+22
View File
@@ -14,6 +14,8 @@ namespace Test
[SkippableFact]
public void Inline_RenderSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -92,6 +94,8 @@ namespace Test
[SkippableFact]
public void Inline_CustomKeyBindingsToAcceptSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Alt+g", PSConsoleReadLine.AcceptSuggestion),
new KeyHandler("Alt+f", PSConsoleReadLine.AcceptNextSuggestionWord));
@@ -156,6 +160,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptNextSuggestionWordCanAcceptMoreThanOneWords()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord),
new KeyHandler("Alt+f", PSConsoleReadLine.AcceptNextSuggestionWord));
@@ -192,6 +198,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptSuggestionWithSelection()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -261,6 +269,8 @@ namespace Test
[SkippableFact]
public void Inline_SetPredictionColor()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
var predictionColor = MakeCombinedColor(ConsoleColor.DarkYellow, ConsoleColor.Yellow);
var predictionColorToCheck = Tuple.Create(ConsoleColor.DarkYellow, ConsoleColor.Yellow);
@@ -288,6 +298,8 @@ namespace Test
[SkippableFact]
public void Inline_HistoryEditsCanUndoProperly()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
SetHistory("git checkout -b branch origin/bbbb");
@@ -320,6 +332,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptSuggestionInVIMode()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Vi);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -367,6 +381,8 @@ namespace Test
[SkippableFact]
public void ViDefect2408()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Vi);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -460,6 +476,8 @@ namespace Test
[SkippableFact]
public void Inline_PluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
// Using the 'Plugin' source will make PSReadLine get prediction from the plugin only.
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
@@ -534,6 +552,8 @@ namespace Test
[SkippableFact]
public void Inline_HistoryAndPluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
// Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from the plugin and history,
// and plugin takes precedence.
TestSetup(KeyMode.Cmd,
@@ -798,6 +818,8 @@ namespace Test
[SkippableFact]
public void Inline_TruncateVeryLongSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(new TestConsole(keyboardLayout: _, width: 10, height: 2), KeyMode.Cmd);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
+16 -12
View File
@@ -672,8 +672,12 @@ namespace Test
[SkippableFact]
public void SelectCommandArgument_HereStringArgs()
{
Skip.If(ScreenReaderModeEnabled, "We're still investigating exactly why this test fails in screen reader mode.");
TestSetup(KeyMode.Cmd);
var continuationPrompt = PSConsoleReadLine.GetOptions().ContinuationPrompt;
Test("", Keys(
"& Test-Sca a1 @'\nabc\n'@ -p1 \"$false\"",
// Command name or command expression should be skipped.
@@ -685,10 +689,10 @@ namespace Test
TokenClassification.None, ' ',
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
@@ -704,10 +708,10 @@ namespace Test
TokenClassification.None, " a1 ",
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.Selection, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
@@ -723,10 +727,10 @@ namespace Test
TokenClassification.None, " a1 ",
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
@@ -744,10 +748,10 @@ namespace Test
TokenClassification.None, ' ',
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
@@ -766,10 +770,10 @@ namespace Test
TokenClassification.None, ' ',
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
@@ -786,10 +790,10 @@ namespace Test
TokenClassification.None, " a1 ",
TokenClassification.String, "@'",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "abc",
NextLine,
TokenClassification.None, ">> ",
TokenClassification.None, continuationPrompt,
TokenClassification.String, "'@",
TokenClassification.None, ' ',
TokenClassification.Parameter, "-p1",
+83 -61
View File
@@ -96,6 +96,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_ListUpdatesWhileTyping()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
// The font effect sequences of the dimmed color used in list view metadata line
@@ -184,6 +186,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -401,6 +405,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+p", PSConsoleReadLine.HistorySearchBackward),
new KeyHandler("Ctrl+l", PSConsoleReadLine.HistorySearchForward));
@@ -620,6 +626,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_Escape()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -783,6 +791,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_DigitArgument()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -943,6 +953,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_CtrlZ()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1076,6 +1088,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_Selection()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1276,6 +1290,8 @@ namespace Test
[SkippableFact]
public void List_HistorySource_NoAcceptanceCallback()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1340,6 +1356,8 @@ namespace Test
[SkippableFact]
public void List_PluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1355,8 +1373,8 @@ namespace Test
TokenClassification.Command, "ec",
NextLine,
TokenClassification.ListPrediction, "<-/3>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1377,9 +1395,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1395,10 +1413,10 @@ namespace Test
TokenClassification.None, " TEXT BEFORE ec",
NextLine,
TokenClassification.ListPrediction, "<1/3>",
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(1/2) LongNamePredic…(1)>'.
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(1/2) LongNamePredic…(1)>'.
dimmedColors, '<',
TokenClassification.ListPrediction, "TestPredictor(1/2) ",
dimmedColors, "LongNamePredic…(1)>",
dimmedColors, "LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
@@ -1419,9 +1437,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1434,10 +1452,10 @@ namespace Test
TokenClassification.Selection, "SOME TEXT BEFORE ec",
NextLine,
TokenClassification.ListPrediction, "<1/3>",
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(1/2) LongNamePredic…(1)>'.
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(1/2) LongNamePredic…(1)>'.
dimmedColors, '<',
TokenClassification.ListPrediction, "TestPredictor(1/2) ",
dimmedColors, "LongNamePredic…(1)>",
dimmedColors, "LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, " SOME TEXT BEFORE ",
@@ -1458,9 +1476,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1473,8 +1491,8 @@ namespace Test
TokenClassification.Command, "j",
NextLine,
TokenClassification.ListPrediction, "<-/3>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1495,9 +1513,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1518,9 +1536,9 @@ namespace Test
TokenClassification.None, " NEW TEXT",
NextLine,
TokenClassification.ListPrediction, "<3/3>",
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(2) LongNamePredic…(1/1)>'.
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<1/3>' plus '<TestPredictor(2) LongNamePredic…(1/1)>'.
dimmedColors, "<TestPredictor(2) ",
TokenClassification.ListPrediction, "LongNamePredic…(1/1)",
TokenClassification.ListPrediction, "LongNamePredic…(1/1)",
dimmedColors, '>',
NextLine,
TokenClassification.ListPrediction, '>',
@@ -1542,9 +1560,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.ListPredictionSelected, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPredictionSelected, ']',
// List view is done, no more list item following.
NextLine,
@@ -1558,8 +1576,8 @@ namespace Test
TokenClassification.None, " NEW TEX",
NextLine,
TokenClassification.ListPrediction, "<-/3>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1582,9 +1600,9 @@ namespace Test
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1604,10 +1622,10 @@ namespace Test
TokenClassification.None, " NEW TEX SOME TEXT AFTER",
NextLine,
TokenClassification.ListPrediction, "<2/3>",
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus '<TestPredictor(2/2) LongNamePredic…(1)>'.
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus '<TestPredictor(2/2) LongNamePredic…(1)>'.
dimmedColors, '<',
TokenClassification.ListPrediction, "TestPredictor(2/2) ",
dimmedColors, "LongNamePredic…(1)>",
dimmedColors, "LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1630,9 +1648,9 @@ namespace Test
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1661,6 +1679,8 @@ namespace Test
[SkippableFact]
public void List_HistoryAndPluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1676,8 +1696,8 @@ namespace Test
TokenClassification.Command, "ec",
NextLine,
TokenClassification.ListPrediction, "<-/5>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/5>' plus '<History(2) TestPredictor(2) …>'.
dimmedColors, "<History(2) TestPredictor(2) …>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/5>' plus '<History(2) TestPredictor(2) …>'.
dimmedColors, "<History(2) TestPredictor(2) …>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
@@ -1716,9 +1736,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1733,10 +1753,10 @@ namespace Test
TokenClassification.Selection, "eca -zoo",
NextLine,
TokenClassification.ListPrediction, "<1/5>",
TokenClassification.None, new string(' ', listWidth - 38), // 38 is the length of '<1/5>' plus '<History(1/2) TestPredictor(2) …>'.
TokenClassification.None, new string(' ', listWidth - 38), // 38 is the length of '<1/5>' plus '<History(1/2) TestPredictor(2) …>'.
dimmedColors, '<',
TokenClassification.ListPrediction, "History(1/2) ",
dimmedColors, "TestPredictor(2) …>",
dimmedColors, "TestPredictor(2) …>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.ListPredictionSelected, ' ',
@@ -1775,9 +1795,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1789,8 +1809,8 @@ namespace Test
TokenClassification.Command, "j",
NextLine,
TokenClassification.ListPrediction, "<-/4>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(1) TestPredictor(2) …>'.
dimmedColors, "<History(1) TestPredictor(2) …>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(1) TestPredictor(2) …>'.
dimmedColors, "<History(1) TestPredictor(2) …>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
@@ -1820,9 +1840,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1842,9 +1862,9 @@ namespace Test
TokenClassification.None, " NEW TEXT",
NextLine,
TokenClassification.ListPrediction, "<4/4>",
TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '<4/4>' plus '<… TestPredictor(2) LongNamePredic…(1/1)>'.
dimmedColors, "<… TestPredictor(2) ",
TokenClassification.ListPrediction, "LongNamePredic…(1/1)",
TokenClassification.None, new string(' ', listWidth - 46), // 46 is the length of '<4/4>' plus '<… TestPredictor(2) LongNamePredic…(1/1)>'.
dimmedColors, "<… TestPredictor(2) ",
TokenClassification.ListPrediction, "LongNamePredic…(1/1)",
dimmedColors, '>',
NextLine,
TokenClassification.ListPrediction, '>',
@@ -1875,9 +1895,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.ListPredictionSelected, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPredictionSelected, ']',
// List view is done, no more list item following.
NextLine,
@@ -1891,8 +1911,8 @@ namespace Test
TokenClassification.None, " NEW TEX",
NextLine,
TokenClassification.ListPrediction, "<-/3>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
TokenClassification.None, new string(' ', listWidth - 42), // 42 is the length of '<-/3>' plus '<TestPredictor(2) LongNamePredic…(1)>'.
dimmedColors, "<TestPredictor(2) LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1915,9 +1935,9 @@ namespace Test
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1937,10 +1957,10 @@ namespace Test
TokenClassification.None, " NEW TEX SOME TEXT AFTER",
NextLine,
TokenClassification.ListPrediction, "<2/3>",
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus '<TestPredictor(2/2) LongNamePredic…(1)>'.
TokenClassification.None, new string(' ', listWidth - 44), // 44 is the length of '<2/3>' plus '<TestPredictor(2/2) LongNamePredic…(1)>'.
dimmedColors, '<',
TokenClassification.ListPrediction, "TestPredictor(2/2) ",
dimmedColors, "LongNamePredic…(1)>",
dimmedColors, "LongNamePredic…(1)>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, " SOME TEXT BEFORE ",
@@ -1963,9 +1983,9 @@ namespace Test
TokenClassification.None, ' ',
emphasisColors, "SOME NEW TEX",
TokenClassification.None, 'T',
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -1994,6 +2014,8 @@ namespace Test
[SkippableFact]
public void List_HistoryAndPluginSource_Deduplication()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -2011,8 +2033,8 @@ namespace Test
TokenClassification.Command, "de-dup",
NextLine,
TokenClassification.ListPrediction, "<-/4>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(2) TestPredictor(1) …>'.
dimmedColors, "<History(2) TestPredictor(1) …>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(2) TestPredictor(1) …>'.
dimmedColors, "<History(2) TestPredictor(1) …>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
@@ -2042,9 +2064,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
@@ -2075,8 +2097,8 @@ namespace Test
TokenClassification.Command, "de-dup",
NextLine,
TokenClassification.ListPrediction, "<-/4>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(2) TestPredictor(1) …>'.
dimmedColors, "<History(2) TestPredictor(1) …>",
TokenClassification.None, new string(' ', listWidth - 36), // 36 is the length of '<-/4>' plus '<History(2) TestPredictor(1) …>'.
dimmedColors, "<History(2) TestPredictor(1) …>",
NextLine,
TokenClassification.ListPrediction, '>',
TokenClassification.None, ' ',
@@ -2105,9 +2127,9 @@ namespace Test
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, new string(' ', listWidth - 32), // 32 is the length of '> SOME NEW TEXT' plus '[LongNamePredic…]'
TokenClassification.None, '[',
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.ListPrediction, "LongNamePredic…",
TokenClassification.None, ']',
// List view is done, no more list item following.
NextLine,
+6
View File
@@ -9,6 +9,8 @@ namespace Test
[SkippableFact]
public void List_MetaLine_And_Paging_Navigation()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
int listWidth = 100;
TestSetup(new TestConsole(keyboardLayout: _, width: listWidth, height: 15), KeyMode.Cmd);
@@ -514,6 +516,8 @@ namespace Test
[SkippableFact]
public void ListView_AdapteTo_ConsoleSize()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// 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;
@@ -850,6 +854,8 @@ namespace Test
[SkippableFact]
public void ListView_TermSize_Warning()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// 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;
+10 -4
View File
@@ -9,6 +9,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_4_Lines()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 22 and width to 60, so the metadata line will be fully rendered
// and maximum 4 lines can be used for tooltip for a selected list item.
int listWidth = 60;
@@ -134,7 +136,7 @@ namespace Test
dimmedColors, " >> Hello", NextLine,
dimmedColors, " Binary", NextLine,
dimmedColors, " World", NextLine,
dimmedColors, " PowerShell is a task automation an… ",
dimmedColors, " PowerShell is a task automation an… ",
TokenClassification.ListPrediction, "(<F4> to view all)",
NextLine,
TokenClassification.ListPrediction, '>',
@@ -206,6 +208,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_2_Lines()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 15 and width to 60, so the metadata line will be fully rendered
// and maximum 2 lines can be used for tooltip for a selected list item.
int listWidth = 60;
@@ -287,7 +291,7 @@ namespace Test
TokenClassification.ListPredictionSelected, ']',
NextLine,
dimmedColors, " >> Hello", NextLine,
dimmedColors, " Binary … ",
dimmedColors, " Binary … ",
TokenClassification.ListPrediction, "(<F4> to view all)",
NextLine,
TokenClassification.ListPrediction, '>',
@@ -363,6 +367,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_1_Line()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 6 and width to 60, so the metadata line will be fully rendered
// and maximum 2 lines can be used for tooltip for a selected list item.
int listWidth = 60;
@@ -406,7 +412,7 @@ namespace Test
TokenClassification.ListPrediction, "Tooltip",
TokenClassification.ListPredictionSelected, ']',
NextLine,
dimmedColors, " >> Hello … ",
dimmedColors, " >> Hello … ",
TokenClassification.ListPrediction, "(<F4> to view all)",
NextLine,
TokenClassification.ListPrediction, '>',
@@ -462,7 +468,7 @@ namespace Test
TokenClassification.ListPrediction, "Tooltip",
TokenClassification.ListPredictionSelected, ']',
NextLine,
dimmedColors, " >> Hello … ",
dimmedColors, " >> Hello … ",
TokenClassification.ListPrediction, "(<F4> to view all)",
// List view is done, no more list item following.
NextLine
+25 -1
View File
@@ -300,6 +300,17 @@ namespace Test
buffer[writePos + i].ForegroundColor = ForegroundColor;
}
}
public virtual void BlankRestOfBuffer()
{
var writePos = CursorTop * BufferWidth + CursorLeft;
for (; writePos < buffer.Length; writePos++)
{
buffer[writePos].UnicodeChar = ' ';
buffer[writePos].BackgroundColor = BackgroundColor;
buffer[writePos].ForegroundColor = ForegroundColor;
}
}
public virtual void Clear()
{
@@ -334,6 +345,7 @@ namespace Test
c.BackgroundColor = (ConsoleColor)((int)c.BackgroundColor ^ 7);
c._negative = b;
}
protected static readonly Dictionary<string, Action<TestConsole>> EscapeSequenceActions = new()
{
{"7", c => ToggleNegative(c, true) },
@@ -376,7 +388,8 @@ namespace Test
c.ForegroundColor = DefaultForeground;
c.BackgroundColor = DefaultBackground;
}},
{"2J", c => c.SetCursorPosition(0, 0) }
{ "0J", c => c.BlankRestOfBuffer() },
{ "2J", c => c.SetCursorPosition(0, 0) },
};
}
@@ -520,6 +533,17 @@ namespace Test
}
}
public override void BlankRestOfBuffer()
{
var writePos = (_offset + CursorTop) * BufferWidth + CursorLeft;
for (; writePos < buffer.Length; writePos++)
{
buffer[writePos].UnicodeChar = ' ';
buffer[writePos].BackgroundColor = BackgroundColor;
buffer[writePos].ForegroundColor = ForegroundColor;
}
}
public override void Clear()
{
_offset = 0;
+27 -41
View File
@@ -12,8 +12,6 @@ namespace Test
const string buffer = "\"\n12345\n1234\n123\n12\n1\n\"";
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test(buffer, Keys(
_.DQuote, _.Enter,
"12345", _.Enter,
@@ -25,22 +23,22 @@ namespace Test
_.Escape,
// move to second line at column 4
"ggj3l", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
"ggj3l", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
// moving down on shorter lines will position the cursor at the end of each logical line
_.j, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
_.j, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 2)),
_.j, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
_.j, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 2)),
// moving back up will position the cursor at the end of shorter lines or at the desired column number
_.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
_.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
_.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
_.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
// move at end of line (column 5)
_.Dollar, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 4)),
_.Dollar, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 4)),
// moving down on shorter lines will position the cursor at the end of each logical line
_.j, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
_.j, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 2)),
_.j, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
_.j, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 2)),
// moving back up will position the cursor at the end of each logical line
_.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 3)),
_.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 4))
_.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 3)),
_.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 4))
));
}
@@ -52,19 +50,17 @@ namespace Test
const string buffer = "\"\nline2\nline3\n\"";
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test(buffer, Keys(
_.DQuote, _.Enter,
"line2", _.Enter,
"line3", _.Enter,
_.DQuote,
_.Escape,
_.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
_.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
// move left
_.h, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
_.l, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 1)),
"2h", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
_.h, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
_.l, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 1)),
"2h", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -75,18 +71,16 @@ namespace Test
const string buffer = "\"\nline2\nline3\n\"";
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test(buffer, Keys(
_.DQuote, _.Enter,
"line2", _.Enter,
"line3", _.Enter,
_.DQuote,
_.Escape,
_.k, _.k, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
_.k, _.k, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
// move right
_.l, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 1)),
"10l", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 4))
_.l, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 1)),
"10l", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 4))
));
}
@@ -97,8 +91,6 @@ namespace Test
const string buffer = "\"Multiline buffer\n containing an empty line\n\nand text aligned on the left\n\"";
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test(buffer, Keys(
_.DQuote, "Multiline buffer", _.Enter,
" containing an empty line", _.Enter,
@@ -107,7 +99,7 @@ namespace Test
_.DQuote,
_.Escape, CheckThat(() => AssertCursorTopIs(4)),
"gg", CheckThat(() => AssertCursorLeftTopIs(0, 0)),
'G', CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength, 4))
'G', CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 4))
));
}
@@ -148,17 +140,15 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
const string buffer = "\"\n line\"";
Test(buffer, Keys(
_.DQuote, _.Enter, " line", _.DQuote, _.Escape, CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 6)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 2, 1)),
_.Dollar, CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 6, 1)),
_.DQuote, _.Enter, " line", _.DQuote, _.Escape, CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 6)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 2, 1)),
_.Dollar, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 6, 1)),
// also works forward
'0', CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength, 1)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 2, 1))
'0', CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 2, 1))
));
}
@@ -167,14 +157,12 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
const string buffer = "\"\n\n\"";
Test(buffer, Keys(
_.DQuote, _.Enter, _.Enter, _.DQuote, _.Escape, _.k,
CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 1)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 1))
CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 1)),
_.Underbar, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 1))
));
}
@@ -193,14 +181,12 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
const string buffer = "\"\n\n\"";
Test(buffer, Keys(
_.DQuote, _.Enter, _.Enter, _.DQuote, _.Escape, _.k,
CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 1)),
_.Dollar, CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 1))
CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 1)),
_.Dollar, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 1))
));
}
+55 -57
View File
@@ -35,7 +35,6 @@ namespace Test
{
TestSetup(KeyMode.Cmd);
int continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
string line_0 = "4444";
string line_1 = "33";
string line_2 = "666666";
@@ -43,8 +42,8 @@ namespace Test
int wrappedLength_1 = 9;
int wrappedLength_2 = 2;
string wrappedLine_1 = new string('8', _console.BufferWidth - continutationPromptLength + wrappedLength_1); // Take 2 physical lines
string wrappedLine_2 = new string('6', _console.BufferWidth - continutationPromptLength + wrappedLength_2); // Take 2 physical lines
string wrappedLine_1 = new string('8', _console.BufferWidth - ContinuationPromptLength + wrappedLength_1); // Take 2 physical lines
string wrappedLine_2 = new string('6', _console.BufferWidth - ContinuationPromptLength + wrappedLength_2); // Take 2 physical lines
Test("", Keys(
"", _.Shift_Enter, // physical line 0
@@ -57,45 +56,45 @@ namespace Test
// Starting at the end of the last line.
// Verify that UpArrow goes to the end of the previous logical line.
CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
// Press Up/Down/Up
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_2, 7)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_2, 7)),
// Press Up/Down/Up
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_1, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_2, 7)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_1, 5)),
// Press Up/Down/Up
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_2.Length, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_2.Length, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_1, 5)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_2.Length, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_2.Length, 3)),
// Press Up/Up
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_1.Length, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_1.Length, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length, 1)),
// Move to left for 1 character, so the cursor now is not at the end of line.
// Verify that DownArrow/UpArrow goes to the previous logical line at the same column.
_.LeftArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 1)),
_.LeftArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 1)),
// Press Down all the way to the end
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_1.Length, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 6)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_1.Length, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 6)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_2, 7)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_3.Length, 8)),
// Press Up all the way to the physical line 1
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(wrappedLength_2, 7)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 6)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 5)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_1.Length, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength + line_0.Length - 1, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 6)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 5)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_1.Length, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + line_0.Length - 1, 1)),
// Clear the input, we were just testing movement
_.Escape
@@ -107,7 +106,6 @@ namespace Test
{
TestSetup(KeyMode.Cmd);
var continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("", Keys(
"4444", _.Shift_Enter,
"666666", _.Shift_Enter,
@@ -118,39 +116,39 @@ namespace Test
// Starting at the end of the next to last line (because it's not blank)
// Verify that Home first goes to the start of the line, then the start of the input.
_.LeftArrow,
_.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 4)),
_.Home, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 4)),
_.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
// Now (because we're at the start), verify first End goes to end of the line
// and the second End goes to the end of the input.
_.End, CheckThat(() => AssertCursorLeftTopIs(4, 0)),
_.End, CheckThat(() => AssertCursorLeftTopIs(0 + continutationPromptLength, 5)),
_.End, CheckThat(() => AssertCursorLeftTopIs(0 + ContinuationPromptLength, 5)),
_.Home, _.Home,
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 5)),
_.LeftArrow, _.Home,
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 0)), // was (4,0), but seems wrong
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 0)), // was (4,0), but seems wrong
// Make sure that movement between lines stays at the end of a line if it starts
// at the end of a line
_.End, _.End,
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(4 + continutationPromptLength, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(8 + continutationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(4 + ContinuationPromptLength, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(8 + ContinuationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(4, 0)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(8 + continutationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(4 + continutationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(0 + continutationPromptLength, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(8 + ContinuationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(4 + ContinuationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(0 + ContinuationPromptLength, 5)),
_.Escape,
_.Shift_Enter,
@@ -160,22 +158,22 @@ namespace Test
"55555", _.Shift_Enter,
"88888888",
_.LeftArrow, _.LeftArrow,
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(5 + continutationPromptLength, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(2 + continutationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(5 + continutationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(5 + ContinuationPromptLength, 4)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(2 + ContinuationPromptLength, 3)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(5 + ContinuationPromptLength, 2)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 1)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
_.UpArrow, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(5 + continutationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(2 + continutationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(5 + continutationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + continutationPromptLength, 5)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(5 + ContinuationPromptLength, 2)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(2 + ContinuationPromptLength, 3)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(5 + ContinuationPromptLength, 4)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(6 + ContinuationPromptLength, 5)),
// Using the input previously entered, check for correct cursor movements when first line is blank
_.Home, _.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(8 + continutationPromptLength, 1)),
_.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(8 + ContinuationPromptLength, 1)),
_.Home, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.Home, CheckThat(() => AssertCursorLeftTopIs(0,0)),
// Clear the input, we were just testing movement
+16
View File
@@ -11,6 +11,8 @@ namespace Test
[SkippableFact]
public void ContinuationPrompt()
{
Skip.If(ScreenReaderModeEnabled, "Continuation prompt is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
Test("", Keys(
@@ -55,6 +57,20 @@ namespace Test
));
}
[SkippableFact]
public void ContinuationPromptForScreenReader()
{
Skip.IfNot(ScreenReaderModeEnabled);
TestSetup(KeyMode.Cmd);
Test("", Keys(
"{\n}",
CheckThat(() => AssertScreenIs(2, TokenClassification.None, '{', NextLine, '}' )),
_.Ctrl_c,
InputAcceptedNow
));
}
[SkippableFact]
public void GetKeyHandlers()
{
+12 -3
View File
@@ -182,7 +182,6 @@ namespace Test
// Make sure <ENTER> when input is incomplete actually puts a newline
// wherever the cursor is.
var continationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("{\n\nd\n}", Keys(
'{',
_.Enter, CheckThat(() => AssertCursorTopIs(1)),
@@ -190,8 +189,8 @@ namespace Test
_.Enter, CheckThat(() => AssertCursorTopIs(2)),
_.Home,
_.RightArrow, CheckThat(() => AssertCursorLeftTopIs(1, 0)),
_.Enter, CheckThat(() => AssertCursorLeftTopIs(continationPrefixLength, 1)),
_.End, CheckThat(() => AssertCursorLeftTopIs(continationPrefixLength, 3)),
_.Enter, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 1)),
_.End, CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength, 3)),
'}'));
// Make sure <ENTER> when input successfully parses accepts the input regardless
@@ -206,6 +205,8 @@ namespace Test
[SkippableFact]
public void MultiLine_ScreenCheck()
{
Skip.If(ScreenReaderModeEnabled, "Continuation prompt is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
var defaultContinuationPrompt = PSConsoleReadLineOptions.DefaultContinuationPrompt;
@@ -306,6 +307,14 @@ namespace Test
CheckThat(() => AssertScreenIs(1,
Tuple.Create(_console.ForegroundColor, _console.BackgroundColor), "PSREADLINE> ",
TokenClassification.Command, "dir"))));
}
[SkippableFact]
public void InvokeTrickyPrompt()
{
Skip.If(ScreenReaderModeEnabled, "We can't test the colors written in this prompt in screen reader mode.");
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+z", PSConsoleReadLine.InvokePrompt));
// Tricky prompt - writes to console directly with colors, uses ^H trick to eliminate trailing space.
using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace))
-6
View File
@@ -82,8 +82,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nhello, world!\n\noh, bitter world!\n\"", Keys(
_.DQuote, _.Enter,
"hello, world!", _.Enter,
@@ -105,8 +103,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("", Keys(
_.DQuote,
"hello, world!", _.Enter,
@@ -143,8 +139,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\ntwo\n\"", Keys(
_.DQuote, _.Enter,
"one", _.Enter,
+27 -2
View File
@@ -130,6 +130,9 @@ namespace Test
internal virtual bool KeyboardHasGreaterThan => true;
internal virtual bool KeyboardHasCtrlRBracket => true;
internal virtual bool KeyboardHasCtrlAt => true;
internal virtual bool ScreenReaderModeEnabled => false;
internal int ContinuationPromptLength => _continuationPromptLength;
static ReadLine()
{
@@ -444,8 +447,13 @@ namespace Test
// that shouldn't be and aren't ever set by any code in PSReadLine, so we'll
// ignore those bits and just check the stuff we do set.
Assert.Equal(expectedBuffer[i].UnicodeChar, consoleBuffer[i].UnicodeChar);
Assert.Equal(expectedBuffer[i].ForegroundColor, consoleBuffer[i].ForegroundColor);
Assert.Equal(expectedBuffer[i].BackgroundColor, consoleBuffer[i].BackgroundColor);
if (!ScreenReaderModeEnabled)
{
// Changing colors is not supported in screen reader mode,
// and this is the simplest way to disable checking that in all the tests.
Assert.Equal(expectedBuffer[i].ForegroundColor, consoleBuffer[i].ForegroundColor);
Assert.Equal(expectedBuffer[i].BackgroundColor, consoleBuffer[i].BackgroundColor);
}
}
}
@@ -540,6 +548,7 @@ namespace Test
private bool _oneTimeInitCompleted;
private object _psrlInstance;
private FieldInfo _psrlConsole, _psrlMockableMethods;
private int _continuationPromptLength;
private static string MakeCombinedColor(ConsoleColor fg, ConsoleColor bg)
=> VTColorUtils.AsEscapeSequence(fg) + VTColorUtils.AsEscapeSequence(bg, isBackground: true);
@@ -582,6 +591,7 @@ namespace Test
ContinuationPrompt = PSConsoleReadLineOptions.DefaultContinuationPrompt,
DingDuration = 1, // Make tests virtually silent when they ding
DingTone = 37, // Make tests virtually silent when they ding
EnableScreenReaderMode = ScreenReaderModeEnabled,
ExtraPromptLineCount = PSConsoleReadLineOptions.DefaultExtraPromptLineCount,
HistoryNoDuplicates = PSConsoleReadLineOptions.DefaultHistoryNoDuplicates,
HistorySaveStyle = HistorySaveStyle.SaveNothing,
@@ -633,6 +643,11 @@ namespace Test
var colorOptions = new SetPSReadLineOption {Colors = colors};
PSConsoleReadLine.SetOptions(colorOptions);
// Cache the continuation prompt length for use in tests
_continuationPromptLength = ScreenReaderModeEnabled
? 0
: PSConsoleReadLine.GetOptions().ContinuationPrompt.Length;
if (!_oneTimeInitCompleted)
{
typeof(PSConsoleReadLine).GetMethod("Initialize", BindingFlags.Instance | BindingFlags.NonPublic)
@@ -668,4 +683,14 @@ namespace Test
internal override bool KeyboardHasCtrlRBracket => false;
internal override bool KeyboardHasCtrlAt => false;
}
public class ScreenReader : Test.ReadLine, IClassFixture<ConsoleFixture>
{
public ScreenReader(ConsoleFixture fixture, ITestOutputHelper output)
: base(fixture, output, "en-US", "windows")
{
}
internal override bool ScreenReaderModeEnabled => true;
}
}
+10 -24
View File
@@ -48,13 +48,11 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("abcd", Keys(
"abcd", _.Escape,
"dd", CheckThat(() => AssertLineIs("")), CheckThat(() => AssertCursorLeftIs(0)),
'p', CheckThat(() => AssertLineIs("\nabcd")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
'P', CheckThat(() => AssertLineIs("\nabcd\nabcd")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
'p', CheckThat(() => AssertLineIs("\nabcd")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
'P', CheckThat(() => AssertLineIs("\nabcd\nabcd")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
"uuu"
));
@@ -206,12 +204,10 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("abc def", Keys(
"abc def", _.Escape,
"dd", CheckThat(() => AssertLineIs("")), CheckThat(() => AssertCursorLeftIs(0)),
'p', CheckThat(() => AssertLineIs("\nabc def")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
'p', CheckThat(() => AssertLineIs("\nabc def")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
"dd", CheckThat(() => AssertLineIs("")), CheckThat(() => AssertCursorLeftIs(0)),
'P', CheckThat(() => AssertLineIs("abc def\n")), CheckThat(() => AssertCursorLeftIs(0)),
"uuuu"
@@ -223,13 +219,11 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("012 456", Keys(
"012 456", _.Escape,
"byyP", CheckThat(() => AssertLineIs("012 456\n012 456")), CheckThat(() => AssertCursorLeftIs(0)),
"u", CheckThat(() => AssertLineIs("012 456")), CheckThat(() => AssertCursorLeftIs(4)),
"p", CheckThat(() => AssertLineIs("012 456\n012 456")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
"p", CheckThat(() => AssertLineIs("012 456\n012 456")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
"u", CheckThat(() => AssertLineIs("012 456")), CheckThat(() => AssertCursorLeftIs(4))
));
}
@@ -338,8 +332,6 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("012", Keys(
"012", _.Escape,
"y0P", CheckThat(() => AssertLineIs("01012")), CheckThat(() => AssertCursorLeftIs(1)),
@@ -358,9 +350,9 @@ namespace Test
" World!", _.Enter,
_.DQuote, _.Escape,
_.k, "5l", // move the cursor to the 'd' character of "World!"
"y0", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0)),
"P", CheckThat(() => AssertLineIs("\"\nHello\n Worl World!\n\"")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 4)),
"u", CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 0))
"y0", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0)),
"P", CheckThat(() => AssertLineIs("\"\nHello\n Worl World!\n\"")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 4)),
"u", CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 0))
));
}
@@ -369,15 +361,13 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nHello\nWorld!\n\"", Keys(
_.DQuote, _.Enter,
"Hello", _.Enter,
"World!", _.Enter,
_.DQuote, _.Escape,
_.k, _.l, // move to the 'o' character of 'World!'
"y$P", CheckThat(() => AssertLineIs("\"\nHello\nWorld!orld!\n\"")), CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 5)),
"y$P", CheckThat(() => AssertLineIs("\"\nHello\nWorld!orld!\n\"")), CheckThat(() => AssertCursorLeftIs(ContinuationPromptLength + 5)),
"u"
));
}
@@ -542,15 +532,13 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nline1\nline1\nline2\nline2\n\"", Keys(
_.DQuote, _.Enter,
"line1", _.Enter,
"line2", _.Enter,
_.DQuote, _.Escape,
_.k, _.k,
"2dd", 'P', 'p', CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 2))
"2dd", 'P', 'p', CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 2))
));
}
@@ -559,15 +547,13 @@ namespace Test
{
TestSetup(KeyMode.Vi);
var continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
Test("\"\nline1\nline1\nline2\nline2\n\"", Keys(
_.DQuote, _.Enter,
"line1", _.Enter,
"line2", _.Enter,
_.DQuote, _.Escape,
_.k, _.k,
"2d", _.Underbar, 'P', 'p', CheckThat(() => AssertCursorLeftTopIs(continuationPrefixLength + 0, 2))
"2d", _.Underbar, 'P', 'p', CheckThat(() => AssertCursorLeftTopIs(ContinuationPromptLength + 0, 2))
));
}
+9 -2
View File
@@ -210,7 +210,14 @@ function Start-TestRun
function RunXunitTestsInNewProcess ([string] $Layout, [string] $OperatingSystem)
{
$filter = "FullyQualifiedName~Test.{0}_{1}" -f ($Layout -replace '-','_'), $OperatingSystem
$filter = if ($Layout) {
Write-Log "Testing $Layout on $OperatingSystem...`n"
"FullyQualifiedName~Test.{0}_{1}" -f ($Layout -replace '-','_'), $OperatingSystem
} else {
## Today, tests for screen-reader mode only run on Windows with the 'en-US' layout.
Write-Log "Testing screen reader mode...`n"
"FullyQualifiedName~Test.ScreenReader"
}
$testResultFile = "xUnitTestResults.{0}.xml" -f $Layout
$testResultFile = Join-Path $testResultFolder $testResultFile
@@ -263,7 +270,6 @@ function Start-TestRun
{
if (Test-Path "KeyInfo-${layout}-windows.json")
{
Write-Log "Testing $layout ..."
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($layout)
# We have to use Start-Process so it creates a new window, because the keyboard
@@ -283,6 +289,7 @@ function Start-TestRun
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($savedLayout)
}
}
RunXunitTestsInNewProcess
}
else
{