diff --git a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs index 361d292ea..e9704e8cd 100644 --- a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs +++ b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs @@ -169,7 +169,8 @@ public PsesDebugServer CreateDebugServerForTempSession( _loggerFactory, inputStream, outputStream, - serviceProvider); + serviceProvider, + isTemp: true); } /// diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 789c727b4..b84c2cbf6 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -30,18 +30,22 @@ internal class PsesDebugServer : IDisposable private bool _startedPses; + private readonly bool _isTemp; + protected readonly ILoggerFactory _loggerFactory; public PsesDebugServer( ILoggerFactory factory, Stream inputStream, Stream outputStream, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider, + bool isTemp = false) { _loggerFactory = factory; _inputStream = inputStream; _outputStream = outputStream; ServiceProvider = serviceProvider; + _isTemp = isTemp; _serverStopped = new TaskCompletionSource(); } @@ -92,6 +96,13 @@ public async Task StartAsync() // We need to make sure the host has been started _startedPses = !await _psesHost.TryStartAsync(new HostStartOptions(), CancellationToken.None).ConfigureAwait(false); + // We need to give the host a handle to the DAP so it can register + // notifications (specifically for sendKeyPress). + if (_isTemp) + { + _psesHost.DebugServer = server; + } + // Ensure the debugger mode is set correctly - this is required for remote debugging to work _psesHost.DebugContext.EnableDebugMode(); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index 1d9813a48..64abf455d 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -24,6 +24,10 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Host { using System.Management.Automation; using System.Management.Automation.Runspaces; + // NOTE: These last three are for a workaround for temporary integrated consoles. + using Microsoft.PowerShell.EditorServices.Handlers; + using Microsoft.PowerShell.EditorServices.Server; + using OmniSharp.Extensions.DebugAdapter.Protocol.Server; internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRunspaceContext, IInternalPowerShellExecutionService { @@ -41,6 +45,14 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns private readonly ILanguageServerFacade _languageServer; + /// + /// TODO: Improve this coupling. It's assigned by + /// so that the PowerShell process started when + /// is true can also receive the required 'sendKeyPress' notification to return from a + /// canceled . + /// + internal IDebugAdapterServerFacade DebugServer; + private readonly HostStartupInfo _hostInfo; private readonly BlockingConcurrentDeque _taskQueue; @@ -1053,11 +1065,6 @@ private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args) private ConsoleKeyInfo ReadKey(bool intercept) { - // PSRL 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 - // NOTE: This requests that the client (the Code extension) send a non-printing key back // to the terminal on stdin, emulating a user pressing a button. This allows // PSReadLine's thread waiting on Console.ReadKey to return. Normally we'd just cancel @@ -1065,8 +1072,24 @@ private ConsoleKeyInfo ReadKey(bool intercept) // input. This leads to a myriad of problems, but we circumvent them by pretending to // press a key, thus allowing ReadKey to return, and us to ignore it. using CancellationTokenRegistration registration = _readKeyCancellationToken.Register( - () => _languageServer?.SendNotification("powerShell/sendKeyPress")); - + () => + { + // For the regular integrated console, we have an associated language server on + // which we can send a notification, and have the client subscribe an action to + // send a key press. + _languageServer?.SendNotification("powerShell/sendKeyPress"); + + // When temporary integrated consoles are spawned, there will be no associated + // language server, but instead a debug adaptor server. In this case, the + // notification sent here will come across as a DebugSessionCustomEvent to which + // 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); return _lastKey.Value;