From 52971d101184d5c52c2ae72eec7965b1f0c879f6 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Wed, 7 Jan 2026 16:03:13 -0600
Subject: [PATCH 01/13] Target dotnet 8.0
---
PSReadLine.build.ps1 | 52 ++++++++++++------------------------
PSReadLine/PSReadLine.csproj | 11 ++++----
global.json | 7 +++++
3 files changed, 29 insertions(+), 41 deletions(-)
create mode 100644 global.json
diff --git a/PSReadLine.build.ps1 b/PSReadLine.build.ps1
index e245dfae3..9eb25d003 100644
--- a/PSReadLine.build.ps1
+++ b/PSReadLine.build.ps1
@@ -19,33 +19,33 @@ param(
[ValidateSet("Debug", "Release")]
[string]$Configuration = (property Configuration Release),
- [ValidateSet("net472", "net6.0")]
- [string]$TestFramework,
-
[switch]$CheckHelpContent
)
Import-Module "$PSScriptRoot/tools/helper.psm1"
-# Final bits to release go here
-$targetDir = "bin/$Configuration/PSReadLine"
+# Dynamically read target framework from project file
+$csprojPath = "$PSScriptRoot/PSReadLine/PSReadLine.csproj"
+[xml]$csproj = Get-Content $csprojPath
+$targetFramework = $csproj.Project.PropertyGroup.TargetFramework | Where-Object { $_ } | Select-Object -First 1
-if (-not $TestFramework) {
- $TestFramework = $IsWindows ? "net472" : "net6.0"
+if (-not $targetFramework) {
+ throw "Could not determine TargetFramework from $csprojPath"
}
+Write-Verbose "Target framework: $targetFramework"
+
+# Final bits to release go here
+$targetDir = "bin/$Configuration/PSReadLine"
+$TestFramework = $targetFramework
+
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/netstandard2.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll"
-}
-
$binaryModuleParams = @{
- Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx, Polyfill/*.cs, Polyfill/Polyfill.csproj }
- Outputs = "PSReadLine/bin/$Configuration/netstandard2.0/Microsoft.PowerShell.PSReadLine.dll"
+ Inputs = { Get-ChildItem PSReadLine/*.cs, PSReadLine/PSReadLine.csproj, PSReadLine/PSReadLineResources.resx }
+ Outputs = "PSReadLine/bin/$Configuration/$targetFramework/Microsoft.PowerShell.PSReadLine.dll"
}
$xUnitTestParams = @{
@@ -53,14 +53,6 @@ $xUnitTestParams = @{
Outputs = "test/bin/$Configuration/$TestFramework/PSReadLine.Tests.dll"
}
-<#
-Synopsis: Build the Polyfiller assembly
-#>
-task BuildPolyfiller @polyFillerParams {
- exec { dotnet publish -c $Configuration -f 'netstandard2.0' Polyfill }
- exec { dotnet publish -c $Configuration -f 'net6.0' Polyfill }
-}
-
<#
Synopsis: Build main binary module
#>
@@ -79,7 +71,7 @@ task BuildXUnitTests @xUnitTestParams {
Synopsis: Run the unit tests
#>
task RunTests BuildMainModule, BuildXUnitTests, {
- Write-Verbose "Run tests targeting '$TestFramework' ..."
+ Write-Verbose "Run tests targeting $targetFramework ..."
Start-TestRun -Configuration $Configuration -Framework $TestFramework
}
@@ -97,7 +89,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 BuildPolyfiller, BuildMainModule, {
+task LayoutModule BuildMainModule, {
if (-not (Test-Path $targetDir -PathType Container)) {
New-Item $targetDir -ItemType Directory -Force > $null
}
@@ -115,17 +107,7 @@ task LayoutModule BuildPolyfiller, BuildMainModule, {
Set-Content -Path (Join-Path $targetDir (Split-Path $file -Leaf)) -Value (ConvertTo-CRLF $content) -Force
}
- if (-not (Test-Path "$targetDir/netstd")) {
- New-Item "$targetDir/netstd" -ItemType Directory -Force > $null
- }
- if (-not (Test-Path "$targetDir/net6plus")) {
- New-Item "$targetDir/net6plus" -ItemType Directory -Force > $null
- }
-
- Copy-Item "Polyfill/bin/$Configuration/netstandard2.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/netstd" -Force
- Copy-Item "Polyfill/bin/$Configuration/net6.0/Microsoft.PowerShell.PSReadLine.Polyfiller.dll" "$targetDir/net6plus" -Force
-
- $binPath = "PSReadLine/bin/$Configuration/netstandard2.0/publish"
+ $binPath = "PSReadLine/bin/$Configuration/$targetFramework/publish"
Copy-Item $binPath/Microsoft.PowerShell.PSReadLine.dll $targetDir
Copy-Item $binPath/Microsoft.PowerShell.Pager.dll $targetDir
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index f67e29e4c..3246bb7a4 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -9,19 +9,18 @@
2.4.5
2.4.5
true
- netstandard2.0
+ net8.0
true
false
9.0
-
-
-
-
+
+
+
-
+
diff --git a/global.json b/global.json
new file mode 100644
index 000000000..7aeeefbf6
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "8.0.416",
+ "rollForward": "latestPatch",
+ "allowPrerelease": false
+ }
+}
From 7d992446bc0d7154962d46ed066be1bc242c3fee Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Wed, 7 Jan 2026 16:21:05 -0600
Subject: [PATCH 02/13] bump module version to 3.0.0 and add error message for
users
---
PSReadLine/PSReadLine.csproj | 6 +++---
PSReadLine/PSReadLine.psd1 | 4 ++--
PSReadLine/PSReadLine.psm1 | 14 ++++++++++++++
3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index 3246bb7a4..8325714a1 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -5,9 +5,9 @@
Microsoft.PowerShell.PSReadLine
Microsoft.PowerShell.PSReadLine
$(NoWarn);CA1416
- 2.4.5.0
- 2.4.5
- 2.4.5
+ 3.0.0.0
+ 3.0.0
+ 3.0.0
true
net8.0
true
diff --git a/PSReadLine/PSReadLine.psd1 b/PSReadLine/PSReadLine.psd1
index 0690e85cc..4ce7eff73 100644
--- a/PSReadLine/PSReadLine.psd1
+++ b/PSReadLine/PSReadLine.psd1
@@ -1,13 +1,13 @@
@{
RootModule = 'PSReadLine.psm1'
NestedModules = @("Microsoft.PowerShell.PSReadLine.dll")
-ModuleVersion = '2.4.5'
+ModuleVersion = '3.0.0'
GUID = '5714753b-2afd-4492-a5fd-01d9e2cff8b5'
Author = 'Microsoft Corporation'
CompanyName = 'Microsoft Corporation'
Copyright = '(c) Microsoft Corporation. All rights reserved.'
Description = 'Great command line editing in the PowerShell console host'
-PowerShellVersion = '5.1'
+PowerShellVersion = '7.4'
FormatsToProcess = 'PSReadLine.format.ps1xml'
AliasesToExport = @()
FunctionsToExport = 'PSConsoleHostReadLine'
diff --git a/PSReadLine/PSReadLine.psm1 b/PSReadLine/PSReadLine.psm1
index 572aee80f..06194371d 100644
--- a/PSReadLine/PSReadLine.psm1
+++ b/PSReadLine/PSReadLine.psm1
@@ -1,3 +1,17 @@
+# Check PowerShell version compatibility
+if ($PSVersionTable.PSVersion -lt [Version]'7.4.0') {
+ $errorMessage = @"
+PSReadLine 3.0+ requires PowerShell 7.4 or later.
+Current version: $($PSVersionTable.PSVersion)
+
+To use PSReadLine with your PowerShell version, install an older version:
+ Install-Module PSReadLine -RequiredVersion 2.4.5 -Force -SkipPublisherCheck
+
+To upgrade PowerShell: https://aka.ms/install-powershell
+"@
+ throw $errorMessage
+}
+
function PSConsoleHostReadLine
{
[System.Diagnostics.DebuggerHidden()]
From 704e5a6f352594d42e929ae5f408cb99856619e5 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Wed, 7 Jan 2026 16:29:08 -0600
Subject: [PATCH 03/13] Remove PolyFill and update MockPSConsole
---
.vscode/launch.json | 2 +-
MockPSConsole/MockPSConsole.csproj | 10 +-
Polyfill/CommandPrediction.cs | 232 -----------------------------
Polyfill/Polyfill.csproj | 22 ---
test/PSReadLine.Tests.csproj | 11 +-
5 files changed, 7 insertions(+), 270 deletions(-)
delete mode 100644 Polyfill/CommandPrediction.cs
delete mode 100644 Polyfill/Polyfill.csproj
diff --git a/.vscode/launch.json b/.vscode/launch.json
index c01006234..f15ddc52f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -14,7 +14,7 @@
"-NoProfile",
"-NoExit",
"-Command",
- "Import-Module '${workspaceFolder}/PSReadLine/bin/Debug/netstandard2.0/PSReadLine.psd1'"
+ "Import-Module '${workspaceFolder}/PSReadLine/bin/Debug/net8.0/PSReadLine.psd1'"
],
"console": "integratedTerminal",
"justMyCode": false,
diff --git a/MockPSConsole/MockPSConsole.csproj b/MockPSConsole/MockPSConsole.csproj
index cebd3941d..7a1cfa3fa 100644
--- a/MockPSConsole/MockPSConsole.csproj
+++ b/MockPSConsole/MockPSConsole.csproj
@@ -4,18 +4,14 @@
Exe
MockPSConsole
MockPSConsole
- net472;net6.0
+ net8.0
512
Program.manifest
true
-
-
-
-
-
-
+
+
diff --git a/Polyfill/CommandPrediction.cs b/Polyfill/CommandPrediction.cs
deleted file mode 100644
index e7b331a8b..000000000
--- a/Polyfill/CommandPrediction.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-#if LEGACY
-
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Management.Automation.Language;
-
-namespace System.Management.Automation.Subsystem.Prediction
-{
- ///
- /// Kinds of prediction clients.
- ///
- public enum PredictionClientKind
- {
- ///
- /// A terminal client, representing the command-line experience.
- ///
- Terminal,
-
- ///
- /// An editor client, representing the editor experience.
- ///
- Editor,
- }
-
- ///
- /// The class represents a client that interacts with predictors.
- ///
- public sealed class PredictionClient
- {
- ///
- /// Gets the client name.
- ///
- [HiddenAttribute]
- public string Name { get; }
-
- ///
- /// Gets the client kind.
- ///
- [HiddenAttribute]
- public PredictionClientKind Kind { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Name of the interactive client.
- /// Kind of the interactive client.
- [HiddenAttribute]
- public PredictionClient(string name, PredictionClientKind kind)
- {
- Name = name;
- Kind = kind;
- }
- }
-
- ///
- /// The class represents the prediction result from a predictor.
- ///
- public sealed class PredictionResult
- {
- ///
- /// Gets the Id of the predictor.
- ///
- [HiddenAttribute]
- public Guid Id { get; }
-
- ///
- /// Gets the name of the predictor.
- ///
- [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, uint? session, List suggestions)
- {
- Id = id;
- Name = name;
- Session = session;
- Suggestions = suggestions;
- }
- }
-
- ///
- /// The class represents a predictive suggestion generated by a predictor.
- ///
- public sealed class PredictiveSuggestion
- {
- ///
- /// Gets the suggestion.
- ///
- [HiddenAttribute]
- public string SuggestionText { get; }
-
- ///
- /// Gets the tooltip of the suggestion.
- ///
- [HiddenAttribute]
- public string ToolTip { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The predictive suggestion text.
- [HiddenAttribute]
- public PredictiveSuggestion(string suggestion)
- : this(suggestion, toolTip: null)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The predictive suggestion text.
- /// The tooltip of the suggestion.
- [HiddenAttribute]
- public PredictiveSuggestion(string suggestion, string toolTip)
- {
- if (string.IsNullOrEmpty(suggestion))
- {
- throw new ArgumentNullException(nameof(suggestion));
- }
-
- SuggestionText = suggestion;
- ToolTip = toolTip;
- }
- }
-
- ///
- /// Provides a set of possible predictions for given input.
- ///
- 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> PredictInputAsync(PredictionClient client, Ast ast, Token[] astTokens)
- {
- return null;
- }
-
- ///
- /// 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> PredictInputAsync(PredictionClient client, Ast ast, Token[] astTokens, int millisecondsTimeout)
- {
- return null;
- }
-
- ///
- /// 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(PredictionClient client, IReadOnlyList history)
- {
- }
-
- ///
- /// Allow registered predictors to know the execution result (success/failure) of the last accepted command line.
- ///
- /// Represents the client that initiates the call.
- /// The last accepted command line.
- /// Whether the execution of the last command line was successful.
- [HiddenAttribute]
- public static void OnCommandLineExecuted(PredictionClient client, string commandLine, bool success)
- {
- }
-
- ///
- /// 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(PredictionClient 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(PredictionClient client, Guid predictorId, uint session, string suggestionText)
- {
- }
- }
-}
-
-#else
-
-using System.Management.Automation.Subsystem.Prediction;
-using System.Runtime.CompilerServices;
-
-[assembly: TypeForwardedTo(typeof(PredictionClientKind))]
-[assembly: TypeForwardedTo(typeof(PredictionClient))]
-[assembly: TypeForwardedTo(typeof(PredictiveSuggestion))]
-[assembly: TypeForwardedTo(typeof(PredictionResult))]
-[assembly: TypeForwardedTo(typeof(CommandPrediction))]
-
-#endif
diff --git a/Polyfill/Polyfill.csproj b/Polyfill/Polyfill.csproj
deleted file mode 100644
index 2cdccfbba..000000000
--- a/Polyfill/Polyfill.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- Microsoft.PowerShell.PSReadLine.Polyfiller
- 1.0.0.0
- netstandard2.0;net6.0
- true
-
-
-
-
-
-
-
-
-
-
-
- $(DefineConstants);LEGACY
-
-
-
diff --git a/test/PSReadLine.Tests.csproj b/test/PSReadLine.Tests.csproj
index 34598cbe9..1ccc25432 100644
--- a/test/PSReadLine.Tests.csproj
+++ b/test/PSReadLine.Tests.csproj
@@ -5,7 +5,7 @@
library
UnitTestPSReadLine
PSReadLine.Tests
- net472;net6.0
+ net8.0
512
{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
False
@@ -14,13 +14,8 @@
9.0
-
-
-
-
-
-
-
+
+
From 15540d6334a77fcafb6ac3d35fe041e44bea9369 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 11:46:44 -0600
Subject: [PATCH 04/13] Point to latest 7.4 SDK, remove polyfill loading
interface, clean up build.ps1
---
MockPSConsole/MockPSConsole.csproj | 2 +-
PSReadLine.build.ps1 | 7 +++----
PSReadLine/OnImportAndRemove.cs | 21 ++-------------------
PSReadLine/PSReadLine.csproj | 6 +++++-
build.ps1 | 18 +++---------------
test/PSReadLine.Tests.csproj | 2 +-
6 files changed, 15 insertions(+), 41 deletions(-)
diff --git a/MockPSConsole/MockPSConsole.csproj b/MockPSConsole/MockPSConsole.csproj
index 7a1cfa3fa..f4407a3b2 100644
--- a/MockPSConsole/MockPSConsole.csproj
+++ b/MockPSConsole/MockPSConsole.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/PSReadLine.build.ps1 b/PSReadLine.build.ps1
index 9eb25d003..4d54be206 100644
--- a/PSReadLine.build.ps1
+++ b/PSReadLine.build.ps1
@@ -37,7 +37,6 @@ Write-Verbose "Target framework: $targetFramework"
# Final bits to release go here
$targetDir = "bin/$Configuration/PSReadLine"
-$TestFramework = $targetFramework
function ConvertTo-CRLF([string] $text) {
$text.Replace("`r`n","`n").Replace("`n","`r`n")
@@ -50,7 +49,7 @@ $binaryModuleParams = @{
$xUnitTestParams = @{
Inputs = { Get-ChildItem test/*.cs, test/*.json, test/PSReadLine.Tests.csproj }
- Outputs = "test/bin/$Configuration/$TestFramework/PSReadLine.Tests.dll"
+ Outputs = "test/bin/$Configuration/$targetFramework/PSReadLine.Tests.dll"
}
<#
@@ -64,7 +63,7 @@ task BuildMainModule @binaryModuleParams {
Synopsis: Build xUnit tests
#>
task BuildXUnitTests @xUnitTestParams {
- exec { dotnet publish -f $TestFramework -c $Configuration test }
+ exec { dotnet publish -f $targetFramework -c $Configuration test }
}
<#
@@ -72,7 +71,7 @@ Synopsis: Run the unit tests
#>
task RunTests BuildMainModule, BuildXUnitTests, {
Write-Verbose "Run tests targeting $targetFramework ..."
- Start-TestRun -Configuration $Configuration -Framework $TestFramework
+ Start-TestRun -Configuration $Configuration -Framework $targetFramework
}
<#
diff --git a/PSReadLine/OnImportAndRemove.cs b/PSReadLine/OnImportAndRemove.cs
index 70208420a..cc91701c0 100644
--- a/PSReadLine/OnImportAndRemove.cs
+++ b/PSReadLine/OnImportAndRemove.cs
@@ -9,29 +9,12 @@ public class OnModuleImportAndRemove : IModuleAssemblyInitializer, IModuleAssemb
{
public void OnImport()
{
- AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
+ // Module initialization - reserved for future use
}
public void OnRemove(PSModuleInfo module)
{
- AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
- }
-
- ///
- /// Load the correct 'Polyfiller' assembly based on the runtime.
- ///
- private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
- {
- if (args.Name != "Microsoft.PowerShell.PSReadLine.Polyfiller, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
- {
- return null;
- }
-
- string root = Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location);
- string subd = (Environment.Version.Major >= 6) ? "net6plus" : "netstd";
- string path = Path.Combine(root, subd, "Microsoft.PowerShell.PSReadLine.Polyfiller.dll");
-
- return Assembly.LoadFrom(path);
+ // Module cleanup - reserved for future use
}
}
}
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index 8325714a1..69cd814d9 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -16,7 +16,11 @@
-
+
+
+ contentFiles
+ All
+
diff --git a/build.ps1 b/build.ps1
index 481f2a271..47adfcbfe 100644
--- a/build.ps1
+++ b/build.ps1
@@ -10,13 +10,13 @@
Check and install prerequisites for the build.
.EXAMPLE
PS > .\build.ps1 -Configuration Release
- Build the main module with 'Release' configuration targeting 'netstandard2.0'.
+ Build the main module with 'Release' configuration targeting 'net8.0'.
.EXAMPLE
PS > .\build.ps1
- Build the main module with the default configuration (Debug) targeting 'netstandard2.0'.
+ Build the main module with the default configuration (Debug) targeting 'net8.0'.
.EXAMPLE
PS > .\build.ps1 -Test
- Run xUnit tests with the default configuration (Debug) and the default target framework (net472 on Windows or net6.0 otherwise).
+ Run xUnit tests with the default configuration (Debug) targeting 'net8.0'.
.PARAMETER Clean
Clean the local repo, but keep untracked files.
.PARAMETER Bootstrap
@@ -25,13 +25,6 @@
Run tests.
.PARAMETER Configuration
The configuration setting for the build. The default value is 'Debug'.
-.PARAMETER Framework
- The target framework when testing:
- - net472: run tests with .NET Framework
- - net6.0: run tests with .NET 6.0
- When not specified, the target framework is determined by the current OS platform:
- - use 'net472' on Windows
- - use 'net6.0' on Unix platforms
#>
[CmdletBinding(DefaultParameterSetName = 'default')]
param(
@@ -47,10 +40,6 @@ param(
[Parameter(ParameterSetName = 'test')]
[switch] $CheckHelpContent,
- [Parameter(ParameterSetName = 'test')]
- [ValidateSet("net472", "net6.0")]
- [string] $Framework,
-
[Parameter(ParameterSetName = 'default')]
[Parameter(ParameterSetName = 'test')]
[ValidateSet("Debug", "Release")]
@@ -93,7 +82,6 @@ if (-not (Get-Module -Name InvokeBuild -ListAvailable)) {
$buildTask = if ($Test) { "RunTests" } else { "ZipRelease" }
$arguments = @{ Task = $buildTask; Configuration = $Configuration }
-if ($Framework) { $arguments.Add("TestFramework", $Framework) }
if ($CheckHelpContent) { $arguments.Add("CheckHelpContent", $true) }
Invoke-Build @arguments
diff --git a/test/PSReadLine.Tests.csproj b/test/PSReadLine.Tests.csproj
index 1ccc25432..2c344b76c 100644
--- a/test/PSReadLine.Tests.csproj
+++ b/test/PSReadLine.Tests.csproj
@@ -15,7 +15,7 @@
-
+
From d45a4f48d897c4ff7a11638c8c83bf37087a3419 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 11:52:52 -0600
Subject: [PATCH 05/13] Use 7.4.13
---
MockPSConsole/MockPSConsole.csproj | 2 +-
PSReadLine/PSReadLine.csproj | 4 ++--
test/PSReadLine.Tests.csproj | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/MockPSConsole/MockPSConsole.csproj b/MockPSConsole/MockPSConsole.csproj
index f4407a3b2..d0f91fa71 100644
--- a/MockPSConsole/MockPSConsole.csproj
+++ b/MockPSConsole/MockPSConsole.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/PSReadLine/PSReadLine.csproj b/PSReadLine/PSReadLine.csproj
index 69cd814d9..f77b56fc8 100644
--- a/PSReadLine/PSReadLine.csproj
+++ b/PSReadLine/PSReadLine.csproj
@@ -16,8 +16,8 @@
-
-
+
+
contentFiles
All
diff --git a/test/PSReadLine.Tests.csproj b/test/PSReadLine.Tests.csproj
index 2c344b76c..3cf7aee80 100644
--- a/test/PSReadLine.Tests.csproj
+++ b/test/PSReadLine.Tests.csproj
@@ -15,7 +15,7 @@
-
+
From 6eca5aacb7916874d6708ee5a0cf6aee2605b5a9 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 13:29:27 -0600
Subject: [PATCH 06/13] use dotnet 8.0.415
---
global.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/global.json b/global.json
index 7aeeefbf6..fc22515fc 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.416",
+ "version": "8.0.415",
"rollForward": "latestPatch",
"allowPrerelease": false
}
From 9c1f75d00e71a05bdee8f0a672028e4d9946eb96 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 13:32:08 -0600
Subject: [PATCH 07/13] Remove error message from psm1
---
PSReadLine/PSReadLine.psm1 | 16 +---------------
1 file changed, 1 insertion(+), 15 deletions(-)
diff --git a/PSReadLine/PSReadLine.psm1 b/PSReadLine/PSReadLine.psm1
index 06194371d..94d094a1e 100644
--- a/PSReadLine/PSReadLine.psm1
+++ b/PSReadLine/PSReadLine.psm1
@@ -1,17 +1,3 @@
-# Check PowerShell version compatibility
-if ($PSVersionTable.PSVersion -lt [Version]'7.4.0') {
- $errorMessage = @"
-PSReadLine 3.0+ requires PowerShell 7.4 or later.
-Current version: $($PSVersionTable.PSVersion)
-
-To use PSReadLine with your PowerShell version, install an older version:
- Install-Module PSReadLine -RequiredVersion 2.4.5 -Force -SkipPublisherCheck
-
-To upgrade PowerShell: https://aka.ms/install-powershell
-"@
- throw $errorMessage
-}
-
function PSConsoleHostReadLine
{
[System.Diagnostics.DebuggerHidden()]
@@ -22,4 +8,4 @@ function PSConsoleHostReadLine
$lastRunStatus = $?
Microsoft.PowerShell.Core\Set-StrictMode -Off
[Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($host.Runspace, $ExecutionContext, $lastRunStatus)
-}
+}
\ No newline at end of file
From 98dc583192706fae010716265650ac477f59150f Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:21:35 -0600
Subject: [PATCH 08/13] update appveryor
---
appveyor.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/appveyor.yml b/appveyor.yml
index 0bc286805..0414d555b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -11,6 +11,7 @@ cache:
install:
- pwsh: |
+ dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org
Write-Host "PS Version: $($PSVersionTable.PSVersion)"
./build.ps1 -Bootstrap
@@ -19,7 +20,7 @@ build_script:
./build.ps1 -Configuration Release
test_script:
- - pwsh: ./build.ps1 -Test -Configuration Release -Framework net472
+ - pwsh: ./build.ps1 -Test -Configuration Release
artifacts:
- path: .\bin\Release\PSReadLine.zip
From 3a614acd86f83dbd2973aa84e4ba0974206c96e3 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:28:48 -0600
Subject: [PATCH 09/13] remove ado feed in apppveyor
---
appveyor.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/appveyor.yml b/appveyor.yml
index 0414d555b..57bc98f24 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -11,6 +11,7 @@ cache:
install:
- pwsh: |
+ dotnet nuget remove source PowerShell_PublicPackages
dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org
Write-Host "PS Version: $($PSVersionTable.PSVersion)"
./build.ps1 -Bootstrap
From ea6bc4f945f3cb117260342fb3cb08e825eede0f Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:55:08 -0600
Subject: [PATCH 10/13] Fix test infrastructure for 256-color sequences
---
test/MockConsole.cs | 60 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 4 deletions(-)
diff --git a/test/MockConsole.cs b/test/MockConsole.cs
index 990698671..1c66348ef 100644
--- a/test/MockConsole.cs
+++ b/test/MockConsole.cs
@@ -233,8 +233,12 @@ public virtual void Write(string s)
var endSequence = s.IndexOfAny(endEscapeChars, i);
var len = endSequence - i - (s[endSequence] != 'm' ? 1 : 2);
var escapeSequence = s.Substring(i + 2, len);
- foreach (var subsequence in escapeSequence.Split(';'))
+ var parts = escapeSequence.Split(';');
+
+ for (int j = 0; j < parts.Length; j++)
{
+ var subsequence = parts[j];
+
if (subsequence is "1" or "2" or "3")
{
// Ignore the font effect sequence: 1 - bold; 2 - dimmed color; 3 - italics
@@ -242,7 +246,29 @@ public virtual void Write(string s)
continue;
}
- EscapeSequenceActions[subsequence](this);
+ // Handle 256-color/RGB sequences: 38 (FG) or 48 (BG)
+ // PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
+ if (subsequence is "38" or "48")
+ {
+ if (j + 1 < parts.Length)
+ {
+ var mode = parts[++j];
+ if (mode == "5" && j + 1 < parts.Length)
+ {
+ j++; // Skip 256-color index
+ }
+ else if (mode == "2" && j + 3 < parts.Length)
+ {
+ j += 3; // Skip RGB values
+ }
+ }
+ continue;
+ }
+
+ if (EscapeSequenceActions.ContainsKey(subsequence))
+ {
+ EscapeSequenceActions[subsequence](this);
+ }
}
i = endSequence;
continue;
@@ -462,8 +488,12 @@ public override void Write(string s)
var endSequence = s.IndexOfAny(endEscapeChars, i);
var len = endSequence - i - (s[endSequence] != 'm' ? 1 : 2);
var escapeSequence = s.Substring(i + 2, len);
- foreach (var subsequence in escapeSequence.Split(';'))
+ var parts = escapeSequence.Split(';');
+
+ for (int j = 0; j < parts.Length; j++)
{
+ var subsequence = parts[j];
+
if (subsequence is "1" or "2" or "3")
{
// Ignore the font effect sequence: 1 - bold; 2 - dimmed color; 3 - italics
@@ -471,7 +501,29 @@ public override void Write(string s)
continue;
}
- EscapeSequenceActions[subsequence](this);
+ // Handle 256-color/RGB sequences: 38 (FG) or 48 (BG)
+ // PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
+ if (subsequence is "38" or "48")
+ {
+ if (j + 1 < parts.Length)
+ {
+ var mode = parts[++j];
+ if (mode == "5" && j + 1 < parts.Length)
+ {
+ j++; // Skip 256-color index
+ }
+ else if (mode == "2" && j + 3 < parts.Length)
+ {
+ j += 3; // Skip RGB values
+ }
+ }
+ continue;
+ }
+
+ if (EscapeSequenceActions.ContainsKey(subsequence))
+ {
+ EscapeSequenceActions[subsequence](this);
+ }
}
i = endSequence;
continue;
From 2ea2cc95bfc0cf525538f99096c3b1ede77cbe48 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 15:15:58 -0600
Subject: [PATCH 11/13] Support 256-color ANSI sequences in MockConsole
---
test/MockConsole.cs | 49 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/test/MockConsole.cs b/test/MockConsole.cs
index 1c66348ef..f126a1ca1 100644
--- a/test/MockConsole.cs
+++ b/test/MockConsole.cs
@@ -250,12 +250,18 @@ public virtual void Write(string s)
// PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
if (subsequence is "38" or "48")
{
+ bool isForeground = subsequence == "38";
if (j + 1 < parts.Length)
{
var mode = parts[++j];
if (mode == "5" && j + 1 < parts.Length)
{
- j++; // Skip 256-color index
+ var colorIndex = parts[++j];
+ var approxColor = Map256ColorToConsoleColor(colorIndex);
+ if (isForeground)
+ ForegroundColor = approxColor;
+ else
+ BackgroundColor = approxColor;
}
else if (mode == "2" && j + 3 < parts.Length)
{
@@ -417,6 +423,39 @@ private static void ToggleNegative(TestConsole c, bool b)
{ "0J", c => c.BlankRestOfBuffer() },
{ "2J", c => c.SetCursorPosition(0, 0) },
};
+
+ protected static ConsoleColor Map256ColorToConsoleColor(string colorIndex)
+ {
+ // Map 256-color palette to nearest ConsoleColor
+ // PSReadLine commonly uses 238 (dark gray)
+ if (!int.TryParse(colorIndex, out var index))
+ return ConsoleColor.White;
+
+ return index switch
+ {
+ // Standard colors (0-15)
+ 0 => ConsoleColor.Black,
+ 1 => ConsoleColor.DarkRed,
+ 2 => ConsoleColor.DarkGreen,
+ 3 => ConsoleColor.DarkYellow,
+ 4 => ConsoleColor.DarkBlue,
+ 5 => ConsoleColor.DarkMagenta,
+ 6 => ConsoleColor.DarkCyan,
+ 7 => ConsoleColor.Gray,
+ 8 => ConsoleColor.DarkGray,
+ 9 => ConsoleColor.Red,
+ 10 => ConsoleColor.Green,
+ 11 => ConsoleColor.Yellow,
+ 12 => ConsoleColor.Blue,
+ 13 => ConsoleColor.Magenta,
+ 14 => ConsoleColor.Cyan,
+ 15 => ConsoleColor.White,
+ // 232-255: Grayscale ramp (238 is commonly used by PSReadLine)
+ >= 232 and <= 255 => ConsoleColor.DarkGray,
+ // Default for other 256 colors: approximate based on ranges
+ _ => ConsoleColor.White
+ };
+ }
}
internal class BasicScrollingConsole : TestConsole
@@ -505,12 +544,18 @@ public override void Write(string s)
// PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
if (subsequence is "38" or "48")
{
+ bool isForeground = subsequence == "38";
if (j + 1 < parts.Length)
{
var mode = parts[++j];
if (mode == "5" && j + 1 < parts.Length)
{
- j++; // Skip 256-color index
+ var colorIndex = parts[++j];
+ var approxColor = Map256ColorToConsoleColor(colorIndex);
+ if (isForeground)
+ ForegroundColor = approxColor;
+ else
+ BackgroundColor = approxColor;
}
else if (mode == "2" && j + 3 < parts.Length)
{
From 835228f888659061380414e645af2dabc3899b45 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 16:23:22 -0600
Subject: [PATCH 12/13] Separately track 256-color codes
---
test/InlinePredictionTest.cs | 12 +++++++-
test/MockConsole.cs | 54 ++++++------------------------------
2 files changed, 20 insertions(+), 46 deletions(-)
diff --git a/test/InlinePredictionTest.cs b/test/InlinePredictionTest.cs
index febf0ac1f..a22cd43fd 100644
--- a/test/InlinePredictionTest.cs
+++ b/test/InlinePredictionTest.cs
@@ -638,7 +638,7 @@ public void Inline_HistoryAndPluginSource_Acceptance()
Assert.Equal(Guid.Empty, _mockedMethods.acceptedPredictorId);
Assert.Null(_mockedMethods.acceptedSuggestion);
Assert.NotNull(_mockedMethods.commandHistory);
- Assert.Equal(1, _mockedMethods.commandHistory.Count);
+ Assert.Single(_mockedMethods.commandHistory);
Assert.Equal("netsh show me", _mockedMethods.commandHistory[0]);
_mockedMethods.ClearPredictionFields();
@@ -839,5 +839,15 @@ public void Inline_TruncateVeryLongSuggestion()
TokenClassification.Command, "vv"))
));
}
+
+ // Example: How to validate 256-color emission for predictions
+ // When PSReadLine emits ANSI escape codes like \x1b[38;5;238m (256-color),
+ // the test console now tracks them in Emitted256Colors list.
+ //
+ // Usage in tests:
+ // CheckThat(() => {
+ // // Verify PSReadLine emitted color 238 for prediction text
+ // Assert.Contains(_console.Emitted256Colors, c => c.ColorIndex == 238);
+ // })
}
}
diff --git a/test/MockConsole.cs b/test/MockConsole.cs
index f126a1ca1..ca1c1a78d 100644
--- a/test/MockConsole.cs
+++ b/test/MockConsole.cs
@@ -178,6 +178,9 @@ public virtual int WindowHeight
public virtual int WindowTop { get; set; }
+ // Track 256-color/RGB escape sequences emitted by PSReadLine
+ public List<(int Position, bool IsForeground, int ColorIndex)> Emitted256Colors { get; } = new();
+
public virtual ConsoleColor BackgroundColor
{
get => _backgroundColor;
@@ -248,6 +251,7 @@ public virtual void Write(string s)
// Handle 256-color/RGB sequences: 38 (FG) or 48 (BG)
// PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
+ // Track these sequences without changing color state
if (subsequence is "38" or "48")
{
bool isForeground = subsequence == "38";
@@ -256,12 +260,8 @@ public virtual void Write(string s)
var mode = parts[++j];
if (mode == "5" && j + 1 < parts.Length)
{
- var colorIndex = parts[++j];
- var approxColor = Map256ColorToConsoleColor(colorIndex);
- if (isForeground)
- ForegroundColor = approxColor;
- else
- BackgroundColor = approxColor;
+ var colorIndex = int.Parse(parts[++j]);
+ Emitted256Colors.Add((writePos, isForeground, colorIndex));
}
else if (mode == "2" && j + 3 < parts.Length)
{
@@ -423,39 +423,6 @@ private static void ToggleNegative(TestConsole c, bool b)
{ "0J", c => c.BlankRestOfBuffer() },
{ "2J", c => c.SetCursorPosition(0, 0) },
};
-
- protected static ConsoleColor Map256ColorToConsoleColor(string colorIndex)
- {
- // Map 256-color palette to nearest ConsoleColor
- // PSReadLine commonly uses 238 (dark gray)
- if (!int.TryParse(colorIndex, out var index))
- return ConsoleColor.White;
-
- return index switch
- {
- // Standard colors (0-15)
- 0 => ConsoleColor.Black,
- 1 => ConsoleColor.DarkRed,
- 2 => ConsoleColor.DarkGreen,
- 3 => ConsoleColor.DarkYellow,
- 4 => ConsoleColor.DarkBlue,
- 5 => ConsoleColor.DarkMagenta,
- 6 => ConsoleColor.DarkCyan,
- 7 => ConsoleColor.Gray,
- 8 => ConsoleColor.DarkGray,
- 9 => ConsoleColor.Red,
- 10 => ConsoleColor.Green,
- 11 => ConsoleColor.Yellow,
- 12 => ConsoleColor.Blue,
- 13 => ConsoleColor.Magenta,
- 14 => ConsoleColor.Cyan,
- 15 => ConsoleColor.White,
- // 232-255: Grayscale ramp (238 is commonly used by PSReadLine)
- >= 232 and <= 255 => ConsoleColor.DarkGray,
- // Default for other 256 colors: approximate based on ranges
- _ => ConsoleColor.White
- };
- }
}
internal class BasicScrollingConsole : TestConsole
@@ -542,6 +509,7 @@ public override void Write(string s)
// Handle 256-color/RGB sequences: 38 (FG) or 48 (BG)
// PSReadLine uses \x1b[38;5;238m and \x1b[48;5;238m for predictions
+ // Track these sequences without changing color state
if (subsequence is "38" or "48")
{
bool isForeground = subsequence == "38";
@@ -550,12 +518,8 @@ public override void Write(string s)
var mode = parts[++j];
if (mode == "5" && j + 1 < parts.Length)
{
- var colorIndex = parts[++j];
- var approxColor = Map256ColorToConsoleColor(colorIndex);
- if (isForeground)
- ForegroundColor = approxColor;
- else
- BackgroundColor = approxColor;
+ var colorIndex = int.Parse(parts[++j]);
+ Emitted256Colors.Add((writePos, isForeground, colorIndex));
}
else if (mode == "2" && j + 3 < parts.Length)
{
From 0870e63e333bd29cc1133e9a19b08578c0d79d60 Mon Sep 17 00:00:00 2001
From: Justin Chung <124807742+jshigetomi@users.noreply.github.com>
Date: Thu, 8 Jan 2026 17:22:39 -0600
Subject: [PATCH 13/13] Fix skipping when 38
---
test/MockConsole.cs | 34 ++++++++++++++++++++--------------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/test/MockConsole.cs b/test/MockConsole.cs
index ca1c1a78d..421a4ce22 100644
--- a/test/MockConsole.cs
+++ b/test/MockConsole.cs
@@ -255,17 +255,20 @@ public virtual void Write(string s)
if (subsequence is "38" or "48")
{
bool isForeground = subsequence == "38";
- if (j + 1 < parts.Length)
+ if (j + 2 < parts.Length)
{
- var mode = parts[++j];
- if (mode == "5" && j + 1 < parts.Length)
+ var mode = parts[j + 1];
+ if (mode == "5")
{
- var colorIndex = int.Parse(parts[++j]);
- Emitted256Colors.Add((writePos, isForeground, colorIndex));
+ if (int.TryParse(parts[j + 2], out var colorIndex))
+ {
+ Emitted256Colors.Add((writePos, isForeground, colorIndex));
+ }
+ j += 2; // Skip mode and color index
}
- else if (mode == "2" && j + 3 < parts.Length)
+ else if (mode == "2" && j + 4 < parts.Length)
{
- j += 3; // Skip RGB values
+ j += 4; // Skip mode and RGB values (r, g, b)
}
}
continue;
@@ -513,17 +516,20 @@ public override void Write(string s)
if (subsequence is "38" or "48")
{
bool isForeground = subsequence == "38";
- if (j + 1 < parts.Length)
+ if (j + 2 < parts.Length)
{
- var mode = parts[++j];
- if (mode == "5" && j + 1 < parts.Length)
+ var mode = parts[j + 1];
+ if (mode == "5")
{
- var colorIndex = int.Parse(parts[++j]);
- Emitted256Colors.Add((writePos, isForeground, colorIndex));
+ if (int.TryParse(parts[j + 2], out var colorIndex))
+ {
+ Emitted256Colors.Add((writePos, isForeground, colorIndex));
+ }
+ j += 2; // Skip mode and color index
}
- else if (mode == "2" && j + 3 < parts.Length)
+ else if (mode == "2" && j + 4 < parts.Length)
{
- j += 3; // Skip RGB values
+ j += 4; // Skip mode and RGB values (r, g, b)
}
}
continue;