diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs index 0439f4484..9328e5440 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugService.cs @@ -930,13 +930,24 @@ internal async void OnDebuggerStopAsync(object sender, DebuggerStopEventArgs e) if (_remoteFileManager is not null && string.IsNullOrEmpty(localScriptPath)) { // Get the current script listing and create the buffer - PSCommand command = new PSCommand().AddScript($"list 1 {int.MaxValue}"); + IReadOnlyList scriptListingLines; + await debugInfoHandle.WaitAsync().ConfigureAwait(false); + try + { + // This command must be run through `ExecuteInDebugger`! + PSCommand psCommand = new PSCommand().AddScript($"list 1 {int.MaxValue}"); - IReadOnlyList scriptListingLines = - await _executionService.ExecutePSCommandAsync( - command, CancellationToken.None).ConfigureAwait(false); + scriptListingLines = + await _executionService.ExecutePSCommandAsync( + psCommand, + CancellationToken.None).ConfigureAwait(false); + } + finally + { + debugInfoHandle.Release(); + } - if (scriptListingLines is not null) + if (scriptListingLines.Count > 0) { int linePrefixLength = 0; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs index c7a983bd8..7c05b3da2 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs @@ -73,9 +73,12 @@ public override IReadOnlyList Run(CancellationToken cancellationToken) // state that we sync with the LSP debugger. The former commands we want to send // through PowerShell's `Debugger.ProcessCommand` so that they work as expected, but // the latter we must not send through it else they pollute the history as this - // PowerShell API does not let us exclude them from it. + // PowerShell API does not let us exclude them from it. Notably we also need to send + // the `prompt` command and our special `list 1 ` through the debugger too. + // The former needs the context in order to show `DBG 1>` etc., and the latter is + // used to gather the lines when debugging a script that isn't in a file. return _pwsh.Runspace.Debugger.InBreakpoint - && (PowerShellExecutionOptions.AddToHistory || IsPromptCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote) + && (PowerShellExecutionOptions.AddToHistory || IsPromptOrListCommand(_psCommand) || _pwsh.Runspace.RunspaceIsRemote) ? ExecuteInDebugger(cancellationToken) : ExecuteNormally(cancellationToken); } @@ -87,7 +90,7 @@ public override IReadOnlyList Run(CancellationToken cancellationToken) public override string ToString() => _psCommand.GetInvocationText(); - private static bool IsPromptCommand(PSCommand command) + private static bool IsPromptOrListCommand(PSCommand command) { if (command.Commands.Count is not 1 || command.Commands[0] is { IsScript: false } or { Parameters.Count: > 0 }) @@ -96,7 +99,8 @@ private static bool IsPromptCommand(PSCommand command) } string commandText = command.Commands[0].CommandText; - return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase); + return commandText.Equals("prompt", StringComparison.OrdinalIgnoreCase) + || commandText.Equals($"list 1 {int.MaxValue}", StringComparison.OrdinalIgnoreCase); } private IReadOnlyList ExecuteNormally(CancellationToken cancellationToken) @@ -301,11 +305,10 @@ private IReadOnlyList ExecuteInDebugger(CancellationToken cancellationT return Array.Empty(); } - // If we've been asked for a PSObject, no need to allocate a new collection - if (typeof(TResult) == typeof(PSObject) - && outputCollection is IReadOnlyList resultCollection) + // If we've been asked for a PSObject, no need to convert + if (typeof(TResult) == typeof(PSObject)) { - return resultCollection; + return new List(outputCollection) as IReadOnlyList; } // Otherwise, convert things over