Skip to content

Commit dd870ec

Browse files
Send sendKeyPress event across DAP for temporary integrated consoles (#1791)
Co-authored-by: Patrick Meinecke <[email protected]>
1 parent f29b4e4 commit dd870ec

File tree

3 files changed

+44
-9
lines changed

3 files changed

+44
-9
lines changed

src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ public PsesDebugServer CreateDebugServerForTempSession(
169169
_loggerFactory,
170170
inputStream,
171171
outputStream,
172-
serviceProvider);
172+
serviceProvider,
173+
isTemp: true);
173174
}
174175

175176
/// <summary>

src/PowerShellEditorServices/Server/PsesDebugServer.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,22 @@ internal class PsesDebugServer : IDisposable
3030

3131
private bool _startedPses;
3232

33+
private readonly bool _isTemp;
34+
3335
protected readonly ILoggerFactory _loggerFactory;
3436

3537
public PsesDebugServer(
3638
ILoggerFactory factory,
3739
Stream inputStream,
3840
Stream outputStream,
39-
IServiceProvider serviceProvider)
41+
IServiceProvider serviceProvider,
42+
bool isTemp = false)
4043
{
4144
_loggerFactory = factory;
4245
_inputStream = inputStream;
4346
_outputStream = outputStream;
4447
ServiceProvider = serviceProvider;
48+
_isTemp = isTemp;
4549
_serverStopped = new TaskCompletionSource<bool>();
4650
}
4751

@@ -92,6 +96,13 @@ public async Task StartAsync()
9296
// We need to make sure the host has been started
9397
_startedPses = !await _psesHost.TryStartAsync(new HostStartOptions(), CancellationToken.None).ConfigureAwait(false);
9498

99+
// We need to give the host a handle to the DAP so it can register
100+
// notifications (specifically for sendKeyPress).
101+
if (_isTemp)
102+
{
103+
_psesHost.DebugServer = server;
104+
}
105+
95106
// Ensure the debugger mode is set correctly - this is required for remote debugging to work
96107
_psesHost.DebugContext.EnableDebugMode();
97108

src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs

+30-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Host
2424
{
2525
using System.Management.Automation;
2626
using System.Management.Automation.Runspaces;
27+
// NOTE: These last three are for a workaround for temporary integrated consoles.
28+
using Microsoft.PowerShell.EditorServices.Handlers;
29+
using Microsoft.PowerShell.EditorServices.Server;
30+
using OmniSharp.Extensions.DebugAdapter.Protocol.Server;
2731

2832
internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRunspaceContext, IInternalPowerShellExecutionService
2933
{
@@ -41,6 +45,14 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns
4145

4246
private readonly ILanguageServerFacade _languageServer;
4347

48+
/// <summary>
49+
/// TODO: Improve this coupling. It's assigned by <see cref="PsesDebugServer.StartAsync()" />
50+
/// so that the PowerShell process started when <see cref="PsesLaunchRequestArguments.CreateTemporaryIntegratedConsole" />
51+
/// is true can also receive the required 'sendKeyPress' notification to return from a
52+
/// canceled <see cref="System.Console.ReadKey()" />.
53+
/// </summary>
54+
internal IDebugAdapterServerFacade DebugServer;
55+
4456
private readonly HostStartupInfo _hostInfo;
4557

4658
private readonly BlockingConcurrentDeque<ISynchronousTask> _taskQueue;
@@ -1053,20 +1065,31 @@ private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args)
10531065

10541066
private ConsoleKeyInfo ReadKey(bool intercept)
10551067
{
1056-
// PSRL doesn't tell us when CtrlC was sent.
1057-
// So instead we keep track of the last key here.
1058-
// This isn't functionally required,
1059-
// but helps us determine when the prompt needs a newline added
1060-
10611068
// NOTE: This requests that the client (the Code extension) send a non-printing key back
10621069
// to the terminal on stdin, emulating a user pressing a button. This allows
10631070
// PSReadLine's thread waiting on Console.ReadKey to return. Normally we'd just cancel
10641071
// this call, but the .NET API ReadKey is not cancellable, and is stuck until we send
10651072
// input. This leads to a myriad of problems, but we circumvent them by pretending to
10661073
// press a key, thus allowing ReadKey to return, and us to ignore it.
10671074
using CancellationTokenRegistration registration = _readKeyCancellationToken.Register(
1068-
() => _languageServer?.SendNotification("powerShell/sendKeyPress"));
1069-
1075+
() =>
1076+
{
1077+
// For the regular integrated console, we have an associated language server on
1078+
// which we can send a notification, and have the client subscribe an action to
1079+
// send a key press.
1080+
_languageServer?.SendNotification("powerShell/sendKeyPress");
1081+
1082+
// When temporary integrated consoles are spawned, there will be no associated
1083+
// language server, but instead a debug adaptor server. In this case, the
1084+
// notification sent here will come across as a DebugSessionCustomEvent to which
1085+
// we can subscribe in the same way.
1086+
DebugServer?.SendNotification("powerShell/sendKeyPress");
1087+
});
1088+
1089+
// PSReadLine doesn't tell us when CtrlC was sent. So instead we keep track of the last
1090+
// key here. This isn't functionally required, but helps us determine when the prompt
1091+
// needs a newline added
1092+
//
10701093
// TODO: We may want to allow users of PSES to override this method call.
10711094
_lastKey = System.Console.ReadKey(intercept);
10721095
return _lastKey.Value;

0 commit comments

Comments
 (0)