mirror of
https://github.com/PowerShell/PSReadLine
synced 2026-06-08 12:12:13 +00:00
844 lines
44 KiB
C#
844 lines
44 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Management.Automation.Language;
|
|
using System.Management.Automation.Subsystem.Prediction;
|
|
using System.Reflection;
|
|
using Microsoft.PowerShell;
|
|
using Xunit;
|
|
|
|
namespace Test
|
|
{
|
|
public partial class ReadLine
|
|
{
|
|
[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);
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// No matching history entry
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("a", Keys(
|
|
'a', CheckThat(() => AssertScreenIs(1, TokenClassification.Command, 'a')),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1, TokenClassification.Command, 'a')),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1, TokenClassification.Command, 'a')),
|
|
CheckThat(() => AssertCursorLeftIs(1))
|
|
));
|
|
|
|
// The 'OnXXXAccepted' callback won't be hit when 'History' is the only source.
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.Null(_mockedMethods.commandHistory);
|
|
|
|
// Different matches as more input coming
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("ech", Keys(
|
|
'e', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'e',
|
|
TokenClassification.InlinePrediction, "ca -zoo")),
|
|
'c', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
TokenClassification.InlinePrediction, "a -zoo")),
|
|
'h', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
TokenClassification.InlinePrediction, "o -bar")),
|
|
// Once accepted, the suggestion text should be blanked out.
|
|
_.Enter, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech"))
|
|
));
|
|
|
|
// The 'OnXXXAccepted' callback won't be hit when 'History' is the only source.
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.Null(_mockedMethods.commandHistory);
|
|
|
|
// Accept all or partial suggestion text with 'ForwardChar' and 'ForwardWord'
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("eca ", Keys(
|
|
'e', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'e',
|
|
TokenClassification.InlinePrediction, "ca -zoo")),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-zoo")),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'e',
|
|
TokenClassification.InlinePrediction, "ca -zoo")),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.InlinePrediction, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(4)),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-zoo")),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.InlinePrediction, "-zoo"))
|
|
));
|
|
|
|
// The 'OnXXXAccepted' callback won't be hit when 'History' is the only source.
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.Null(_mockedMethods.commandHistory);
|
|
}
|
|
|
|
[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));
|
|
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
|
|
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("eca ", Keys(
|
|
"ec", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
TokenClassification.InlinePrediction, "a -zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
_.Alt_g, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(8)),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
TokenClassification.InlinePrediction, "a -zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
_.Alt_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.InlinePrediction, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(4)),
|
|
_.Alt_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(8)),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.InlinePrediction, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(4)),
|
|
|
|
// Revert back to 'ec'.
|
|
_.Ctrl_z, CheckThat(() => AssertCursorLeftIs(2)),
|
|
// Move cursor to column 0.
|
|
_.LeftArrow, _.LeftArrow,
|
|
CheckThat(() => AssertCursorLeftIs(0)),
|
|
_.Alt_g, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(8)),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
TokenClassification.InlinePrediction, "a -zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
// Move cursor to column 0 again.
|
|
_.LeftArrow, _.LeftArrow,
|
|
CheckThat(() => AssertCursorLeftIs(0)),
|
|
_.Alt_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "eca",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.InlinePrediction, "-zoo")),
|
|
CheckThat(() => AssertCursorLeftIs(4))
|
|
));
|
|
}
|
|
|
|
[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));
|
|
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
|
|
|
|
SetHistory("abc def ghi jkl");
|
|
Test("abc def ghi jkl", Keys(
|
|
'a', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'a',
|
|
TokenClassification.InlinePrediction, "bc def ghi jkl")),
|
|
_.Alt_3, _.Ctrl_f,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "abc",
|
|
TokenClassification.None, " def ghi ",
|
|
TokenClassification.InlinePrediction, "jkl")),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'a',
|
|
TokenClassification.InlinePrediction, "bc def ghi jkl")),
|
|
_.Alt_3, _.Alt_f,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "abc",
|
|
TokenClassification.None, " def ghi ",
|
|
TokenClassification.InlinePrediction, "jkl")),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'a',
|
|
TokenClassification.InlinePrediction, "bc def ghi jkl")),
|
|
_.Alt_8, _.Alt_f,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "abc",
|
|
TokenClassification.None, " def ghi jkl"))
|
|
));
|
|
}
|
|
|
|
[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);
|
|
|
|
SetHistory("git diff --cached", "git diff");
|
|
Test("git diff", Keys(
|
|
"git", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.InlinePrediction, " diff")),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " diff")),
|
|
_.Ctrl_z, CheckThat(() => AssertCursorLeftIs(3)),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " diff",
|
|
TokenClassification.InlinePrediction, " --cached")),
|
|
|
|
// Perform visual selection and then accept suggestion.
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.InlinePrediction, " diff")),
|
|
_.LeftArrow, _.Shift_RightArrow,
|
|
CheckThat(() =>
|
|
{
|
|
PSConsoleReadLine.GetSelectionState(out var start, out var length);
|
|
Assert.Equal(2, start);
|
|
Assert.Equal(1, length);
|
|
}),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " diff")),
|
|
|
|
// Perform visual selection and then accept next suggestion word.
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.InlinePrediction, " diff")),
|
|
_.LeftArrow, _.Shift_RightArrow,
|
|
CheckThat(() =>
|
|
{
|
|
PSConsoleReadLine.GetSelectionState(out var start, out var length);
|
|
Assert.Equal(2, start);
|
|
Assert.Equal(1, length);
|
|
}),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " diff",
|
|
TokenClassification.InlinePrediction, " --cached"))
|
|
));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Inline_DisablePrediction()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
using var disp = SetPrediction(PredictionSource.None, PredictionViewStyle.InlineView);
|
|
|
|
Test("ech", Keys(
|
|
'e', CheckThat(() => AssertScreenIs(1, TokenClassification.Command, 'e')),
|
|
'c', CheckThat(() => AssertScreenIs(1, TokenClassification.Command, "ec")),
|
|
'h', CheckThat(() => AssertScreenIs(1, TokenClassification.Command, "ech")),
|
|
_.Enter, CheckThat(() => AssertScreenIs(1, TokenClassification.Command, "ech"))
|
|
));
|
|
}
|
|
|
|
[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);
|
|
|
|
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
|
|
PSConsoleReadLine.SetOptions(new SetPSReadLineOption { Colors = new Hashtable() { { "InlinePrediction", predictionColor } } });
|
|
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("ech", Keys(
|
|
'e', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'e',
|
|
predictionColorToCheck, "ca -zoo")),
|
|
'c', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
predictionColorToCheck, "a -zoo")),
|
|
'h', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
predictionColorToCheck, "o -bar")),
|
|
// Once accepted, the suggestion text should be blanked out.
|
|
_.Enter, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech"))
|
|
));
|
|
}
|
|
|
|
[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");
|
|
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
|
|
|
|
// Accept partial suggestion.
|
|
Test("git checkout ", Keys(
|
|
"git ch", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " ch",
|
|
TokenClassification.InlinePrediction, "eckout -b branch origin/bbbb")),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " checkout ",
|
|
TokenClassification.InlinePrediction, "-b branch origin/bbbb")),
|
|
_.Enter, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " checkout "))
|
|
));
|
|
|
|
// Get the last command line from history, and revert the line.
|
|
// 'RevertLine' will undo all edits of the history command.
|
|
Test("", Keys(
|
|
_.UpArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " checkout ")),
|
|
_.Escape));
|
|
}
|
|
|
|
[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);
|
|
|
|
SetHistory("echo -bar", "eca -zoo");
|
|
Test("echo", Keys(
|
|
'e', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, 'e',
|
|
TokenClassification.InlinePrediction, "ca -zoo")),
|
|
'c', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ec",
|
|
TokenClassification.InlinePrediction, "a -zoo")),
|
|
'h', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
TokenClassification.InlinePrediction, "o -bar")),
|
|
CheckThat(() => AssertCursorLeftIs(3)),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "echo",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-bar")),
|
|
CheckThat(() => AssertCursorLeftIs(9)),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
TokenClassification.InlinePrediction, "o -bar")),
|
|
CheckThat(() => AssertCursorLeftIs(3)),
|
|
|
|
// Suggestion should be cleared when switching to the command mode.
|
|
_.Escape, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
|
|
'i', _.RightArrow, 'o',
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "echo",
|
|
TokenClassification.InlinePrediction, " -bar")),
|
|
CheckThat(() => AssertCursorLeftIs(4)),
|
|
|
|
_.RightArrow, _.Escape,
|
|
CheckThat(() => AssertCursorLeftIs(8)),
|
|
_.Ctrl_z, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "echo")),
|
|
CheckThat(() => AssertCursorLeftIs(3))
|
|
));
|
|
}
|
|
|
|
[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);
|
|
|
|
SetHistory("echo -bar");
|
|
Test("ech", Keys(
|
|
"abcd",
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "abcd")),
|
|
_.Escape, 'S',
|
|
CheckThat(() => AssertLineIs(string.Empty)),
|
|
CheckThat(() => AssertCursorLeftIs(0)),
|
|
"ech",
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
TokenClassification.InlinePrediction, "o -bar")),
|
|
_.RightArrow,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "echo",
|
|
TokenClassification.None, ' ',
|
|
TokenClassification.Parameter, "-bar")),
|
|
CheckThat(() => AssertCursorLeftIs(9)),
|
|
_.Ctrl_z,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "ech",
|
|
TokenClassification.InlinePrediction, "o -bar"))));
|
|
}
|
|
|
|
private const uint MiniSessionId = 56;
|
|
private static readonly Guid predictorId_0 = Guid.Parse("c69fa9fc-6157-4877-8cf4-a9c83a0128af");
|
|
private static readonly Guid predictorId_1 = Guid.Parse("b45b5fbe-90fa-486c-9c87-e7940fdd6273");
|
|
private static readonly Guid predictorId_2 = Guid.Parse("74a86463-033b-44a3-b386-41ee191c94be");
|
|
private static readonly Guid predictorId_3 = Guid.Parse("19e98622-99e0-41f7-9ee0-a8bed92cde51");
|
|
|
|
/// <summary>
|
|
/// Mocked implementation of 'PredictInput'.
|
|
/// </summary>
|
|
internal static List<PredictionResult> MockedPredictInput(Ast ast, Token[] tokens)
|
|
{
|
|
var ctor = typeof(PredictionResult).GetConstructor(
|
|
BindingFlags.NonPublic | BindingFlags.Instance, null,
|
|
new[] { typeof(Guid), typeof(string), typeof(uint), typeof(List<PredictiveSuggestion>) }, null);
|
|
|
|
var input = ast.Extent.Text;
|
|
if (input is "netsh")
|
|
{
|
|
return null;
|
|
}
|
|
else if (input is "tooltip")
|
|
{
|
|
var suggestions_0 = new List<PredictiveSuggestion>
|
|
{
|
|
new PredictiveSuggestion("tooltip NO1", "Hello\nBinary\nWorld\nPowerShell is a task automation and configuration management program from Microsoft"),
|
|
new PredictiveSuggestion("tooltip NO2", "Hello\nWorld"),
|
|
};
|
|
|
|
return new List<PredictionResult>
|
|
{
|
|
(PredictionResult)ctor.Invoke(
|
|
new object[] { predictorId_0, "Tooltip", MiniSessionId, suggestions_0 }),
|
|
};
|
|
}
|
|
|
|
var suggestions_1 = new List<PredictiveSuggestion>
|
|
{
|
|
new PredictiveSuggestion($"SOME TEXT BEFORE {input}"),
|
|
new PredictiveSuggestion($"{input} SOME TEXT AFTER"),
|
|
};
|
|
var suggestions_2 = new List<PredictiveSuggestion>
|
|
{
|
|
new PredictiveSuggestion($"SOME NEW TEXT"),
|
|
};
|
|
|
|
var result = new List<PredictionResult>
|
|
{
|
|
(PredictionResult)ctor.Invoke(
|
|
new object[] { predictorId_1, "TestPredictor", MiniSessionId, suggestions_1 }),
|
|
(PredictionResult)ctor.Invoke(
|
|
new object[] { predictorId_2, "LongNamePredictor", MiniSessionId, suggestions_2 }),
|
|
};
|
|
|
|
// Return an extra source if it's for testing the metadata line.
|
|
if (input == "metadata-line")
|
|
{
|
|
result.Add((PredictionResult)ctor.Invoke(
|
|
new object[] { predictorId_3, "Metadata", MiniSessionId, suggestions_2 }));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
[SkippableFact]
|
|
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));
|
|
using var disp = SetPrediction(PredictionSource.Plugin, PredictionViewStyle.InlineView);
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// When plugin returns any results, it will be used for inline view.
|
|
SetHistory("git diff");
|
|
Test("git SOME TEXT AFTER", Keys(
|
|
"git", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.InlinePrediction, " SOME TEXT AFTER")),
|
|
// `OnSuggestionDisplayed` should be fired for only one predictor because we are in 'inline' view.
|
|
CheckThat(() => AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, -1)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
// 'ctrl+f' will trigger 'OnSuggestionAccepted'.
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME ",
|
|
TokenClassification.InlinePrediction, "TEXT AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Equal("git SOME TEXT AFTER", _mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
// Subsequent 'ctrl+f' or 'rightarrow' on the same suggestion won't trigger 'OnSuggestionAccepted' again.
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME TEXT ",
|
|
TokenClassification.InlinePrediction, "AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME TEXT AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory))
|
|
));
|
|
|
|
// 'Enter' will trigger 'OnCommandLineAccepted'.
|
|
Assert.Empty(_mockedMethods.displayedSuggestions);
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.NotNull(_mockedMethods.commandHistory);
|
|
Assert.Equal(2, _mockedMethods.commandHistory.Count);
|
|
Assert.Equal("git diff", _mockedMethods.commandHistory[0]);
|
|
Assert.Equal("git SOME TEXT AFTER", _mockedMethods.commandHistory[1]);
|
|
|
|
// When plugin doesn't return any results, no suggestion will be shown for inline view because history is not used.
|
|
// The mocked implementation of 'PredictInput' treats 'netsh' as the hint to return nothing.
|
|
_mockedMethods.ClearPredictionFields();
|
|
SetHistory("netsh show me");
|
|
Test("netsh", Keys(
|
|
"netsh", CheckThat(() => AssertScreenIs(1, TokenClassification.Command, "netsh"))
|
|
));
|
|
|
|
// 'Enter' will trigger 'OnCommandLineAccepted', because plugin is in use.
|
|
// Also, we still have `OnSuggestionDisplayed` fired, from the typing of each character of `nets`.
|
|
AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, -1);
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.NotNull(_mockedMethods.commandHistory);
|
|
Assert.Equal(2, _mockedMethods.commandHistory.Count);
|
|
Assert.Equal("netsh show me", _mockedMethods.commandHistory[0]);
|
|
Assert.Equal("netsh", _mockedMethods.commandHistory[1]);
|
|
}
|
|
|
|
[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,
|
|
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
|
|
using var disp = SetPrediction(PredictionSource.HistoryAndPlugin, PredictionViewStyle.InlineView);
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// When plugin returns any results, it will be used for inline view.
|
|
SetHistory("git diff");
|
|
Test("git SOME TEXT AFTER", Keys(
|
|
"git", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.InlinePrediction, " SOME TEXT AFTER")),
|
|
// `OnSuggestionDisplayed` should be fired for only one predictor because we are in 'inline' view.
|
|
CheckThat(() => AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, -1)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME ",
|
|
TokenClassification.InlinePrediction, "TEXT AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Equal("git SOME TEXT AFTER", _mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME TEXT ",
|
|
TokenClassification.InlinePrediction, "AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "git",
|
|
TokenClassification.None, " SOME TEXT AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory))
|
|
));
|
|
|
|
Assert.Empty(_mockedMethods.displayedSuggestions);
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.NotNull(_mockedMethods.commandHistory);
|
|
Assert.Equal(2, _mockedMethods.commandHistory.Count);
|
|
Assert.Equal("git diff", _mockedMethods.commandHistory[0]);
|
|
Assert.Equal("git SOME TEXT AFTER", _mockedMethods.commandHistory[1]);
|
|
|
|
// When plugin doesn't return any results, history will be used for inline view.
|
|
// The mocked implementation of 'PredictInput' treats 'netsh' as the hint to return nothing.
|
|
_mockedMethods.ClearPredictionFields();
|
|
SetHistory("netsh show me");
|
|
Test("netsh show me", Keys(
|
|
"netsh", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.InlinePrediction, " show me")),
|
|
// Yeah, we still have `OnSuggestionDisplayed` fired, from the typing of each character of `nets`.
|
|
CheckThat(() => AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, -1)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
// 'ctrl+f' won't trigger 'OnSuggestionAccepted' as the suggestion is from history.
|
|
_.Ctrl_f, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.None, " show ",
|
|
TokenClassification.InlinePrediction, "me")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
// 'rightarrow' won't trigger 'OnSuggestionAccepted' as the suggestion is from history.
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.None, " show me")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory))
|
|
));
|
|
|
|
Assert.Empty(_mockedMethods.displayedSuggestions);
|
|
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
|
|
Assert.Null(_mockedMethods.acceptedSuggestion);
|
|
Assert.NotNull(_mockedMethods.commandHistory);
|
|
Assert.Single(_mockedMethods.commandHistory);
|
|
Assert.Equal("netsh show me", _mockedMethods.commandHistory[0]);
|
|
|
|
_mockedMethods.ClearPredictionFields();
|
|
SetHistory("netsh show me");
|
|
Test("netsh SOME TEXT AFTER", Keys(
|
|
"netsh", CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.InlinePrediction, " show me")),
|
|
// Yeah, we still have `OnSuggestionDisplayed` fired, from the typing of each character of `nets`.
|
|
CheckThat(() => AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, countOrIndex: -1)),
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
|
|
// Now mimic pressing a space key. This will trigger the refreshing of suggestions even though the
|
|
// current history suggestion still applies to the new input, because plugin is in use and we favor
|
|
// plugin over history results.
|
|
' ', CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.None, " ",
|
|
TokenClassification.InlinePrediction, " SOME TEXT AFTER")),
|
|
CheckThat(() => AssertDisplayedSuggestions(count: 1, predictorId_1, MiniSessionId, countOrIndex: -1)),
|
|
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
|
|
|
|
CheckThat(() => _mockedMethods.ClearPredictionFields()),
|
|
// 'RightArrow' will trigger 'OnSuggestionAccepted' as the suggestion is now from plugin.
|
|
_.RightArrow, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "netsh",
|
|
TokenClassification.None, " SOME TEXT AFTER")),
|
|
CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
|
|
CheckThat(() => Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId)),
|
|
CheckThat(() => Assert.Equal("netsh SOME TEXT AFTER", _mockedMethods.acceptedSuggestion)),
|
|
CheckThat(() => Assert.Null(_mockedMethods.commandHistory))
|
|
));
|
|
|
|
Assert.Empty(_mockedMethods.displayedSuggestions);
|
|
Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId);
|
|
Assert.Equal("netsh SOME TEXT AFTER", _mockedMethods.acceptedSuggestion);
|
|
Assert.NotNull(_mockedMethods.commandHistory);
|
|
Assert.Equal(2, _mockedMethods.commandHistory.Count);
|
|
Assert.Equal("netsh show me", _mockedMethods.commandHistory[0]);
|
|
Assert.Equal("netsh SOME TEXT AFTER", _mockedMethods.commandHistory[1]);
|
|
_mockedMethods.ClearPredictionFields();
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Inline_NoneSource_ExecutionStatus()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
using var disp = SetPrediction(PredictionSource.None, PredictionViewStyle.InlineView);
|
|
|
|
// The last accepted command line would be "yay" after this.
|
|
Test("yay", Keys("yay"));
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// We always pass in 'true' as the execution status of the last command line,
|
|
// and that feedback will be reported when
|
|
// 1. the plugin source is in use;
|
|
// 2. the last accepted command is not a whitespace string.
|
|
Test(" ", Keys(
|
|
// Since we set the prediction source to be 'None', this feedback won't be reported.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
" "));
|
|
|
|
Test("abc", Keys(
|
|
// The prediction source is 'None', and the last accepted command is a whitespace string,
|
|
// so this feedback won't be reported.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"abc"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Inline_HistorySource_ExecutionStatus()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
|
|
|
|
// The last accepted command line would be "yay" after this.
|
|
Test("yay", Keys("yay"));
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// We always pass in 'true' as the execution status of the last command line,
|
|
// and that feedback will be reported when
|
|
// 1. the plugin source is in use;
|
|
// 2. the last accepted command is not a whitespace string.
|
|
Test(" ", Keys(
|
|
// Since we set the prediction source to be 'History', this feedback won't be reported.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
" "));
|
|
|
|
Test("abc", Keys(
|
|
// The plugin source is in use, but the last accepted command is a whitespace string.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"abc"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Inline_PluginSource_ExecutionStatus()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
using var disp = SetPrediction(PredictionSource.Plugin, PredictionViewStyle.InlineView);
|
|
|
|
// The last accepted command line would be an empty string after this.
|
|
Test("", Keys(_.Enter));
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// We always pass in 'true' as the execution status of the last command line,
|
|
// and that feedback will be reported when
|
|
// 1. the plugin source is in use;
|
|
// 2. the last accepted command is not a whitespace string.
|
|
Test("yay", Keys(
|
|
// The plugin source is in use, but the last accepted command is an empty string.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"yay"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
|
|
// The last accepted command line would be a whitespace string with 3 space characters after this.
|
|
Test(" ", Keys(
|
|
// The plugin source is in use, and the last accepted command is "yay".
|
|
CheckThat(() => Assert.True(_mockedMethods.lastCommandRunStatus)),
|
|
" "));
|
|
|
|
Assert.True(_mockedMethods.lastCommandRunStatus);
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
Test("abc", Keys(
|
|
// The plugin source is in use, but the last accepted command is a whitespace string.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"abc"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Inline_HistoryAndPluginSource_ExecutionStatus()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
using var disp = SetPrediction(PredictionSource.HistoryAndPlugin, PredictionViewStyle.InlineView);
|
|
|
|
// The last accepted command line would be an empty string after this.
|
|
Test("", Keys(_.Enter));
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
// We always pass in 'true' as the execution status of the last command line,
|
|
// and that feedback will be reported when
|
|
// 1. the plugin source is in use;
|
|
// 2. the last accepted command is not a whitespace string.
|
|
Test("yay", Keys(
|
|
// The plugin source is in use, but the last accepted command is an empty string.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"yay"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
|
|
// The last accepted command line would be a whitespace string with 3 space characters after this.
|
|
Test(" ", Keys(
|
|
// The plugin source is in use, and the last accepted command is "yay".
|
|
CheckThat(() => Assert.True(_mockedMethods.lastCommandRunStatus)),
|
|
" "));
|
|
|
|
Assert.True(_mockedMethods.lastCommandRunStatus);
|
|
_mockedMethods.ClearPredictionFields();
|
|
|
|
Test("abc", Keys(
|
|
// The plugin source is in use, but the last accepted command is a whitespace string.
|
|
CheckThat(() => Assert.Null(_mockedMethods.lastCommandRunStatus)),
|
|
"abc"));
|
|
|
|
Assert.Null(_mockedMethods.lastCommandRunStatus);
|
|
}
|
|
|
|
[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);
|
|
|
|
// Truncate long suggestion to make sure the user input is not scrolled up-off the console buffer.
|
|
SetHistory(new string('v', 25));
|
|
Test("vv", Keys(
|
|
'v', CheckThat(() => AssertScreenIs(2,
|
|
TokenClassification.Command, 'v',
|
|
TokenClassification.InlinePrediction, new string('v', 9),
|
|
TokenClassification.InlinePrediction, new string('v', 6) + "...")),
|
|
'v', CheckThat(() => AssertScreenIs(2,
|
|
TokenClassification.Command, "vv",
|
|
TokenClassification.InlinePrediction, new string('v', 8),
|
|
TokenClassification.InlinePrediction, new string('v', 6) + "...")),
|
|
// Once accepted, the suggestion text should be blanked out.
|
|
_.Enter, CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "vv"))
|
|
));
|
|
}
|
|
}
|
|
}
|