diff --git a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
index 5b0d81875..8f69e7e45 100644
--- a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
+++ b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs
@@ -178,11 +178,12 @@ public void StartScriptDiagnostics(ScriptFile[] filesToAnalyze)
/// The script to format.
/// The settings to use with the formatter.
/// Optionally, the range that should be formatted.
+ /// The token used to cancel the task.
/// The text of the formatted PowerShell script.
- public Task FormatAsync(string scriptFileContents, Hashtable formatSettings, int[] formatRange = null)
+ public Task FormatAsync(string scriptFileContents, Hashtable formatSettings, int[] formatRange, CancellationToken cancellationToken)
{
EnsureEngineSettingsCurrent();
- return AnalysisEngine.FormatAsync(scriptFileContents, formatSettings, formatRange);
+ return AnalysisEngine.FormatAsync(scriptFileContents, formatSettings, formatRange, cancellationToken);
}
///
diff --git a/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs b/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs
index 371950bc0..92510d3e1 100644
--- a/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs
+++ b/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs
@@ -7,6 +7,7 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
@@ -127,8 +128,9 @@ private PssaCmdletAnalysisEngine(
/// The full text of a script.
/// The formatter settings to use.
/// A possible range over which to run the formatter.
+ /// The token used to cancel the task.
/// Formatted script as string
- public async Task FormatAsync(string scriptDefinition, Hashtable formatSettings, int[] rangeList)
+ public async Task FormatAsync(string scriptDefinition, Hashtable formatSettings, int[] rangeList, CancellationToken cancellationToken)
{
// We cannot use Range type therefore this workaround of using -1 default value.
// Invoke-Formatter throws a ParameterBinderValidationException if the ScriptDefinition is an empty string.
@@ -148,7 +150,7 @@ public async Task FormatAsync(string scriptDefinition, Hashtable formatS
psCommand.AddParameter("Range", rangeList);
}
- PowerShellResult result = await InvokePowerShellAsync(psCommand).ConfigureAwait(false);
+ PowerShellResult result = await InvokePowerShellAsync(psCommand, cancellationToken).ConfigureAwait(false);
if (result is null)
{
@@ -156,6 +158,12 @@ public async Task FormatAsync(string scriptDefinition, Hashtable formatS
return null;
}
+ if (cancellationToken.IsCancellationRequested)
+ {
+ _logger.LogWarning("Formatting request canceled");
+ return null;
+ }
+
if (result.HasErrors)
{
StringBuilder errorBuilder = new StringBuilder().Append(s_indentJoin);
@@ -257,7 +265,7 @@ public void Dispose() =>
private async Task GetSemanticMarkersFromCommandAsync(PSCommand command)
{
- PowerShellResult result = await InvokePowerShellAsync(command).ConfigureAwait(false);
+ PowerShellResult result = await InvokePowerShellAsync(command, CancellationToken.None).ConfigureAwait(false);
IReadOnlyCollection diagnosticResults = result?.Output ?? s_emptyDiagnosticResult;
_logger.LogDebug(string.Format("Found {0} violations", diagnosticResults.Count));
@@ -274,11 +282,24 @@ private async Task GetSemanticMarkersFromCommandAsync(PSComm
}
// TODO: Deduplicate this logic and cleanup using lessons learned from pipeline rewrite.
- private Task InvokePowerShellAsync(PSCommand command) => Task.Run(() => InvokePowerShell(command));
+ private async Task InvokePowerShellAsync(PSCommand command, CancellationToken cancellationToken)
+ {
+ try
+ {
+ return await Task.Run(() => InvokePowerShell(
+ command,
+ cancellationToken), cancellationToken).ConfigureAwait(false);
+ }
+ catch (TaskCanceledException)
+ {
+ return await Task.FromCanceled(cancellationToken).ConfigureAwait(false);
+ }
+ }
- private PowerShellResult InvokePowerShell(PSCommand command)
+ private PowerShellResult InvokePowerShell(PSCommand command, CancellationToken cancellationToken = default)
{
using PowerShell pwsh = PowerShell.Create(RunspaceMode.NewRunspace);
+ using CancellationTokenRegistration registration = cancellationToken.Register(() => pwsh.Stop());
pwsh.RunspacePool = _analysisRunspacePool;
pwsh.Commands = command;
PowerShellResult result = null;
diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs
index 5b72f25cd..dd48a5bbc 100644
--- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs
+++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs
@@ -68,7 +68,8 @@ public override async Task Handle(DocumentFormattingParams re
formattedScript = await _analysisService.FormatAsync(
scriptFile.Contents,
pssaSettings,
- null).ConfigureAwait(false);
+ null,
+ cancellationToken).ConfigureAwait(false);
if (formattedScript is null)
{
@@ -154,7 +155,8 @@ public override async Task Handle(DocumentRangeFormattingPara
formattedScript = await _analysisService.FormatAsync(
scriptFile.Contents,
pssaSettings,
- rangeList).ConfigureAwait(false);
+ rangeList,
+ cancellationToken).ConfigureAwait(false);
if (formattedScript is null)
{