mirror of
				https://github.com/spectreconsole/spectre.console.git
				synced 2025-10-25 15:19:23 +00:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			0.51.1
			...
			c5e2409b38
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c5e2409b38 | ||
|  | 749f0fded8 | ||
|  | f5f61ca610 | ||
|  | d90e94dbb3 | ||
|  | 169abca986 | ||
|  | 3c2156268c | ||
|  | 6fb81103f0 | ||
|  | 880e83b27c | ||
|  | 0b270e1ccd | ||
|  | 2d9e8069fd | ||
|  | b551bbd244 | ||
|  | 3a70fbec75 | 
							
								
								
									
										4
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -18,12 +18,12 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|  |  | ||||||
|       - name: Setup .NET SDK |       - name: Setup .NET SDK | ||||||
|         uses: actions/setup-dotnet@v4 |         uses: actions/setup-dotnet@v5 | ||||||
|         with: |         with: | ||||||
|           dotnet-version: | |           dotnet-version: | | ||||||
|             8.0.x |             8.0.x | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								.github/workflows/publish.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/publish.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -24,12 +24,12 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|  |  | ||||||
|       - name: Setup .NET SDK |       - name: Setup .NET SDK | ||||||
|         uses: actions/setup-dotnet@v4 |         uses: actions/setup-dotnet@v5 | ||||||
|         with: |         with: | ||||||
|           dotnet-version: | |           dotnet-version: | | ||||||
|             8.0.x |             8.0.x | ||||||
| @@ -53,17 +53,17 @@ jobs: | |||||||
|     runs-on: windows-latest |     runs-on: windows-latest | ||||||
|     steps: |     steps: | ||||||
|     - name: Checkout |     - name: Checkout | ||||||
|       uses: actions/checkout@v4 |       uses: actions/checkout@v5 | ||||||
|       with: |       with: | ||||||
|         fetch-depth: 0 |         fetch-depth: 0 | ||||||
|  |  | ||||||
|     - name: Setup .NET SDK |     - name: Setup .NET SDK | ||||||
|       uses: actions/setup-dotnet@v4 |       uses: actions/setup-dotnet@v5 | ||||||
|  |  | ||||||
|     - name: Setup Node.js |     - name: Setup Node.js | ||||||
|       uses: actions/setup-node@v4 |       uses: actions/setup-node@v6 | ||||||
|       with: |       with: | ||||||
|         node-version: '16' |         node-version: '22' | ||||||
|  |  | ||||||
|     - name: Cache dependencies |     - name: Cache dependencies | ||||||
|       uses: actions/cache@v4 |       uses: actions/cache@v4 | ||||||
|   | |||||||
| @@ -38,8 +38,8 @@ | |||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="Microsoft.Playwright" Version="1.52.0" /> |     <PackageReference Include="Microsoft.Playwright" Version="1.55.0" /> | ||||||
|     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |     <PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> | ||||||
|     <PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.72" /> |     <PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.72" /> | ||||||
|     <PackageReference Include="Statiq.Common" Version="1.0.0-beta.72" /> |     <PackageReference Include="Statiq.Common" Version="1.0.0-beta.72" /> | ||||||
|     <PackageReference Include="Statiq.Web" Version="1.0.0-beta.60" /> |     <PackageReference Include="Statiq.Web" Version="1.0.0-beta.60" /> | ||||||
|   | |||||||
| @@ -1,25 +0,0 @@ | |||||||
|  |  | ||||||
| Microsoft Visual Studio Solution File, Format Version 12.00 |  | ||||||
| # Visual Studio Version 16 |  | ||||||
| VisualStudioVersion = 16.0.30011.22 |  | ||||||
| MinimumVisualStudioVersion = 10.0.40219.1 |  | ||||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docs", "Docs.csproj", "{C337F609-A890-4E52-BDA3-91658039B0E3}" |  | ||||||
| EndProject |  | ||||||
| Global |  | ||||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution |  | ||||||
| 		Debug|Any CPU = Debug|Any CPU |  | ||||||
| 		Release|Any CPU = Release|Any CPU |  | ||||||
| 	EndGlobalSection |  | ||||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution |  | ||||||
| 		{C337F609-A890-4E52-BDA3-91658039B0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |  | ||||||
| 		{C337F609-A890-4E52-BDA3-91658039B0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU |  | ||||||
| 		{C337F609-A890-4E52-BDA3-91658039B0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU |  | ||||||
| 		{C337F609-A890-4E52-BDA3-91658039B0E3}.Release|Any CPU.Build.0 = Release|Any CPU |  | ||||||
| 	EndGlobalSection |  | ||||||
| 	GlobalSection(SolutionProperties) = preSolution |  | ||||||
| 		HideSolutionNode = FALSE |  | ||||||
| 	EndGlobalSection |  | ||||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution |  | ||||||
| 		SolutionGuid = {2FB3922B-494A-45EB-A479-FC507B8E107C} |  | ||||||
| 	EndGlobalSection |  | ||||||
| EndGlobal |  | ||||||
							
								
								
									
										3
									
								
								docs/Docs.slnx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/Docs.slnx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | <Solution> | ||||||
|  |   <Project Path="Docs.csproj" /> | ||||||
|  | </Solution> | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| { |  | ||||||
|   "sdk": { |  | ||||||
|     "version": "9.0.202", |  | ||||||
|     "rollForward": "latestFeature" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,11 +1,13 @@ | |||||||
| Title: Spectre.Console 0.51 released! | Title: Spectre.Console 0.51.1 released! | ||||||
| Description: Not a substitute for human interaction. | Description: Not a substitute for human interaction. | ||||||
| Published: 2025-09-07 | Published: 2025-09-07 | ||||||
| Category: Release Notes | Category: Release Notes | ||||||
| Excluded: false | Excluded: false | ||||||
| --- | --- | ||||||
|  |  | ||||||
| Version 0.51 of Spectre.Console has been released! | Version `0.51.1` of Spectre.Console has been released! | ||||||
|  |    | ||||||
|  | _Note: Due to an issue discovered after the release of version 0.51.0, that version has now been unlisted. Let’s all pretend it never existed 😅_ | ||||||
|  |  | ||||||
| ## What's Changed | ## What's Changed | ||||||
|  |  | ||||||
| @@ -38,4 +40,4 @@ Version 0.51 of Spectre.Console has been released! | |||||||
| * [@mitchdenny](https://github.com/mitchdenny) made their first contribution in [#1881](https://github.com/spectreconsole/spectre.console/pull/1881) | * [@mitchdenny](https://github.com/mitchdenny) made their first contribution in [#1881](https://github.com/spectreconsole/spectre.console/pull/1881) | ||||||
| * [@mattfennerom](https://github.com/mattfennerom) made their first contribution in [#1890](https://github.com/spectreconsole/spectre.console/pull/1890) | * [@mattfennerom](https://github.com/mattfennerom) made their first contribution in [#1890](https://github.com/spectreconsole/spectre.console/pull/1890) | ||||||
|  |  | ||||||
| **Full Changelog**: [0.50.0...0.51.0](https://github.com/spectreconsole/spectre.console/compare/0.50.0...0.51.0) | **Full Changelog**: [0.50.0...0.51.0](https://github.com/spectreconsole/spectre.console/compare/0.50.0...0.51.1) | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | Title: Spectre.Console 0.52.0 released! | ||||||
|  | Description: Don't eat (too much) glue. | ||||||
|  | Published: 2025-10-10 | ||||||
|  | Category: Release Notes | ||||||
|  | Excluded: false | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | Version `0.52.0` of Spectre.Console has been released! | ||||||
|  |  | ||||||
|  | Exciting things are happening. We’ve merged support for my love child, OpenCli, in this release. That means you can now pass the parameter `--help-dump-opencli` to your application to get an [OpenCli](https://opencli.org) description dumped to stdout. | ||||||
|  |  | ||||||
|  | ## What's Changed | ||||||
|  |  | ||||||
|  | * Add OpenCLI integration to Spectre.Console.Cli by [@patriksvensson](https://github.com/patriksvensson) in [#1909](https://github.com/spectreconsole/spectre.console/pull/1909) | ||||||
|  | * Fix OPENCLI_VISIBILITY_INTERNAL to DefineConstants concat by [@devlead](https://github.com/devlead) in [#1912](https://github.com/spectreconsole/spectre.console/pull/1912) | ||||||
|  |  | ||||||
|  | **Full Changelog**: https://github.com/spectreconsole/spectre.console/compare/0.51.1...0.52.0 | ||||||
							
								
								
									
										22
									
								
								docs/input/cli/opencli.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								docs/input/cli/opencli.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Title: OpenCLI Integration | ||||||
|  | Order: 15 | ||||||
|  | Description: OpenCLI integration | ||||||
|  | Highlights: | ||||||
|  |  - Generate OpenCLI descriptions | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | From version `0.52.0` and above, you will be able to generate [OpenCLI](https://opencli.org) | ||||||
|  | descriptions from your `Spectre.Console.Cli` applications. | ||||||
|  |  | ||||||
|  | Simply add the `--help-dump-opencli` option to your application, and an  | ||||||
|  | OpenCLI description will be written to stdout. | ||||||
|  |  | ||||||
|  | ```shell | ||||||
|  | $ ./myapp --help-dump-opencli | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | If you want to save it to disk, pipe it to a file. | ||||||
|  |  | ||||||
|  | ```shell | ||||||
|  | $ ./myapp --help-dump-opencli > myapp.openapi.json | ||||||
|  | ``` | ||||||
| @@ -3,7 +3,7 @@ | |||||||
|   "isRoot": true, |   "isRoot": true, | ||||||
|   "tools": { |   "tools": { | ||||||
|     "cake.tool": { |     "cake.tool": { | ||||||
|       "version": "5.0.0", |       "version": "5.1.0", | ||||||
|       "commands": [ |       "commands": [ | ||||||
|         "dotnet-cake" |         "dotnet-cake" | ||||||
|       ] |       ] | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "$schema": "http://json.schemastore.org/global", |   "$schema": "http://json.schemastore.org/global", | ||||||
|   "sdk": { |   "sdk": { | ||||||
|     "version": "9.0.202", |     "version": "9.0.305", | ||||||
|     "rollForward": "latestFeature" |     "rollForward": "latestFeature" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System.IO; | using System.IO; | ||||||
|  | using System.Threading; | ||||||
| using Generator.Models; | using Generator.Models; | ||||||
| using Scriban; | using Scriban; | ||||||
| using Spectre.Console.Cli; | using Spectre.Console.Cli; | ||||||
| @@ -21,7 +22,7 @@ namespace Generator.Commands | |||||||
|             public string Input { get; set; } |             public string Input { get; set; } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override int Execute(CommandContext context, Settings settings) |         public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var templates = new FilePath[] |             var templates = new FilePath[] | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ using System.Collections.Generic; | |||||||
| using System.IO; | using System.IO; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
|  | using System.Threading; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using AngleSharp.Html.Parser; | using AngleSharp.Html.Parser; | ||||||
| using Generator.Models; | using Generator.Models; | ||||||
| @@ -39,7 +40,7 @@ namespace Generator.Commands | |||||||
|             _parser = new HtmlParser(); |             _parser = new HtmlParser(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override async Task<int> ExecuteAsync(CommandContext context, Settings settings) |         public override async Task<int> ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var output = new DirectoryPath(settings.Output); |             var output = new DirectoryPath(settings.Output); | ||||||
|             if (!_fileSystem.Directory.Exists(settings.Output)) |             if (!_fileSystem.Directory.Exists(settings.Output)) | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; | |||||||
| using System.IO; | using System.IO; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Text; | using System.Text; | ||||||
|  | using System.Threading; | ||||||
| using Generator.Commands.Samples; | using Generator.Commands.Samples; | ||||||
| using Spectre.Console; | using Spectre.Console; | ||||||
| using Spectre.Console.Cli; | using Spectre.Console.Cli; | ||||||
| @@ -38,7 +39,7 @@ namespace Generator.Commands | |||||||
|             _console = new AsciiCastConsole(console); |             _console = new AsciiCastConsole(console); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings) |         public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var samples = typeof(BaseSample).Assembly |             var samples = typeof(BaseSample).Assembly | ||||||
|                 .GetTypes() |                 .GetTypes() | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.IO; | using System.IO; | ||||||
|  | using System.Threading; | ||||||
| using Generator.Models; | using Generator.Models; | ||||||
| using Scriban; | using Scriban; | ||||||
| using Spectre.Console.Cli; | using Spectre.Console.Cli; | ||||||
| @@ -16,7 +17,7 @@ namespace Generator.Commands | |||||||
|             _fileSystem = new FileSystem(); |             _fileSystem = new FileSystem(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override int Execute(CommandContext context, GeneratorSettings settings) |         public override int Execute(CommandContext context, GeneratorSettings settings, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             // Read the spinner model. |             // Read the spinner model. | ||||||
|             var spinners = new List<Spinner>(); |             var spinners = new List<Spinner>(); | ||||||
|   | |||||||
| @@ -45,9 +45,9 @@ | |||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="AngleSharp" Version="1.3.0" /> |     <PackageReference Include="AngleSharp" Version="1.3.0" /> | ||||||
|     <PackageReference Include="Humanizer.Core" Version="2.14.1" /> |     <PackageReference Include="Humanizer.Core" Version="2.14.1" /> | ||||||
|     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> |     <PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> | ||||||
|     <PackageReference Include="Scriban" Version="6.2.1" /> |     <PackageReference Include="Scriban" Version="6.4.0" /> | ||||||
|     <PackageReference Include="Spectre.IO" Version="0.18.0" /> |     <PackageReference Include="Spectre.IO" Version="0.20.0" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|   | |||||||
| @@ -100,5 +100,8 @@ dotnet_diagnostic.RCS1047.severity = none | |||||||
| # RCS1090: Call 'ConfigureAwait(false)'. | # RCS1090: Call 'ConfigureAwait(false)'. | ||||||
| dotnet_diagnostic.RCS1090.severity = warning | dotnet_diagnostic.RCS1090.severity = warning | ||||||
|  |  | ||||||
| # The file header is missing or not located at the top of the file | # SA1633: The file header is missing or not located at the top of the file | ||||||
| dotnet_diagnostic.SA1633.severity = none | dotnet_diagnostic.SA1633.severity = none | ||||||
|  |  | ||||||
|  | # CA2016: Forward the CancellationToken parameter to methods that take one | ||||||
|  | dotnet_diagnostic.CA2016.severity = warning | ||||||
| @@ -4,22 +4,23 @@ | |||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageVersion Include="IsExternalInit" Version="1.0.3" /> |     <PackageVersion Include="IsExternalInit" Version="1.0.3" /> | ||||||
|     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" /> |     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" /> | ||||||
|     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> |     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" /> | ||||||
|     <PackageVersion Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0" /> |     <PackageVersion Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0" /> | ||||||
|     <PackageVersion Include="MinVer" PrivateAssets="All" Version="6.0.0" /> |     <PackageVersion Include="MinVer" PrivateAssets="All" Version="6.0.0" /> | ||||||
|  |     <PackageVersion Include="OpenCli.Sources" Version="0.5.0" /> | ||||||
|     <PackageVersion Include="PolySharp" Version="1.15.0" /> |     <PackageVersion Include="PolySharp" Version="1.15.0" /> | ||||||
|     <PackageVersion Include="Roslynator.Analyzers" PrivateAssets="All" Version="4.14.0" /> |     <PackageVersion Include="Roslynator.Analyzers" PrivateAssets="All" Version="4.14.1" /> | ||||||
|     <PackageVersion Include="Shouldly" Version="4.3.0" /> |     <PackageVersion Include="Shouldly" Version="4.3.0" /> | ||||||
|     <PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" /> |     <PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" /> | ||||||
|     <PackageVersion Include="Spectre.Verify.Extensions" Version="28.16.0" /> |     <PackageVersion Include="Spectre.Verify.Extensions" Version="28.16.0" /> | ||||||
|     <PackageVersion Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.2.0-beta.556" /> |     <PackageVersion Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.2.0-beta.556" /> | ||||||
|     <PackageVersion Include="System.Memory" Version="4.6.3" /> |     <PackageVersion Include="System.Memory" Version="4.6.3" /> | ||||||
|     <PackageVersion Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" /> |     <PackageVersion Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" /> | ||||||
|     <PackageVersion Include="Verify.Xunit" Version="30.5.0" /> |     <PackageVersion Include="Verify.Xunit" Version="31.0.1" /> | ||||||
|     <PackageVersion Include="Wcwidth.Sources" Version="2.0.0" /> |     <PackageVersion Include="Wcwidth.Sources" Version="3.0.0" /> | ||||||
|     <PackageVersion Include="xunit" Version="2.9.3" /> |     <PackageVersion Include="xunit" Version="2.9.3" /> | ||||||
|     <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3"> |     <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5"> | ||||||
|       <PrivateAssets>all</PrivateAssets> |       <PrivateAssets>all</PrivateAssets> | ||||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||||
|     </PackageVersion> |     </PackageVersion> | ||||||
|   | |||||||
| @@ -9,19 +9,20 @@ public abstract class AsyncCommand : ICommand<EmptyCommandSettings> | |||||||
|     /// Executes the command. |     /// Executes the command. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>An integer indicating whether or not the command executed successfully.</returns> |     /// <returns>An integer indicating whether or not the command executed successfully.</returns> | ||||||
|     public abstract Task<int> ExecuteAsync(CommandContext context); |     public abstract Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings) |     Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return ExecuteAsync(context); |         return ExecuteAsync(context, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand.Execute(CommandContext context, CommandSettings settings) |     Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return ExecuteAsync(context); |         return ExecuteAsync(context, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|   | |||||||
| @@ -23,8 +23,9 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings> | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|     /// <param name="settings">The settings.</param> |     /// <param name="settings">The settings.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>An integer indicating whether or not the command executed successfully.</returns> |     /// <returns>An integer indicating whether or not the command executed successfully.</returns> | ||||||
|     public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings); |     public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings) |     ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings) | ||||||
| @@ -33,15 +34,15 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand.Execute(CommandContext context, CommandSettings settings) |     Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         Debug.Assert(settings is TSettings, "Command settings is of unexpected type."); |         Debug.Assert(settings is TSettings, "Command settings is of unexpected type."); | ||||||
|         return ExecuteAsync(context, (TSettings)settings); |         return ExecuteAsync(context, (TSettings)settings, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand<TSettings>.Execute(CommandContext context, TSettings settings) |     Task<int> ICommand<TSettings>.ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return ExecuteAsync(context, settings); |         return ExecuteAsync(context, settings, cancellationToken); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -10,19 +10,20 @@ public abstract class Command : ICommand<EmptyCommandSettings> | |||||||
|     /// Executes the command. |     /// Executes the command. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>An integer indicating whether or not the command executed successfully.</returns> |     /// <returns>An integer indicating whether or not the command executed successfully.</returns> | ||||||
|     public abstract int Execute(CommandContext context); |     public abstract int Execute(CommandContext context, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings) |     Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return Task.FromResult(Execute(context)); |         return Task.FromResult(Execute(context, cancellationToken)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand.Execute(CommandContext context, CommandSettings settings) |     Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return Task.FromResult(Execute(context)); |         return Task.FromResult(Execute(context, cancellationToken)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|   | |||||||
| @@ -26,10 +26,7 @@ public sealed class CommandApp : ICommandApp | |||||||
|         _executor = new CommandExecutor(registrar); |         _executor = new CommandExecutor(registrar); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Configures the command line application. |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="configuration">The configuration.</param> |  | ||||||
|     public void Configure(Action<IConfigurator> configuration) |     public void Configure(Action<IConfigurator> configuration) | ||||||
|     { |     { | ||||||
|         if (configuration == null) |         if (configuration == null) | ||||||
| @@ -51,22 +48,14 @@ public sealed class CommandApp : ICommandApp | |||||||
|         return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>()); |         return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Runs the command line application with specified arguments. |     public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default) | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="args">The arguments.</param> |  | ||||||
|     /// <returns>The exit code from the executed command.</returns> |  | ||||||
|     public int Run(IEnumerable<string> args) |  | ||||||
|     { |     { | ||||||
|         return RunAsync(args).GetAwaiter().GetResult(); |         return RunAsync(args, cancellationToken).GetAwaiter().GetResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Runs the command line application with specified arguments. |     public async Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default) | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="args">The arguments.</param> |  | ||||||
|     /// <returns>The exit code from the executed command.</returns> |  | ||||||
|     public async Task<int> RunAsync(IEnumerable<string> args) |  | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
| @@ -79,13 +68,14 @@ public sealed class CommandApp : ICommandApp | |||||||
|                     cli.AddCommand<VersionCommand>(CliConstants.Commands.Version); |                     cli.AddCommand<VersionCommand>(CliConstants.Commands.Version); | ||||||
|                     cli.AddCommand<XmlDocCommand>(CliConstants.Commands.XmlDoc); |                     cli.AddCommand<XmlDocCommand>(CliConstants.Commands.XmlDoc); | ||||||
|                     cli.AddCommand<ExplainCommand>(CliConstants.Commands.Explain); |                     cli.AddCommand<ExplainCommand>(CliConstants.Commands.Explain); | ||||||
|  |                     cli.AddCommand<OpenCliGeneratorCommand>(CliConstants.Commands.OpenCli); | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|                 _executed = true; |                 _executed = true; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return await _executor |             return await _executor | ||||||
|                 .Execute(_configurator, args) |                 .ExecuteAsync(_configurator, args, cancellationToken) | ||||||
|                 .ConfigureAwait(false); |                 .ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
| @@ -108,6 +98,11 @@ public sealed class CommandApp : ICommandApp | |||||||
|                 return _configurator.Settings.ExceptionHandler(ex, null); |                 return _configurator.Settings.ExceptionHandler(ex, null); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             if (ex is OperationCanceledException) | ||||||
|  |             { | ||||||
|  |                 return _configurator.Settings.CancellationExitCode; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             // Render the exception. |             // Render the exception. | ||||||
|             var pretty = GetRenderableErrorMessage(ex); |             var pretty = GetRenderableErrorMessage(ex); | ||||||
|             if (pretty != null) |             if (pretty != null) | ||||||
|   | |||||||
| @@ -25,33 +25,22 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp | |||||||
|         _defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>(); |         _defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Configures the command line application. |  | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="configuration">The configuration.</param> |  | ||||||
|     public void Configure(Action<IConfigurator> configuration) |     public void Configure(Action<IConfigurator> configuration) | ||||||
|     { |     { | ||||||
|         _app.Configure(configuration); |         _app.Configure(configuration); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Runs the command line application with specified arguments. |     public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default) | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="args">The arguments.</param> |  | ||||||
|     /// <returns>The exit code from the executed command.</returns> |  | ||||||
|     public int Run(IEnumerable<string> args) |  | ||||||
|     { |     { | ||||||
|         return _app.Run(args); |         return _app.Run(args, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <inheritdoc/> | ||||||
|     /// Runs the command line application with specified arguments. |     public Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default) | ||||||
|     /// </summary> |  | ||||||
|     /// <param name="args">The arguments.</param> |  | ||||||
|     /// <returns>The exit code from the executed command.</returns> |  | ||||||
|     public Task<int> RunAsync(IEnumerable<string> args) |  | ||||||
|     { |     { | ||||||
|         return _app.RunAsync(args); |         return _app.RunAsync(args, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     internal Configurator GetConfigurator() |     internal Configurator GetConfigurator() | ||||||
|   | |||||||
| @@ -24,8 +24,9 @@ public abstract class Command<TSettings> : ICommand<TSettings> | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|     /// <param name="settings">The settings.</param> |     /// <param name="settings">The settings.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>An integer indicating whether or not the command executed successfully.</returns> |     /// <returns>An integer indicating whether or not the command executed successfully.</returns> | ||||||
|     public abstract int Execute(CommandContext context, TSettings settings); |     public abstract int Execute(CommandContext context, TSettings settings, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings) |     ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings) | ||||||
| @@ -34,15 +35,15 @@ public abstract class Command<TSettings> : ICommand<TSettings> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand.Execute(CommandContext context, CommandSettings settings) |     Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         Debug.Assert(settings is TSettings, "Command settings is of unexpected type."); |         Debug.Assert(settings is TSettings, "Command settings is of unexpected type."); | ||||||
|         return Task.FromResult(Execute(context, (TSettings)settings)); |         return Task.FromResult(Execute(context, (TSettings)settings, cancellationToken)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <inheritdoc/> |     /// <inheritdoc/> | ||||||
|     Task<int> ICommand<TSettings>.Execute(CommandContext context, TSettings settings) |     Task<int> ICommand<TSettings>.ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return Task.FromResult(Execute(context, settings)); |         return Task.FromResult(Execute(context, settings, cancellationToken)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -201,6 +201,24 @@ public static class ConfiguratorExtensions | |||||||
|         return configurator; |         return configurator; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Tells the command line application to return the specified exit code when it's aborted through the <see cref="CancellationToken"/>. | ||||||
|  |     /// The default cancellation exit code is 130. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="configurator">The configurator.</param> | ||||||
|  |     /// <param name="exitCode">The exit code to return in case of cancellation.</param> | ||||||
|  |     /// <returns>A configurator that can be used to configure the application further.</returns> | ||||||
|  |     public static IConfigurator CancellationExitCode(this IConfigurator configurator, int exitCode) | ||||||
|  |     { | ||||||
|  |         if (configurator == null) | ||||||
|  |         { | ||||||
|  |             throw new ArgumentNullException(nameof(configurator)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         configurator.Settings.CancellationExitCode = exitCode; | ||||||
|  |         return configurator; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Configures case sensitivity. |     /// Configures case sensitivity. | ||||||
|     /// </summary> |     /// </summary> | ||||||
| @@ -304,14 +322,14 @@ public static class ConfiguratorExtensions | |||||||
|     public static ICommandConfigurator AddDelegate( |     public static ICommandConfigurator AddDelegate( | ||||||
|         this IConfigurator configurator, |         this IConfigurator configurator, | ||||||
|         string name, |         string name, | ||||||
|         Func<CommandContext, int> func) |         Func<CommandContext, CancellationToken, int> func) | ||||||
|     { |     { | ||||||
|         if (configurator == null) |         if (configurator == null) | ||||||
|         { |         { | ||||||
|             throw new ArgumentNullException(nameof(configurator)); |             throw new ArgumentNullException(nameof(configurator)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c)); |         return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -324,14 +342,14 @@ public static class ConfiguratorExtensions | |||||||
|     public static ICommandConfigurator AddAsyncDelegate( |     public static ICommandConfigurator AddAsyncDelegate( | ||||||
|         this IConfigurator configurator, |         this IConfigurator configurator, | ||||||
|         string name, |         string name, | ||||||
|         Func<CommandContext, Task<int>> func) |         Func<CommandContext, CancellationToken, Task<int>> func) | ||||||
|     { |     { | ||||||
|         if (configurator == null) |         if (configurator == null) | ||||||
|         { |         { | ||||||
|             throw new ArgumentNullException(nameof(configurator)); |             throw new ArgumentNullException(nameof(configurator)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _) => func(c)); |         return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -345,7 +363,7 @@ public static class ConfiguratorExtensions | |||||||
|     public static ICommandConfigurator AddDelegate<TSettings>( |     public static ICommandConfigurator AddDelegate<TSettings>( | ||||||
|         this IConfigurator<TSettings>? configurator, |         this IConfigurator<TSettings>? configurator, | ||||||
|         string name, |         string name, | ||||||
|         Func<CommandContext, int> func) |         Func<CommandContext, CancellationToken, int> func) | ||||||
|         where TSettings : CommandSettings |         where TSettings : CommandSettings | ||||||
|     { |     { | ||||||
|         if (typeof(TSettings).IsAbstract) |         if (typeof(TSettings).IsAbstract) | ||||||
| @@ -358,7 +376,7 @@ public static class ConfiguratorExtensions | |||||||
|             throw new ArgumentNullException(nameof(configurator)); |             throw new ArgumentNullException(nameof(configurator)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return configurator.AddDelegate<TSettings>(name, (c, _) => func(c)); |         return configurator.AddDelegate<TSettings>(name, (c, _, ct) => func(c, ct)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -372,7 +390,7 @@ public static class ConfiguratorExtensions | |||||||
|     public static ICommandConfigurator AddAsyncDelegate<TSettings>( |     public static ICommandConfigurator AddAsyncDelegate<TSettings>( | ||||||
|         this IConfigurator<TSettings> configurator, |         this IConfigurator<TSettings> configurator, | ||||||
|         string name, |         string name, | ||||||
|         Func<CommandContext, Task<int>> func) |         Func<CommandContext, CancellationToken, Task<int>> func) | ||||||
|         where TSettings : CommandSettings |         where TSettings : CommandSettings | ||||||
|     { |     { | ||||||
|         if (configurator == null) |         if (configurator == null) | ||||||
| @@ -380,7 +398,7 @@ public static class ConfiguratorExtensions | |||||||
|             throw new ArgumentNullException(nameof(configurator)); |             throw new ArgumentNullException(nameof(configurator)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return configurator.AddAsyncDelegate<TSettings>(name, (c, _) => func(c)); |         return configurator.AddAsyncDelegate<TSettings>(name, (c, _, ct) => func(c, ct)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ public interface ICommand | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|     /// <param name="settings">The settings.</param> |     /// <param name="settings">The settings.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>The validation result.</returns> |     /// <returns>The validation result.</returns> | ||||||
|     Task<int> Execute(CommandContext context, CommandSettings settings); |     Task<int> ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken); | ||||||
| } | } | ||||||
| @@ -15,13 +15,15 @@ public interface ICommandApp | |||||||
|     /// Runs the command line application with specified arguments. |     /// Runs the command line application with specified arguments. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="args">The arguments.</param> |     /// <param name="args">The arguments.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the application.</param> | ||||||
|     /// <returns>The exit code from the executed command.</returns> |     /// <returns>The exit code from the executed command.</returns> | ||||||
|     int Run(IEnumerable<string> args); |     int Run(IEnumerable<string> args, CancellationToken cancellationToken = default); | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Runs the command line application with specified arguments. |     /// Runs the command line application with specified arguments. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="args">The arguments.</param> |     /// <param name="args">The arguments.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the application.</param> | ||||||
|     /// <returns>The exit code from the executed command.</returns> |     /// <returns>The exit code from the executed command.</returns> | ||||||
|     Task<int> RunAsync(IEnumerable<string> args); |     Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default); | ||||||
| } | } | ||||||
| @@ -88,6 +88,12 @@ public interface ICommandAppSettings | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     bool PropagateExceptions { get; set; } |     bool PropagateExceptions { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the value used as the application exit code when it's aborted through the <see cref="CancellationToken"/>. | ||||||
|  |     /// The default cancellation exit code is 130. | ||||||
|  |     /// </summary> | ||||||
|  |     int CancellationExitCode { get; set; } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Gets or sets a value indicating whether or not examples should be validated. |     /// Gets or sets a value indicating whether or not examples should be validated. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ public interface ICommand<TSettings> : ICommandLimiter<TSettings> | |||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="context">The command context.</param> |     /// <param name="context">The command context.</param> | ||||||
|     /// <param name="settings">The settings.</param> |     /// <param name="settings">The settings.</param> | ||||||
|  |     /// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param> | ||||||
|     /// <returns>An integer indicating whether or not the command executed successfully.</returns> |     /// <returns>An integer indicating whether or not the command executed successfully.</returns> | ||||||
|     Task<int> Execute(CommandContext context, TSettings settings); |     Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken); | ||||||
| } | } | ||||||
| @@ -48,7 +48,7 @@ public interface IConfigurator | |||||||
|     /// <param name="name">The name of the command.</param> |     /// <param name="name">The name of the command.</param> | ||||||
|     /// <param name="func">The delegate to execute as part of command execution.</param> |     /// <param name="func">The delegate to execute as part of command execution.</param> | ||||||
|     /// <returns>A command configurator that can be used to configure the command further.</returns> |     /// <returns>A command configurator that can be used to configure the command further.</returns> | ||||||
|     ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func) |     ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, int> func) | ||||||
|         where TSettings : CommandSettings; |         where TSettings : CommandSettings; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -58,7 +58,7 @@ public interface IConfigurator | |||||||
|     /// <param name="name">The name of the command.</param> |     /// <param name="name">The name of the command.</param> | ||||||
|     /// <param name="func">The delegate to execute as part of command execution.</param> |     /// <param name="func">The delegate to execute as part of command execution.</param> | ||||||
|     /// <returns>A command configurator that can be used to configure the command further.</returns> |     /// <returns>A command configurator that can be used to configure the command further.</returns> | ||||||
|     ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func) |     ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, Task<int>> func) | ||||||
|         where TSettings : CommandSettings; |         where TSettings : CommandSettings; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ public interface IConfigurator<in TSettings> | |||||||
|     /// <param name="name">The name of the command.</param> |     /// <param name="name">The name of the command.</param> | ||||||
|     /// <param name="func">The delegate to execute as part of command execution.</param> |     /// <param name="func">The delegate to execute as part of command execution.</param> | ||||||
|     /// <returns>A command configurator that can be used to configure the command further.</returns> |     /// <returns>A command configurator that can be used to configure the command further.</returns> | ||||||
|     ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func) |     ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, int> func) | ||||||
|         where TDerivedSettings : TSettings; |         where TDerivedSettings : TSettings; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
| @@ -64,7 +64,7 @@ public interface IConfigurator<in TSettings> | |||||||
|     /// <param name="name">The name of the command.</param> |     /// <param name="name">The name of the command.</param> | ||||||
|     /// <param name="func">The delegate to execute as part of command execution.</param> |     /// <param name="func">The delegate to execute as part of command execution.</param> | ||||||
|     /// <returns>A command configurator that can be used to configure the command further.</returns> |     /// <returns>A command configurator that can be used to configure the command further.</returns> | ||||||
|     ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func) |     ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, Task<int>> func) | ||||||
|         where TDerivedSettings : TSettings; |         where TDerivedSettings : TSettings; | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ internal sealed class CommandExecutor | |||||||
|         _registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor)); |         _registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async Task<int> Execute(IConfiguration configuration, IEnumerable<string> args) |     public async Task<int> ExecuteAsync(IConfiguration configuration, IEnumerable<string> args, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         CommandTreeParserResult parsedResult; |         CommandTreeParserResult parsedResult; | ||||||
|  |  | ||||||
| @@ -68,6 +68,13 @@ internal sealed class CommandExecutor | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // OpenCLI? | ||||||
|  |             if (firstArgument.Equals(CliConstants.DumpHelpOpenCliOption, StringComparison.OrdinalIgnoreCase)) | ||||||
|  |             { | ||||||
|  |                 // Replace all arguments with the opencligen command | ||||||
|  |                 arguments = ["cli", "opencli"]; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Parse and map the model against the arguments. |         // Parse and map the model against the arguments. | ||||||
| @@ -118,7 +125,7 @@ internal sealed class CommandExecutor | |||||||
|                 leaf.Command.Data); |                 leaf.Command.Data); | ||||||
|  |  | ||||||
|             // Execute the command tree. |             // Execute the command tree. | ||||||
|             return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false); |             return await ExecuteAsync(leaf, parsedResult.Tree, context, resolver, configuration, cancellationToken).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -215,12 +222,13 @@ internal sealed class CommandExecutor | |||||||
|         return (parsedResult, tokenizerResult); |         return (parsedResult, tokenizerResult); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static async Task<int> Execute( |     private static async Task<int> ExecuteAsync( | ||||||
|         CommandTree leaf, |         CommandTree leaf, | ||||||
|         CommandTree tree, |         CommandTree tree, | ||||||
|         CommandContext context, |         CommandContext context, | ||||||
|         ITypeResolver resolver, |         ITypeResolver resolver, | ||||||
|         IConfiguration configuration) |         IConfiguration configuration, | ||||||
|  |         CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
| @@ -249,7 +257,7 @@ internal sealed class CommandExecutor | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             // Execute the command. |             // Execute the command. | ||||||
|             var result = await command.Execute(context, settings); |             var result = await command.ExecuteAsync(context, settings, cancellationToken); | ||||||
|             foreach (var interceptor in interceptors) |             foreach (var interceptor in interceptors) | ||||||
|             { |             { | ||||||
|                 interceptor.InterceptResult(context, settings, ref result); |                 interceptor.InterceptResult(context, settings, ref result); | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Cli; | |||||||
|  |  | ||||||
| [Description("Displays diagnostics about CLI configurations")] | [Description("Displays diagnostics about CLI configurations")] | ||||||
| [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | ||||||
| internal sealed class ExplainCommand : Command<ExplainCommand.Settings> | internal sealed class ExplainCommand : Command<ExplainCommand.Settings>, IBuiltInCommand | ||||||
| { | { | ||||||
|     private readonly CommandModel _commandModel; |     private readonly CommandModel _commandModel; | ||||||
|     private readonly IAnsiConsole _writer; |     private readonly IAnsiConsole _writer; | ||||||
| @@ -27,7 +27,7 @@ internal sealed class ExplainCommand : Command<ExplainCommand.Settings> | |||||||
|         public bool IncludeHidden { get; set; } |         public bool IncludeHidden { get; set; } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, Settings settings) |     public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var tree = new Tree("CLI Configuration"); |         var tree = new Tree("CLI Configuration"); | ||||||
|         tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name")); |         tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name")); | ||||||
|   | |||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | namespace Spectre.Console.Cli; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// Represents a built-in command. | ||||||
|  | /// Used as a marker interface. | ||||||
|  | /// </summary> | ||||||
|  | internal interface IBuiltInCommand : ICommand | ||||||
|  | { | ||||||
|  | } | ||||||
| @@ -0,0 +1,224 @@ | |||||||
|  | using OpenCli; | ||||||
|  |  | ||||||
|  | namespace Spectre.Console.Cli; | ||||||
|  |  | ||||||
|  | internal sealed class OpenCliGeneratorCommand : Command, IBuiltInCommand | ||||||
|  | { | ||||||
|  |     private readonly IConfiguration _configuration; | ||||||
|  |     private readonly CommandModel _model; | ||||||
|  |  | ||||||
|  |     public OpenCliGeneratorCommand(IConfiguration configuration, CommandModel model) | ||||||
|  |     { | ||||||
|  |         _configuration = configuration; | ||||||
|  |         _model = model ?? throw new ArgumentNullException(nameof(model)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public override int Execute(CommandContext context, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var document = new OpenCliDocument | ||||||
|  |         { | ||||||
|  |             OpenCli = "0.1-draft", | ||||||
|  |             Info = new OpenCliInfo | ||||||
|  |             { | ||||||
|  |                 Title = ((ICommandModel)_model).ApplicationName, Version = _model.ApplicationVersion ?? "1.0", | ||||||
|  |             }, | ||||||
|  |             Commands = CreateCommands(_model.Commands), | ||||||
|  |             Arguments = CreateArguments(_model.DefaultCommand?.GetArguments()), | ||||||
|  |             Options = CreateOptions(_model.DefaultCommand?.GetOptions()), | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var writer = _configuration.Settings.Console.GetConsole(); | ||||||
|  |         writer.WriteLine(document.Write()); | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private List<OpenCliCommand> CreateCommands(IList<CommandInfo> commands) | ||||||
|  |     { | ||||||
|  |         var result = new List<OpenCliCommand>(); | ||||||
|  |  | ||||||
|  |         foreach (var command in commands.OrderBy(o => o.Name, StringComparer.OrdinalIgnoreCase)) | ||||||
|  |         { | ||||||
|  |             if (typeof(IBuiltInCommand).IsAssignableFrom(command.CommandType)) | ||||||
|  |             { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var openCliCommand = new OpenCliCommand | ||||||
|  |             { | ||||||
|  |                 Name = command.Name, | ||||||
|  |                 Aliases = | ||||||
|  |                 [ | ||||||
|  |                     ..command.Aliases.OrderBy(str => str) | ||||||
|  |                 ], | ||||||
|  |                 Commands = CreateCommands(command.Children), | ||||||
|  |                 Arguments = CreateArguments(command.GetArguments()), | ||||||
|  |                 Options = CreateOptions(command.GetOptions()), | ||||||
|  |                 Description = command.Description, | ||||||
|  |                 Hidden = command.IsHidden, | ||||||
|  |                 Examples = [..command.Examples.Select(example => string.Join(" ", example))], | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             // Skip branches without commands | ||||||
|  |             if (command.IsBranch && openCliCommand.Commands.Count == 0) | ||||||
|  |             { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             result.Add(openCliCommand); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private List<OpenCliArgument> CreateArguments(IEnumerable<CommandArgument>? arguments) | ||||||
|  |     { | ||||||
|  |         var result = new List<OpenCliArgument>(); | ||||||
|  |  | ||||||
|  |         if (arguments == null) | ||||||
|  |         { | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach (var argument in arguments.OrderBy(x => x.Position)) | ||||||
|  |         { | ||||||
|  |             var metadata = default(List<OpenCliMetadata>); | ||||||
|  |             if (argument.ParameterType != typeof(void) && | ||||||
|  |                 argument.ParameterType != typeof(bool)) | ||||||
|  |             { | ||||||
|  |                 metadata = | ||||||
|  |                 [ | ||||||
|  |                     new OpenCliMetadata { Name = "ClrType", Value = argument.ParameterType.ToCliTypeString(), }, | ||||||
|  |                 ]; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             result.Add(new OpenCliArgument | ||||||
|  |             { | ||||||
|  |                 Name = argument.Value, | ||||||
|  |                 Required = argument.IsRequired, | ||||||
|  |                 Arity = new OpenCliArity | ||||||
|  |                 { | ||||||
|  |                     // TODO: Look into this | ||||||
|  |                     Minimum = 1, | ||||||
|  |                     Maximum = 1, | ||||||
|  |                 }, | ||||||
|  |                 Description = argument.Description, | ||||||
|  |                 Hidden = argument.IsHidden, | ||||||
|  |                 Metadata = metadata, | ||||||
|  |                 AcceptedValues = null, | ||||||
|  |                 Group = null, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private List<OpenCliOption> CreateOptions(IEnumerable<CommandOption>? options) | ||||||
|  |     { | ||||||
|  |         var result = new List<OpenCliOption>(); | ||||||
|  |  | ||||||
|  |         if (options == null) | ||||||
|  |         { | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach (var option in options.OrderBy(o => o.GetOptionName(), StringComparer.OrdinalIgnoreCase)) | ||||||
|  |         { | ||||||
|  |             var arguments = new List<OpenCliArgument>(); | ||||||
|  |             if (option.ParameterType != typeof(void) && | ||||||
|  |                 option.ParameterType != typeof(bool)) | ||||||
|  |             { | ||||||
|  |                 arguments.Add(new OpenCliArgument | ||||||
|  |                 { | ||||||
|  |                     Name = option.ValueName ?? "VALUE", | ||||||
|  |                     Required = !option.ValueIsOptional, | ||||||
|  |                     Arity = new OpenCliArity | ||||||
|  |                     { | ||||||
|  |                         // TODO: Look into this | ||||||
|  |                         Minimum = option.ValueIsOptional | ||||||
|  |                             ? 0 | ||||||
|  |                             : 1, | ||||||
|  |                         Maximum = 1, | ||||||
|  |                     }, | ||||||
|  |                     AcceptedValues = null, | ||||||
|  |                     Group = null, | ||||||
|  |                     Hidden = null, | ||||||
|  |                     Metadata = | ||||||
|  |                     [ | ||||||
|  |                         new OpenCliMetadata | ||||||
|  |                         { | ||||||
|  |                             Name = "ClrType", | ||||||
|  |                             Value = option.ParameterType.ToCliTypeString(), | ||||||
|  |                         }, | ||||||
|  |                     ], | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var optionMetadata = default(List<OpenCliMetadata>); | ||||||
|  |             if (arguments.Count == 0 && option.ParameterType != typeof(void) && | ||||||
|  |                 option.ParameterType != typeof(bool)) | ||||||
|  |             { | ||||||
|  |                 optionMetadata = | ||||||
|  |                 [ | ||||||
|  |                     new OpenCliMetadata { Name = "ClrType", Value = option.ParameterType.ToCliTypeString(), }, | ||||||
|  |                 ]; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var (optionName, optionAliases) = GetOptionNames(option); | ||||||
|  |             result.Add(new OpenCliOption | ||||||
|  |             { | ||||||
|  |                 Name = optionName, | ||||||
|  |                 Required = option.IsRequired, | ||||||
|  |                 Aliases = [..optionAliases.OrderBy(str => str)], | ||||||
|  |                 Arguments = arguments, | ||||||
|  |                 Description = option.Description, | ||||||
|  |                 Group = null, | ||||||
|  |                 Hidden = option.IsHidden, | ||||||
|  |                 Recursive = option.IsShadowed, // TODO: Is this correct? | ||||||
|  |                 Metadata = optionMetadata, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static (string Name, HashSet<string> Aliases) GetOptionNames(CommandOption option) | ||||||
|  |     { | ||||||
|  |         var name = GetOptionName(option); | ||||||
|  |         var aliases = new HashSet<string>(); | ||||||
|  |  | ||||||
|  |         if (option.LongNames.Count > 0) | ||||||
|  |         { | ||||||
|  |             foreach (var alias in option.LongNames.Skip(1)) | ||||||
|  |             { | ||||||
|  |                 aliases.Add("--" + alias); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             foreach (var alias in option.ShortNames) | ||||||
|  |             { | ||||||
|  |                 aliases.Add("-" + alias); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             foreach (var alias in option.LongNames) | ||||||
|  |             { | ||||||
|  |                 aliases.Add("--" + alias); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             foreach (var alias in option.ShortNames.Skip(1)) | ||||||
|  |             { | ||||||
|  |                 aliases.Add("-" + alias); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return (name, aliases); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static string GetOptionName(CommandOption option) | ||||||
|  |     { | ||||||
|  |         return option.LongNames.Count > 0 | ||||||
|  |             ? "--" + option.LongNames[0] | ||||||
|  |             : "-" + option.ShortNames[0]; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Cli; | |||||||
|  |  | ||||||
| [Description("Displays the CLI library version")] | [Description("Displays the CLI library version")] | ||||||
| [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | ||||||
| internal sealed class VersionCommand : Command<VersionCommand.Settings> | internal sealed class VersionCommand : Command, IBuiltInCommand | ||||||
| { | { | ||||||
|     private readonly IAnsiConsole _writer; |     private readonly IAnsiConsole _writer; | ||||||
|  |  | ||||||
| @@ -11,11 +11,7 @@ internal sealed class VersionCommand : Command<VersionCommand.Settings> | |||||||
|         _writer = configuration.Settings.Console.GetConsole(); |         _writer = configuration.Settings.Console.GetConsole(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public sealed class Settings : CommandSettings |     public override int Execute(CommandContext context, CancellationToken cancellationToken) | ||||||
|     { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, Settings settings) |  | ||||||
|     { |     { | ||||||
|         _writer.MarkupLine( |         _writer.MarkupLine( | ||||||
|             "[yellow]Spectre.Cli[/] version [aqua]{0}[/]", |             "[yellow]Spectre.Cli[/] version [aqua]{0}[/]", | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Cli; | |||||||
|  |  | ||||||
| [Description("Generates an XML representation of the CLI configuration.")] | [Description("Generates an XML representation of the CLI configuration.")] | ||||||
| [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | [SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")] | ||||||
| internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings> | internal sealed class XmlDocCommand : Command, IBuiltInCommand | ||||||
| { | { | ||||||
|     private readonly CommandModel _model; |     private readonly CommandModel _model; | ||||||
|     private readonly IAnsiConsole _writer; |     private readonly IAnsiConsole _writer; | ||||||
| @@ -13,11 +13,7 @@ internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings> | |||||||
|         _writer = configuration.Settings.Console.GetConsole(); |         _writer = configuration.Settings.Console.GetConsole(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public sealed class Settings : CommandSettings |     public override int Execute(CommandContext context, CancellationToken cancellationToken) | ||||||
|     { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, Settings settings) |  | ||||||
|     { |     { | ||||||
|         _writer.Write(Serialize(_model), Style.Plain); |         _writer.Write(Serialize(_model), Style.Plain); | ||||||
|         return 0; |         return 0; | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings | |||||||
|     public HelpProviderStyle? HelpProviderStyles { get; set; } |     public HelpProviderStyle? HelpProviderStyles { get; set; } | ||||||
|     public bool StrictParsing { get; set; } |     public bool StrictParsing { get; set; } | ||||||
|     public bool ConvertFlagsToRemainingArguments { get; set; } |     public bool ConvertFlagsToRemainingArguments { get; set; } | ||||||
|  |     public int CancellationExitCode { get; set; } | ||||||
|  |  | ||||||
|     public ParsingMode ParsingMode => |     public ParsingMode ParsingMode => | ||||||
|         StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed; |         StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed; | ||||||
| @@ -33,6 +34,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings | |||||||
|         TrimTrailingPeriod = true; |         TrimTrailingPeriod = true; | ||||||
|         HelpProviderStyles = HelpProviderStyle.Default; |         HelpProviderStyles = HelpProviderStyle.Default; | ||||||
|         ConvertFlagsToRemainingArguments = false; |         ConvertFlagsToRemainingArguments = false; | ||||||
|  |         CancellationExitCode = 130; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName) |     public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName) | ||||||
|   | |||||||
| @@ -56,19 +56,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig | |||||||
|         return new CommandConfigurator(command); |         return new CommandConfigurator(command); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func) |     public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, int> func) | ||||||
|         where TSettings : CommandSettings |         where TSettings : CommandSettings | ||||||
|     { |     { | ||||||
|         var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>( |         var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>( | ||||||
|             name, (context, settings) => Task.FromResult(func(context, (TSettings)settings)))); |             name, (context, settings, cancellationToken) => Task.FromResult(func(context, (TSettings)settings, cancellationToken)))); | ||||||
|         return new CommandConfigurator(command); |         return new CommandConfigurator(command); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func) |     public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, Task<int>> func) | ||||||
|         where TSettings : CommandSettings |         where TSettings : CommandSettings | ||||||
|     { |     { | ||||||
|         var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>( |         var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>( | ||||||
|             name, (context, settings) => func(context, (TSettings)settings))); |             name, (context, settings, cancellationToken) => func(context, (TSettings)settings, cancellationToken))); | ||||||
|         return new CommandConfigurator(command); |         return new CommandConfigurator(command); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -46,21 +46,21 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf | |||||||
|         return configurator; |         return configurator; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func) |     public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, int> func) | ||||||
|         where TDerivedSettings : TSettings |         where TDerivedSettings : TSettings | ||||||
|     { |     { | ||||||
|         var command = ConfiguredCommand.FromDelegate<TDerivedSettings>( |         var command = ConfiguredCommand.FromDelegate<TDerivedSettings>( | ||||||
|             name, (context, settings) => Task.FromResult(func(context, (TDerivedSettings)settings))); |             name, (context, settings, cancellationToken) => Task.FromResult(func(context, (TDerivedSettings)settings, cancellationToken))); | ||||||
|  |  | ||||||
|         _command.Children.Add(command); |         _command.Children.Add(command); | ||||||
|         return new CommandConfigurator(command); |         return new CommandConfigurator(command); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func) |     public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, Task<int>> func) | ||||||
|         where TDerivedSettings : TSettings |         where TDerivedSettings : TSettings | ||||||
|     { |     { | ||||||
|         var command = ConfiguredCommand.FromDelegate<TDerivedSettings>( |         var command = ConfiguredCommand.FromDelegate<TDerivedSettings>( | ||||||
|             name, (context, settings) => func(context, (TDerivedSettings)settings)); |             name, (context, settings, cancellationToken) => func(context, (TDerivedSettings)settings, cancellationToken)); | ||||||
|  |  | ||||||
|         _command.Children.Add(command); |         _command.Children.Add(command); | ||||||
|         return new CommandConfigurator(command); |         return new CommandConfigurator(command); | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ internal sealed class ConfiguredCommand | |||||||
|     public object? Data { get; set; } |     public object? Data { get; set; } | ||||||
|     public Type? CommandType { get; } |     public Type? CommandType { get; } | ||||||
|     public Type SettingsType { get; } |     public Type SettingsType { get; } | ||||||
|     public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; } |     public Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? Delegate { get; } | ||||||
|     public bool IsDefaultCommand { get; } |     public bool IsDefaultCommand { get; } | ||||||
|     public bool IsHidden { get; set; } |     public bool IsHidden { get; set; } | ||||||
|  |  | ||||||
| @@ -19,7 +19,7 @@ internal sealed class ConfiguredCommand | |||||||
|         string name, |         string name, | ||||||
|         Type? commandType, |         Type? commandType, | ||||||
|         Type settingsType, |         Type settingsType, | ||||||
|         Func<CommandContext, CommandSettings, Task<int>>? @delegate, |         Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? @delegate, | ||||||
|         bool isDefaultCommand) |         bool isDefaultCommand) | ||||||
|     { |     { | ||||||
|         Name = name; |         Name = name; | ||||||
| @@ -60,7 +60,7 @@ internal sealed class ConfiguredCommand | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static ConfiguredCommand FromDelegate<TSettings>( |     public static ConfiguredCommand FromDelegate<TSettings>( | ||||||
|         string name, Func<CommandContext, CommandSettings, Task<int>>? @delegate = null) |         string name, Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? @delegate = null) | ||||||
|         where TSettings : CommandSettings |         where TSettings : CommandSettings | ||||||
|     { |     { | ||||||
|         return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false); |         return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ internal static class CliConstants | |||||||
|     public const string DefaultCommandName = "__default_command"; |     public const string DefaultCommandName = "__default_command"; | ||||||
|     public const string True = "true"; |     public const string True = "true"; | ||||||
|     public const string False = "false"; |     public const string False = "false"; | ||||||
|  |     public const string DumpHelpOpenCliOption = "--help-dump-opencli"; | ||||||
|  |  | ||||||
|     public static string[] AcceptedBooleanValues { get; } = new string[] |     public static string[] AcceptedBooleanValues { get; } = new string[] | ||||||
|     { |     { | ||||||
| @@ -18,5 +19,6 @@ internal static class CliConstants | |||||||
|         public const string Version = "version"; |         public const string Version = "version"; | ||||||
|         public const string XmlDoc = "xmldoc"; |         public const string XmlDoc = "xmldoc"; | ||||||
|         public const string Explain = "explain"; |         public const string Explain = "explain"; | ||||||
|  |         public const string OpenCli = "opencli"; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -2,16 +2,16 @@ namespace Spectre.Console.Cli; | |||||||
|  |  | ||||||
| internal sealed class DelegateCommand : ICommand | internal sealed class DelegateCommand : ICommand | ||||||
| { | { | ||||||
|     private readonly Func<CommandContext, CommandSettings, Task<int>> _func; |     private readonly Func<CommandContext, CommandSettings, CancellationToken, Task<int>> _func; | ||||||
|  |  | ||||||
|     public DelegateCommand(Func<CommandContext, CommandSettings, Task<int>> func) |     public DelegateCommand(Func<CommandContext, CommandSettings, CancellationToken, Task<int>> func) | ||||||
|     { |     { | ||||||
|         _func = func; |         _func = func; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Task<int> Execute(CommandContext context, CommandSettings settings) |     public Task<int> ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return _func(context, settings); |         return _func(context, settings, cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ValidationResult Validate(CommandContext context, CommandSettings settings) |     public ValidationResult Validate(CommandContext context, CommandSettings settings) | ||||||
|   | |||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | #if NETSTANDARD2_0 | ||||||
|  | namespace System.IO; | ||||||
|  |  | ||||||
|  | // Polyfills needed for OpenCli. | ||||||
|  | // This can be removed once me migrate over to the Polyfill library. | ||||||
|  | internal static class OpenCliExtensions | ||||||
|  | { | ||||||
|  |     public static Task<string> ReadToEndAsync(this StreamReader reader, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         return reader.ReadToEndAsync(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @@ -16,4 +16,19 @@ internal static class TypeExtensions | |||||||
|  |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Taken from https://github.com/dotnet/sdk/blob/main/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/TypeExtensions.cs#L15 | ||||||
|  |     // Licensed under MIT | ||||||
|  |     public static string ToCliTypeString(this Type type) | ||||||
|  |     { | ||||||
|  |         var typeName = type.FullName ?? string.Empty; | ||||||
|  |         if (!type.IsGenericType) | ||||||
|  |         { | ||||||
|  |             return typeName; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var genericTypeName = typeName.Substring(0, typeName.IndexOf('`')); | ||||||
|  |         var genericTypes = string.Join(", ", type.GenericTypeArguments.Select(generic => generic.ToCliTypeString())); | ||||||
|  |         return $"{genericTypeName}<{genericTypes}>"; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @@ -8,7 +8,7 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo | |||||||
|     public object? Data { get; } |     public object? Data { get; } | ||||||
|     public Type? CommandType { get; } |     public Type? CommandType { get; } | ||||||
|     public Type SettingsType { get; } |     public Type SettingsType { get; } | ||||||
|     public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; } |     public Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? Delegate { get; } | ||||||
|     public bool IsDefaultCommand { get; } |     public bool IsDefaultCommand { get; } | ||||||
|     public CommandInfo? Parent { get; } |     public CommandInfo? Parent { get; } | ||||||
|     public IList<CommandInfo> Children { get; } |     public IList<CommandInfo> Children { get; } | ||||||
|   | |||||||
| @@ -2,6 +2,16 @@ namespace Spectre.Console.Cli; | |||||||
|  |  | ||||||
| internal static class CommandInfoExtensions | internal static class CommandInfoExtensions | ||||||
| { | { | ||||||
|  |     public static IEnumerable<CommandArgument>? GetArguments(this CommandInfo? command) | ||||||
|  |     { | ||||||
|  |         return command?.Parameters.OfType<CommandArgument>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IEnumerable<CommandOption>? GetOptions(this CommandInfo? command) | ||||||
|  |     { | ||||||
|  |         return command?.Parameters.OfType<CommandOption>(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static bool HaveParentWithOption(this CommandInfo command, CommandOption option) |     public static bool HaveParentWithOption(this CommandInfo command, CommandOption option) | ||||||
|     { |     { | ||||||
|         var parent = command?.Parent; |         var parent = command?.Parent; | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ internal abstract class CommandParameter : ICommandParameterInfo, ICommandParame | |||||||
|         DefaultValue = defaultValue; |         DefaultValue = defaultValue; | ||||||
|         PairDeconstructor = deconstructor; |         PairDeconstructor = deconstructor; | ||||||
|         ValueProvider = valueProvider; |         ValueProvider = valueProvider; | ||||||
|         Validators = new List<ParameterValidationAttribute>(validators ?? Array.Empty<ParameterValidationAttribute>()); |         Validators = new List<ParameterValidationAttribute>(validators ?? []); | ||||||
|         IsRequired = required; |         IsRequired = required; | ||||||
|         IsHidden = isHidden; |         IsHidden = isHidden; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ global using System.IO; | |||||||
| global using System.Linq; | global using System.Linq; | ||||||
| global using System.Reflection; | global using System.Reflection; | ||||||
| global using System.Text; | global using System.Text; | ||||||
|  | global using System.Threading; | ||||||
| global using System.Threading.Tasks; | global using System.Threading.Tasks; | ||||||
| global using System.Xml; | global using System.Xml; | ||||||
| global using Spectre.Console.Cli.Help; | global using Spectre.Console.Cli.Help; | ||||||
|   | |||||||
| @@ -3,11 +3,11 @@ | |||||||
|   <PropertyGroup> |   <PropertyGroup> | ||||||
|     <TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks> |     <TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks> | ||||||
|     <IsPackable>true</IsPackable> |     <IsPackable>true</IsPackable> | ||||||
|   </PropertyGroup> |  | ||||||
|   <PropertyGroup> |  | ||||||
|     <IsAotCompatible Condition="'$(TargetFramework)' != 'netstandard2.0'">false</IsAotCompatible> |     <IsAotCompatible Condition="'$(TargetFramework)' != 'netstandard2.0'">false</IsAotCompatible> | ||||||
|     <IsTrimmable>false</IsTrimmable> |     <IsTrimmable>false</IsTrimmable> | ||||||
|  |     <DefineConstants>$(DefineConstants);OPENCLI_VISIBILITY_INTERNAL</DefineConstants> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
|   <ItemGroup Label="REMOVE THIS"> |   <ItemGroup Label="REMOVE THIS"> | ||||||
|     <InternalsVisibleTo Include="Spectre.Console.Cli.Tests"/> |     <InternalsVisibleTo Include="Spectre.Console.Cli.Tests"/> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| @@ -18,6 +18,7 @@ | |||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |  | ||||||
|   <ItemGroup Label="Dependencies"> |   <ItemGroup Label="Dependencies"> | ||||||
|  |     <PackageReference Include="OpenCli.Sources" /> | ||||||
|     <PackageReference Include="PolySharp"> |     <PackageReference Include="PolySharp"> | ||||||
|       <PrivateAssets>all</PrivateAssets> |       <PrivateAssets>all</PrivateAssets> | ||||||
|       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> |       <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ public sealed class CommandAppTester | |||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             Run(args, Console, c => c.PropagateExceptions()); |             RunAsync(args, Console, c => c.PropagateExceptions()).GetAwaiter().GetResult(); | ||||||
|             throw new InvalidOperationException("Expected an exception to be thrown, but there was none."); |             throw new InvalidOperationException("Expected an exception to be thrown, but there was none."); | ||||||
|         } |         } | ||||||
|         catch (T ex) |         catch (T ex) | ||||||
| @@ -129,53 +129,21 @@ public sealed class CommandAppTester | |||||||
|     /// <returns>The result.</returns> |     /// <returns>The result.</returns> | ||||||
|     public CommandAppResult Run(params string[] args) |     public CommandAppResult Run(params string[] args) | ||||||
|     { |     { | ||||||
|         return Run(args, Console); |         return RunAsync(args, Console).GetAwaiter().GetResult(); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private CommandAppResult Run(string[] args, TestConsole console, Action<IConfigurator>? config = null) |  | ||||||
|     { |  | ||||||
|         CommandContext? context = null; |  | ||||||
|         CommandSettings? settings = null; |  | ||||||
|  |  | ||||||
|         var app = new CommandApp(Registrar); |  | ||||||
|         _appConfiguration?.Invoke(app); |  | ||||||
|  |  | ||||||
|         if (_configuration != null) |  | ||||||
|         { |  | ||||||
|             app.Configure(_configuration); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (config != null) |  | ||||||
|         { |  | ||||||
|             app.Configure(config); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         app.Configure(c => c.ConfigureConsole(console)); |  | ||||||
|         app.Configure(c => c.SetInterceptor(new CallbackCommandInterceptor((ctx, s) => |  | ||||||
|         { |  | ||||||
|             context = ctx; |  | ||||||
|             settings = s; |  | ||||||
|         }))); |  | ||||||
|  |  | ||||||
|         var result = app.Run(args); |  | ||||||
|  |  | ||||||
|         var output = console.Output.NormalizeLineEndings(); |  | ||||||
|         output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output; |  | ||||||
|  |  | ||||||
|         return new CommandAppResult(result, output, context, settings); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Runs the command application asynchronously. |     /// Runs the command application asynchronously. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="args">The arguments.</param> |     /// <param name="args">The arguments.</param> | ||||||
|  |     /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> | ||||||
|     /// <returns>The result.</returns> |     /// <returns>The result.</returns> | ||||||
|     public async Task<CommandAppResult> RunAsync(params string[] args) |     public async Task<CommandAppResult> RunAsync(string[]? args = null, CancellationToken cancellationToken = default) | ||||||
|     { |     { | ||||||
|         return await RunAsync(args, Console); |         return await RunAsync(args ?? [], Console, cancellationToken: cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private async Task<CommandAppResult> RunAsync(string[] args, TestConsole console, Action<IConfigurator>? config = null) |     private async Task<CommandAppResult> RunAsync(string[] args, TestConsole console, Action<IConfigurator>? config = null, CancellationToken cancellationToken = default) | ||||||
|     { |     { | ||||||
|         CommandContext? context = null; |         CommandContext? context = null; | ||||||
|         CommandSettings? settings = null; |         CommandSettings? settings = null; | ||||||
| @@ -200,7 +168,7 @@ public sealed class CommandAppTester | |||||||
|             settings = s; |             settings = s; | ||||||
|         }))); |         }))); | ||||||
|  |  | ||||||
|         var result = await app.RunAsync(args); |         var result = await app.RunAsync(args, cancellationToken); | ||||||
|  |  | ||||||
|         var output = console.Output.NormalizeLineEndings(); |         var output = console.Output.NormalizeLineEndings(); | ||||||
|         output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output; |         output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output; | ||||||
|   | |||||||
| @@ -3,16 +3,17 @@ namespace Spectre.Console.Tests; | |||||||
| public static class Constants | public static class Constants | ||||||
| { | { | ||||||
|     public static string[] VersionCommand { get; } = |     public static string[] VersionCommand { get; } = | ||||||
|         new[] |     [ | ||||||
|         { |         CliConstants.Commands.Branch, | ||||||
|                 CliConstants.Commands.Branch, |         CliConstants.Commands.Version | ||||||
|                 CliConstants.Commands.Version, |     ]; | ||||||
|         }; |  | ||||||
|  |  | ||||||
|     public static string[] XmlDocCommand { get; } = |     public static string[] XmlDocCommand { get; } = | ||||||
|         new[] |     [ | ||||||
|         { |         CliConstants.Commands.Branch, | ||||||
|                 CliConstants.Commands.Branch, |         CliConstants.Commands.XmlDoc | ||||||
|                 CliConstants.Commands.XmlDoc, |     ]; | ||||||
|         }; |  | ||||||
|  |     public static string[] OpenCliOption { get; } = | ||||||
|  |         [CliConstants.DumpHelpOpenCliOption]; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,10 +9,10 @@ public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettin | |||||||
|         _console = console; |         _console = console; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings) |     public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         // Simulate a long running asynchronous task |         // Simulate a long running asynchronous task | ||||||
|         await Task.Delay(200); |         await Task.Delay(200, cancellationToken); | ||||||
|  |  | ||||||
|         if (settings.ThrowException) |         if (settings.ThrowException) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public class CatCommand : AnimalCommand<CatSettings> | public class CatCommand : AnimalCommand<CatSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, CatSettings settings) |     public override int Execute(CommandContext context, CatSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ public class DogCommand : AnimalCommand<DogSettings> | |||||||
|         return base.Validate(context, settings); |         return base.Validate(context, settings); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, DogSettings settings) |     public override int Execute(CommandContext context, DogSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ public sealed class DumpRemainingCommand : Command<EmptyCommandSettings> | |||||||
|         _console = console; |         _console = console; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, EmptyCommandSettings settings) |     public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         if (context.Remaining.Raw.Count > 0) |         if (context.Remaining.Raw.Count > 0) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public sealed class EmptyCommand : Command<EmptyCommandSettings> | public sealed class EmptyCommand : Command<EmptyCommandSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, EmptyCommandSettings settings) |     public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
| public sealed class GenericCommand<TSettings> : Command<TSettings> | public sealed class GenericCommand<TSettings> : Command<TSettings> | ||||||
|     where TSettings : CommandSettings |     where TSettings : CommandSettings | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, TSettings settings) |     public override int Execute(CommandContext context, TSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
| [Description("The giraffe command.")] | [Description("The giraffe command.")] | ||||||
| public sealed class GiraffeCommand : Command<GiraffeSettings> | public sealed class GiraffeCommand : Command<GiraffeSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, GiraffeSettings settings) |     public override int Execute(CommandContext context, GiraffeSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings> | |||||||
|         _console = console; |         _console = console; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings) |     public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         _console.WriteLine(settings.Greeting); |         _console.WriteLine(settings.Greeting); | ||||||
|         return 0; |         return 0; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings> | public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, HiddenOptionSettings settings) |     public override int Execute(CommandContext context, HiddenOptionSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
| [Description("The horse command.")] | [Description("The horse command.")] | ||||||
| public class HorseCommand : AnimalCommand<HorseSettings> | public class HorseCommand : AnimalCommand<HorseSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, HorseSettings settings) |     public override int Execute(CommandContext context, HorseSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public sealed class InvalidCommand : Command<InvalidSettings> | public sealed class InvalidCommand : Command<InvalidSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, InvalidSettings settings) |     public override int Execute(CommandContext context, InvalidSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
| [Description("The lion command.")] | [Description("The lion command.")] | ||||||
| public class LionCommand : AnimalCommand<LionSettings> | public class LionCommand : AnimalCommand<LionSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, LionSettings settings) |     public override int Execute(CommandContext context, LionSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ public sealed class NoDescriptionCommand : Command<EmptyCommandSettings> | |||||||
|     [CommandOption("-f|--foo <VALUE>")] |     [CommandOption("-f|--foo <VALUE>")] | ||||||
|     public int Foo { get; set; } |     public int Foo { get; set; } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, EmptyCommandSettings settings) |     public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public class OptionVectorCommand : Command<OptionVectorSettings> | public class OptionVectorCommand : Command<OptionVectorSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, OptionVectorSettings settings) |     public override int Execute(CommandContext context, OptionVectorSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
|  |  | ||||||
| public sealed class ThrowingCommand : Command<ThrowingCommandSettings> | public sealed class ThrowingCommand : Command<ThrowingCommandSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, ThrowingCommandSettings settings) |     public override int Execute(CommandContext context, ThrowingCommandSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         throw new InvalidOperationException("W00t?"); |         throw new InvalidOperationException("W00t?"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data; | |||||||
| [Description("The turtle command.")] | [Description("The turtle command.")] | ||||||
| public class TurtleCommand : AnimalCommand<TurtleSettings> | public class TurtleCommand : AnimalCommand<TurtleSettings> | ||||||
| { | { | ||||||
|     public override int Execute(CommandContext context, TurtleSettings settings) |     public override int Execute(CommandContext context, TurtleSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ public sealed class VersionCommand : Command<VersionSettings> | |||||||
|         _console = console; |         _console = console; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public override int Execute(CommandContext context, VersionSettings settings) |     public override int Execute(CommandContext context, VersionSettings settings, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         _console.WriteLine($"VersionCommand ran, Version: {settings.Version ?? string.Empty}"); |         _console.WriteLine($"VersionCommand ran, Version: {settings.Version ?? string.Empty}"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,190 @@ | |||||||
|  | { | ||||||
|  |   "opencli": "0.1-draft", | ||||||
|  |   "info": { | ||||||
|  |     "title": "my-app", | ||||||
|  |     "version": "1.2.3" | ||||||
|  |   }, | ||||||
|  |   "commands": [ | ||||||
|  |     { | ||||||
|  |       "name": "animals", | ||||||
|  |       "commands": [ | ||||||
|  |         { | ||||||
|  |           "name": "cat", | ||||||
|  |           "options": [ | ||||||
|  |             { | ||||||
|  |               "name": "--agility", | ||||||
|  |               "required": false, | ||||||
|  |               "arguments": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "VALUE", | ||||||
|  |                   "required": true, | ||||||
|  |                   "arity": { | ||||||
|  |                     "minimum": 1, | ||||||
|  |                     "maximum": 1 | ||||||
|  |                   }, | ||||||
|  |                   "metadata": [ | ||||||
|  |                     { | ||||||
|  |                       "name": "ClrType", | ||||||
|  |                       "value": "System.Int32" | ||||||
|  |                     } | ||||||
|  |                   ] | ||||||
|  |                 } | ||||||
|  |               ], | ||||||
|  |               "description": "The agility between 0 and 100.", | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "name": "--alive", | ||||||
|  |               "required": false, | ||||||
|  |               "aliases": [ | ||||||
|  |                 "--not-dead", | ||||||
|  |                 "-a" | ||||||
|  |               ], | ||||||
|  |               "description": "Indicates whether or not the animal is alive.", | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "name": "--name", | ||||||
|  |               "required": false, | ||||||
|  |               "aliases": [ | ||||||
|  |                 "--pet-name", | ||||||
|  |                 "-n", | ||||||
|  |                 "-p" | ||||||
|  |               ], | ||||||
|  |               "arguments": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "VALUE", | ||||||
|  |                   "required": true, | ||||||
|  |                   "arity": { | ||||||
|  |                     "minimum": 1, | ||||||
|  |                     "maximum": 1 | ||||||
|  |                   }, | ||||||
|  |                   "metadata": [ | ||||||
|  |                     { | ||||||
|  |                       "name": "ClrType", | ||||||
|  |                       "value": "System.String" | ||||||
|  |                     } | ||||||
|  |                   ] | ||||||
|  |                 } | ||||||
|  |               ], | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "arguments": [ | ||||||
|  |             { | ||||||
|  |               "name": "LEGS", | ||||||
|  |               "required": false, | ||||||
|  |               "arity": { | ||||||
|  |                 "minimum": 1, | ||||||
|  |                 "maximum": 1 | ||||||
|  |               }, | ||||||
|  |               "description": "The number of legs.", | ||||||
|  |               "hidden": false, | ||||||
|  |               "metadata": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "ClrType", | ||||||
|  |                   "value": "System.Int32" | ||||||
|  |                 } | ||||||
|  |               ] | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "hidden": false, | ||||||
|  |           "examples": [] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "name": "dog", | ||||||
|  |           "options": [ | ||||||
|  |             { | ||||||
|  |               "name": "--alive", | ||||||
|  |               "required": false, | ||||||
|  |               "aliases": [ | ||||||
|  |                 "--not-dead", | ||||||
|  |                 "-a" | ||||||
|  |               ], | ||||||
|  |               "description": "Indicates whether or not the animal is alive.", | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "name": "--good-boy", | ||||||
|  |               "required": false, | ||||||
|  |               "aliases": [ | ||||||
|  |                 "-g" | ||||||
|  |               ], | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "name": "--name", | ||||||
|  |               "required": false, | ||||||
|  |               "aliases": [ | ||||||
|  |                 "--pet-name", | ||||||
|  |                 "-n", | ||||||
|  |                 "-p" | ||||||
|  |               ], | ||||||
|  |               "arguments": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "VALUE", | ||||||
|  |                   "required": true, | ||||||
|  |                   "arity": { | ||||||
|  |                     "minimum": 1, | ||||||
|  |                     "maximum": 1 | ||||||
|  |                   }, | ||||||
|  |                   "metadata": [ | ||||||
|  |                     { | ||||||
|  |                       "name": "ClrType", | ||||||
|  |                       "value": "System.String" | ||||||
|  |                     } | ||||||
|  |                   ] | ||||||
|  |                 } | ||||||
|  |               ], | ||||||
|  |               "recursive": false, | ||||||
|  |               "hidden": false | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "arguments": [ | ||||||
|  |             { | ||||||
|  |               "name": "LEGS", | ||||||
|  |               "required": false, | ||||||
|  |               "arity": { | ||||||
|  |                 "minimum": 1, | ||||||
|  |                 "maximum": 1 | ||||||
|  |               }, | ||||||
|  |               "description": "The number of legs.", | ||||||
|  |               "hidden": false, | ||||||
|  |               "metadata": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "ClrType", | ||||||
|  |                   "value": "System.Int32" | ||||||
|  |                 } | ||||||
|  |               ] | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               "name": "AGE", | ||||||
|  |               "required": true, | ||||||
|  |               "arity": { | ||||||
|  |                 "minimum": 1, | ||||||
|  |                 "maximum": 1 | ||||||
|  |               }, | ||||||
|  |               "hidden": false, | ||||||
|  |               "metadata": [ | ||||||
|  |                 { | ||||||
|  |                   "name": "ClrType", | ||||||
|  |                   "value": "System.Int32" | ||||||
|  |                 } | ||||||
|  |               ] | ||||||
|  |             } | ||||||
|  |           ], | ||||||
|  |           "description": "The dog command.", | ||||||
|  |           "hidden": false, | ||||||
|  |           "examples": [] | ||||||
|  |         } | ||||||
|  |       ], | ||||||
|  |       "hidden": false, | ||||||
|  |       "examples": [] | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
| @@ -5,6 +5,7 @@ global using System.Diagnostics.CodeAnalysis; | |||||||
| global using System.Globalization; | global using System.Globalization; | ||||||
| global using System.Linq; | global using System.Linq; | ||||||
| global using System.Runtime.CompilerServices; | global using System.Runtime.CompilerServices; | ||||||
|  | global using System.Threading; | ||||||
| global using System.Threading.Tasks; | global using System.Threading.Tasks; | ||||||
| global using Shouldly; | global using Shouldly; | ||||||
| global using Spectre.Console.Cli; | global using Spectre.Console.Cli; | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ public sealed partial class CommandAppTests | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // When |             // When | ||||||
|             var result = await Record.ExceptionAsync(async () => |             var exception = await Record.ExceptionAsync(async () => | ||||||
|                     await app.RunAsync(new[] |                     await app.RunAsync(new[] | ||||||
|                         { |                         { | ||||||
|                         "--ThrowException", |                         "--ThrowException", | ||||||
| @@ -61,10 +61,64 @@ public sealed partial class CommandAppTests | |||||||
|                         })); |                         })); | ||||||
|  |  | ||||||
|             // Then |             // Then | ||||||
|             result.ShouldBeOfType<Exception>().And(ex => |             exception.ShouldBeOfType<Exception>().And(ex => | ||||||
|             { |             { | ||||||
|                 ex.Message.ShouldBe("Throwing exception asynchronously"); |                 ex.Message.ShouldBe("Throwing exception asynchronously"); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         [Fact] | ||||||
|  |         public async Task Should_Throw_OperationCanceledException_When_Propagated_And_Cancelled() | ||||||
|  |         { | ||||||
|  |             // Given | ||||||
|  |             var app = new CommandAppTester(); | ||||||
|  |             app.SetDefaultCommand<AsynchronousCommand>(); | ||||||
|  |             app.Configure(config => | ||||||
|  |             { | ||||||
|  |                 config.PropagateExceptions(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // When | ||||||
|  |             var exception = await Record.ExceptionAsync(async () => | ||||||
|  |                 await app.RunAsync(cancellationToken: new CancellationToken(canceled: true))); | ||||||
|  |  | ||||||
|  |             // Then | ||||||
|  |             exception.ShouldNotBeNull(); | ||||||
|  |             exception.ShouldBeAssignableTo<OperationCanceledException>(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [Fact] | ||||||
|  |         public async Task Should_Return_Default_Exit_Code_When_Cancelled() | ||||||
|  |         { | ||||||
|  |             // Given | ||||||
|  |             var app = new CommandAppTester(); | ||||||
|  |             app.SetDefaultCommand<AsynchronousCommand>(); | ||||||
|  |  | ||||||
|  |             // When | ||||||
|  |             var result = await app.RunAsync(cancellationToken: new CancellationToken(canceled: true)); | ||||||
|  |  | ||||||
|  |             // Then | ||||||
|  |             result.ExitCode.ShouldBe(130); | ||||||
|  |             result.Output.ShouldBeEmpty(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [Fact] | ||||||
|  |         public async Task Should_Return_Custom_Exit_Code_When_Cancelled() | ||||||
|  |         { | ||||||
|  |             // Given | ||||||
|  |             var app = new CommandAppTester(); | ||||||
|  |             app.SetDefaultCommand<AsynchronousCommand>(); | ||||||
|  |             app.Configure(config => | ||||||
|  |             { | ||||||
|  |                 config.CancellationExitCode(123); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // When | ||||||
|  |             var result = await app.RunAsync(cancellationToken: new CancellationToken(canceled: true)); | ||||||
|  |  | ||||||
|  |             // Then | ||||||
|  |             result.ExitCode.ShouldBe(123); | ||||||
|  |             result.Output.ShouldBeEmpty(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -28,12 +28,12 @@ public sealed partial class CommandAppTests | |||||||
|  |  | ||||||
|     public class NullableCommand : Command<NullableSettings> |     public class NullableCommand : Command<NullableSettings> | ||||||
|     { |     { | ||||||
|         public override int Execute(CommandContext context, NullableSettings settings) => 0; |         public override int Execute(CommandContext context, NullableSettings settings, CancellationToken cancellationToken) => 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class NullableWithInitCommand : Command<NullableWithInitSettings> |     public class NullableWithInitCommand : Command<NullableWithInitSettings> | ||||||
|     { |     { | ||||||
|         public override int Execute(CommandContext context, NullableWithInitSettings settings) => 0; |         public override int Execute(CommandContext context, NullableWithInitSettings settings, CancellationToken cancellationToken) => 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ public sealed partial class CommandAppTests | |||||||
|                     _dep = dep; |                     _dep = dep; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 public override int Execute(CommandContext context, CustomInheritedCommandSettings settings) |                 public override int Execute(CommandContext context, CustomInheritedCommandSettings settings, CancellationToken cancellationToken) | ||||||
|                 { |                 { | ||||||
|                     return 0; |                     return 0; | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ public sealed partial class CommandAppTests | |||||||
|             { |             { | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             public override int Execute(CommandContext context, Settings settings) |             public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken) | ||||||
|             { |             { | ||||||
|                 return 0; |                 return 0; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | namespace Spectre.Console.Tests.Unit.Cli; | ||||||
|  |  | ||||||
|  | public sealed partial class CommandAppTests | ||||||
|  | { | ||||||
|  |     [ExpectationPath("OpenCli")] | ||||||
|  |     public sealed partial class OpenCli | ||||||
|  |     { | ||||||
|  |         [Fact] | ||||||
|  |         [Expectation("Generate")] | ||||||
|  |         public Task Should_Output_OpenCli_Description() | ||||||
|  |         { | ||||||
|  |             // Given | ||||||
|  |             var fixture = new CommandAppTester(); | ||||||
|  |             fixture.Configure(config => | ||||||
|  |             { | ||||||
|  |                 config.SetApplicationName("my-app"); | ||||||
|  |                 config.SetApplicationVersion("1.2.3"); | ||||||
|  |  | ||||||
|  |                 config.AddBranch("animals", animals => | ||||||
|  |                 { | ||||||
|  |                     animals.AddCommand<DogCommand>("dog"); | ||||||
|  |                     animals.AddCommand<CatCommand>("cat"); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // When | ||||||
|  |             var result = fixture.Run(Constants.OpenCliOption); | ||||||
|  |  | ||||||
|  |             // Then | ||||||
|  |             return Verifier.Verify(result.Output); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1117,7 +1117,7 @@ public sealed partial class CommandAppTests | |||||||
|             { |             { | ||||||
|                 config.PropagateExceptions(); |                 config.PropagateExceptions(); | ||||||
|                 config.AddDelegate<DogSettings>( |                 config.AddDelegate<DogSettings>( | ||||||
|                     "foo", (context, settings) => |                     "foo", (context, settings, _) => | ||||||
|                     { |                     { | ||||||
|                         dog = settings; |                         dog = settings; | ||||||
|                         data = (int)context.Data; |                         data = (int)context.Data; | ||||||
| @@ -1145,7 +1145,7 @@ public sealed partial class CommandAppTests | |||||||
|             { |             { | ||||||
|                 cfg.AddBranch("a", d => |                 cfg.AddBranch("a", d => | ||||||
|                 { |                 { | ||||||
|                     d.AddDelegate("b", _ => 0); |                     d.AddDelegate("b", (_, _) => 0); | ||||||
|                 }); |                 }); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
| @@ -1165,7 +1165,7 @@ public sealed partial class CommandAppTests | |||||||
|             var app = new CommandAppTester(); |             var app = new CommandAppTester(); | ||||||
|             app.Configure(cfg => |             app.Configure(cfg => | ||||||
|             { |             { | ||||||
|                 cfg.AddDelegate("a", _ => 0); |                 cfg.AddDelegate("a", (_, _) => 0); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // When |             // When | ||||||
| @@ -1189,7 +1189,7 @@ public sealed partial class CommandAppTests | |||||||
|             { |             { | ||||||
|                 config.PropagateExceptions(); |                 config.PropagateExceptions(); | ||||||
|                 config.AddAsyncDelegate<DogSettings>( |                 config.AddAsyncDelegate<DogSettings>( | ||||||
|                     "foo", (context, settings) => |                     "foo", (context, settings, _) => | ||||||
|                     { |                     { | ||||||
|                         dog = settings; |                         dog = settings; | ||||||
|                         data = (int)context.Data; |                         data = (int)context.Data; | ||||||
| @@ -1222,7 +1222,7 @@ public sealed partial class CommandAppTests | |||||||
|                 config.AddBranch<AnimalSettings>("foo", foo => |                 config.AddBranch<AnimalSettings>("foo", foo => | ||||||
|                 { |                 { | ||||||
|                     foo.AddDelegate<DogSettings>( |                     foo.AddDelegate<DogSettings>( | ||||||
|                         "bar", (context, settings) => |                         "bar", (context, settings, _) => | ||||||
|                         { |                         { | ||||||
|                             dog = settings; |                             dog = settings; | ||||||
|                             data = (int)context.Data; |                             data = (int)context.Data; | ||||||
| @@ -1256,7 +1256,7 @@ public sealed partial class CommandAppTests | |||||||
|                 config.AddBranch<AnimalSettings>("foo", foo => |                 config.AddBranch<AnimalSettings>("foo", foo => | ||||||
|                 { |                 { | ||||||
|                     foo.AddAsyncDelegate<DogSettings>( |                     foo.AddAsyncDelegate<DogSettings>( | ||||||
|                         "bar", (context, settings) => |                         "bar", (context, settings, _) => | ||||||
|                         { |                         { | ||||||
|                             dog = settings; |                             dog = settings; | ||||||
|                             data = (int)context.Data; |                             data = (int)context.Data; | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ public sealed class CommandAppTesterTests | |||||||
|             _console = console; |             _console = console; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings) |         public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             _console.Write(settings.Greeting); |             _console.Write(settings.Greeting); | ||||||
|             return 0; |             return 0; | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ public sealed class InteractiveCommandTests | |||||||
|             _console = console; |             _console = console; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public override int Execute(CommandContext context) |         public override int Execute(CommandContext context, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var fruits = _console.Prompt( |             var fruits = _console.Prompt( | ||||||
|                 new MultiSelectionPrompt<string>() |                 new MultiSelectionPrompt<string>() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user