Skip to content

Commit b105808

Browse files
committed
Implement cancellation properly, make F8 work
1 parent 9266126 commit b105808

13 files changed

+139
-63
lines changed

integrated.sln

+14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "..\PSScriptAnalyz
1919
EndProject
2020
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rules", "..\PSScriptAnalyzer\Rules\Rules.csproj", "{6F224847-AC0E-4901-B5BA-7DE81D8B3FBC}"
2121
EndProject
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.Commands.Utility", "..\PowerShell\src\Microsoft.PowerShell.Commands.Utility\Microsoft.PowerShell.Commands.Utility.csproj", "{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}"
23+
EndProject
2224
Global
2325
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2426
Debug|Any CPU = Debug|Any CPU
@@ -116,6 +118,18 @@ Global
116118
{6F224847-AC0E-4901-B5BA-7DE81D8B3FBC}.Release|x64.Build.0 = Release|Any CPU
117119
{6F224847-AC0E-4901-B5BA-7DE81D8B3FBC}.Release|x86.ActiveCfg = Release|Any CPU
118120
{6F224847-AC0E-4901-B5BA-7DE81D8B3FBC}.Release|x86.Build.0 = Release|Any CPU
121+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
122+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|Any CPU.Build.0 = Debug|Any CPU
123+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|x64.ActiveCfg = Debug|Any CPU
124+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|x64.Build.0 = Debug|Any CPU
125+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|x86.ActiveCfg = Debug|Any CPU
126+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Debug|x86.Build.0 = Debug|Any CPU
127+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|Any CPU.ActiveCfg = Release|Any CPU
128+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|Any CPU.Build.0 = Release|Any CPU
129+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|x64.ActiveCfg = Release|Any CPU
130+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|x64.Build.0 = Release|Any CPU
131+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|x86.ActiveCfg = Release|Any CPU
132+
{E64BB0B6-9D93-4B6D-9EFF-F669FD5BA842}.Release|x86.Build.0 = Release|Any CPU
119133
EndGlobalSection
120134
GlobalSection(NestedProjects) = preSolution
121135
{00158E75-B6E0-43E4-98E9-BDAC8EEF4C09} = {31BB87E9-A4A1-4266-B150-CEACE7C424C4}

src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.PowerShell.EditorServices.Services.Analysis;
1515
using Microsoft.PowerShell.EditorServices.Services.Configuration;
1616
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
17-
using OmniSharp.Extensions.LanguageServer.Protocol;
1817
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
1918
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
2019
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
@@ -133,7 +132,7 @@ public AnalysisService(
133132
/// <param name="filesToAnalyze">The files to run script analysis on.</param>
134133
/// <param name="cancellationToken">A cancellation token to cancel this call with.</param>
135134
/// <returns>A task that finishes when script diagnostics have been published.</returns>
136-
public void RunScriptDiagnostics(
135+
public void StartScriptDiagnostics(
137136
ScriptFile[] filesToAnalyze)
138137
{
139138
if (_configurationService.CurrentSettings.ScriptAnalysis.Enable == false)

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,15 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
111111
// This seems to be the simplest way to invoke a script block (which contains breakpoint information) via the PowerShell API.
112112
var cmd = new PSCommand().AddScript(". $args[0]").AddArgument(ast.GetScriptBlock());
113113
await _executionService
114-
.ExecutePSCommandAsync<object>(cmd, new PowerShellExecutionOptions { WriteOutputToHost = true, WriteErrorsToHost = true }, CancellationToken.None)
114+
.ExecutePSCommandAsync<object>(cmd, new PowerShellExecutionOptions { WriteOutputToHost = true }, CancellationToken.None)
115115
.ConfigureAwait(false);
116116
}
117117
else
118118
{
119119
await _executionService
120120
.ExecutePSCommandAsync(
121121
new PSCommand().AddScript(untitledScript.Contents),
122-
new PowerShellExecutionOptions { WriteOutputToHost = true, WriteErrorsToHost = true },
122+
new PowerShellExecutionOptions { WriteOutputToHost = true },
123123
CancellationToken.None)
124124
.ConfigureAwait(false);
125125
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task<DisconnectResponse> Handle(DisconnectArguments request, Cancel
4545
if (_debugStateService.ExecutionCompleted == false)
4646
{
4747
_debugStateService.ExecutionCompleted = true;
48-
_executionService.CancelCurrentTask();
48+
//_executionService.CancelCurrentTask();
4949

5050
if (_debugStateService.IsInteractiveDebugSession && _debugStateService.IsAttachSession)
5151
{

src/PowerShellEditorServices/Services/PowerShell/Execution/PowerShellExecutionOptions.cs

-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,8 @@
22
{
33
public struct PowerShellExecutionOptions
44
{
5-
public static PowerShellExecutionOptions Default = new PowerShellExecutionOptions
6-
{
7-
WriteOutputToHost = true,
8-
WriteErrorsToHost = true,
9-
};
10-
115
public bool WriteOutputToHost { get; set; }
126

13-
public bool WriteErrorsToHost { get; set; }
14-
157
public bool AddToHistory { get; set; }
168

179
public bool WriteInputToHost { get; set; }

src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousPowerShellTask.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public override Collection<TResult> Run(CancellationToken cancellationToken)
7171
{
7272
Logger.LogWarning($"Runtime exception occurred while executing command:{Environment.NewLine}{Environment.NewLine}{e}");
7373

74-
if (!_executionOptions.WriteErrorsToHost)
74+
if (!_executionOptions.WriteOutputToHost)
7575
{
7676
throw;
7777
}

src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousTask.cs

+25-23
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,63 @@ internal interface ISynchronousTask
99
{
1010
bool IsCanceled { get; }
1111

12-
void ExecuteSynchronously(ref CancellationTokenSource cancellationSource, CancellationToken threadCancellationToken);
12+
void ExecuteSynchronously(CancellationToken threadCancellationToken);
1313
}
1414

1515
internal abstract class SynchronousTask<TResult> : ISynchronousTask
1616
{
1717
private readonly TaskCompletionSource<TResult> _taskCompletionSource;
1818

19-
private readonly CancellationToken _taskCancellationToken;
19+
private readonly CancellationToken _taskRequesterCancellationToken;
2020

2121
private bool _executionCanceled;
2222

2323
protected SynchronousTask(ILogger logger, CancellationToken cancellationToken)
2424
{
2525
Logger = logger;
2626
_taskCompletionSource = new TaskCompletionSource<TResult>();
27-
_taskCancellationToken = cancellationToken;
27+
_taskRequesterCancellationToken = cancellationToken;
2828
_executionCanceled = false;
2929
}
3030

3131
protected ILogger Logger { get; }
3232

3333
public Task<TResult> Task => _taskCompletionSource.Task;
3434

35-
public bool IsCanceled => _taskCancellationToken.IsCancellationRequested || _executionCanceled;
35+
public bool IsCanceled => _executionCanceled || _taskRequesterCancellationToken.IsCancellationRequested;
3636

3737
public abstract TResult Run(CancellationToken cancellationToken);
3838

3939
public abstract override string ToString();
4040

41-
public void ExecuteSynchronously(ref CancellationTokenSource cancellationSource, CancellationToken threadCancellation)
41+
public void ExecuteSynchronously(CancellationToken executorCancellationToken)
4242
{
43-
if (_taskCancellationToken.IsCancellationRequested || threadCancellation.IsCancellationRequested)
43+
using (var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_taskRequesterCancellationToken, executorCancellationToken))
4444
{
45-
Cancel();
46-
return;
47-
}
45+
if (cancellationSource.IsCancellationRequested)
46+
{
47+
SetCanceled();
48+
return;
49+
}
4850

49-
cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_taskCancellationToken, threadCancellation);
50-
try
51-
{
52-
TResult result = Run(cancellationSource.Token);
51+
try
52+
{
53+
TResult result = Run(cancellationSource.Token);
5354

54-
_taskCompletionSource.SetResult(result);
55-
}
56-
catch (OperationCanceledException)
57-
{
58-
Cancel();
59-
}
60-
catch (Exception e)
61-
{
62-
_taskCompletionSource.SetException(e);
55+
_taskCompletionSource.SetResult(result);
56+
}
57+
catch (OperationCanceledException)
58+
{
59+
SetCanceled();
60+
}
61+
catch (Exception e)
62+
{
63+
_taskCompletionSource.SetException(e);
64+
}
6365
}
6466
}
6567

66-
private void Cancel()
68+
private void SetCanceled()
6769
{
6870
_executionCanceled = true;
6971
_taskCompletionSource.SetCanceled();

src/PowerShellEditorServices/Services/PowerShell/PowerShellConsoleService.cs

+39-19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution;
44
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
55
using System;
6+
using System.Collections.ObjectModel;
7+
using System.Linq;
68
using System.Management.Automation;
79
using System.Text;
810
using System.Threading;
@@ -16,15 +18,13 @@ public static PowerShellConsoleService CreateAndStart(
1618
ILoggerFactory loggerFactory,
1719
PowerShellExecutionService executionService)
1820
{
19-
var consoleService = new PowerShellConsoleService(
21+
return new PowerShellConsoleService(
2022
loggerFactory,
2123
executionService,
2224
executionService.EngineIntrinsics,
2325
executionService.EditorServicesHost,
2426
executionService.ReadLine,
2527
executionService.PSReadLineProxy);
26-
27-
return consoleService;
2828
}
2929

3030
private readonly ILogger _logger;
@@ -45,6 +45,8 @@ public static PowerShellConsoleService CreateAndStart(
4545

4646
private CancellationTokenSource _currentCommandCancellationSource;
4747

48+
private bool _canCancel;
49+
4850
private PowerShellConsoleService(
4951
ILoggerFactory loggerFactory,
5052
PowerShellExecutionService executionService,
@@ -78,21 +80,36 @@ public void StartRepl()
7880

7981
public void CancelCurrentPrompt()
8082
{
81-
_currentCommandCancellationSource?.Cancel();
83+
if (_canCancel)
84+
{
85+
_currentCommandCancellationSource?.Cancel();
86+
}
87+
}
88+
89+
public void Stop()
90+
{
91+
_replLoopCancellationSource.Cancel();
8292
}
8393

8494
private async Task RunReplLoopAsync()
8595
{
8696
while (!_replLoopCancellationSource.IsCancellationRequested)
8797
{
8898
_currentCommandCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_replLoopCancellationSource.Token);
89-
99+
_canCancel = true;
90100
try
91101
{
92-
await InvokePromptFunctionAsync().ConfigureAwait(false);
102+
string promptString = (await GetPromptOutputAsync().ConfigureAwait(false)).FirstOrDefault() ?? "PS> ";
103+
104+
WritePrompt(promptString);
93105

94106
string userInput = await InvokeReadLineAsync().ConfigureAwait(false);
95107

108+
if (_currentCommandCancellationSource.IsCancellationRequested)
109+
{
110+
continue;
111+
}
112+
96113
await InvokeInputAsync(userInput).ConfigureAwait(false);
97114
}
98115
catch (OperationCanceledException)
@@ -103,25 +120,28 @@ private async Task RunReplLoopAsync()
103120
{
104121

105122
}
123+
finally
124+
{
125+
_canCancel = false;
126+
_currentCommandCancellationSource.Dispose();
127+
_currentCommandCancellationSource = null;
128+
}
106129
}
107130
}
108131

109-
private Task InvokePromptFunctionAsync()
132+
private Task<Collection<string>> GetPromptOutputAsync()
110133
{
111-
var promptCommand = new PSCommand()
112-
.AddCommand("prompt")
113-
.AddCommand("Write-Host")
114-
.AddParameter("NoNewline");
115-
116-
var executionOptions = new PowerShellExecutionOptions
117-
{
118-
WriteOutputToHost = true,
119-
};
134+
var promptCommand = new PSCommand().AddCommand("prompt");
120135

121-
return _executionService.ExecutePSCommandAsync(
136+
return _executionService.ExecutePSCommandAsync<string>(
122137
promptCommand,
123-
executionOptions,
124-
_currentCommandCancellationSource.Token);
138+
new PowerShellExecutionOptions(),
139+
CancellationToken.None);
140+
}
141+
142+
private void WritePrompt(string promptString)
143+
{
144+
_editorServicesHost.UI.Write(promptString);
125145
}
126146

127147
private Task<string> InvokeReadLineAsync()

0 commit comments

Comments
 (0)