diff --git a/module/PowerShellEditorServices/Start-EditorServices.ps1 b/module/PowerShellEditorServices/Start-EditorServices.ps1
index 47ba523c2..9721cf536 100644
--- a/module/PowerShellEditorServices/Start-EditorServices.ps1
+++ b/module/PowerShellEditorServices/Start-EditorServices.ps1
@@ -52,10 +52,10 @@ param(
[ValidateSet("Diagnostic", "Verbose", "Normal", "Warning", "Error")]
$LogLevel,
- [Parameter(Mandatory=$true)]
- [ValidateNotNullOrEmpty()]
- [string]
- $SessionDetailsPath,
+ [Parameter(Mandatory=$true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $SessionDetailsPath,
[switch]
$EnableConsoleRepl,
@@ -63,6 +63,9 @@ param(
[switch]
$UseLegacyReadLine,
+ [switch]
+ $UseHostReadKey,
+
[switch]
$DebugServiceOnly,
diff --git a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs
index 3d31ede06..599b14662 100644
--- a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs
+++ b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs
@@ -161,6 +161,12 @@ public StartEditorServicesCommand()
[Parameter]
public SwitchParameter UseLegacyReadLine { get; set; }
+ ///
+ /// When set and the console is enabled and legacy readline
+ /// is enabled, console operations will use PSHostreadline implementation will be used instead of PSReadLine.
+ ///
+ [Parameter]
+ public SwitchParameter UseHostReadKey { get; set; }
///
/// When set, do not enable LSP service, only the debug adapter.
///
@@ -345,8 +351,9 @@ private EditorServicesConfig CreateConfigObject()
hostInfo,
Host,
SessionDetailsPath,
- bundledModulesPath,
- LogPath)
+ bundledModulesPath,
+ LogPath,
+ UseHostReadKey)
{
FeatureFlags = FeatureFlags,
LogLevel = LogLevel,
diff --git a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs
index f8b6ef30f..81c70dd3e 100644
--- a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs
+++ b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs
@@ -17,7 +17,7 @@ public enum ConsoleReplKind
/// Use a REPL with the legacy readline implementation. This is generally used when PSReadLine is unavailable.
LegacyReadLine = 1,
/// Use a REPL with the PSReadLine module for console interaction.
- PSReadLine = 2,
+ PSReadLine = 2
}
///
@@ -39,13 +39,15 @@ public EditorServicesConfig(
PSHost psHost,
string sessionDetailsPath,
string bundledModulePath,
- string logPath)
+ string logPath,
+ bool useHostReadKey)
{
HostInfo = hostInfo;
PSHost = psHost;
SessionDetailsPath = sessionDetailsPath;
BundledModulePath = bundledModulePath;
LogPath = logPath;
+ UseHostReadKey = useHostReadKey;
}
///
@@ -72,6 +74,7 @@ public EditorServicesConfig(
/// The path to use for logging for Editor Services.
///
public string LogPath { get; }
+ public bool UseHostReadKey { get; }
///
/// Names of or paths to any additional modules to load on startup.
@@ -88,7 +91,7 @@ public EditorServicesConfig(
/// (including none to disable the integrated console).
///
public ConsoleReplKind ConsoleRepl { get; set; } = ConsoleReplKind.None;
-
+
///
/// The minimum log level to log events with.
///
diff --git a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs
index 924619283..e1bc6f24f 100644
--- a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs
+++ b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs
@@ -112,7 +112,7 @@ public static EditorServicesLoader Create(
string asmPath = Path.Combine(s_psesDependencyDirPath, $"{asmName.Name}.dll");
- logger.Log(PsesLogLevel.Verbose, "Loading PSES DLL using new assembly load context");
+ logger.Log(PsesLogLevel.Verbose, $"Loading {asmName.Name}.dll using new assembly load context");
return psesLoadContext.LoadFromAssemblyPath(asmPath);
};
diff --git a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs
index 3e6626f20..d7743b96a 100644
--- a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs
+++ b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs
@@ -290,6 +290,7 @@ private HostStartupInfo CreateHostStartupInfo()
(int)_config.LogLevel,
consoleReplEnabled: _config.ConsoleRepl != ConsoleReplKind.None,
usesLegacyReadLine: _config.ConsoleRepl == ConsoleReplKind.LegacyReadLine,
+ useHostReadKey: _config.UseHostReadKey,
bundledModulePath: _config.BundledModulePath);
}
diff --git a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs
index e34df5912..d35a87b91 100644
--- a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs
+++ b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs
@@ -80,7 +80,11 @@ public sealed class HostStartupInfo
/// If the console REPL is not enabled, this setting will be ignored.
///
public bool UsesLegacyReadLine { get; }
-
+ ///
+ /// If true, the legacy PSES readline implementation but calls ReadKey in the provided host
+ /// If the console REPL is not enabled, this setting will be ignored.
+ ///
+ public bool UseHostReadKey { get; }
///
/// The PowerShell host to use with Editor Services.
///
@@ -153,6 +157,7 @@ public HostStartupInfo(
int logLevel,
bool consoleReplEnabled,
bool usesLegacyReadLine,
+ bool useHostReadKey,
string bundledModulePath)
{
Name = name ?? DefaultHostName;
@@ -167,6 +172,7 @@ public HostStartupInfo(
LogLevel = logLevel;
ConsoleReplEnabled = consoleReplEnabled;
UsesLegacyReadLine = usesLegacyReadLine;
+ UseHostReadKey = useHostReadKey;
BundledModulePath = bundledModulePath;
}
diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs
index 39e12974b..4b78931cb 100644
--- a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs
+++ b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs
@@ -63,6 +63,7 @@ public override string ReadLine(CancellationToken cancellationToken)
// because the window could have been resized before then
int promptStartCol = initialCursorCol;
int promptStartRow = initialCursorRow;
+
int consoleWidth = Console.WindowWidth;
switch (keyInfo.Key)
diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs
index cda3af925..c3513a5fd 100644
--- a/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs
+++ b/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs
@@ -17,6 +17,7 @@ internal class PsrlReadLine : TerminalReadLine
private readonly PsesInternalHost _psesHost;
private readonly EngineIntrinsics _engineIntrinsics;
+
public PsrlReadLine(
PSReadLineProxy psrlProxy,
@@ -29,7 +30,7 @@ public PsrlReadLine(
_psesHost = psesHost;
_engineIntrinsics = engineIntrinsics;
_psrlProxy.OverrideReadKey(readKeyFunc);
- _psrlProxy.OverrideIdleHandler(onIdleAction);
+ _psrlProxy.OverrideIdleHandler(onIdleAction);
}
public override string ReadLine(CancellationToken cancellationToken) => _psesHost.InvokeDelegate(
@@ -39,7 +40,7 @@ public override string ReadLine(CancellationToken cancellationToken) => _psesHos
cancellationToken);
protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) => _psesHost.ReadKey(intercept: true, cancellationToken);
-
+
private string InvokePSReadLine(CancellationToken cancellationToken)
{
EngineIntrinsics engineIntrinsics = _psesHost.IsRunspacePushed ? null : _engineIntrinsics;
diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
index 271518add..0f267cb5b 100644
--- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
+++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
@@ -98,6 +98,7 @@ public PsesInternalHost(
_logger = loggerFactory.CreateLogger();
_languageServer = languageServer;
_hostInfo = hostInfo;
+
// Respect a user provided bundled module path.
if (Directory.Exists(hostInfo.BundledModulePath))
@@ -136,6 +137,7 @@ public PsesInternalHost(
Version = hostInfo.Version;
DebugContext = new PowerShellDebugContext(loggerFactory, this);
+
UI = hostInfo.ConsoleReplEnabled
? new EditorServicesConsolePSHostUserInterface(loggerFactory, hostInfo.PSHost.UI)
: new NullPSHostUI();
@@ -1071,6 +1073,24 @@ private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args)
StopDebugContext();
}
}
+ ///
+ /// This method is sent to PSReadLine as a workaround for issues with the System.Console
+ /// implementation. Functionally it is the same as System.Console.ReadKey,
+ /// with the exception that it will not lock the standard input stream.
+ ///
+ ///
+ /// Determines whether to display the pressed key in the console window.
+ /// true to not display the pressed key; otherwise, false.
+ ///
+ ///
+ /// The that can be used to cancel the request.
+ ///
+ ///
+ /// An object that describes the ConsoleKey constant and Unicode character, if any,
+ /// that correspond to the pressed console key. The ConsoleKeyInfo object also describes,
+ /// in a bitwise combination of ConsoleModifiers values, whether one or more Shift, Alt,
+ /// or Ctrl modifier keys was pressed simultaneously with the console key.
+ ///
private ConsoleKeyInfo ReadKey(bool intercept)
{
@@ -1094,13 +1114,24 @@ private ConsoleKeyInfo ReadKey(bool intercept)
// we can subscribe in the same way.
DebugServer?.SendNotification("powerShell/sendKeyPress");
});
-
+
// PSReadLine doesn't tell us when CtrlC was sent. So instead we keep track of the last
// key here. This isn't functionally required, but helps us determine when the prompt
// needs a newline added
//
// TODO: We may want to allow users of PSES to override this method call.
- _lastKey = System.Console.ReadKey(intercept);
+ if (!_hostInfo.UseHostReadKey)
+ {
+ _lastKey = System.Console.ReadKey(intercept);
+ }
+ else
+ {
+ KeyInfo keyInfo = _hostInfo.PSHost.UI.RawUI.ReadKey();
+ _lastKey = new(keyInfo.Character, (ConsoleKey)keyInfo.Character, (keyInfo.ControlKeyState & ControlKeyStates.ShiftPressed) > 0,
+ (keyInfo.ControlKeyState & (ControlKeyStates.RightAltPressed | ControlKeyStates.LeftAltPressed)) > 0,
+ (keyInfo.ControlKeyState & (ControlKeyStates.RightCtrlPressed | ControlKeyStates.LeftCtrlPressed)) > 0);
+ }
+
return _lastKey.Value;
}
diff --git a/test/PowerShellEditorServices.Test/PsesHostFactory.cs b/test/PowerShellEditorServices.Test/PsesHostFactory.cs
index 3f7ab95b9..81852d163 100644
--- a/test/PowerShellEditorServices.Test/PsesHostFactory.cs
+++ b/test/PowerShellEditorServices.Test/PsesHostFactory.cs
@@ -57,6 +57,7 @@ public static PsesInternalHost Create(ILoggerFactory loggerFactory)
logLevel: (int)LogLevel.None,
consoleReplEnabled: false,
usesLegacyReadLine: false,
+ useHostReadKey: false,
bundledModulePath: BundledModulePath);
PsesInternalHost psesHost = new(loggerFactory, null, testHostDetails);