Skip to content

Commit aab80d1

Browse files
Fix TEMP debugging (#1065)
* refactor out servicecollection additions * initial working TEMP debugging * this makes me cry * wait for completion of script * address rob's feedback * address Patrick's feedback
1 parent 3daba99 commit aab80d1

File tree

7 files changed

+167
-83
lines changed

7 files changed

+167
-83
lines changed

module/PowerShellEditorServices/PowerShellEditorServices.psm1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,12 @@ function Start-EditorServicesHost {
150150
}
151151

152152
if ($DebugServiceOnly.IsPresent) {
153-
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false);
153+
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $true);
154154
} elseif($Stdio.IsPresent) {
155155
$editorServicesHost.StartLanguageService($languageServiceConfig, $profilePaths);
156156
} else {
157157
$editorServicesHost.StartLanguageService($languageServiceConfig, $profilePaths);
158-
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $true);
158+
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false);
159159
}
160160

161161
return $editorServicesHost

src/PowerShellEditorServices/Hosting/EditorServicesHost.cs

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Microsoft.PowerShell.EditorServices.Server;
2222
using Microsoft.PowerShell.EditorServices.Services;
2323
using Microsoft.PowerShell.EditorServices.Utility;
24+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
2425
using Serilog;
2526

2627
namespace Microsoft.PowerShell.EditorServices.Hosting
@@ -284,14 +285,33 @@ public void StartLanguageService(
284285
/// </summary>
285286
/// <param name="config">The config that contains information on the communication protocol that will be used.</param>
286287
/// <param name="profilePaths">The profiles that will be loaded in the session.</param>
287-
/// <param name="useExistingSession">Determines if we will reuse the session that we have.</param>
288+
/// <param name="useTempSession">Determines if we will make a new session typically used for temporary console debugging.</param>
288289
public void StartDebugService(
289290
EditorServiceTransportConfig config,
290291
ProfilePaths profilePaths,
291-
bool useExistingSession)
292+
bool useTempSession)
292293
{
293294
_logger.LogInformation($"Debug NamedPipe: {config.InOutPipeName}\nDebug OutPipe: {config.OutPipeName}");
294295

296+
IServiceProvider serviceProvider = null;
297+
if (useTempSession)
298+
{
299+
serviceProvider = new ServiceCollection()
300+
.AddLogging(builder => builder
301+
.ClearProviders()
302+
.AddSerilog()
303+
.SetMinimumLevel(LogLevel.Trace))
304+
.AddSingleton<ILanguageServer>(provider => null)
305+
.AddPsesLanguageServices(
306+
profilePaths,
307+
_featureFlags,
308+
_enableConsoleRepl,
309+
_internalHost,
310+
_hostDetails,
311+
_additionalModules)
312+
.BuildServiceProvider();
313+
}
314+
295315
switch (config.TransportType)
296316
{
297317
case EditorServiceTransportType.NamedPipe:
@@ -312,7 +332,7 @@ public void StartDebugService(
312332
.ContinueWith(async task =>
313333
{
314334
_logger.LogInformation("Starting debug server");
315-
await _debugServer.StartAsync(_languageServer.LanguageServer.Services);
335+
await _debugServer.StartAsync(serviceProvider ?? _languageServer.LanguageServer.Services, useTempSession);
316336
_logger.LogInformation(
317337
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
318338
});
@@ -325,25 +345,11 @@ public void StartDebugService(
325345
Console.OpenStandardInput(),
326346
Console.OpenStandardOutput());
327347

348+
_logger.LogInformation("Starting debug server");
328349
Task.Run(async () =>
329350
{
330-
_logger.LogInformation("Starting debug server");
331-
332-
IServiceProvider serviceProvider = useExistingSession
333-
? _languageServer.LanguageServer.Services
334-
: new ServiceCollection().AddSingleton<PowerShellContextService>(
335-
(provider) => PowerShellContextService.Create(
336-
_factory,
337-
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
338-
profilePaths,
339-
_featureFlags,
340-
_enableConsoleRepl,
341-
_internalHost,
342-
_hostDetails,
343-
_additionalModules))
344-
.BuildServiceProvider();
345-
346-
await _debugServer.StartAsync(serviceProvider);
351+
352+
await _debugServer.StartAsync(serviceProvider ?? _languageServer.LanguageServer.Services, useTempSession);
347353
_logger.LogInformation(
348354
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
349355
});
@@ -353,14 +359,19 @@ public void StartDebugService(
353359
throw new NotSupportedException($"The transport {config.TransportType} is not supported");
354360
}
355361

356-
if(!alreadySubscribedDebug)
362+
// If the instance of PSES is being used for debugging only, then we don't want to allow automatic restarting
363+
// because the user can simply spin up a new PSES if they need to.
364+
// This design decision was done since this "debug-only PSES" is used in the "Temporary Integrated Console debugging"
365+
// feature which does not want PSES to be restarted so that the user can see the output of the last debug
366+
// session.
367+
if(!alreadySubscribedDebug && !useTempSession)
357368
{
358369
alreadySubscribedDebug = true;
359370
_debugServer.SessionEnded += (sender, eventArgs) =>
360371
{
361372
_debugServer.Dispose();
362373
alreadySubscribedDebug = false;
363-
StartDebugService(config, profilePaths, useExistingSession);
374+
StartDebugService(config, profilePaths, useTempSession);
364375
};
365376
}
366377
}
@@ -378,8 +389,20 @@ public void StopServices()
378389
/// </summary>
379390
public void WaitForCompletion()
380391
{
381-
// TODO: We need a way to know when to complete this task!
382-
_languageServer.WaitForShutdown().Wait();
392+
// If _languageServer is not null, then we are either using:
393+
// Stdio - that only uses a LanguageServer so we return when that has shutdown.
394+
// NamedPipes - that uses both LanguageServer and DebugServer, but LanguageServer
395+
// is the core of PowerShell Editor Services and if that shuts down,
396+
// we want the whole process to shutdown.
397+
if (_languageServer != null)
398+
{
399+
_languageServer.WaitForShutdown().GetAwaiter().GetResult();
400+
return;
401+
}
402+
403+
// If there is no LanguageServer, then we must be running with the DebugServiceOnly switch
404+
// (used in Temporary console debugging) and we need to wait for the DebugServer to shutdown.
405+
_debugServer.WaitForShutdown().GetAwaiter().GetResult();
383406
}
384407

385408
#endregion

src/PowerShellEditorServices/Server/PsesDebugServer.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public class PsesDebugServer : IDisposable
2626

2727
private PowerShellContextService _powerShellContextService;
2828

29+
private readonly TaskCompletionSource<bool> _serverStopped;
30+
2931
public PsesDebugServer(
3032
ILoggerFactory factory,
3133
Stream inputStream,
@@ -34,9 +36,10 @@ public PsesDebugServer(
3436
_loggerFactory = factory;
3537
_inputStream = inputStream;
3638
_outputStream = outputStream;
39+
_serverStopped = new TaskCompletionSource<bool>();
3740
}
3841

39-
public async Task StartAsync(IServiceProvider languageServerServiceProvider)
42+
public async Task StartAsync(IServiceProvider languageServerServiceProvider, bool useTempSession)
4043
{
4144
_jsonRpcServer = await JsonRpcServer.From(options =>
4245
{
@@ -50,14 +53,13 @@ public async Task StartAsync(IServiceProvider languageServerServiceProvider)
5053
_powerShellContextService = languageServerServiceProvider.GetService<PowerShellContextService>();
5154
_powerShellContextService.IsDebugServerActive = true;
5255

56+
// Needed to make sure PSReadLine's static properties are initialized in the pipeline thread.
57+
_powerShellContextService
58+
.ExecuteScriptStringAsync("[System.Runtime.CompilerServices.RuntimeHelpers]::RunClassConstructor([Microsoft.PowerShell.PSConsoleReadLine].TypeHandle)")
59+
.Wait();
60+
5361
options.Services = new ServiceCollection()
54-
.AddSingleton(_powerShellContextService)
55-
.AddSingleton(languageServerServiceProvider.GetService<WorkspaceService>())
56-
.AddSingleton(languageServerServiceProvider.GetService<RemoteFileManagerService>())
57-
.AddSingleton<PsesDebugServer>(this)
58-
.AddSingleton<DebugService>()
59-
.AddSingleton<DebugStateService>()
60-
.AddSingleton<DebugEventHandlerService>();
62+
.AddPsesDebugServices(languageServerServiceProvider, this, useTempSession);
6163

6264
options
6365
.WithInput(_inputStream)
@@ -95,6 +97,12 @@ public void Dispose()
9597
{
9698
_powerShellContextService.IsDebugServerActive = false;
9799
_jsonRpcServer.Dispose();
100+
_serverStopped.SetResult(true);
101+
}
102+
103+
public async Task WaitForShutdown()
104+
{
105+
await _serverStopped.Task;
98106
}
99107

100108
#region Events

src/PowerShellEditorServices/Server/PsesLanguageServer.cs

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -65,43 +65,13 @@ public async Task StartAsync()
6565
.WithInput(input)
6666
.WithOutput(output)
6767
.WithServices(serviceCollection => serviceCollection
68-
.AddSingleton<WorkspaceService>()
69-
.AddSingleton<SymbolsService>()
70-
.AddSingleton<ConfigurationService>()
71-
.AddSingleton<PowerShellContextService>(
72-
(provider) =>
73-
PowerShellContextService.Create(
74-
provider.GetService<ILoggerFactory>(),
75-
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
76-
_profilePaths,
77-
_featureFlags,
78-
_enableConsoleRepl,
79-
_internalHost,
80-
_hostDetails,
81-
_additionalModules))
82-
.AddSingleton<TemplateService>()
83-
.AddSingleton<EditorOperationsService>()
84-
.AddSingleton<RemoteFileManagerService>()
85-
.AddSingleton<ExtensionService>(
86-
(provider) =>
87-
{
88-
var extensionService = new ExtensionService(
89-
provider.GetService<PowerShellContextService>(),
90-
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>());
91-
extensionService.InitializeAsync(
92-
serviceProvider: provider,
93-
editorOperations: provider.GetService<EditorOperationsService>())
94-
.Wait();
95-
return extensionService;
96-
})
97-
.AddSingleton<AnalysisService>(
98-
(provider) =>
99-
{
100-
return AnalysisService.Create(
101-
provider.GetService<ConfigurationService>(),
102-
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
103-
provider.GetService<ILoggerFactory>().CreateLogger<AnalysisService>());
104-
}))
68+
.AddPsesLanguageServices(
69+
_profilePaths,
70+
_featureFlags,
71+
_enableConsoleRepl,
72+
_internalHost,
73+
_hostDetails,
74+
_additionalModules))
10575
.ConfigureLogging(builder => builder
10676
.AddSerilog(Log.Logger)
10777
.SetMinimumLevel(LogLevel.Trace))
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Management.Automation.Host;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Logging;
11+
using Microsoft.PowerShell.EditorServices.Hosting;
12+
using Microsoft.PowerShell.EditorServices.Services;
13+
14+
namespace Microsoft.PowerShell.EditorServices.Server
15+
{
16+
internal static class PsesServiceCollectionExtensions
17+
{
18+
public static IServiceCollection AddPsesLanguageServices (
19+
this IServiceCollection collection,
20+
ProfilePaths profilePaths,
21+
HashSet<string> featureFlags,
22+
bool enableConsoleRepl,
23+
PSHost internalHost,
24+
HostDetails hostDetails,
25+
string[] additionalModules)
26+
{
27+
return collection.AddSingleton<WorkspaceService>()
28+
.AddSingleton<SymbolsService>()
29+
.AddSingleton<ConfigurationService>()
30+
.AddSingleton<PowerShellContextService>(
31+
(provider) =>
32+
PowerShellContextService.Create(
33+
provider.GetService<ILoggerFactory>(),
34+
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
35+
profilePaths,
36+
featureFlags,
37+
enableConsoleRepl,
38+
internalHost,
39+
hostDetails,
40+
additionalModules))
41+
.AddSingleton<TemplateService>()
42+
.AddSingleton<EditorOperationsService>()
43+
.AddSingleton<RemoteFileManagerService>()
44+
.AddSingleton<ExtensionService>(
45+
(provider) =>
46+
{
47+
var extensionService = new ExtensionService(
48+
provider.GetService<PowerShellContextService>(),
49+
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>());
50+
extensionService.InitializeAsync(
51+
serviceProvider: provider,
52+
editorOperations: provider.GetService<EditorOperationsService>())
53+
.Wait();
54+
return extensionService;
55+
})
56+
.AddSingleton<AnalysisService>(
57+
(provider) =>
58+
{
59+
return AnalysisService.Create(
60+
provider.GetService<ConfigurationService>(),
61+
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
62+
provider.GetService<ILoggerFactory>().CreateLogger<AnalysisService>());
63+
});
64+
}
65+
66+
public static IServiceCollection AddPsesDebugServices(
67+
this IServiceCollection collection,
68+
IServiceProvider languageServiceProvider,
69+
PsesDebugServer psesDebugServer,
70+
bool useTempSession)
71+
{
72+
return collection.AddSingleton(languageServiceProvider.GetService<PowerShellContextService>())
73+
.AddSingleton(languageServiceProvider.GetService<WorkspaceService>())
74+
.AddSingleton(languageServiceProvider.GetService<RemoteFileManagerService>())
75+
.AddSingleton<PsesDebugServer>(psesDebugServer)
76+
.AddSingleton<DebugService>()
77+
.AddSingleton<DebugStateService>(new DebugStateService
78+
{
79+
OwnsEditorSession = useTempSession
80+
})
81+
.AddSingleton<DebugEventHandlerService>();
82+
}
83+
}
84+
}

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,17 @@ public ConfigurationDoneHandler(
4343
_workspaceService = workspaceService;
4444
}
4545

46-
public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request, CancellationToken cancellationToken)
46+
public async Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request, CancellationToken cancellationToken)
4747
{
4848
_debugService.IsClientAttached = true;
4949

50+
if (_debugStateService.OwnsEditorSession)
51+
{
52+
// If this is a debug-only session, we need to start
53+
// the command loop manually
54+
_powerShellContextService.ConsoleReader.StartCommandLoop();
55+
}
56+
5057
if (!string.IsNullOrEmpty(_debugStateService.ScriptToLaunch))
5158
{
5259
if (_powerShellContextService.SessionState == PowerShellContextState.Ready)
@@ -63,14 +70,6 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
6370

6471
if (_debugStateService.IsInteractiveDebugSession)
6572
{
66-
if (_debugStateService.OwnsEditorSession)
67-
{
68-
// If this is a debug-only session, we need to start
69-
// the command loop manually
70-
// TODO: Bring this back
71-
//_editorSession.HostInput.StartCommandLoop();
72-
}
73-
7473
if (_debugService.IsDebuggerStopped)
7574
{
7675
if (_debugService.CurrentDebuggerStoppedEventArgs != null)
@@ -88,7 +87,7 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
8887
}
8988
}
9089

91-
return Task.FromResult(new ConfigurationDoneResponse());
90+
return new ConfigurationDoneResponse();
9291
}
9392

9493
private async Task LaunchScriptAsync(string scriptToLaunch)

src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,7 +1793,7 @@ private void OnExecutionStatusChanged(
17931793

17941794
private void PowerShellContext_RunspaceChangedAsync(object sender, RunspaceChangedEventArgs e)
17951795
{
1796-
_languageServer.SendNotification(
1796+
_languageServer?.SendNotification(
17971797
"powerShell/runspaceChanged",
17981798
new MinifiedRunspaceDetails(e.NewRunspace));
17991799
}
@@ -1831,7 +1831,7 @@ public MinifiedRunspaceDetails(RunspaceDetails eventArgs)
18311831
/// <param name="e">details of the execution status change</param>
18321832
private void PowerShellContext_ExecutionStatusChangedAsync(object sender, ExecutionStatusChangedEventArgs e)
18331833
{
1834-
_languageServer.SendNotification(
1834+
_languageServer?.SendNotification(
18351835
"powerShell/executionStatusChanged",
18361836
e);
18371837
}

0 commit comments

Comments
 (0)