diff --git a/MockPSConsole/MockPSConsole.csproj b/MockPSConsole/MockPSConsole.csproj
index 4bca152fc..47b8bf414 100644
--- a/MockPSConsole/MockPSConsole.csproj
+++ b/MockPSConsole/MockPSConsole.csproj
@@ -4,7 +4,7 @@
Exe
MockPSConsole
MockPSConsole
- net461;net5.0
+ net461;net6.0
512
Program.manifest
true
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/PSReadLine.build.ps1 b/PSReadLine.build.ps1
index 620019ffd..23ec7f425 100644
--- a/PSReadLine.build.ps1
+++ b/PSReadLine.build.ps1
@@ -19,7 +19,7 @@ param(
[ValidateSet("Debug", "Release")]
[string]$Configuration = (property Configuration Release),
- [ValidateSet("net461", "net5.0")]
+ [ValidateSet("net461", "net6.0")]
[string]$Framework,
[switch]$CheckHelpContent
@@ -32,7 +32,7 @@ $targetDir = "bin/$Configuration/PSReadLine"
if (-not $Framework)
{
- $Framework = if ($PSVersionTable.PSEdition -eq "Core") { "net5.0" } else { "net461" }
+ $Framework = if ($PSVersionTable.PSEdition -eq "Core") { "net6.0" } else { "net461" }
}
Write-Verbose "Building for '$Framework'" -Verbose
@@ -65,9 +65,9 @@ $mockPSConsoleParams = @{
Synopsis: Build the Polyfiller assembly
#>
task BuildPolyfiller @polyFillerParams -If ($Framework -eq "net461") {
- ## Build both "net461" and "net5.0"
+ ## Build both "net461" and "net6.0"
exec { dotnet publish -f "net461" -c $Configuration Polyfill }
- exec { dotnet publish -f "net5.0" -c $Configuration Polyfill }
+ exec { dotnet publish -f "net6.0" -c $Configuration Polyfill }
}
<#
@@ -132,12 +132,12 @@ task LayoutModule BuildPolyfiller, BuildMainModule, {
if (-not (Test-Path "$targetDir/net461")) {
New-Item "$targetDir/net461" -ItemType Directory -Force > $null
}
- if (-not (Test-Path "$targetDir/net5.0")) {
- New-Item "$targetDir/net5.0" -ItemType Directory -Force > $null
+ if (-not (Test-Path "$targetDir/net6plus")) {
+ New-Item "$targetDir/net6plus" -ItemType Directory -Force > $null
}
Copy-Item "Polyfill/bin/$Configuration/net461/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net461" -Force
- Copy-Item "Polyfill/bin/$Configuration/net5.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net5.0" -Force
+ Copy-Item "Polyfill/bin/$Configuration/net6.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net6plus" -Force
}
$binPath = "PSReadLine/bin/$Configuration/$Framework/publish"
diff --git a/PSReadLine/OnImportAndRemove.cs b/PSReadLine/OnImportAndRemove.cs
index 653fa8e0c..644c9f09f 100644
--- a/PSReadLine/OnImportAndRemove.cs
+++ b/PSReadLine/OnImportAndRemove.cs
@@ -28,7 +28,7 @@ private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
}
string root = Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location);
- string subd = (Environment.Version.Major >= 5) ? "net5.0" : "net461";
+ string subd = (Environment.Version.Major >= 6) ? "net6plus" : "net461";
string path = Path.Combine(root, subd, "Microsoft.PowerShell.PSReadLine.Polyfiller.dll");
return Assembly.LoadFrom(path);
diff --git a/PSReadLine/Options.cs b/PSReadLine/Options.cs
index ca7aa77e6..19f366513 100644
--- a/PSReadLine/Options.cs
+++ b/PSReadLine/Options.cs
@@ -143,7 +143,7 @@ private void SetOptionsInternal(SetPSReadLineOption options)
}
bool notTest = ReferenceEquals(_mockableMethods, this);
- if ((options.PredictionSource & PredictionSource.Plugin) != 0 && Environment.Version.Major < 5 && notTest)
+ if ((options.PredictionSource & PredictionSource.Plugin) != 0 && Environment.Version.Major < 6 && notTest)
{
throw new ArgumentException(PSReadLineResources.PredictionPluginNotSupported);
}
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index a01628906..e041fa0d4 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -9,7 +9,7 @@
2.2.0
2.2.0-beta1
true
- net461;net5.0
+ net461;net6.0
true
9.0
@@ -20,8 +20,8 @@
-
-
+
+
diff --git a/PSReadLine/PSReadLine.sln b/PSReadLine/PSReadLine.sln
index 93c5b6c7e..3ce7ce89f 100644
--- a/PSReadLine/PSReadLine.sln
+++ b/PSReadLine/PSReadLine.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSReadLine", "PSReadLine.csproj", "{615788CB-1B9A-4B34-97B3-4608686E59CA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Polyfill", "..\Polyfill\Polyfill.csproj", "{DE521A7D-A3BE-4A07-BE75-5AB7D87E799D}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MockPSConsole", "..\MockPSConsole\MockPSConsole.csproj", "{08218B1A-8B85-4722-9E3F-4D6C0BF58AD8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSReadLine.Tests", "..\test\PSReadLine.Tests.csproj", "{8ED51D01-158C-4B29-824A-35B9B861E45A}"
diff --git a/PSReadLine/PSReadLineResources.Designer.cs b/PSReadLine/PSReadLineResources.Designer.cs
index bde0f9e37..b576eb53f 100644
--- a/PSReadLine/PSReadLineResources.Designer.cs
+++ b/PSReadLine/PSReadLineResources.Designer.cs
@@ -705,7 +705,7 @@ internal static string PredictiveSuggestionNotSupported {
}
///
- /// Looks up a localized string similar to The prediction plugin source is not supported in this version of PowerShell. The 7.1 or a higher version of PowerShell is required to use this source.
+ /// Looks up a localized string similar to The prediction plugin source is not supported in this version of PowerShell. The 7.2 or a higher version of PowerShell is required to use this source.
///
internal static string PredictionPluginNotSupported
{
diff --git a/PSReadLine/PSReadLineResources.resx b/PSReadLine/PSReadLineResources.resx
index 1d042bda4..7c9044318 100644
--- a/PSReadLine/PSReadLineResources.resx
+++ b/PSReadLine/PSReadLineResources.resx
@@ -815,7 +815,7 @@ Or not saving history with:
The predictive suggestion feature cannot be enabled because the console output doesn't support virtual terminal processing or it's redirected.
- The prediction plugin source is not supported in this version of PowerShell. The 7.1 or a higher version of PowerShell is required to use this source.
+ The prediction plugin source is not supported in this version of PowerShell. The 7.2 or a higher version of PowerShell is required to use this source.
Delete the current logical line and up to the end of the multiline buffer
diff --git a/PSReadLine/Prediction.Entry.cs b/PSReadLine/Prediction.Entry.cs
index 4d619727c..a60d05e2e 100644
--- a/PSReadLine/Prediction.Entry.cs
+++ b/PSReadLine/Prediction.Entry.cs
@@ -15,14 +15,21 @@ public partial class PSConsoleReadLine
private struct SuggestionEntry
{
internal readonly Guid PredictorId;
+ internal readonly uint? PredictorSession;
internal readonly string Source;
internal readonly string SuggestionText;
internal readonly int InputMatchIndex;
- internal SuggestionEntry(string soruce, Guid id, string suggestion, int matchIndex)
+ internal SuggestionEntry(string suggestion, int matchIndex)
+ : this(source: "History", predictorId: Guid.Empty, predictorSession: null, suggestion, matchIndex)
{
- Source = soruce;
- PredictorId = id;
+ }
+
+ internal SuggestionEntry(string source, Guid predictorId, uint? predictorSession, string suggestion, int matchIndex)
+ {
+ Source = source;
+ PredictorId = predictorId;
+ PredictorSession = predictorSession;
SuggestionText = suggestion;
InputMatchIndex = matchIndex;
}
diff --git a/PSReadLine/Prediction.Views.cs b/PSReadLine/Prediction.Views.cs
index f585194a2..ad207e95b 100644
--- a/PSReadLine/Prediction.Views.cs
+++ b/PSReadLine/Prediction.Views.cs
@@ -128,7 +128,6 @@ protected string GetOneHistorySuggestion(string text)
/// Maximum number of results to return.
protected List GetHistorySuggestions(string input, int count)
{
- const string source = "History";
List results = null;
int remainingCount = count;
@@ -164,7 +163,7 @@ protected List GetHistorySuggestions(string input, int count)
_cacheHistorySet.Add(line);
if (matchIndex == 0)
{
- results.Add(new SuggestionEntry(source, Guid.Empty, line, matchIndex));
+ results.Add(new SuggestionEntry(line, matchIndex));
if (--remainingCount == 0)
{
break;
@@ -172,7 +171,7 @@ protected List GetHistorySuggestions(string input, int count)
}
else if (_cacheHistoryList.Count < remainingCount)
{
- _cacheHistoryList.Add(new SuggestionEntry(source, Guid.Empty, line, matchIndex));
+ _cacheHistoryList.Add(new SuggestionEntry(line, matchIndex));
}
}
@@ -440,11 +439,19 @@ private void AggregateSuggestions()
break;
}
- for (int i = 0; i < _cacheList2[index]; i++)
+ int num = _cacheList2[index];
+ for (int i = 0; i < num; i++)
{
string sugText = item.Suggestions[i].SuggestionText ?? string.Empty;
int matchIndex = sugText.IndexOf(_inputText, comparison);
- _listItems.Add(new SuggestionEntry(item.Name, item.Id, sugText, matchIndex));
+ _listItems.Add(new SuggestionEntry(item.Name, item.Id, item.Session, sugText, matchIndex));
+ }
+
+ if (item.Session.HasValue)
+ {
+ // Send feedback only if the mini-session id is specified.
+ // When it's not specified, we consider the predictor doesn't accept feedback.
+ _singleton._mockableMethods.OnSuggestionDisplayed(item.Id, item.Session.Value, num);
}
}
}
@@ -517,9 +524,11 @@ internal override void OnSuggestionAccepted()
if (_listItems != null && _selectedIndex != -1)
{
var item = _listItems[_selectedIndex];
- if (item.PredictorId != Guid.Empty)
+ if (item.PredictorSession.HasValue)
{
- _singleton._mockableMethods.OnSuggestionAccepted(item.PredictorId, item.SuggestionText);
+ // Send feedback only if the mini-session id is specified.
+ // When it's not specified, we consider the predictor doesn't accept feedback.
+ _singleton._mockableMethods.OnSuggestionAccepted(item.PredictorId, item.PredictorSession.Value, item.SuggestionText);
}
}
}
@@ -577,6 +586,7 @@ internal void UpdateListSelection(int move)
private class PredictionInlineView : PredictionViewBase
{
private Guid _predictorId;
+ private uint? _predictorSession;
private string _suggestionText;
private string _lastInputText;
private bool _alreadyAccepted;
@@ -613,6 +623,7 @@ internal override void GetSuggestion(string userInput)
{
_suggestionText = GetOneHistorySuggestion(userInput);
_predictorId = Guid.Empty;
+ _predictorSession = null;
}
}
}
@@ -634,15 +645,27 @@ private void AggregateSuggestions()
continue;
}
+ int index = 0;
foreach (var sug in item.Suggestions)
{
if (sug.SuggestionText != null &&
sug.SuggestionText.StartsWith(_inputText, _singleton._options.HistoryStringComparison))
{
_predictorId = item.Id;
+ _predictorSession = item.Session;
_suggestionText = sug.SuggestionText;
+
+ if (_predictorSession.HasValue)
+ {
+ // Send feedback only if the mini-session id is specified.
+ // When it's not specified, we consider the predictor doesn't accept feedback.
+ _singleton._mockableMethods.OnSuggestionDisplayed(_predictorId, _predictorSession.Value, -index);
+ }
+
return;
}
+
+ index++;
}
}
}
@@ -673,10 +696,13 @@ internal override void OnSuggestionAccepted()
return;
}
- if (!_alreadyAccepted && _suggestionText != null && _predictorId != Guid.Empty)
+ if (!_alreadyAccepted && _suggestionText != null && _predictorSession.HasValue)
{
_alreadyAccepted = true;
- _singleton._mockableMethods.OnSuggestionAccepted(_predictorId, _suggestionText);
+
+ // Send feedback only if the mini-session id is specified.
+ // When it's not specified, we consider the predictor doesn't accept feedback.
+ _singleton._mockableMethods.OnSuggestionAccepted(_predictorId, _predictorSession.Value, _suggestionText);
}
}
@@ -725,6 +751,7 @@ internal override void Reset()
base.Reset();
_suggestionText = _lastInputText = null;
_predictorId = Guid.Empty;
+ _predictorSession = null;
_alreadyAccepted = false;
}
diff --git a/PSReadLine/Prediction.cs b/PSReadLine/Prediction.cs
index aa33fb8c6..597cb02c5 100644
--- a/PSReadLine/Prediction.cs
+++ b/PSReadLine/Prediction.cs
@@ -16,23 +16,31 @@ namespace Microsoft.PowerShell
{
public partial class PSConsoleReadLine
{
+ private const string PSReadLine = "PSReadLine";
+
// Stub helper methods so prediction can be mocked
[ExcludeFromCodeCoverage]
Task> IPSConsoleReadLineMockableMethods.PredictInput(Ast ast, Token[] tokens)
{
- return CommandPrediction.PredictInput(ast, tokens);
+ return CommandPrediction.PredictInput(PSReadLine, ast, tokens);
}
[ExcludeFromCodeCoverage]
- void IPSConsoleReadLineMockableMethods.OnCommandLineAccepted(IReadOnlyList history)
+ void IPSConsoleReadLineMockableMethods.OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex)
{
- CommandPrediction.OnCommandLineAccepted(history);
+ CommandPrediction.OnSuggestionDisplayed(PSReadLine, predictorId, session, countOrIndex);
}
[ExcludeFromCodeCoverage]
- void IPSConsoleReadLineMockableMethods.OnSuggestionAccepted(Guid predictorId, string suggestionText)
+ void IPSConsoleReadLineMockableMethods.OnSuggestionAccepted(Guid predictorId, uint session, string suggestionText)
+ {
+ CommandPrediction.OnSuggestionAccepted(PSReadLine, predictorId, session, suggestionText);
+ }
+
+ [ExcludeFromCodeCoverage]
+ void IPSConsoleReadLineMockableMethods.OnCommandLineAccepted(IReadOnlyList history)
{
- CommandPrediction.OnSuggestionAccepted(predictorId, suggestionText);
+ CommandPrediction.OnCommandLineAccepted(PSReadLine, history);
}
private readonly Prediction _prediction;
diff --git a/PSReadLine/PublicAPI.cs b/PSReadLine/PublicAPI.cs
index 945c45a36..e661b29a9 100644
--- a/PSReadLine/PublicAPI.cs
+++ b/PSReadLine/PublicAPI.cs
@@ -28,7 +28,8 @@ public interface IPSConsoleReadLineMockableMethods
bool RunspaceIsRemote(Runspace runspace);
Task> PredictInput(Ast ast, Token[] tokens);
void OnCommandLineAccepted(IReadOnlyList history);
- void OnSuggestionAccepted(Guid predictorId, string suggestionText);
+ void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex);
+ void OnSuggestionAccepted(Guid predictorId, uint session, string suggestionText);
void RenderFullHelp(string content, string regexPatternToScrollTo);
object GetDynamicHelpContent(string commandName, string parameterName, bool isFullHelp);
}
diff --git a/PSReadLine/ReadLine.cs b/PSReadLine/ReadLine.cs
index 62e7c2227..c3912371a 100644
--- a/PSReadLine/ReadLine.cs
+++ b/PSReadLine/ReadLine.cs
@@ -668,7 +668,7 @@ private PSConsoleReadLine()
}
if (hostName == null)
{
- hostName = "PSReadLine";
+ hostName = PSReadLine;
}
_options = new PSConsoleReadLineOptions(hostName);
_prediction = new Prediction(this);
diff --git a/Polyfill/CommandPrediction.cs b/Polyfill/CommandPrediction.cs
index b5792086e..3b0c18aa6 100644
--- a/Polyfill/CommandPrediction.cs
+++ b/Polyfill/CommandPrediction.cs
@@ -23,16 +23,24 @@ public sealed class PredictionResult
[HiddenAttribute]
public string Name { get; }
+ ///
+ /// Gets the mini-session id that represents a specific invocation that returns this result.
+ /// When it's not specified, it's considered by a client that the predictor doesn't expect feedback.
+ ///
+ [HiddenAttribute]
+ public uint? Session { get; }
+
///
/// Gets the suggestions.
///
[HiddenAttribute]
public IReadOnlyList Suggestions { get; }
- internal PredictionResult(Guid id, string name, List suggestions)
+ internal PredictionResult(Guid id, string name, uint? session, List suggestions)
{
Id = id;
Name = name;
+ Session = session;
Suggestions = suggestions;
}
}
@@ -90,11 +98,12 @@ public static class CommandPrediction
///
/// Collect the predictive suggestions from registered predictors using the default timeout.
///
+ /// Represents the client that initiates the call.
/// The object from parsing the current command line input.
/// The objects from parsing the current command line input.
/// A list of objects.
[HiddenAttribute]
- public static Task> PredictInput(Ast ast, Token[] astTokens)
+ public static Task> PredictInput(string client, Ast ast, Token[] astTokens)
{
return null;
}
@@ -102,12 +111,13 @@ public static Task> PredictInput(Ast ast, Token[] astToke
///
/// Collect the predictive suggestions from registered predictors using the specified timeout.
///
+ /// Represents the client that initiates the call.
/// The object from parsing the current command line input.
/// The objects from parsing the current command line input.
/// The milliseconds to timeout.
/// A list of objects.
[HiddenAttribute]
- public static Task> PredictInput(Ast ast, Token[] astTokens, int millisecondsTimeout)
+ public static Task> PredictInput(string client, Ast ast, Token[] astTokens, int millisecondsTimeout)
{
return null;
}
@@ -115,19 +125,37 @@ public static Task> PredictInput(Ast ast, Token[] astToke
///
/// Allow registered predictors to do early processing when a command line is accepted.
///
+ /// Represents the client that initiates the call.
/// History command lines provided as references for prediction.
[HiddenAttribute]
- public static void OnCommandLineAccepted(IReadOnlyList history)
+ public static void OnCommandLineAccepted(string client, IReadOnlyList history)
+ {
+ }
+
+ ///
+ /// Send feedback to a predictor when one or more suggestions from it were displayed to the user.
+ ///
+ /// Represents the client that initiates the call.
+ /// The identifier of the predictor whose prediction result was accepted.
+ /// The mini-session where the displayed suggestions came from.
+ ///
+ /// When the value is > 0, it's the number of displayed suggestions from the list returned in , starting from the index 0.
+ /// When the value is <= 0, it means a single suggestion from the list got displayed, and the index is the absolute value.
+ ///
+ [HiddenAttribute]
+ public static void OnSuggestionDisplayed(string client, Guid predictorId, uint session, int countOrIndex)
{
}
///
/// Send feedback to predictors about their last suggestions.
///
+ /// Represents the client that initiates the call.
/// The identifier of the predictor whose prediction result was accepted.
+ /// The mini-session where the accepted suggestion came from.
/// The accepted suggestion text.
[HiddenAttribute]
- public static void OnSuggestionAccepted(Guid predictorId, string suggestionText)
+ public static void OnSuggestionAccepted(string client, Guid predictorId, uint session, string suggestionText)
{
}
}
diff --git a/Polyfill/Polyfill.csproj b/Polyfill/Polyfill.csproj
index 95cafac7d..1e0ff4d44 100644
--- a/Polyfill/Polyfill.csproj
+++ b/Polyfill/Polyfill.csproj
@@ -3,7 +3,7 @@
Microsoft.PowerShell.PSReadLine.Polyfiller
1.0.0.0
- net461;net5.0
+ net461;net6.0
true
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/build.ps1 b/build.ps1
index c3461c877..25e935bc0 100644
--- a/build.ps1
+++ b/build.ps1
@@ -39,7 +39,7 @@ param(
[ValidateSet("Debug", "Release")]
[string] $Configuration = "Debug",
- [ValidateSet("net461", "net5.0")]
+ [ValidateSet("net461", "net6.0")]
[string] $Framework
)
diff --git a/test/InlinePredictionTest.cs b/test/InlinePredictionTest.cs
index 91cc03a61..43fd485e9 100644
--- a/test/InlinePredictionTest.cs
+++ b/test/InlinePredictionTest.cs
@@ -364,6 +364,7 @@ public void Inline_AcceptSuggestionInVIMode()
));
}
+ private const uint MiniSessionId = 56;
private static readonly Guid predictorId_1 = Guid.Parse("b45b5fbe-90fa-486c-9c87-e7940fdd6273");
private static readonly Guid predictorId_2 = Guid.Parse("74a86463-033b-44a3-b386-41ee191c94be");
@@ -374,7 +375,7 @@ internal static List MockedPredictInput(Ast ast, Token[] token
{
var ctor = typeof(PredictionResult).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance, null,
- new[] { typeof(Guid), typeof(string), typeof(List) }, null);
+ new[] { typeof(Guid), typeof(string), typeof(uint), typeof(List) }, null);
var input = ast.Extent.Text;
if (input == "netsh")
@@ -395,9 +396,9 @@ internal static List MockedPredictInput(Ast ast, Token[] token
return new List
{
(PredictionResult)ctor.Invoke(
- new object[] { predictorId_1, "TestPredictor", suggestions_1 }),
+ new object[] { predictorId_1, "TestPredictor", MiniSessionId, suggestions_1 }),
(PredictionResult)ctor.Invoke(
- new object[] { predictorId_2, "LongNamePredictor", suggestions_2 }),
+ new object[] { predictorId_2, "LongNamePredictor", MiniSessionId, suggestions_2 }),
};
}
@@ -416,11 +417,15 @@ public void Inline_PluginSource_Acceptance()
"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)),
@@ -430,18 +435,21 @@ public void Inline_PluginSource_Acceptance()
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);
@@ -458,6 +466,8 @@ public void Inline_PluginSource_Acceptance()
));
// '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);
@@ -482,10 +492,14 @@ public void Inline_HistoryAndPluginSource_Acceptance()
"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)),
@@ -494,17 +508,20 @@ public void Inline_HistoryAndPluginSource_Acceptance()
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);
@@ -520,11 +537,15 @@ public void Inline_HistoryAndPluginSource_Acceptance()
"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)),
@@ -532,11 +553,13 @@ public void Inline_HistoryAndPluginSource_Acceptance()
_.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);
diff --git a/test/ListPredictionTest.cs b/test/ListPredictionTest.cs
index 24545122d..82c6978dd 100644
--- a/test/ListPredictionTest.cs
+++ b/test/ListPredictionTest.cs
@@ -37,6 +37,15 @@ private Disposable SetPrediction(PredictionSource source, PredictionViewStyle vi
new SetPSReadLineOption { PredictionSource = oldSource, PredictionViewStyle = oldView }));
}
+ private void AssertDisplayedSuggestions(int count, Guid predictorId, uint session, int countOrIndex)
+ {
+ Assert.Equal(count, _mockedMethods.displayedSuggestions.Count);
+ _mockedMethods.displayedSuggestions.TryGetValue(predictorId, out var tuple);
+ Assert.NotNull(tuple);
+ Assert.Equal(session, tuple.Item1);
+ Assert.Equal(countOrIndex, tuple.Item2);
+ }
+
[SkippableFact]
public void List_RenderSuggestion_NoMatching()
{
@@ -958,6 +967,10 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
+ CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.DownArrow,
CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, "SOME",
@@ -990,6 +1003,8 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the list.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Shift_Home,
CheckThat(() => AssertScreenIs(5,
TokenClassification.Selection, "SOME TEXT BEFORE ec",
@@ -1021,6 +1036,8 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when selecting the input.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
"j",
CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, "j",
@@ -1052,6 +1069,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
CheckThat(() => Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId)),
CheckThat(() => Assert.Equal("SOME TEXT BEFORE ec", _mockedMethods.acceptedSuggestion)),
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
@@ -1090,6 +1110,8 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the input.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Backspace,
CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, "SOME",
@@ -1124,6 +1146,9 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
CheckThat(() => Assert.Equal(predictorId_2, _mockedMethods.acceptedPredictorId)),
CheckThat(() => Assert.Equal("SOME NEW TEXT", _mockedMethods.acceptedSuggestion)),
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
@@ -1163,6 +1188,8 @@ public void List_PluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the input.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
// Once accepted, the list should be cleared.
_.Enter, CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "SOME",
@@ -1171,6 +1198,8 @@ public void List_PluginSource_Acceptance()
TokenClassification.None, new string(' ', windowWidth)))
));
+ // `OnSuggestionDisplayed` should not be fired when 'Enter' accepting the input.
+ Assert.Empty(_mockedMethods.displayedSuggestions);
Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId);
Assert.Equal("SOME NEW TEX SOME TEXT AFTER", _mockedMethods.acceptedSuggestion);
Assert.Equal(3, _mockedMethods.commandHistory.Count);
@@ -1240,6 +1269,10 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
+ CheckThat(() => _mockedMethods.ClearPredictionFields()),
_.DownArrow, _.Shift_Home,
CheckThat(() => AssertScreenIs(7,
TokenClassification.Selection, "eca -zoo",
@@ -1289,6 +1322,8 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the list.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
'j', CheckThat(() => AssertScreenIs(6,
TokenClassification.Command, "j",
NextLine,
@@ -1328,6 +1363,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
// Update the selected item won't trigger 'acceptance' callbacks if the item is from history.
CheckThat(() => Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId)),
CheckThat(() => Assert.Null(_mockedMethods.acceptedSuggestion)),
@@ -1374,6 +1412,8 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the list.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
_.Backspace,
CheckThat(() => AssertScreenIs(5,
TokenClassification.Command, "SOME",
@@ -1408,6 +1448,9 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should be fired for both predictors.
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_1, MiniSessionId, 2)),
+ CheckThat(() => AssertDisplayedSuggestions(count: 2, predictorId_2, MiniSessionId, 1)),
CheckThat(() => Assert.Equal(predictorId_2, _mockedMethods.acceptedPredictorId)),
CheckThat(() => Assert.Equal("SOME NEW TEXT", _mockedMethods.acceptedSuggestion)),
CheckThat(() => Assert.Null(_mockedMethods.commandHistory)),
@@ -1447,6 +1490,8 @@ public void List_HistoryAndPluginSource_Acceptance()
NextLine,
TokenClassification.None, new string(' ', windowWidth)
)),
+ // `OnSuggestionDisplayed` should not be fired when navigating the list.
+ CheckThat(() => Assert.Empty(_mockedMethods.displayedSuggestions)),
// Once accepted, the list should be cleared.
_.Enter, CheckThat(() => AssertScreenIs(2,
TokenClassification.Command, "SOME",
@@ -1455,6 +1500,7 @@ public void List_HistoryAndPluginSource_Acceptance()
TokenClassification.None, new string(' ', windowWidth)))
));
+ Assert.Empty(_mockedMethods.displayedSuggestions);
Assert.Equal(predictorId_1, _mockedMethods.acceptedPredictorId);
Assert.Equal("SOME NEW TEX SOME TEXT AFTER", _mockedMethods.acceptedSuggestion);
Assert.Equal(4, _mockedMethods.commandHistory.Count);
diff --git a/test/PSReadLine.Tests.csproj b/test/PSReadLine.Tests.csproj
index 4c1a993d1..c27ce274a 100644
--- a/test/PSReadLine.Tests.csproj
+++ b/test/PSReadLine.Tests.csproj
@@ -5,7 +5,7 @@
library
UnitTestPSReadLine
PSReadLine.Tests
- net461;net5.0
+ net461;net6.0
512
{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
False
@@ -23,8 +23,8 @@
-
-
+
+
diff --git a/test/UnitTestReadLine.cs b/test/UnitTestReadLine.cs
index 21d05ea4e..e3c5dd06b 100644
--- a/test/UnitTestReadLine.cs
+++ b/test/UnitTestReadLine.cs
@@ -26,12 +26,14 @@ internal class MockedMethods : IPSConsoleReadLineMockableMethods
internal Guid acceptedPredictorId;
internal string acceptedSuggestion;
internal string helpContentRendered;
+ internal Dictionary> displayedSuggestions = new Dictionary>();
internal void ClearPredictionFields()
{
commandHistory = null;
acceptedPredictorId = Guid.Empty;
acceptedSuggestion = null;
+ displayedSuggestions.Clear();
}
public void Ding()
@@ -63,7 +65,12 @@ public void OnCommandLineAccepted(IReadOnlyList history)
commandHistory = history;
}
- public void OnSuggestionAccepted(Guid predictorId, string suggestionText)
+ public void OnSuggestionDisplayed(Guid predictorId, uint session, int countOrIndex)
+ {
+ displayedSuggestions[predictorId] = Tuple.Create(session, countOrIndex);
+ }
+
+ public void OnSuggestionAccepted(Guid predictorId, uint session, string suggestionText)
{
acceptedPredictorId = predictorId;
acceptedSuggestion = suggestionText;
diff --git a/tools/helper.psm1 b/tools/helper.psm1
index d941a5fa0..c5e4e0d9e 100644
--- a/tools/helper.psm1
+++ b/tools/helper.psm1
@@ -1,5 +1,5 @@
-$MinimalSDKVersion = '5.0.100'
+$MinimalSDKVersion = '6.0.100-preview.1.21103.13'
$IsWindowsEnv = [System.Environment]::OSVersion.Platform -eq "Win32NT"
$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path
$LocalDotnetDirPath = if ($IsWindowsEnv) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" }