diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psm1 b/module/PowerShellEditorServices/PowerShellEditorServices.psm1
index b37b6ee65..5861b9420 100644
--- a/module/PowerShellEditorServices/PowerShellEditorServices.psm1
+++ b/module/PowerShellEditorServices/PowerShellEditorServices.psm1
@@ -150,12 +150,12 @@ function Start-EditorServicesHost {
}
if ($DebugServiceOnly.IsPresent) {
- $editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false);
+ $editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $true);
} elseif($Stdio.IsPresent) {
$editorServicesHost.StartLanguageService($languageServiceConfig, $profilePaths);
} else {
$editorServicesHost.StartLanguageService($languageServiceConfig, $profilePaths);
- $editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $true);
+ $editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false);
}
return $editorServicesHost
diff --git a/src/PowerShellEditorServices/Hosting/EditorServicesHost.cs b/src/PowerShellEditorServices/Hosting/EditorServicesHost.cs
index fd14dd787..68c004c0c 100644
--- a/src/PowerShellEditorServices/Hosting/EditorServicesHost.cs
+++ b/src/PowerShellEditorServices/Hosting/EditorServicesHost.cs
@@ -21,6 +21,7 @@
using Microsoft.PowerShell.EditorServices.Server;
using Microsoft.PowerShell.EditorServices.Services;
using Microsoft.PowerShell.EditorServices.Utility;
+using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using Serilog;
namespace Microsoft.PowerShell.EditorServices.Hosting
@@ -284,14 +285,33 @@ public void StartLanguageService(
///
/// The config that contains information on the communication protocol that will be used.
/// The profiles that will be loaded in the session.
- /// Determines if we will reuse the session that we have.
+ /// Determines if we will make a new session typically used for temporary console debugging.
public void StartDebugService(
EditorServiceTransportConfig config,
ProfilePaths profilePaths,
- bool useExistingSession)
+ bool useTempSession)
{
_logger.LogInformation($"Debug NamedPipe: {config.InOutPipeName}\nDebug OutPipe: {config.OutPipeName}");
+ IServiceProvider serviceProvider = null;
+ if (useTempSession)
+ {
+ serviceProvider = new ServiceCollection()
+ .AddLogging(builder => builder
+ .ClearProviders()
+ .AddSerilog()
+ .SetMinimumLevel(LogLevel.Trace))
+ .AddSingleton(provider => null)
+ .AddPsesLanguageServices(
+ profilePaths,
+ _featureFlags,
+ _enableConsoleRepl,
+ _internalHost,
+ _hostDetails,
+ _additionalModules)
+ .BuildServiceProvider();
+ }
+
switch (config.TransportType)
{
case EditorServiceTransportType.NamedPipe:
@@ -312,7 +332,7 @@ public void StartDebugService(
.ContinueWith(async task =>
{
_logger.LogInformation("Starting debug server");
- await _debugServer.StartAsync(_languageServer.LanguageServer.Services);
+ await _debugServer.StartAsync(serviceProvider ?? _languageServer.LanguageServer.Services, useTempSession);
_logger.LogInformation(
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
});
@@ -325,25 +345,11 @@ public void StartDebugService(
Console.OpenStandardInput(),
Console.OpenStandardOutput());
+ _logger.LogInformation("Starting debug server");
Task.Run(async () =>
{
- _logger.LogInformation("Starting debug server");
-
- IServiceProvider serviceProvider = useExistingSession
- ? _languageServer.LanguageServer.Services
- : new ServiceCollection().AddSingleton(
- (provider) => PowerShellContextService.Create(
- _factory,
- provider.GetService(),
- profilePaths,
- _featureFlags,
- _enableConsoleRepl,
- _internalHost,
- _hostDetails,
- _additionalModules))
- .BuildServiceProvider();
-
- await _debugServer.StartAsync(serviceProvider);
+
+ await _debugServer.StartAsync(serviceProvider ?? _languageServer.LanguageServer.Services, useTempSession);
_logger.LogInformation(
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
});
@@ -353,14 +359,19 @@ public void StartDebugService(
throw new NotSupportedException($"The transport {config.TransportType} is not supported");
}
- if(!alreadySubscribedDebug)
+ // If the instance of PSES is being used for debugging only, then we don't want to allow automatic restarting
+ // because the user can simply spin up a new PSES if they need to.
+ // This design decision was done since this "debug-only PSES" is used in the "Temporary Integrated Console debugging"
+ // feature which does not want PSES to be restarted so that the user can see the output of the last debug
+ // session.
+ if(!alreadySubscribedDebug && !useTempSession)
{
alreadySubscribedDebug = true;
_debugServer.SessionEnded += (sender, eventArgs) =>
{
_debugServer.Dispose();
alreadySubscribedDebug = false;
- StartDebugService(config, profilePaths, useExistingSession);
+ StartDebugService(config, profilePaths, useTempSession);
};
}
}
@@ -378,8 +389,20 @@ public void StopServices()
///
public void WaitForCompletion()
{
- // TODO: We need a way to know when to complete this task!
- _languageServer.WaitForShutdown().Wait();
+ // If _languageServer is not null, then we are either using:
+ // Stdio - that only uses a LanguageServer so we return when that has shutdown.
+ // NamedPipes - that uses both LanguageServer and DebugServer, but LanguageServer
+ // is the core of PowerShell Editor Services and if that shuts down,
+ // we want the whole process to shutdown.
+ if (_languageServer != null)
+ {
+ _languageServer.WaitForShutdown().GetAwaiter().GetResult();
+ return;
+ }
+
+ // If there is no LanguageServer, then we must be running with the DebugServiceOnly switch
+ // (used in Temporary console debugging) and we need to wait for the DebugServer to shutdown.
+ _debugServer.WaitForShutdown().GetAwaiter().GetResult();
}
#endregion
diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs
index 860dbc845..98474b0be 100644
--- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs
+++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs
@@ -26,6 +26,8 @@ public class PsesDebugServer : IDisposable
private PowerShellContextService _powerShellContextService;
+ private readonly TaskCompletionSource _serverStopped;
+
public PsesDebugServer(
ILoggerFactory factory,
Stream inputStream,
@@ -34,9 +36,10 @@ public PsesDebugServer(
_loggerFactory = factory;
_inputStream = inputStream;
_outputStream = outputStream;
+ _serverStopped = new TaskCompletionSource();
}
- public async Task StartAsync(IServiceProvider languageServerServiceProvider)
+ public async Task StartAsync(IServiceProvider languageServerServiceProvider, bool useTempSession)
{
_jsonRpcServer = await JsonRpcServer.From(options =>
{
@@ -50,14 +53,13 @@ public async Task StartAsync(IServiceProvider languageServerServiceProvider)
_powerShellContextService = languageServerServiceProvider.GetService();
_powerShellContextService.IsDebugServerActive = true;
+ // Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
+ _powerShellContextService
+ .ExecuteScriptStringAsync("[System.Runtime.CompilerServices.RuntimeHelpers]::RunClassConstructor([Microsoft.PowerShell.PSConsoleReadLine].TypeHandle)")
+ .Wait();
+
options.Services = new ServiceCollection()
- .AddSingleton(_powerShellContextService)
- .AddSingleton(languageServerServiceProvider.GetService())
- .AddSingleton(languageServerServiceProvider.GetService())
- .AddSingleton(this)
- .AddSingleton()
- .AddSingleton()
- .AddSingleton();
+ .AddPsesDebugServices(languageServerServiceProvider, this, useTempSession);
options
.WithInput(_inputStream)
@@ -95,6 +97,12 @@ public void Dispose()
{
_powerShellContextService.IsDebugServerActive = false;
_jsonRpcServer.Dispose();
+ _serverStopped.SetResult(true);
+ }
+
+ public async Task WaitForShutdown()
+ {
+ await _serverStopped.Task;
}
#region Events
diff --git a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
index 3d6901479..caddc5e5f 100644
--- a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
+++ b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
@@ -65,43 +65,13 @@ public async Task StartAsync()
.WithInput(input)
.WithOutput(output)
.WithServices(serviceCollection => serviceCollection
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton(
- (provider) =>
- PowerShellContextService.Create(
- provider.GetService(),
- provider.GetService(),
- _profilePaths,
- _featureFlags,
- _enableConsoleRepl,
- _internalHost,
- _hostDetails,
- _additionalModules))
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton(
- (provider) =>
- {
- var extensionService = new ExtensionService(
- provider.GetService(),
- provider.GetService());
- extensionService.InitializeAsync(
- serviceProvider: provider,
- editorOperations: provider.GetService())
- .Wait();
- return extensionService;
- })
- .AddSingleton(
- (provider) =>
- {
- return AnalysisService.Create(
- provider.GetService(),
- provider.GetService(),
- provider.GetService().CreateLogger());
- }))
+ .AddPsesLanguageServices(
+ _profilePaths,
+ _featureFlags,
+ _enableConsoleRepl,
+ _internalHost,
+ _hostDetails,
+ _additionalModules))
.ConfigureLogging(builder => builder
.AddSerilog(Log.Logger)
.SetMinimumLevel(LogLevel.Trace))
diff --git a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs
new file mode 100644
index 000000000..cd66847b0
--- /dev/null
+++ b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs
@@ -0,0 +1,84 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Management.Automation.Host;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.PowerShell.EditorServices.Hosting;
+using Microsoft.PowerShell.EditorServices.Services;
+
+namespace Microsoft.PowerShell.EditorServices.Server
+{
+ internal static class PsesServiceCollectionExtensions
+ {
+ public static IServiceCollection AddPsesLanguageServices (
+ this IServiceCollection collection,
+ ProfilePaths profilePaths,
+ HashSet featureFlags,
+ bool enableConsoleRepl,
+ PSHost internalHost,
+ HostDetails hostDetails,
+ string[] additionalModules)
+ {
+ return collection.AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton(
+ (provider) =>
+ PowerShellContextService.Create(
+ provider.GetService(),
+ provider.GetService(),
+ profilePaths,
+ featureFlags,
+ enableConsoleRepl,
+ internalHost,
+ hostDetails,
+ additionalModules))
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton(
+ (provider) =>
+ {
+ var extensionService = new ExtensionService(
+ provider.GetService(),
+ provider.GetService());
+ extensionService.InitializeAsync(
+ serviceProvider: provider,
+ editorOperations: provider.GetService())
+ .Wait();
+ return extensionService;
+ })
+ .AddSingleton(
+ (provider) =>
+ {
+ return AnalysisService.Create(
+ provider.GetService(),
+ provider.GetService(),
+ provider.GetService().CreateLogger());
+ });
+ }
+
+ public static IServiceCollection AddPsesDebugServices(
+ this IServiceCollection collection,
+ IServiceProvider languageServiceProvider,
+ PsesDebugServer psesDebugServer,
+ bool useTempSession)
+ {
+ return collection.AddSingleton(languageServiceProvider.GetService())
+ .AddSingleton(languageServiceProvider.GetService())
+ .AddSingleton(languageServiceProvider.GetService())
+ .AddSingleton(psesDebugServer)
+ .AddSingleton()
+ .AddSingleton(new DebugStateService
+ {
+ OwnsEditorSession = useTempSession
+ })
+ .AddSingleton();
+ }
+ }
+}
diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs
index addf829a1..747a8a9b2 100644
--- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs
+++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs
@@ -43,10 +43,17 @@ public ConfigurationDoneHandler(
_workspaceService = workspaceService;
}
- public Task Handle(ConfigurationDoneArguments request, CancellationToken cancellationToken)
+ public async Task Handle(ConfigurationDoneArguments request, CancellationToken cancellationToken)
{
_debugService.IsClientAttached = true;
+ if (_debugStateService.OwnsEditorSession)
+ {
+ // If this is a debug-only session, we need to start
+ // the command loop manually
+ _powerShellContextService.ConsoleReader.StartCommandLoop();
+ }
+
if (!string.IsNullOrEmpty(_debugStateService.ScriptToLaunch))
{
if (_powerShellContextService.SessionState == PowerShellContextState.Ready)
@@ -63,14 +70,6 @@ public Task Handle(ConfigurationDoneArguments request
if (_debugStateService.IsInteractiveDebugSession)
{
- if (_debugStateService.OwnsEditorSession)
- {
- // If this is a debug-only session, we need to start
- // the command loop manually
- // TODO: Bring this back
- //_editorSession.HostInput.StartCommandLoop();
- }
-
if (_debugService.IsDebuggerStopped)
{
if (_debugService.CurrentDebuggerStoppedEventArgs != null)
@@ -88,7 +87,7 @@ public Task Handle(ConfigurationDoneArguments request
}
}
- return Task.FromResult(new ConfigurationDoneResponse());
+ return new ConfigurationDoneResponse();
}
private async Task LaunchScriptAsync(string scriptToLaunch)
diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs
index dfe550c18..a1f54a0de 100644
--- a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs
+++ b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs
@@ -1793,7 +1793,7 @@ private void OnExecutionStatusChanged(
private void PowerShellContext_RunspaceChangedAsync(object sender, RunspaceChangedEventArgs e)
{
- _languageServer.SendNotification(
+ _languageServer?.SendNotification(
"powerShell/runspaceChanged",
new MinifiedRunspaceDetails(e.NewRunspace));
}
@@ -1831,7 +1831,7 @@ public MinifiedRunspaceDetails(RunspaceDetails eventArgs)
/// details of the execution status change
private void PowerShellContext_ExecutionStatusChangedAsync(object sender, ExecutionStatusChangedEventArgs e)
{
- _languageServer.SendNotification(
+ _languageServer?.SendNotification(
"powerShell/executionStatusChanged",
e);
}