Skip to content

Commit fd1265e

Browse files
authored
Add the prediction ListView and also hook up with the CommandPrediction APIs introduced in PS 7.1 (#1909)
1 parent 3856776 commit fd1265e

38 files changed

+4282
-710
lines changed

MockPSConsole/MockPSConsole.csproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
<OutputType>Exe</OutputType>
55
<RootNamespace>MockPSConsole</RootNamespace>
66
<AssemblyName>MockPSConsole</AssemblyName>
7-
<TargetFrameworks>net461;netcoreapp3.1</TargetFrameworks>
7+
<TargetFrameworks>net461;net5.0</TargetFrameworks>
88
<FileAlignment>512</FileAlignment>
99
<ApplicationManifest>Program.manifest</ApplicationManifest>
10+
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
1011
</PropertyGroup>
1112

12-
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
13-
<RuntimeFrameworkVersion>3.1.6</RuntimeFrameworkVersion>
14-
</PropertyGroup>
15-
16-
<ItemGroup>
13+
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
1714
<PackageReference Include="System.Xml.XDocument" version="4.3.0" />
1815
<PackageReference Include="System.Data.DataSetExtensions" version="4.5.0" />
1916
<PackageReference Include="Microsoft.CSharp" version="4.5.0" />
20-
<PackageReference Include="PowerShellStandard.Library" version="5.1.0" Condition="'$(TargetFramework)' == 'net461'" />
21-
<PackageReference Include="Microsoft.PowerShell.SDK" version="7.0.3" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
17+
<PackageReference Include="PowerShellStandard.Library" version="5.1.0" />
18+
</ItemGroup>
19+
20+
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
21+
<PackageReference Include="Microsoft.PowerShell.SDK" version="7.1.0-rc.2" />
2222
</ItemGroup>
2323

2424
<ItemGroup>

PSReadLine.build.ps1

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ param(
1919
[ValidateSet("Debug", "Release")]
2020
[string]$Configuration = (property Configuration Release),
2121

22-
[ValidateSet("net461", "netcoreapp3.1")]
22+
[ValidateSet("net461", "net5.0")]
2323
[string]$Framework,
2424

2525
[switch]$CheckHelpContent
@@ -32,7 +32,7 @@ $targetDir = "bin/$Configuration/PSReadLine"
3232

3333
if (-not $Framework)
3434
{
35-
$Framework = if ($PSVersionTable.PSEdition -eq "Core") { "netcoreapp3.1" } else { "net461" }
35+
$Framework = if ($PSVersionTable.PSEdition -eq "Core") { "net5.0" } else { "net461" }
3636
}
3737

3838
Write-Verbose "Building for '$Framework'" -Verbose
@@ -41,8 +41,13 @@ function ConvertTo-CRLF([string] $text) {
4141
$text.Replace("`r`n","`n").Replace("`n","`r`n")
4242
}
4343

44+
$polyFillerParams = @{
45+
Inputs = { Get-ChildItem Polyfill/*.cs, Polyfill/Polyfill.csproj }
46+
Outputs = "Polyfill/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine.Polyfiller.dll"
47+
}
48+
4449
$binaryModuleParams = @{
45-
Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx }
50+
Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx, Polyfill/*.cs, Polyfill/Polyfill.csproj }
4651
Outputs = "PSReadLine/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine2.dll"
4752
}
4853

@@ -56,6 +61,15 @@ $mockPSConsoleParams = @{
5661
Outputs = "MockPSConsole/bin/$Configuration/$Framework/MockPSConsole.dll"
5762
}
5863

64+
<#
65+
Synopsis: Build the Polyfiller assembly
66+
#>
67+
task BuildPolyfiller @polyFillerParams -If ($Framework -eq "net461") {
68+
## Build both "net461" and "net5.0"
69+
exec { dotnet publish -f "net461" -c $Configuration Polyfill }
70+
exec { dotnet publish -f "net5.0" -c $Configuration Polyfill }
71+
}
72+
5973
<#
6074
Synopsis: Build main binary module
6175
#>
@@ -108,7 +122,7 @@ task CheckHelpContent -If $CheckHelpContent {
108122
<#
109123
Synopsis: Copy all of the files that belong in the module to one place in the layout for installation
110124
#>
111-
task LayoutModule BuildMainModule, {
125+
task LayoutModule BuildPolyfiller, BuildMainModule, {
112126
if (-not (Test-Path $targetDir -PathType Container)) {
113127
New-Item $targetDir -ItemType Directory -Force > $null
114128
}
@@ -120,22 +134,30 @@ task LayoutModule BuildMainModule, {
120134
'PSReadLine/PSReadLine.format.ps1xml',
121135
'PSReadLine/PSReadLine.psm1'
122136

123-
foreach ($file in $extraFiles)
124-
{
137+
foreach ($file in $extraFiles) {
125138
# ensure files have \r\n line endings as the signing tool only uses those endings to avoid mixed endings
126139
$content = Get-Content -Path $file -Raw
127140
Set-Content -Path (Join-Path $targetDir (Split-Path $file -Leaf)) -Value (ConvertTo-CRLF $content) -Force
128141
}
129142

143+
if ($Framework -eq "net461") {
144+
if (-not (Test-Path "$targetDir/net461")) {
145+
New-Item "$targetDir/net461" -ItemType Directory -Force > $null
146+
}
147+
if (-not (Test-Path "$targetDir/net5.0")) {
148+
New-Item "$targetDir/net5.0" -ItemType Directory -Force > $null
149+
}
150+
151+
Copy-Item "Polyfill/bin/$Configuration/net461/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net461" -Force
152+
Copy-Item "Polyfill/bin/$Configuration/net5.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net5.0" -Force
153+
}
154+
130155
$binPath = "PSReadLine/bin/$Configuration/$Framework/publish"
131156
Copy-Item $binPath/Microsoft.PowerShell.PSReadLine2.dll $targetDir
132157

133-
if (Test-Path $binPath/System.Runtime.InteropServices.RuntimeInformation.dll)
134-
{
158+
if (Test-Path $binPath/System.Runtime.InteropServices.RuntimeInformation.dll) {
135159
Copy-Item $binPath/System.Runtime.InteropServices.RuntimeInformation.dll $targetDir
136-
}
137-
else
138-
{
160+
} else {
139161
Write-Warning "Build using $Framework is not sufficient to be downlevel compatible"
140162
}
141163

@@ -145,8 +167,7 @@ task LayoutModule BuildMainModule, {
145167
$version = $versionInfo.FileVersion
146168
$semVer = $versionInfo.ProductVersion
147169

148-
if ($semVer -match "(.*)-(.*)")
149-
{
170+
if ($semVer -match "(.*)-(.*)") {
150171
# Make sure versions match
151172
if ($matches[1] -ne $version) { throw "AssemblyFileVersion mismatch with AssemblyInformationalVersion" }
152173
$prerelease = $matches[2]
@@ -159,8 +180,7 @@ task LayoutModule BuildMainModule, {
159180
$moduleManifestContent | Set-Content -Path $targetDir/PSReadLine.psd1
160181

161182
# Make sure we don't ship any read-only files
162-
foreach ($file in (Get-ChildItem -Recurse -File $targetDir))
163-
{
183+
foreach ($file in (Get-ChildItem -Recurse -File $targetDir)) {
164184
$file.IsReadOnly = $false
165185
}
166186
}, CheckHelpContent

PSReadLine/BasicEditing.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public static void SelfInsert(ConsoleKeyInfo? key = null, object arg = null)
5757
/// </summary>
5858
public static void RevertLine(ConsoleKeyInfo? key = null, object arg = null)
5959
{
60+
if (_singleton._prediction.RevertSuggestion())
61+
{
62+
return;
63+
}
64+
6065
if (_singleton._statusIsErrorMessage)
6166
{
6267
// After an edit, clear the error message
@@ -80,7 +85,7 @@ public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null)
8085
_singleton.ClearStatusMessage(false);
8186
_singleton._current = _singleton._buffer.Length;
8287

83-
using var _ = _singleton.PredictionOff();
88+
using var _ = _singleton._prediction.DisableScoped();
8489
_singleton.ForceRender();
8590

8691
_singleton._console.Write("\x1b[91m^C\x1b[0m");
@@ -210,7 +215,7 @@ public static void DeleteCharOrExit(ConsoleKeyInfo? key = null, object arg = nul
210215

211216
private bool AcceptLineImpl(bool validate)
212217
{
213-
using var _ = PredictionOff();
218+
using var _ = _prediction.DisableScoped();
214219

215220
ParseInput();
216221
if (_parseErrors.Any(e => e.IncompleteInput))
@@ -272,12 +277,16 @@ private bool AcceptLineImpl(bool validate)
272277

273278
// Let public API set cursor to end of line incase end of line is end of buffer
274279
SetCursorPosition(_current);
275-
if (_suggestionText != null)
280+
281+
if (_prediction.ActiveView is PredictionListView listView)
276282
{
277-
ResetSuggestion();
278-
_console.BlankRestOfLine();
283+
// Send feedback to prediction plugin if a list item is accepted as the final command line.
284+
listView.OnSuggestionAccepted();
279285
}
280286

287+
// Clear the prediction view if there is one.
288+
_prediction.ActiveView.Clear(cursorAtEol: true);
289+
281290
_console.Write("\n");
282291
_inputAccepted = true;
283292
return true;

PSReadLine/Cmdlets.cs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,16 @@ public enum AddToHistoryOption
6666

6767
public enum PredictionSource
6868
{
69-
None,
70-
History,
69+
None = 1,
70+
History = 2,
71+
Plugin = 4,
72+
HistoryAndPlugin = History | Plugin,
73+
}
74+
75+
public enum PredictionViewStyle
76+
{
77+
InlineView,
78+
ListView,
7179
}
7280

7381
public class PSConsoleReadLineOptions
@@ -85,9 +93,14 @@ public class PSConsoleReadLineOptions
8593
public const ConsoleColor DefaultEmphasisColor = ConsoleColor.Cyan;
8694
public const ConsoleColor DefaultErrorColor = ConsoleColor.Red;
8795

88-
// Use dark black by default for the suggestion text.
8996
// Find the most suitable color using https://stackoverflow.com/a/33206814
90-
public const string DefaultInlinePredictionColor = "\x1b[38;5;238m";
97+
// Default prediction color settings:
98+
// - use FG color 'dark black' for the inline-view suggestion text
99+
// - use FG color 'yellow' for the list-view suggestion text
100+
// - use BG color 'dark black' for the selected list-view suggestion text
101+
public const string DefaultInlinePredictionColor = "\x1b[38;5;238m";
102+
public const string DefaultListPredictionColor = "\x1b[33m";
103+
public const string DefaultListPredictionSelectedColor = "\x1b[48;5;238m";
91104

92105
public static EditMode DefaultEditMode = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
93106
? EditMode.Windows
@@ -144,6 +157,8 @@ public class PSConsoleReadLineOptions
144157
/// </summary>
145158
public const PredictionSource DefaultPredictionSource = PredictionSource.None;
146159

160+
public const PredictionViewStyle DefaultPredictionViewStyle = PredictionViewStyle.InlineView;
161+
147162
/// <summary>
148163
/// How long in milliseconds should we wait before concluding
149164
/// the input is not an escape sequence?
@@ -171,6 +186,7 @@ public PSConsoleReadLineOptions(string hostName)
171186
HistorySaveStyle = DefaultHistorySaveStyle;
172187
AnsiEscapeTimeout = DefaultAnsiEscapeTimeout;
173188
PredictionSource = DefaultPredictionSource;
189+
PredictionViewStyle = DefaultPredictionViewStyle;
174190
MaximumHistoryCount = 0;
175191

176192
var historyFileName = hostName + "_history.txt";
@@ -309,6 +325,7 @@ public object ContinuationPromptColor
309325

310326
public bool HistorySearchCaseSensitive { get; set; }
311327
internal StringComparison HistoryStringComparison => HistorySearchCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
328+
internal StringComparer HistoryStringComparer => HistorySearchCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
312329

313330
/// <summary>
314331
/// How are command and insert modes indicated when in vi edit mode?
@@ -331,6 +348,11 @@ public object ContinuationPromptColor
331348
/// </summary>
332349
public PredictionSource PredictionSource { get; set; }
333350

351+
/// <summary>
352+
/// Sets the view style for rendering predictive suggestions.
353+
/// </summary>
354+
public PredictionViewStyle PredictionViewStyle { get; set; }
355+
334356
/// <summary>
335357
/// How long in milliseconds should we wait before concluding
336358
/// the input is not an escape sequence?
@@ -457,6 +479,18 @@ public object InlinePredictionColor
457479
set => _inlinePredictionColor = VTColorUtils.AsEscapeSequence(value);
458480
}
459481

482+
public object ListPredictionColor
483+
{
484+
get => _listPredictionColor;
485+
set => _listPredictionColor = VTColorUtils.AsEscapeSequence(value);
486+
}
487+
488+
public object ListPredictionSelectedColor
489+
{
490+
get => _listPredictionSelectedColor;
491+
set => _listPredictionSelectedColor = VTColorUtils.AsEscapeSequence(value);
492+
}
493+
460494
internal string _defaultTokenColor;
461495
internal string _commentColor;
462496
internal string _keywordColor;
@@ -472,6 +506,8 @@ public object InlinePredictionColor
472506
internal string _errorColor;
473507
internal string _selectionColor;
474508
internal string _inlinePredictionColor;
509+
internal string _listPredictionColor;
510+
internal string _listPredictionSelectedColor;
475511

476512
internal void ResetColors()
477513
{
@@ -489,7 +525,9 @@ internal void ResetColors()
489525
MemberColor = DefaultNumberColor;
490526
EmphasisColor = DefaultEmphasisColor;
491527
ErrorColor = DefaultErrorColor;
492-
InlinePredictionColor = DefaultInlinePredictionColor;
528+
InlinePredictionColor = DefaultInlinePredictionColor;
529+
ListPredictionColor = DefaultListPredictionColor;
530+
ListPredictionSelectedColor = DefaultListPredictionSelectedColor;
493531

494532
var bg = Console.BackgroundColor;
495533
if (fg == VTColorUtils.UnknownColor || bg == VTColorUtils.UnknownColor)
@@ -527,6 +565,8 @@ internal void SetColor(string property, object value)
527565
{"Member", (o, v) => o.MemberColor = v},
528566
{"Selection", (o, v) => o.SelectionColor = v},
529567
{"InlinePrediction", (o, v) => o.InlinePredictionColor = v},
568+
{"ListPrediction", (o, v) => o.ListPredictionColor = v},
569+
{"ListPredictionSelected", (o, v) => o.ListPredictionSelectedColor = v},
530570
};
531571

532572
Interlocked.CompareExchange(ref ColorSetters, setters, null);
@@ -550,7 +590,9 @@ public class GetPSReadLineOption : PSCmdlet
550590
[ExcludeFromCodeCoverage]
551591
protected override void EndProcessing()
552592
{
553-
WriteObject(PSConsoleReadLine.GetOptions());
593+
var options = PSConsoleReadLine.GetOptions();
594+
WriteObject(options);
595+
PSConsoleReadLine.WarnWhenWindowSizeTooSmallForView(options.PredictionViewStyle, this);
554596
}
555597
}
556598

@@ -741,6 +783,14 @@ public PredictionSource PredictionSource
741783
}
742784
internal PredictionSource? _predictionSource;
743785

786+
[Parameter]
787+
public PredictionViewStyle PredictionViewStyle
788+
{
789+
get => _predictionViewStyle.GetValueOrDefault();
790+
set => _predictionViewStyle = value;
791+
}
792+
internal PredictionViewStyle? _predictionViewStyle;
793+
744794
[Parameter]
745795
public Hashtable Colors { get; set; }
746796

0 commit comments

Comments
 (0)