From 4b545cad1f496f65244c9f43aa948536f70f277d Mon Sep 17 00:00:00 2001 From: dkattan <1424395+dkattan@users.noreply.github.com> Date: Thu, 24 Mar 2022 15:16:40 -0500 Subject: [PATCH 1/3] Added a -UseHostReadKey switch to instruct LegacyReadLine to use the ReadKey provided by the host instead of Console.ReadKey --- .../Start-EditorServices.ps1 | 11 ++-- .../Commands/StartEditorServicesCommand.cs | 11 +++- .../Configuration/EditorServicesConfig.cs | 9 ++- .../EditorServicesLoader.cs | 4 +- .../Internal/EditorServicesRunner.cs | 1 + .../Hosting/HostStartupInfo.cs | 8 ++- .../PowerShell/Console/LegacyReadLine.cs | 17 +++-- .../PowerShell/Console/PsrlReadLine.cs | 14 ++++- .../PowerShell/Console/TerminalReadLine.cs | 2 + ...orServicesConsolePSHostRawUserInterface.cs | 52 +++++++++++++-- ...ditorServicesConsolePSHostUserInterface.cs | 9 ++- .../PowerShell/Host/PsesInternalHost.cs | 63 ++++++++++++++++++- 12 files changed, 173 insertions(+), 28 deletions(-) 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..204de1b55 100644 --- a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs +++ b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs @@ -107,12 +107,12 @@ public static EditorServicesLoader Create( // We only want the Editor Services DLL; the new ALC will lazily load its dependencies automatically if (!string.Equals(asmName.Name, "Microsoft.PowerShell.EditorServices", StringComparison.Ordinal)) { - return null; + //return null; } 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..fbc72f39f 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs @@ -24,15 +24,19 @@ internal class LegacyReadLine : TerminalReadLine private readonly Action _onIdleAction; + private IConsoleOperations _consoleOperations; + public LegacyReadLine( PsesInternalHost psesHost, Func readKeyFunc, - Action onIdleAction) + Action onIdleAction, + IConsoleOperations consoleOperations) { _psesHost = psesHost; _readKeyTasks = new Task[2]; _readKeyFunc = readKeyFunc; _onIdleAction = onIdleAction; + _consoleOperations = consoleOperations; } public override string ReadLine(CancellationToken cancellationToken) @@ -46,12 +50,12 @@ public override string ReadLine(CancellationToken cancellationToken) StringBuilder inputLine = new(); - int initialCursorCol = Console.CursorLeft; - int initialCursorRow = Console.CursorTop; + int initialCursorCol = _consoleOperations?.GetCursorLeft(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.X; + int initialCursorRow = _consoleOperations?.GetCursorTop(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.Y; ; int currentCursorIndex = 0; - - Console.TreatControlCAsInput = true; + if(_consoleOperations != null) + Console.TreatControlCAsInput = true; try { @@ -63,7 +67,8 @@ 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; + + int consoleWidth = _consoleOperations is not null ? Console.WindowWidth : _psesHost.UI.RawUI.WindowSize.Width; switch (keyInfo.Key) { diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs index cda3af925..b4ecd583b 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs @@ -17,19 +17,22 @@ internal class PsrlReadLine : TerminalReadLine private readonly PsesInternalHost _psesHost; private readonly EngineIntrinsics _engineIntrinsics; + private IConsoleOperations _consoleOperations; public PsrlReadLine( PSReadLineProxy psrlProxy, PsesInternalHost psesHost, EngineIntrinsics engineIntrinsics, Func readKeyFunc, - Action onIdleAction) + Action onIdleAction, + IConsoleOperations consoleOperations) { _psrlProxy = psrlProxy; _psesHost = psesHost; _engineIntrinsics = engineIntrinsics; _psrlProxy.OverrideReadKey(readKeyFunc); _psrlProxy.OverrideIdleHandler(onIdleAction); + _consoleOperations = consoleOperations; } public override string ReadLine(CancellationToken cancellationToken) => _psesHost.InvokeDelegate( @@ -40,6 +43,15 @@ public override string ReadLine(CancellationToken cancellationToken) => _psesHos protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) => _psesHost.ReadKey(intercept: true, cancellationToken); + protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) + { + return _consoleOperations.ReadKey(intercept: true, cancellationToken); + } + + #endregion + + #region Private Methods + private string InvokePSReadLine(CancellationToken cancellationToken) { EngineIntrinsics engineIntrinsics = _psesHost.IsRunspacePushed ? null : _engineIntrinsics; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs index 8d05edc18..0dac40546 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs @@ -9,6 +9,8 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console internal abstract class TerminalReadLine : IReadLine { + private IConsoleOperations _consoleOperations; + public abstract string ReadLine(CancellationToken cancellationToken); protected abstract ConsoleKeyInfo ReadKey(CancellationToken cancellationToken); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs index 8a90f0503..2bfd3bb08 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs @@ -24,10 +24,12 @@ internal class EditorServicesConsolePSHostRawUserInterface : PSHostRawUserInterf /// public EditorServicesConsolePSHostRawUserInterface( ILoggerFactory loggerFactory, - PSHostRawUserInterface internalRawUI) + PSHostRawUserInterface internalRawUI, + IConsoleOperations consoleOperations) { _logger = loggerFactory.CreateLogger(); _internalRawUI = internalRawUI; + _consoleOperations = consoleOperations; } #endregion @@ -39,8 +41,28 @@ public EditorServicesConsolePSHostRawUserInterface( /// public override ConsoleColor BackgroundColor { - get => System.Console.BackgroundColor; - set => System.Console.BackgroundColor = value; + get + { + if (_consoleOperations is not null) + { + return System.Console.BackgroundColor; + } + else + { + return _internalRawUI.BackgroundColor; + } + } + set + { + if (_consoleOperations is not null) + { + System.Console.BackgroundColor = value; + } + else + { + _internalRawUI.BackgroundColor = value; + } + } } /// @@ -48,8 +70,28 @@ public override ConsoleColor BackgroundColor /// public override ConsoleColor ForegroundColor { - get => System.Console.ForegroundColor; - set => System.Console.ForegroundColor = value; + get + { + if (_consoleOperations is not null) + { + return System.Console.ForegroundColor; + } + else + { + return _internalRawUI.ForegroundColor; + } + } + set + { + if (_consoleOperations is not null) + { + System.Console.ForegroundColor = value; + } + else + { + _internalRawUI.ForegroundColor = value; + } + } } /// diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs index fb6d2c861..166583a66 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs @@ -27,7 +27,14 @@ public EditorServicesConsolePSHostUserInterface( PSHostUserInterface underlyingHostUI) { _underlyingHostUI = underlyingHostUI; - RawUI = new EditorServicesConsolePSHostRawUserInterface(loggerFactory, underlyingHostUI.RawUI); + RawUI = new EditorServicesConsolePSHostRawUserInterface(loggerFactory, underlyingHostUI.RawUI, consoleOperations); + + _consoleHostUI = GetConsoleHostUI(_underlyingHostUI); + + if (_consoleHostUI != null) + { + SetConsoleHostUIToInteractive(_consoleHostUI); + } } public override bool SupportsVirtualTerminal => _underlyingHostUI.SupportsVirtualTerminal; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index 271518add..53eedb935 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,14 @@ public PsesInternalHost( Version = hostInfo.Version; DebugContext = new PowerShellDebugContext(loggerFactory, this); + if (!_hostInfo.UseHostReadKey) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _consoleOperations = new WindowsConsoleOperations(); + } + _consoleOperations = new UnixConsoleOperations(); + } UI = hostInfo.ConsoleReplEnabled ? new EditorServicesConsolePSHostUserInterface(loggerFactory, hostInfo.PSHost.UI) : new NullPSHostUI(); @@ -955,7 +964,7 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace) // If we've been configured to use it, or if we can't load PSReadLine, use the legacy readline if (hostStartupInfo.UsesLegacyReadLine || !TryLoadPSReadLine(pwsh, engineIntrinsics, out IReadLine readLine)) { - readLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle); + readLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle, _consoleOperations); } readLineProvider.OverrideReadLine(readLine); @@ -1071,7 +1080,55 @@ 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. + /// + internal ConsoleKeyInfo SafeReadKey(bool intercept, CancellationToken cancellationToken) + { + try + { + if (_consoleOperations is not null) + { + return _consoleOperations.ReadKey(intercept, cancellationToken); + } + else + { + var keyInfo = UI.RawUI.ReadKey(ReadKeyOptions.AllowCtrlC); + + return new ConsoleKeyInfo( + keyChar: keyInfo.Character, + key: ConsoleKey.DownArrow, + shift: (keyInfo.ControlKeyState & ControlKeyStates.ShiftPressed) > 0, + alt: (keyInfo.ControlKeyState & (ControlKeyStates.RightAltPressed | ControlKeyStates.LeftAltPressed)) > 0, + control: (keyInfo.ControlKeyState > 0) + ); + } + } + catch (OperationCanceledException) + { + return new ConsoleKeyInfo( + keyChar: ' ', + key: ConsoleKey.DownArrow, + shift: false, + alt: false, + control: false); + } + } private ConsoleKeyInfo ReadKey(bool intercept) { // NOTE: This requests that the client (the Code extension) send a non-printing key back @@ -1248,7 +1305,7 @@ internal bool TryLoadPSReadLine(PowerShell pwsh, EngineIntrinsics engineIntrinsi try { PSReadLineProxy psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, s_bundledModulePath, pwsh); - psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle); + psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle, _consoleOperations); return true; } catch (Exception e) From b6f3596d302fb95e78159effeada3caf63a5d0d5 Mon Sep 17 00:00:00 2001 From: dkattan <1424395+dkattan@users.noreply.github.com> Date: Thu, 24 Mar 2022 15:34:05 -0500 Subject: [PATCH 2/3] Corrected build issues. Removed ConsoleProxy. Reverted changes to EditorServicesLoader --- .../EditorServicesLoader.cs | 2 +- .../Host/EditorServicesConsolePSHostUserInterface.cs | 7 ------- test/PowerShellEditorServices.Test/PsesHostFactory.cs | 1 + 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs index 204de1b55..e1bc6f24f 100644 --- a/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs +++ b/src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs @@ -107,7 +107,7 @@ public static EditorServicesLoader Create( // We only want the Editor Services DLL; the new ALC will lazily load its dependencies automatically if (!string.Equals(asmName.Name, "Microsoft.PowerShell.EditorServices", StringComparison.Ordinal)) { - //return null; + return null; } string asmPath = Path.Combine(s_psesDependencyDirPath, $"{asmName.Name}.dll"); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs index 166583a66..bdf867f93 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs @@ -28,13 +28,6 @@ public EditorServicesConsolePSHostUserInterface( { _underlyingHostUI = underlyingHostUI; RawUI = new EditorServicesConsolePSHostRawUserInterface(loggerFactory, underlyingHostUI.RawUI, consoleOperations); - - _consoleHostUI = GetConsoleHostUI(_underlyingHostUI); - - if (_consoleHostUI != null) - { - SetConsoleHostUIToInteractive(_consoleHostUI); - } } public override bool SupportsVirtualTerminal => _underlyingHostUI.SupportsVirtualTerminal; 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); From 46d2732c73e7aa533f6c62f84b76cd10e07fafd6 Mon Sep 17 00:00:00 2001 From: dkattan <1424395+dkattan@users.noreply.github.com> Date: Fri, 20 May 2022 16:34:17 -0500 Subject: [PATCH 3/3] Rebased. Compiles. Needs testing. --- .../PowerShell/Console/LegacyReadLine.cs | 16 ++--- .../PowerShell/Console/PsrlReadLine.cs | 19 ++---- .../PowerShell/Console/TerminalReadLine.cs | 2 - ...orServicesConsolePSHostRawUserInterface.cs | 52 ++-------------- ...ditorServicesConsolePSHostUserInterface.cs | 2 +- .../PowerShell/Host/PsesInternalHost.cs | 60 ++++++------------- 6 files changed, 33 insertions(+), 118 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs index fbc72f39f..4b78931cb 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs @@ -24,19 +24,15 @@ internal class LegacyReadLine : TerminalReadLine private readonly Action _onIdleAction; - private IConsoleOperations _consoleOperations; - public LegacyReadLine( PsesInternalHost psesHost, Func readKeyFunc, - Action onIdleAction, - IConsoleOperations consoleOperations) + Action onIdleAction) { _psesHost = psesHost; _readKeyTasks = new Task[2]; _readKeyFunc = readKeyFunc; _onIdleAction = onIdleAction; - _consoleOperations = consoleOperations; } public override string ReadLine(CancellationToken cancellationToken) @@ -50,12 +46,12 @@ public override string ReadLine(CancellationToken cancellationToken) StringBuilder inputLine = new(); - int initialCursorCol = _consoleOperations?.GetCursorLeft(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.X; - int initialCursorRow = _consoleOperations?.GetCursorTop(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.Y; ; + int initialCursorCol = Console.CursorLeft; + int initialCursorRow = Console.CursorTop; int currentCursorIndex = 0; - if(_consoleOperations != null) - Console.TreatControlCAsInput = true; + + Console.TreatControlCAsInput = true; try { @@ -68,7 +64,7 @@ public override string ReadLine(CancellationToken cancellationToken) int promptStartCol = initialCursorCol; int promptStartRow = initialCursorRow; - int consoleWidth = _consoleOperations is not null ? Console.WindowWidth : _psesHost.UI.RawUI.WindowSize.Width; + 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 b4ecd583b..c3513a5fd 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs @@ -17,22 +17,20 @@ internal class PsrlReadLine : TerminalReadLine private readonly PsesInternalHost _psesHost; private readonly EngineIntrinsics _engineIntrinsics; - private IConsoleOperations _consoleOperations; + public PsrlReadLine( PSReadLineProxy psrlProxy, PsesInternalHost psesHost, EngineIntrinsics engineIntrinsics, Func readKeyFunc, - Action onIdleAction, - IConsoleOperations consoleOperations) + Action onIdleAction) { _psrlProxy = psrlProxy; _psesHost = psesHost; _engineIntrinsics = engineIntrinsics; _psrlProxy.OverrideReadKey(readKeyFunc); - _psrlProxy.OverrideIdleHandler(onIdleAction); - _consoleOperations = consoleOperations; + _psrlProxy.OverrideIdleHandler(onIdleAction); } public override string ReadLine(CancellationToken cancellationToken) => _psesHost.InvokeDelegate( @@ -42,16 +40,7 @@ public override string ReadLine(CancellationToken cancellationToken) => _psesHos cancellationToken); protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) => _psesHost.ReadKey(intercept: true, cancellationToken); - - protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) - { - return _consoleOperations.ReadKey(intercept: true, cancellationToken); - } - - #endregion - - #region Private Methods - + private string InvokePSReadLine(CancellationToken cancellationToken) { EngineIntrinsics engineIntrinsics = _psesHost.IsRunspacePushed ? null : _engineIntrinsics; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs b/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs index 0dac40546..8d05edc18 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs @@ -9,8 +9,6 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console internal abstract class TerminalReadLine : IReadLine { - private IConsoleOperations _consoleOperations; - public abstract string ReadLine(CancellationToken cancellationToken); protected abstract ConsoleKeyInfo ReadKey(CancellationToken cancellationToken); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs index 2bfd3bb08..8a90f0503 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostRawUserInterface.cs @@ -24,12 +24,10 @@ internal class EditorServicesConsolePSHostRawUserInterface : PSHostRawUserInterf /// public EditorServicesConsolePSHostRawUserInterface( ILoggerFactory loggerFactory, - PSHostRawUserInterface internalRawUI, - IConsoleOperations consoleOperations) + PSHostRawUserInterface internalRawUI) { _logger = loggerFactory.CreateLogger(); _internalRawUI = internalRawUI; - _consoleOperations = consoleOperations; } #endregion @@ -41,28 +39,8 @@ public EditorServicesConsolePSHostRawUserInterface( /// public override ConsoleColor BackgroundColor { - get - { - if (_consoleOperations is not null) - { - return System.Console.BackgroundColor; - } - else - { - return _internalRawUI.BackgroundColor; - } - } - set - { - if (_consoleOperations is not null) - { - System.Console.BackgroundColor = value; - } - else - { - _internalRawUI.BackgroundColor = value; - } - } + get => System.Console.BackgroundColor; + set => System.Console.BackgroundColor = value; } /// @@ -70,28 +48,8 @@ public override ConsoleColor BackgroundColor /// public override ConsoleColor ForegroundColor { - get - { - if (_consoleOperations is not null) - { - return System.Console.ForegroundColor; - } - else - { - return _internalRawUI.ForegroundColor; - } - } - set - { - if (_consoleOperations is not null) - { - System.Console.ForegroundColor = value; - } - else - { - _internalRawUI.ForegroundColor = value; - } - } + get => System.Console.ForegroundColor; + set => System.Console.ForegroundColor = value; } /// diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs index bdf867f93..fb6d2c861 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHostUserInterface.cs @@ -27,7 +27,7 @@ public EditorServicesConsolePSHostUserInterface( PSHostUserInterface underlyingHostUI) { _underlyingHostUI = underlyingHostUI; - RawUI = new EditorServicesConsolePSHostRawUserInterface(loggerFactory, underlyingHostUI.RawUI, consoleOperations); + RawUI = new EditorServicesConsolePSHostRawUserInterface(loggerFactory, underlyingHostUI.RawUI); } public override bool SupportsVirtualTerminal => _underlyingHostUI.SupportsVirtualTerminal; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index 53eedb935..0f267cb5b 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -137,14 +137,7 @@ public PsesInternalHost( Version = hostInfo.Version; DebugContext = new PowerShellDebugContext(loggerFactory, this); - if (!_hostInfo.UseHostReadKey) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - _consoleOperations = new WindowsConsoleOperations(); - } - _consoleOperations = new UnixConsoleOperations(); - } + UI = hostInfo.ConsoleReplEnabled ? new EditorServicesConsolePSHostUserInterface(loggerFactory, hostInfo.PSHost.UI) : new NullPSHostUI(); @@ -964,7 +957,7 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace) // If we've been configured to use it, or if we can't load PSReadLine, use the legacy readline if (hostStartupInfo.UsesLegacyReadLine || !TryLoadPSReadLine(pwsh, engineIntrinsics, out IReadLine readLine)) { - readLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle, _consoleOperations); + readLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle); } readLineProvider.OverrideReadLine(readLine); @@ -1098,37 +1091,7 @@ private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args) /// in a bitwise combination of ConsoleModifiers values, whether one or more Shift, Alt, /// or Ctrl modifier keys was pressed simultaneously with the console key. /// - internal ConsoleKeyInfo SafeReadKey(bool intercept, CancellationToken cancellationToken) - { - try - { - if (_consoleOperations is not null) - { - return _consoleOperations.ReadKey(intercept, cancellationToken); - } - else - { - var keyInfo = UI.RawUI.ReadKey(ReadKeyOptions.AllowCtrlC); - - return new ConsoleKeyInfo( - keyChar: keyInfo.Character, - key: ConsoleKey.DownArrow, - shift: (keyInfo.ControlKeyState & ControlKeyStates.ShiftPressed) > 0, - alt: (keyInfo.ControlKeyState & (ControlKeyStates.RightAltPressed | ControlKeyStates.LeftAltPressed)) > 0, - control: (keyInfo.ControlKeyState > 0) - ); - } - } - catch (OperationCanceledException) - { - return new ConsoleKeyInfo( - keyChar: ' ', - key: ConsoleKey.DownArrow, - shift: false, - alt: false, - control: false); - } - } + private ConsoleKeyInfo ReadKey(bool intercept) { // NOTE: This requests that the client (the Code extension) send a non-printing key back @@ -1151,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; } @@ -1305,7 +1279,7 @@ internal bool TryLoadPSReadLine(PowerShell pwsh, EngineIntrinsics engineIntrinsi try { PSReadLineProxy psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, s_bundledModulePath, pwsh); - psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle, _consoleOperations); + psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle); return true; } catch (Exception e)