Skip to content

Commit 7275b4a

Browse files
committed
WIP: FOUR tests passing! With less hacks.
1 parent 8d1e246 commit 7275b4a

File tree

8 files changed

+137
-207
lines changed

8 files changed

+137
-207
lines changed

src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ private void OnDebuggerResuming(object sender, DebuggerResumingEventArgs e)
131131
_debugAdapterServer.SendNotification(EventNames.Continued,
132132
new ContinuedEvent
133133
{
134-
AllThreadsContinued = true,
135134
ThreadId = ThreadsHandler.PipelineThread.Id,
135+
AllThreadsContinued = true,
136136
});
137137
}
138138

@@ -151,7 +151,7 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e)
151151
BreakpointDetails.Create(e.Breakpoint, e.UpdateType)
152152
);
153153

154-
string reason = (e.UpdateType) switch {
154+
string reason = e.UpdateType switch {
155155
BreakpointUpdateType.Set => BreakpointEventReason.New,
156156
BreakpointUpdateType.Removed => BreakpointEventReason.Removed,
157157
BreakpointUpdateType.Enabled => BreakpointEventReason.Changed,

src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs

+28-68
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal class DebugService
3535
private readonly ILogger _logger;
3636
private readonly IInternalPowerShellExecutionService _executionService;
3737
private readonly BreakpointService _breakpointService;
38-
private readonly RemoteFileManagerService remoteFileManager;
38+
private readonly RemoteFileManagerService _remoteFileManager;
3939

4040
private readonly PsesInternalHost _psesHost;
4141

@@ -82,26 +82,6 @@ internal class DebugService
8282
/// Initializes a new instance of the DebugService class and uses
8383
/// the given PowerShellContext for all future operations.
8484
/// </summary>
85-
/// <param name="powerShellContext">
86-
/// The PowerShellContext to use for all debugging operations.
87-
/// </param>
88-
/// <param name="logger">An ILogger implementation used for writing log messages.</param>
89-
//public DebugService(PowerShellContextService powerShellContext, ILogger logger)
90-
// : this(powerShellContext, null, logger)
91-
//{
92-
//}
93-
94-
/// <summary>
95-
/// Initializes a new instance of the DebugService class and uses
96-
/// the given PowerShellContext for all future operations.
97-
/// </summary>
98-
/// <param name="powerShellContext">
99-
/// The PowerShellContext to use for all debugging operations.
100-
/// </param>
101-
//// <param name = "remoteFileManager" >
102-
//// A RemoteFileManagerService instance to use for accessing files in remote sessions.
103-
//// </param>
104-
/// <param name="logger">An ILogger implementation used for writing log messages.</param>
10585
public DebugService(
10686
IInternalPowerShellExecutionService executionService,
10787
IPowerShellDebugContext debugContext,
@@ -120,8 +100,7 @@ public DebugService(
120100
_debugContext.DebuggerStopped += OnDebuggerStopAsync;
121101
_debugContext.DebuggerResuming += OnDebuggerResuming;
122102
_debugContext.BreakpointUpdated += OnBreakpointUpdated;
123-
124-
this.remoteFileManager = remoteFileManager;
103+
_remoteFileManager = remoteFileManager;
125104

126105
invocationTypeScriptPositionProperty =
127106
typeof(InvocationInfo)
@@ -150,32 +129,27 @@ public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
150129

151130
string scriptPath = scriptFile.FilePath;
152131
// Make sure we're using the remote script path
153-
if (_psesHost.CurrentRunspace.IsOnRemoteMachine && remoteFileManager is not null)
132+
if (_psesHost.CurrentRunspace.IsOnRemoteMachine && _remoteFileManager is not null)
154133
{
155-
if (!remoteFileManager.IsUnderRemoteTempPath(scriptPath))
134+
if (!_remoteFileManager.IsUnderRemoteTempPath(scriptPath))
156135
{
157136
_logger.LogTrace($"Could not set breakpoints for local path '{scriptPath}' in a remote session.");
158-
159137
return Array.Empty<BreakpointDetails>();
160138
}
161139

162-
string mappedPath = remoteFileManager.GetMappedPath(scriptPath, _psesHost.CurrentRunspace);
163-
164-
scriptPath = mappedPath;
140+
scriptPath = _remoteFileManager.GetMappedPath(scriptPath, _psesHost.CurrentRunspace);
165141
}
166-
else if (temporaryScriptListingPath is not null
167-
&& temporaryScriptListingPath.Equals(scriptPath, StringComparison.CurrentCultureIgnoreCase))
142+
else if (temporaryScriptListingPath?.Equals(scriptPath, StringComparison.CurrentCultureIgnoreCase) == true)
168143
{
169144
_logger.LogTrace($"Could not set breakpoint on temporary script listing path '{scriptPath}'.");
170-
171145
return Array.Empty<BreakpointDetails>();
172146
}
173147

174148
// Fix for issue #123 - file paths that contain wildcard chars [ and ] need to
175149
// quoted and have those wildcard chars escaped.
176150
string escapedScriptPath = PathUtils.WildcardEscapePath(scriptPath);
177151

178-
if (dscBreakpoints is null || !dscBreakpoints.IsDscResourcePath(escapedScriptPath))
152+
if (dscBreakpoints?.IsDscResourcePath(escapedScriptPath) != true)
179153
{
180154
if (clearExisting)
181155
{
@@ -185,11 +159,7 @@ public async Task<BreakpointDetails[]> SetLineBreakpointsAsync(
185159
return (await _breakpointService.SetBreakpointsAsync(escapedScriptPath, breakpoints).ConfigureAwait(false)).ToArray();
186160
}
187161

188-
return await dscBreakpoints.SetLineBreakpointsAsync(
189-
_executionService,
190-
escapedScriptPath,
191-
breakpoints)
192-
.ConfigureAwait(false);
162+
return await dscBreakpoints.SetLineBreakpointsAsync(_executionService, escapedScriptPath, breakpoints).ConfigureAwait(false);
193163
}
194164

195165
/// <summary>
@@ -362,7 +332,7 @@ public VariableDetailsBase GetVariableFromExpression(string variableExpression)
362332
variableName,
363333
StringComparison.CurrentCultureIgnoreCase));
364334

365-
if (resolvedVariable is not null && resolvedVariable.IsExpandable)
335+
if (resolvedVariable?.IsExpandable == true)
366336
{
367337
// Continue by searching in this variable's children.
368338
variableList = GetVariables(resolvedVariable.Id);
@@ -476,10 +446,10 @@ public async Task<string> SetVariableAsync(int variableContainerReferenceId, str
476446
{
477447
_logger.LogTrace($"Setting variable '{name}' using conversion to value: {expressionResult ?? "<null>"}");
478448

479-
psVariable.Value = await _executionService.ExecuteDelegateAsync<object>(
449+
psVariable.Value = await _executionService.ExecuteDelegateAsync(
480450
"PS debugger argument converter",
481451
ExecutionOptions.Default,
482-
(pwsh, cancellationToken) =>
452+
(pwsh, _) =>
483453
{
484454
var engineIntrinsics = (EngineIntrinsics)pwsh.Runspace.SessionStateProxy.GetVariable("ExecutionContext");
485455

@@ -634,17 +604,14 @@ private async Task FetchStackFramesAndVariablesAsync(string scriptNameOverride)
634604
nextVariableId = VariableDetailsBase.FirstVariableId;
635605
variables = new List<VariableDetailsBase>
636606
{
637-
638607
// Create a dummy variable for index 0, should never see this.
639608
new VariableDetails("Dummy", null)
640609
};
641610

642611
// Must retrieve in order of broadest to narrowest scope for efficient
643612
// deduplication: global, script, local.
644613
globalScopeVariables = await FetchVariableContainerAsync(VariableContainerDetails.GlobalScopeName).ConfigureAwait(false);
645-
646614
scriptScopeVariables = await FetchVariableContainerAsync(VariableContainerDetails.ScriptScopeName).ConfigureAwait(false);
647-
648615
localScopeVariables = await FetchVariableContainerAsync(VariableContainerDetails.LocalScopeName).ConfigureAwait(false);
649616

650617
await FetchStackFramesAsync(scriptNameOverride).ConfigureAwait(false);
@@ -662,9 +629,7 @@ private Task<VariableContainerDetails> FetchVariableContainerAsync(string scope)
662629

663630
private async Task<VariableContainerDetails> FetchVariableContainerAsync(string scope, bool autoVarsOnly)
664631
{
665-
PSCommand psCommand = new PSCommand()
666-
.AddCommand("Get-Variable")
667-
.AddParameter("Scope", scope);
632+
PSCommand psCommand = new PSCommand().AddCommand("Get-Variable").AddParameter("Scope", scope);
668633

669634
var scopeVariableContainer = new VariableContainerDetails(nextVariableId++, "Scope: " + scope);
670635
variables.Add(scopeVariableContainer);
@@ -674,17 +639,13 @@ private async Task<VariableContainerDetails> FetchVariableContainerAsync(string
674639
{
675640
results = await _executionService.ExecutePSCommandAsync<PSObject>(psCommand, CancellationToken.None).ConfigureAwait(false);
676641
}
677-
catch (CmdletInvocationException ex)
642+
// It's possible to be asked to run `Get-Variable -Scope N` where N is a number that
643+
// exceeds the available scopes. In this case, the command throws this exception, but
644+
// there's nothing we can do about it, nor can we know the number of scopes that exist,
645+
// and we shouldn't crash the debugger, so we just return no results instead. All other
646+
// exceptions should be thrown again.
647+
catch (CmdletInvocationException ex) when (ex.ErrorRecord.CategoryInfo.Reason.Equals("PSArgumentOutOfRangeException"))
678648
{
679-
// It's possible to be asked to run `Get-Variable -Scope N` where N is a number that
680-
// exceeds the available scopes. In this case, the command throws this exception,
681-
// but there's nothing we can do about it, nor can we know the number of scopes that
682-
// exist, and we shouldn't crash the debugger, so we just return no results instead.
683-
// All other exceptions should be thrown again.
684-
if (!ex.ErrorRecord.CategoryInfo.Reason.Equals("PSArgumentOutOfRangeException"))
685-
{
686-
throw;
687-
}
688649
results = null;
689650
}
690651

@@ -749,8 +710,8 @@ private static bool ShouldAddAsVariable(VariableInfo variableInfo)
749710
{
750711
// Filter built-in constant or readonly variables like $true, $false, $null, etc.
751712
ScopedItemOptions variableScope = variableInfo.Variable.Options;
752-
var constantAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.Constant;
753-
var readonlyAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.ReadOnly;
713+
const ScopedItemOptions constantAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.Constant;
714+
const ScopedItemOptions readonlyAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.ReadOnly;
754715
if (((variableScope & constantAllScope) == constantAllScope)
755716
|| ((variableScope & readonlyAllScope) == readonlyAllScope))
756717
{
@@ -909,11 +870,11 @@ private async Task FetchStackFramesAsync(string scriptNameOverride)
909870
stackFrameDetailsEntry.ScriptPath = scriptNameOverride;
910871
}
911872
else if (isOnRemoteMachine
912-
&& remoteFileManager is not null
873+
&& _remoteFileManager is not null
913874
&& !string.Equals(stackFrameScriptPath, StackFrameDetails.NoFileScriptPath))
914875
{
915876
stackFrameDetailsEntry.ScriptPath =
916-
remoteFileManager.GetMappedPath(stackFrameScriptPath, _psesHost.CurrentRunspace);
877+
_remoteFileManager.GetMappedPath(stackFrameScriptPath, _psesHost.CurrentRunspace);
917878
}
918879

919880
stackFrameDetailList.Add(stackFrameDetailsEntry);
@@ -957,7 +918,7 @@ internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e)
957918
string localScriptPath = e.InvocationInfo?.ScriptName;
958919

959920
// If there's no ScriptName, get the "list" of the current source
960-
if (remoteFileManager is not null && string.IsNullOrEmpty(localScriptPath))
921+
if (_remoteFileManager is not null && string.IsNullOrEmpty(localScriptPath))
961922
{
962923
// Get the current script listing and create the buffer
963924
PSCommand command = new PSCommand().AddScript($"list 1 {int.MaxValue}");
@@ -978,7 +939,7 @@ await _executionService.ExecutePSCommandAsync<PSObject>(
978939
.Where(s => s is not null));
979940

980941
temporaryScriptListingPath =
981-
remoteFileManager.CreateTemporaryFile(
942+
_remoteFileManager.CreateTemporaryFile(
982943
$"[{_psesHost.CurrentRunspace.SessionDetails.ComputerName}] {TemporaryScriptFileName}",
983944
scriptListing,
984945
_psesHost.CurrentRunspace);
@@ -1001,19 +962,18 @@ await _executionService.ExecutePSCommandAsync<PSObject>(
1001962
// If this is a remote connection and the debugger stopped at a line
1002963
// in a script file, get the file contents
1003964
if (_psesHost.CurrentRunspace.IsOnRemoteMachine
1004-
&& remoteFileManager is not null
965+
&& _remoteFileManager is not null
1005966
&& !noScriptName)
1006967
{
1007968
localScriptPath =
1008-
await remoteFileManager.FetchRemoteFileAsync(
969+
await _remoteFileManager.FetchRemoteFileAsync(
1009970
e.InvocationInfo.ScriptName,
1010971
_psesHost.CurrentRunspace).ConfigureAwait(false);
1011972
}
1012973

1013974
if (stackFrameDetails.Length > 0)
1014975
{
1015976
// Augment the top stack frame with details from the stop event
1016-
1017977
if (e.InvocationInfo is not null
1018978
&& invocationTypeScriptPositionProperty.GetValue(e.InvocationInfo) is IScriptExtent scriptExtent)
1019979
{
@@ -1056,9 +1016,9 @@ private void OnBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e)
10561016
// TODO: This could be either a path or a script block!
10571017
string scriptPath = lineBreakpoint.Script;
10581018
if (_psesHost.CurrentRunspace.IsOnRemoteMachine
1059-
&& remoteFileManager is not null)
1019+
&& _remoteFileManager is not null)
10601020
{
1061-
string mappedPath = remoteFileManager.GetMappedPath(scriptPath, _psesHost.CurrentRunspace);
1021+
string mappedPath = _remoteFileManager.GetMappedPath(scriptPath, _psesHost.CurrentRunspace);
10621022

10631023
if (mappedPath is null)
10641024
{

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

+14-15
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
7070

7171
if (_debugStateService.OwnsEditorSession)
7272
{
73-
// If this is a debug-only session, we need to start
73+
// TODO: If this is a debug-only session, we need to start
7474
// the command loop manually
7575
//_powerShellContextService.ConsoleReader.StartCommandLoop();
7676
}
@@ -81,22 +81,19 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
8181
.HandleErrorsAsync(_logger);
8282
}
8383

84-
if (_debugStateService.IsInteractiveDebugSession)
84+
if (_debugStateService.IsInteractiveDebugSession && _debugService.IsDebuggerStopped)
8585
{
86-
if (_debugService.IsDebuggerStopped)
86+
if (_debugService.CurrentDebuggerStoppedEventArgs is not null)
8787
{
88-
if (_debugService.CurrentDebuggerStoppedEventArgs != null)
89-
{
90-
// If this is an interactive session and there's a pending breakpoint,
91-
// send that information along to the debugger client
92-
_debugEventHandlerService.TriggerDebuggerStopped(_debugService.CurrentDebuggerStoppedEventArgs);
93-
}
94-
else
95-
{
96-
// If this is an interactive session and there's a pending breakpoint that has not been propagated through
97-
// the debug service, fire the debug service's OnDebuggerStop event.
98-
_debugService.OnDebuggerStopAsync(null, _debugContext.LastStopEventArgs);
99-
}
88+
// If this is an interactive session and there's a pending breakpoint,
89+
// send that information along to the debugger client
90+
_debugEventHandlerService.TriggerDebuggerStopped(_debugService.CurrentDebuggerStoppedEventArgs);
91+
}
92+
else
93+
{
94+
// If this is an interactive session and there's a pending breakpoint that has not been propagated through
95+
// the debug service, fire the debug service's OnDebuggerStop event.
96+
_debugService.OnDebuggerStopAsync(null, _debugContext.LastStopEventArgs);
10097
}
10198
}
10299

@@ -119,6 +116,8 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
119116
ScriptBlockAst ast = Parser.ParseInput(untitledScript.Contents, untitledScript.DocumentUri.ToString(), out Token[] tokens, out ParseError[] errors);
120117

121118
// This seems to be the simplest way to invoke a script block (which contains breakpoint information) via the PowerShell API.
119+
//
120+
// TODO: Fix this so the added script doesn't show up.
122121
var cmd = new PSCommand().AddScript(". $args[0]").AddArgument(ast.GetScriptBlock());
123122
await _executionService
124123
.ExecutePSCommandAsync<object>(cmd, CancellationToken.None, s_debuggerExecutionOptions)

src/PowerShellEditorServices/Services/Extension/EditorOperationsService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void ClearTerminal()
264264
if (!TestHasLanguageServer(warnUser: false))
265265
{
266266
return;
267-
};
267+
}
268268

269269
_languageServer.SendNotification("editor/clearTerminal");
270270
}

src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public void StepOver()
106106

107107
public void SetDebugResuming(DebuggerResumeAction debuggerResumeAction)
108108
{
109+
// TODO: Why are we exiting?
109110
_psesHost.SetExit();
110111

111112
if (LastStopEventArgs is not null)
@@ -115,7 +116,11 @@ public void SetDebugResuming(DebuggerResumeAction debuggerResumeAction)
115116

116117
// We need to tell whatever is happening right now in the debug prompt to wrap up so we can continue
117118
// TODO: Except in the case of unit tests?
118-
_psesHost.CancelCurrentTask();
119+
if (_psesHost.UI is not NullPSHostUI)
120+
// if (debuggerResumeAction is not DebuggerResumeAction.Continue)
121+
{
122+
_psesHost.CancelCurrentTask();
123+
}
119124
}
120125

121126
// This must be called AFTER the new PowerShell has been pushed
@@ -124,7 +129,6 @@ public void EnterDebugLoop(CancellationToken loopCancellationToken)
124129
RaiseDebuggerStoppedEvent();
125130
}
126131

127-
128132
// This must be called BEFORE the debug PowerShell has been popped
129133
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This method may acquire an implementation later, at which point it will need instance data")]
130134
public void ExitDebugLoop()
@@ -144,7 +148,7 @@ public void SetDebuggerResumed()
144148

145149
public void ProcessDebuggerResult(DebuggerCommandResults debuggerResult)
146150
{
147-
if (debuggerResult.ResumeAction != null)
151+
if (debuggerResult.ResumeAction is not null)
148152
{
149153
SetDebugResuming(debuggerResult.ResumeAction.Value);
150154
RaiseDebuggerResumingEvent(new DebuggerResumingEventArgs(debuggerResult.ResumeAction.Value));

0 commit comments

Comments
 (0)