Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the prediction ListView and also hook up with the CommandPrediction APIs introduced in PS 7.1 #1909

Merged
merged 18 commits into from
Nov 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions MockPSConsole/MockPSConsole.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
<OutputType>Exe</OutputType>
<RootNamespace>MockPSConsole</RootNamespace>
<AssemblyName>MockPSConsole</AssemblyName>
<TargetFrameworks>net461;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net461;net5.0</TargetFrameworks>
<FileAlignment>512</FileAlignment>
<ApplicationManifest>Program.manifest</ApplicationManifest>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<RuntimeFrameworkVersion>3.1.6</RuntimeFrameworkVersion>
</PropertyGroup>

<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="System.Xml.XDocument" version="4.3.0" />
<PackageReference Include="System.Data.DataSetExtensions" version="4.5.0" />
<PackageReference Include="Microsoft.CSharp" version="4.5.0" />
<PackageReference Include="PowerShellStandard.Library" version="5.1.0" Condition="'$(TargetFramework)' == 'net461'" />
<PackageReference Include="Microsoft.PowerShell.SDK" version="7.0.3" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
<PackageReference Include="PowerShellStandard.Library" version="5.1.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.PowerShell.SDK" version="7.1.0-rc.2" />
</ItemGroup>

<ItemGroup>
Expand Down
50 changes: 35 additions & 15 deletions PSReadLine.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ param(
[ValidateSet("Debug", "Release")]
[string]$Configuration = (property Configuration Release),

[ValidateSet("net461", "netcoreapp3.1")]
[ValidateSet("net461", "net5.0")]
[string]$Framework,

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

if (-not $Framework)
{
$Framework = if ($PSVersionTable.PSEdition -eq "Core") { "netcoreapp3.1" } else { "net461" }
$Framework = if ($PSVersionTable.PSEdition -eq "Core") { "net5.0" } else { "net461" }
}

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

$polyFillerParams = @{
Inputs = { Get-ChildItem Polyfill/*.cs, Polyfill/Polyfill.csproj }
Outputs = "Polyfill/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine.Polyfiller.dll"
}

$binaryModuleParams = @{
Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx }
Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx, Polyfill/*.cs, Polyfill/Polyfill.csproj }
Outputs = "PSReadLine/bin/$Configuration/$Framework/Microsoft.PowerShell.PSReadLine2.dll"
}

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

<#
Synopsis: Build the Polyfiller assembly
#>
task BuildPolyfiller @polyFillerParams -If ($Framework -eq "net461") {
## Build both "net461" and "net5.0"
exec { dotnet publish -f "net461" -c $Configuration Polyfill }
exec { dotnet publish -f "net5.0" -c $Configuration Polyfill }
}

<#
Synopsis: Build main binary module
#>
Expand Down Expand Up @@ -108,7 +122,7 @@ task CheckHelpContent -If $CheckHelpContent {
<#
Synopsis: Copy all of the files that belong in the module to one place in the layout for installation
#>
task LayoutModule BuildMainModule, {
task LayoutModule BuildPolyfiller, BuildMainModule, {
if (-not (Test-Path $targetDir -PathType Container)) {
New-Item $targetDir -ItemType Directory -Force > $null
}
Expand All @@ -120,22 +134,30 @@ task LayoutModule BuildMainModule, {
'PSReadLine/PSReadLine.format.ps1xml',
'PSReadLine/PSReadLine.psm1'

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

if ($Framework -eq "net461") {
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
}

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
}

$binPath = "PSReadLine/bin/$Configuration/$Framework/publish"
Copy-Item $binPath/Microsoft.PowerShell.PSReadLine2.dll $targetDir

if (Test-Path $binPath/System.Runtime.InteropServices.RuntimeInformation.dll)
{
if (Test-Path $binPath/System.Runtime.InteropServices.RuntimeInformation.dll) {
Copy-Item $binPath/System.Runtime.InteropServices.RuntimeInformation.dll $targetDir
}
else
{
} else {
Write-Warning "Build using $Framework is not sufficient to be downlevel compatible"
}

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

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

# Make sure we don't ship any read-only files
foreach ($file in (Get-ChildItem -Recurse -File $targetDir))
{
foreach ($file in (Get-ChildItem -Recurse -File $targetDir)) {
$file.IsReadOnly = $false
}
}, CheckHelpContent
Expand Down
19 changes: 14 additions & 5 deletions PSReadLine/BasicEditing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public static void SelfInsert(ConsoleKeyInfo? key = null, object arg = null)
/// </summary>
public static void RevertLine(ConsoleKeyInfo? key = null, object arg = null)
{
if (_singleton._prediction.RevertSuggestion())
{
return;
}

if (_singleton._statusIsErrorMessage)
{
// After an edit, clear the error message
Expand All @@ -80,7 +85,7 @@ public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null)
_singleton.ClearStatusMessage(false);
_singleton._current = _singleton._buffer.Length;

using var _ = _singleton.PredictionOff();
using var _ = _singleton._prediction.DisableScoped();
_singleton.ForceRender();

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

private bool AcceptLineImpl(bool validate)
{
using var _ = PredictionOff();
using var _ = _prediction.DisableScoped();

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

// Let public API set cursor to end of line incase end of line is end of buffer
SetCursorPosition(_current);
if (_suggestionText != null)

if (_prediction.ActiveView is PredictionListView listView)
{
ResetSuggestion();
_console.BlankRestOfLine();
// Send feedback to prediction plugin if a list item is accepted as the final command line.
listView.OnSuggestionAccepted();
}

// Clear the prediction view if there is one.
_prediction.ActiveView.Clear(cursorAtEol: true);

_console.Write("\n");
_inputAccepted = true;
return true;
Expand Down
62 changes: 56 additions & 6 deletions PSReadLine/Cmdlets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,16 @@ public enum AddToHistoryOption

public enum PredictionSource
{
None,
History,
None = 1,
History = 2,
Plugin = 4,
HistoryAndPlugin = History | Plugin,
}

public enum PredictionViewStyle
{
InlineView,
ListView,
}

public class PSConsoleReadLineOptions
Expand All @@ -85,9 +93,14 @@ public class PSConsoleReadLineOptions
public const ConsoleColor DefaultEmphasisColor = ConsoleColor.Cyan;
public const ConsoleColor DefaultErrorColor = ConsoleColor.Red;

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

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

public const PredictionViewStyle DefaultPredictionViewStyle = PredictionViewStyle.InlineView;

/// <summary>
/// How long in milliseconds should we wait before concluding
/// the input is not an escape sequence?
Expand Down Expand Up @@ -171,6 +186,7 @@ public PSConsoleReadLineOptions(string hostName)
HistorySaveStyle = DefaultHistorySaveStyle;
AnsiEscapeTimeout = DefaultAnsiEscapeTimeout;
PredictionSource = DefaultPredictionSource;
PredictionViewStyle = DefaultPredictionViewStyle;
MaximumHistoryCount = 0;

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

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

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

/// <summary>
/// Sets the view style for rendering predictive suggestions.
/// </summary>
public PredictionViewStyle PredictionViewStyle { get; set; }

/// <summary>
/// How long in milliseconds should we wait before concluding
/// the input is not an escape sequence?
Expand Down Expand Up @@ -457,6 +479,18 @@ public object InlinePredictionColor
set => _inlinePredictionColor = VTColorUtils.AsEscapeSequence(value);
}

public object ListPredictionColor
{
get => _listPredictionColor;
set => _listPredictionColor = VTColorUtils.AsEscapeSequence(value);
}

public object ListPredictionSelectedColor
{
get => _listPredictionSelectedColor;
set => _listPredictionSelectedColor = VTColorUtils.AsEscapeSequence(value);
}

internal string _defaultTokenColor;
internal string _commentColor;
internal string _keywordColor;
Expand All @@ -472,6 +506,8 @@ public object InlinePredictionColor
internal string _errorColor;
internal string _selectionColor;
internal string _inlinePredictionColor;
internal string _listPredictionColor;
internal string _listPredictionSelectedColor;

internal void ResetColors()
{
Expand All @@ -489,7 +525,9 @@ internal void ResetColors()
MemberColor = DefaultNumberColor;
EmphasisColor = DefaultEmphasisColor;
ErrorColor = DefaultErrorColor;
InlinePredictionColor = DefaultInlinePredictionColor;
InlinePredictionColor = DefaultInlinePredictionColor;
ListPredictionColor = DefaultListPredictionColor;
ListPredictionSelectedColor = DefaultListPredictionSelectedColor;

var bg = Console.BackgroundColor;
if (fg == VTColorUtils.UnknownColor || bg == VTColorUtils.UnknownColor)
Expand Down Expand Up @@ -527,6 +565,8 @@ internal void SetColor(string property, object value)
{"Member", (o, v) => o.MemberColor = v},
{"Selection", (o, v) => o.SelectionColor = v},
{"InlinePrediction", (o, v) => o.InlinePredictionColor = v},
{"ListPrediction", (o, v) => o.ListPredictionColor = v},
{"ListPredictionSelected", (o, v) => o.ListPredictionSelectedColor = v},
};

Interlocked.CompareExchange(ref ColorSetters, setters, null);
Expand All @@ -550,7 +590,9 @@ public class GetPSReadLineOption : PSCmdlet
[ExcludeFromCodeCoverage]
protected override void EndProcessing()
{
WriteObject(PSConsoleReadLine.GetOptions());
var options = PSConsoleReadLine.GetOptions();
WriteObject(options);
PSConsoleReadLine.WarnWhenWindowSizeTooSmallForView(options.PredictionViewStyle, this);
}
}

Expand Down Expand Up @@ -741,6 +783,14 @@ public PredictionSource PredictionSource
}
internal PredictionSource? _predictionSource;

[Parameter]
public PredictionViewStyle PredictionViewStyle
{
get => _predictionViewStyle.GetValueOrDefault();
set => _predictionViewStyle = value;
}
internal PredictionViewStyle? _predictionViewStyle;

[Parameter]
public Hashtable Colors { get; set; }

Expand Down
Loading