Skip to content

Commit 230d3b6

Browse files
committed
Add top-level CancellationToken support to Spectre.Console.Cli
Also raise CA2016 (forward the CancellationToken parameter to methods that take one) to warning Fixes #701
1 parent b551bbd commit 230d3b6

55 files changed

Lines changed: 222 additions & 174 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

resources/scripts/Generator/Commands/ColorGeneratorCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.IO;
2+
using System.Threading;
23
using Generator.Models;
34
using Scriban;
45
using Spectre.Console.Cli;
@@ -21,7 +22,7 @@ public sealed class Settings : GeneratorSettings
2122
public string Input { get; set; }
2223
}
2324

24-
public override int Execute(CommandContext context, Settings settings)
25+
public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken)
2526
{
2627
var templates = new FilePath[]
2728
{

resources/scripts/Generator/Commands/EmojiGeneratorCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using System.Linq;
44
using System.Net.Http;
5+
using System.Threading;
56
using System.Threading.Tasks;
67
using AngleSharp.Html.Parser;
78
using Generator.Models;
@@ -39,7 +40,7 @@ public EmojiGeneratorCommand()
3940
_parser = new HtmlParser();
4041
}
4142

42-
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
43+
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken)
4344
{
4445
var output = new DirectoryPath(settings.Output);
4546
if (!_fileSystem.Directory.Exists(settings.Output))

resources/scripts/Generator/Commands/SampleCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Text;
7+
using System.Threading;
78
using Generator.Commands.Samples;
89
using Spectre.Console;
910
using Spectre.Console.Cli;
@@ -38,7 +39,7 @@ public SampleCommand(IAnsiConsole console)
3839
_console = new AsciiCastConsole(console);
3940
}
4041

41-
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
42+
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings, CancellationToken cancellationToken)
4243
{
4344
var samples = typeof(BaseSample).Assembly
4445
.GetTypes()

resources/scripts/Generator/Commands/SpinnerGeneratorCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.IO;
3+
using System.Threading;
34
using Generator.Models;
45
using Scriban;
56
using Spectre.Console.Cli;
@@ -16,7 +17,7 @@ public SpinnerGeneratorCommand()
1617
_fileSystem = new FileSystem();
1718
}
1819

19-
public override int Execute(CommandContext context, GeneratorSettings settings)
20+
public override int Execute(CommandContext context, GeneratorSettings settings, CancellationToken cancellationToken)
2021
{
2122
// Read the spinner model.
2223
var spinners = new List<Spinner>();

src/.editorconfig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,8 @@ dotnet_diagnostic.RCS1047.severity = none
100100
# RCS1090: Call 'ConfigureAwait(false)'.
101101
dotnet_diagnostic.RCS1090.severity = warning
102102

103-
# The file header is missing or not located at the top of the file
104-
dotnet_diagnostic.SA1633.severity = none
103+
# SA1633: The file header is missing or not located at the top of the file
104+
dotnet_diagnostic.SA1633.severity = none
105+
106+
# CA2016: Forward the CancellationToken parameter to methods that take one
107+
dotnet_diagnostic.CA2016.severity = warning

src/Spectre.Console.Cli/AsyncCommand.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,20 @@ public abstract class AsyncCommand : ICommand<EmptyCommandSettings>
99
/// Executes the command.
1010
/// </summary>
1111
/// <param name="context">The command context.</param>
12+
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
1213
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
13-
public abstract Task<int> ExecuteAsync(CommandContext context);
14+
public abstract Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken);
1415

1516
/// <inheritdoc/>
16-
Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings)
17+
Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
1718
{
18-
return ExecuteAsync(context);
19+
return ExecuteAsync(context, cancellationToken);
1920
}
2021

2122
/// <inheritdoc/>
22-
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
23+
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
2324
{
24-
return ExecuteAsync(context);
25+
return ExecuteAsync(context, cancellationToken);
2526
}
2627

2728
/// <inheritdoc/>

src/Spectre.Console.Cli/AsyncCommandOfT.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ public virtual ValidationResult Validate(CommandContext context, TSettings setti
2323
/// </summary>
2424
/// <param name="context">The command context.</param>
2525
/// <param name="settings">The settings.</param>
26+
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
2627
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
27-
public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings);
28+
public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken);
2829

2930
/// <inheritdoc/>
3031
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
@@ -33,15 +34,15 @@ ValidationResult ICommand.Validate(CommandContext context, CommandSettings setti
3334
}
3435

3536
/// <inheritdoc/>
36-
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
37+
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
3738
{
3839
Debug.Assert(settings is TSettings, "Command settings is of unexpected type.");
39-
return ExecuteAsync(context, (TSettings)settings);
40+
return ExecuteAsync(context, (TSettings)settings, cancellationToken);
4041
}
4142

4243
/// <inheritdoc/>
43-
Task<int> ICommand<TSettings>.Execute(CommandContext context, TSettings settings)
44+
Task<int> ICommand<TSettings>.ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken)
4445
{
45-
return ExecuteAsync(context, settings);
46+
return ExecuteAsync(context, settings, cancellationToken);
4647
}
4748
}

src/Spectre.Console.Cli/Command.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@ public abstract class Command : ICommand<EmptyCommandSettings>
1010
/// Executes the command.
1111
/// </summary>
1212
/// <param name="context">The command context.</param>
13+
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
1314
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
14-
public abstract int Execute(CommandContext context);
15+
public abstract int Execute(CommandContext context, CancellationToken cancellationToken);
1516

1617
/// <inheritdoc/>
17-
Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings)
18+
Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
1819
{
19-
return Task.FromResult(Execute(context));
20+
return Task.FromResult(Execute(context, cancellationToken));
2021
}
2122

2223
/// <inheritdoc/>
23-
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
24+
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
2425
{
25-
return Task.FromResult(Execute(context));
26+
return Task.FromResult(Execute(context, cancellationToken));
2627
}
2728

2829
/// <inheritdoc/>

src/Spectre.Console.Cli/CommandApp.cs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ public CommandApp(ITypeRegistrar? registrar = null)
2626
_executor = new CommandExecutor(registrar);
2727
}
2828

29-
/// <summary>
30-
/// Configures the command line application.
31-
/// </summary>
32-
/// <param name="configuration">The configuration.</param>
29+
/// <inheritdoc/>
3330
public void Configure(Action<IConfigurator> configuration)
3431
{
3532
if (configuration == null)
@@ -51,22 +48,14 @@ public DefaultCommandConfigurator SetDefaultCommand<TCommand>()
5148
return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>());
5249
}
5350

54-
/// <summary>
55-
/// Runs the command line application with specified arguments.
56-
/// </summary>
57-
/// <param name="args">The arguments.</param>
58-
/// <returns>The exit code from the executed command.</returns>
59-
public int Run(IEnumerable<string> args)
51+
/// <inheritdoc/>
52+
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
6053
{
61-
return RunAsync(args).GetAwaiter().GetResult();
54+
return RunAsync(args, cancellationToken).GetAwaiter().GetResult();
6255
}
6356

64-
/// <summary>
65-
/// Runs the command line application with specified arguments.
66-
/// </summary>
67-
/// <param name="args">The arguments.</param>
68-
/// <returns>The exit code from the executed command.</returns>
69-
public async Task<int> RunAsync(IEnumerable<string> args)
57+
/// <inheritdoc/>
58+
public async Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
7059
{
7160
try
7261
{
@@ -86,7 +75,7 @@ public async Task<int> RunAsync(IEnumerable<string> args)
8675
}
8776

8877
return await _executor
89-
.Execute(_configurator, args)
78+
.ExecuteAsync(_configurator, args, cancellationToken)
9079
.ConfigureAwait(false);
9180
}
9281
catch (Exception ex)
@@ -109,6 +98,11 @@ public async Task<int> RunAsync(IEnumerable<string> args)
10998
return _configurator.Settings.ExceptionHandler(ex, null);
11099
}
111100

101+
if (ex is OperationCanceledException)
102+
{
103+
return _configurator.Settings.CancellationExitCode;
104+
}
105+
112106
// Render the exception.
113107
var pretty = GetRenderableErrorMessage(ex);
114108
if (pretty != null)

src/Spectre.Console.Cli/CommandAppOfT.cs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,22 @@ public CommandApp(ITypeRegistrar? registrar = null)
2525
_defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>();
2626
}
2727

28-
/// <summary>
29-
/// Configures the command line application.
30-
/// </summary>
31-
/// <param name="configuration">The configuration.</param>
28+
/// <inheritdoc/>
3229
public void Configure(Action<IConfigurator> configuration)
3330
{
3431
_app.Configure(configuration);
3532
}
3633

37-
/// <summary>
38-
/// Runs the command line application with specified arguments.
39-
/// </summary>
40-
/// <param name="args">The arguments.</param>
41-
/// <returns>The exit code from the executed command.</returns>
42-
public int Run(IEnumerable<string> args)
34+
/// <inheritdoc/>
35+
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
4336
{
44-
return _app.Run(args);
37+
return _app.Run(args, cancellationToken);
4538
}
4639

47-
/// <summary>
48-
/// Runs the command line application with specified arguments.
49-
/// </summary>
50-
/// <param name="args">The arguments.</param>
51-
/// <returns>The exit code from the executed command.</returns>
52-
public Task<int> RunAsync(IEnumerable<string> args)
40+
/// <inheritdoc/>
41+
public Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
5342
{
54-
return _app.RunAsync(args);
43+
return _app.RunAsync(args, cancellationToken);
5544
}
5645

5746
internal Configurator GetConfigurator()

0 commit comments

Comments
 (0)