Skip to content

Commit f4d015f

Browse files
committed
Added a -UseHostReadKey switch to instruct LegacyReadLine to use the ReadKey provided by the host instead of Console.ReadKey
1 parent cae6b99 commit f4d015f

File tree

13 files changed

+198
-86
lines changed

13 files changed

+198
-86
lines changed

module/PowerShellEditorServices/Start-EditorServices.ps1

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,20 @@ param(
5252
[ValidateSet("Diagnostic", "Verbose", "Normal", "Warning", "Error")]
5353
$LogLevel,
5454

55-
[Parameter(Mandatory=$true)]
56-
[ValidateNotNullOrEmpty()]
57-
[string]
58-
$SessionDetailsPath,
55+
[Parameter(Mandatory=$true)]
56+
[ValidateNotNullOrEmpty()]
57+
[string]
58+
$SessionDetailsPath,
5959

6060
[switch]
6161
$EnableConsoleRepl,
6262

6363
[switch]
6464
$UseLegacyReadLine,
6565

66+
[switch]
67+
$UseHostReadKey,
68+
6669
[switch]
6770
$DebugServiceOnly,
6871

src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ public StartEditorServicesCommand()
176176
[Parameter]
177177
public SwitchParameter UseLegacyReadLine { get; set; }
178178

179+
/// <summary>
180+
/// When set and the console is enabled and legacy readline
181+
/// is enabled, console operations will use PSHostreadline implementation will be used instead of PSReadLine.
182+
/// </summary>
183+
[Parameter]
184+
public SwitchParameter UseHostReadKey { get; set; }
179185
/// <summary>
180186
/// When set, do not enable LSP service, only the debug adapter.
181187
/// </summary>
@@ -359,8 +365,9 @@ private EditorServicesConfig CreateConfigObject()
359365
hostInfo,
360366
Host,
361367
SessionDetailsPath,
362-
bundledModulesPath,
363-
LogPath)
368+
bundledModulesPath,
369+
LogPath,
370+
UseHostReadKey)
364371
{
365372
FeatureFlags = FeatureFlags,
366373
LogLevel = LogLevel,

src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public enum ConsoleReplKind
1717
/// <summary>Use a REPL with the legacy readline implementation. This is generally used when PSReadLine is unavailable.</summary>
1818
LegacyReadLine = 1,
1919
/// <summary>Use a REPL with the PSReadLine module for console interaction.</summary>
20-
PSReadLine = 2,
20+
PSReadLine = 2
2121
}
2222

2323
/// <summary>
@@ -39,13 +39,15 @@ public EditorServicesConfig(
3939
PSHost psHost,
4040
string sessionDetailsPath,
4141
string bundledModulePath,
42-
string logPath)
42+
string logPath,
43+
bool useHostReadKey)
4344
{
4445
HostInfo = hostInfo;
4546
PSHost = psHost;
4647
SessionDetailsPath = sessionDetailsPath;
4748
BundledModulePath = bundledModulePath;
4849
LogPath = logPath;
50+
UseHostReadKey = useHostReadKey;
4951
}
5052

5153
/// <summary>
@@ -72,6 +74,7 @@ public EditorServicesConfig(
7274
/// The path to use for logging for Editor Services.
7375
/// </summary>
7476
public string LogPath { get; }
77+
public bool UseHostReadKey { get; }
7578

7679
/// <summary>
7780
/// Names of or paths to any additional modules to load on startup.
@@ -88,7 +91,7 @@ public EditorServicesConfig(
8891
/// (including none to disable the integrated console).
8992
/// </summary>
9093
public ConsoleReplKind ConsoleRepl { get; set; } = ConsoleReplKind.None;
91-
94+
9295
/// <summary>
9396
/// The minimum log level to log events with.
9497
/// </summary>

src/PowerShellEditorServices.Hosting/EditorServicesLoader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ public static EditorServicesLoader Create(
103103
// We only want the Editor Services DLL; the new ALC will lazily load its dependencies automatically
104104
if (!string.Equals(asmName.Name, "Microsoft.PowerShell.EditorServices", StringComparison.Ordinal))
105105
{
106-
return null;
106+
//return null;
107107
}
108108

109109
string asmPath = Path.Combine(s_psesDependencyDirPath, $"{asmName.Name}.dll");
110110

111-
logger.Log(PsesLogLevel.Verbose, "Loading PSES DLL using new assembly load context");
111+
logger.Log(PsesLogLevel.Verbose, $"Loading {asmName.Name}.dll using new assembly load context");
112112

113113
return psesLoadContext.LoadFromAssemblyPath(asmPath);
114114
};

src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ private HostStartupInfo CreateHostStartupInfo()
290290
(int)_config.LogLevel,
291291
consoleReplEnabled: _config.ConsoleRepl != ConsoleReplKind.None,
292292
usesLegacyReadLine: _config.ConsoleRepl == ConsoleReplKind.LegacyReadLine,
293+
useHostReadKey: _config.UseHostReadKey,
293294
bundledModulePath: _config.BundledModulePath);
294295
}
295296

src/PowerShellEditorServices/Hosting/HostStartupInfo.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ public sealed class HostStartupInfo
8080
/// If the console REPL is not enabled, this setting will be ignored.
8181
/// </summary>
8282
public bool UsesLegacyReadLine { get; }
83-
83+
/// <summary>
84+
/// If true, the legacy PSES readline implementation but calls ReadKey in the provided host
85+
/// If the console REPL is not enabled, this setting will be ignored.
86+
/// </summary>
87+
public bool UseHostReadKey { get; }
8488
/// <summary>
8589
/// The PowerShell host to use with Editor Services.
8690
/// </summary>
@@ -154,6 +158,7 @@ public HostStartupInfo(
154158
int logLevel,
155159
bool consoleReplEnabled,
156160
bool usesLegacyReadLine,
161+
bool useHostReadKey,
157162
string bundledModulePath)
158163
{
159164
Name = name ?? DefaultHostName;
@@ -168,6 +173,7 @@ public HostStartupInfo(
168173
LogLevel = logLevel;
169174
ConsoleReplEnabled = consoleReplEnabled;
170175
UsesLegacyReadLine = usesLegacyReadLine;
176+
UseHostReadKey = useHostReadKey;
171177
BundledModulePath = bundledModulePath;
172178
}
173179

src/PowerShellEditorServices/Services/PowerShell/Console/ConsoleProxy.cs

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,10 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
1212
/// Provides asynchronous implementations of the <see cref="Console" /> API's as well as
1313
/// synchronous implementations that work around platform specific issues.
1414
/// </summary>
15-
internal static class ConsoleProxy
15+
internal static class ConsoleProxy
1616
{
1717
private static readonly IConsoleOperations s_consoleProxy;
1818

19-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline", Justification = "Platform specific initialization")]
20-
static ConsoleProxy()
21-
{
22-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
23-
{
24-
s_consoleProxy = new WindowsConsoleOperations();
25-
return;
26-
}
27-
28-
s_consoleProxy = new UnixConsoleOperations();
29-
}
30-
3119
/// <summary>
3220
/// Obtains the next character or function key pressed by the user asynchronously.
3321
/// Does not block when other console API's are called.
@@ -161,39 +149,6 @@ public static Task<int> GetCursorTopAsync() =>
161149
public static Task<int> GetCursorTopAsync(CancellationToken cancellationToken) =>
162150
s_consoleProxy.GetCursorTopAsync(cancellationToken);
163151

164-
/// <summary>
165-
/// This method is sent to PSReadLine as a workaround for issues with the System.Console
166-
/// implementation. Functionally it is the same as System.Console.ReadKey,
167-
/// with the exception that it will not lock the standard input stream.
168-
/// </summary>
169-
/// <param name="intercept">
170-
/// Determines whether to display the pressed key in the console window.
171-
/// true to not display the pressed key; otherwise, false.
172-
/// </param>
173-
/// <param name="cancellationToken">
174-
/// The <see cref="CancellationToken" /> that can be used to cancel the request.
175-
/// </param>
176-
/// <returns>
177-
/// An object that describes the ConsoleKey constant and Unicode character, if any,
178-
/// that correspond to the pressed console key. The ConsoleKeyInfo object also describes,
179-
/// in a bitwise combination of ConsoleModifiers values, whether one or more Shift, Alt,
180-
/// or Ctrl modifier keys was pressed simultaneously with the console key.
181-
/// </returns>
182-
internal static ConsoleKeyInfo SafeReadKey(bool intercept, CancellationToken cancellationToken)
183-
{
184-
try
185-
{
186-
return s_consoleProxy.ReadKey(intercept, cancellationToken);
187-
}
188-
catch (OperationCanceledException)
189-
{
190-
return new ConsoleKeyInfo(
191-
keyChar: ' ',
192-
ConsoleKey.DownArrow,
193-
shift: false,
194-
alt: false,
195-
control: false);
196-
}
197-
}
152+
198153
}
199154
}

src/PowerShellEditorServices/Services/PowerShell/Console/LegacyReadLine.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,19 @@ internal class LegacyReadLine : TerminalReadLine
2525

2626
private readonly Action<CancellationToken> _onIdleAction;
2727

28+
private IConsoleOperations _consoleOperations;
29+
2830
public LegacyReadLine(
2931
PsesInternalHost psesHost,
3032
Func<bool, ConsoleKeyInfo> readKeyFunc,
31-
Action<CancellationToken> onIdleAction)
33+
Action<CancellationToken> onIdleAction,
34+
IConsoleOperations consoleOperations)
3235
{
3336
_psesHost = psesHost;
3437
_readKeyTasks = new Task[2];
3538
_readKeyFunc = readKeyFunc;
3639
_onIdleAction = onIdleAction;
40+
_consoleOperations = consoleOperations;
3741
}
3842

3943
public override string ReadLine(CancellationToken cancellationToken)
@@ -47,12 +51,12 @@ public override string ReadLine(CancellationToken cancellationToken)
4751

4852
StringBuilder inputLine = new();
4953

50-
int initialCursorCol = ConsoleProxy.GetCursorLeft(cancellationToken);
51-
int initialCursorRow = ConsoleProxy.GetCursorTop(cancellationToken);
54+
int initialCursorCol = _consoleOperations?.GetCursorLeft(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.X;
55+
int initialCursorRow = _consoleOperations?.GetCursorTop(cancellationToken) ?? _psesHost.UI.RawUI.CursorPosition.Y; ;
5256

5357
int currentCursorIndex = 0;
54-
55-
Console.TreatControlCAsInput = true;
58+
if(_consoleOperations != null)
59+
Console.TreatControlCAsInput = true;
5660

5761
try
5862
{
@@ -64,7 +68,8 @@ public override string ReadLine(CancellationToken cancellationToken)
6468
// because the window could have been resized before then
6569
int promptStartCol = initialCursorCol;
6670
int promptStartRow = initialCursorRow;
67-
int consoleWidth = Console.WindowWidth;
71+
72+
int consoleWidth = _consoleOperations is not null ? Console.WindowWidth : _psesHost.UI.RawUI.WindowSize.Width;
6873

6974
switch (keyInfo.Key)
7075
{

src/PowerShellEditorServices/Services/PowerShell/Console/PsrlReadLine.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ internal class PsrlReadLine : TerminalReadLine
1717
private readonly PsesInternalHost _psesHost;
1818

1919
private readonly EngineIntrinsics _engineIntrinsics;
20+
private IConsoleOperations _consoleOperations;
2021

2122
#region Constructors
2223

@@ -25,13 +26,15 @@ public PsrlReadLine(
2526
PsesInternalHost psesHost,
2627
EngineIntrinsics engineIntrinsics,
2728
Func<bool, ConsoleKeyInfo> readKeyFunc,
28-
Action<CancellationToken> onIdleAction)
29+
Action<CancellationToken> onIdleAction,
30+
IConsoleOperations consoleOperations)
2931
{
3032
_psrlProxy = psrlProxy;
3133
_psesHost = psesHost;
3234
_engineIntrinsics = engineIntrinsics;
3335
_psrlProxy.OverrideReadKey(readKeyFunc);
3436
_psrlProxy.OverrideIdleHandler(onIdleAction);
37+
_consoleOperations = consoleOperations;
3538
}
3639

3740
#endregion
@@ -40,7 +43,10 @@ public PsrlReadLine(
4043

4144
public override string ReadLine(CancellationToken cancellationToken) => _psesHost.InvokeDelegate(representation: "ReadLine", new ExecutionOptions { MustRunInForeground = true }, InvokePSReadLine, cancellationToken);
4245

43-
protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken) => ConsoleProxy.ReadKey(intercept: true, cancellationToken);
46+
protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken)
47+
{
48+
return _consoleOperations.ReadKey(intercept: true, cancellationToken);
49+
}
4450

4551
#endregion
4652

src/PowerShellEditorServices/Services/PowerShell/Console/TerminalReadLine.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
1212

1313
internal abstract class TerminalReadLine : IReadLine
1414
{
15+
private IConsoleOperations _consoleOperations;
16+
1517
public abstract string ReadLine(CancellationToken cancellationToken);
1618

1719
protected abstract ConsoleKeyInfo ReadKey(CancellationToken cancellationToken);
@@ -69,8 +71,8 @@ public SecureString ReadSecureLine(CancellationToken cancellationToken)
6971
}
7072
else if (previousInputLength > 0 && currentInputLength < previousInputLength)
7173
{
72-
int row = ConsoleProxy.GetCursorTop(cancellationToken);
73-
int col = ConsoleProxy.GetCursorLeft(cancellationToken);
74+
int row = _consoleOperations.GetCursorTop(cancellationToken);
75+
int col = _consoleOperations.GetCursorLeft(cancellationToken);
7476

7577
// Back up the cursor before clearing the character
7678
col--;

0 commit comments

Comments
 (0)