fixed line-endings

This commit is contained in:
Nils Andresen 2023-11-27 12:41:08 +01:00 committed by Patrik Svensson
parent 989c0b9904
commit 44300c871f
79 changed files with 2696 additions and 2696 deletions

View File

@ -1,77 +1,77 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Docs.Extensions; using Docs.Extensions;
using Docs.Shortcodes; using Docs.Shortcodes;
using Docs.Utilities; using Docs.Utilities;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Statiq.App; using Statiq.App;
using Statiq.Common; using Statiq.Common;
using Statiq.Core; using Statiq.Core;
using Statiq.Web; using Statiq.Web;
namespace Docs namespace Docs
{ {
public static class Program public static class Program
{ {
public static async Task<int> Main(string[] args) => public static async Task<int> Main(string[] args) =>
await Bootstrapper.Factory await Bootstrapper.Factory
.CreateWeb(args) .CreateWeb(args)
.AddSetting(Keys.Host, "spectreconsole.net") .AddSetting(Keys.Host, "spectreconsole.net")
.AddSetting(Keys.LinksUseHttps, true) .AddSetting(Keys.LinksUseHttps, true)
.AddSetting(Constants.EditLink, ConfigureEditLink()) .AddSetting(Constants.EditLink, ConfigureEditLink())
.AddSetting(Constants.SourceFiles, new List<string> .AddSetting(Constants.SourceFiles, new List<string>
{ {
"../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs" "../../src/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs"
}) })
.AddSetting(Constants.ExampleSourceFiles, new List<string> .AddSetting(Constants.ExampleSourceFiles, new List<string>
{ {
"../../examples/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../examples/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
} }
) )
.ConfigureServices(i => .ConfigureServices(i =>
{ {
i.AddSingleton(new TypeNameLinks()); i.AddSingleton(new TypeNameLinks());
}) })
.ConfigureSite("spectreconsole", "spectre.console", "main") .ConfigureSite("spectreconsole", "spectre.console", "main")
.AddShortcode("Children", typeof(ChildrenShortcode)) .AddShortcode("Children", typeof(ChildrenShortcode))
.AddShortcode("ColorTable", typeof(ColorTableShortcode)) .AddShortcode("ColorTable", typeof(ColorTableShortcode))
.AddShortcode("EmojiTable", typeof(EmojiTableShortcode)) .AddShortcode("EmojiTable", typeof(EmojiTableShortcode))
.AddShortcode("Alert", typeof(AlertShortcode)) .AddShortcode("Alert", typeof(AlertShortcode))
.AddShortcode("Info", typeof(InfoShortcode)) .AddShortcode("Info", typeof(InfoShortcode))
.AddShortcode("AsciiCast", typeof(AsciiCastShortcode)) .AddShortcode("AsciiCast", typeof(AsciiCastShortcode))
.AddShortcode("Example", typeof(ExampleSnippet)) .AddShortcode("Example", typeof(ExampleSnippet))
.AddPipelines() .AddPipelines()
.BuildPipeline( .BuildPipeline(
"Bootstrap", "Bootstrap",
builder => builder builder => builder
.WithInputReadFiles("../node_modules/asciinema-player/dist/bundle/asciinema-player.js") .WithInputReadFiles("../node_modules/asciinema-player/dist/bundle/asciinema-player.js")
.WithProcessModules(new SetDestination(Config.FromDocument(doc => new NormalizedPath($"./assets/{doc.Source.FileName}")), true)) .WithProcessModules(new SetDestination(Config.FromDocument(doc => new NormalizedPath($"./assets/{doc.Source.FileName}")), true))
.WithOutputWriteFiles() .WithOutputWriteFiles()
) )
.AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("npm", "install --audit false --fund false --progress false") .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("npm", "install --audit false --fund false --progress false")
{ {
LogErrors = false LogErrors = false
}) })
.AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("dotnet", "playwright install chromium")) .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("dotnet", "playwright install chromium"))
.AddProcess(ProcessTiming.BeforeDeployment, _ => new ProcessLauncher("npm", "run build:tailwind") .AddProcess(ProcessTiming.BeforeDeployment, _ => new ProcessLauncher("npm", "run build:tailwind")
{ {
LogErrors = false LogErrors = false
}) })
.RunAsync(); .RunAsync();
private static Config<string> ConfigureEditLink() private static Config<string> ConfigureEditLink()
{ {
return Config.FromDocument((doc, ctx) => return Config.FromDocument((doc, ctx) =>
{ {
return string.Format("https://github.com/{0}/{1}/edit/{2}/docs/input/{3}", return string.Format("https://github.com/{0}/{1}/edit/{2}/docs/input/{3}",
ctx.GetString(Constants.Site.Owner), ctx.GetString(Constants.Site.Owner),
ctx.GetString(Constants.Site.Repository), ctx.GetString(Constants.Site.Repository),
ctx.GetString(Constants.Site.Branch), ctx.GetString(Constants.Site.Branch),
doc.Source.GetRelativeInputPath()); doc.Source.GetRelativeInputPath());
}); });
} }
} }
} }

View File

@ -4,7 +4,7 @@ Description: "*Spectre.Console* makes it easy to write text with different style
Highlights: Highlights:
- Bold, Italic, Underline, strikethrough - Bold, Italic, Underline, strikethrough
- Dim, Invert - Dim, Invert
- Conceal, slowblink, rapidblink - Conceal, slowblink, rapidblink
- Links - Links
--- ---
@ -46,9 +46,9 @@ Note that what styles that can be used is defined by the system or your terminal
<tr> <tr>
<td><code>strikethrough</code></td> <td><code>strikethrough</code></td>
<td>Shows text with a horizontal line through the center</td> <td>Shows text with a horizontal line through the center</td>
</tr> </tr>
<tr> <tr>
<td><code>link</link></td> <td><code>link</link></td>
<td>Creates a clickable link within text</td> <td>Creates a clickable link within text</td>
</tr> </tr>
</table> </table>

View File

@ -1,47 +1,47 @@
Title: Command Help Title: Command Help
Order: 13 Order: 13
Description: "Console applications built with *Spectre.Console.Cli* include automatically generated help command line help." Description: "Console applications built with *Spectre.Console.Cli* include automatically generated help command line help."
--- ---
Console applications built with `Spectre.Console.Cli` include automatically generated help which is displayed when `-h` or `--help` has been specified on the command line. Console applications built with `Spectre.Console.Cli` include automatically generated help which is displayed when `-h` or `--help` has been specified on the command line.
The automatically generated help is derived from the configured commands and their command settings. The automatically generated help is derived from the configured commands and their command settings.
The help is also context aware and tailored depending on what has been specified on the command line before it. For example, The help is also context aware and tailored depending on what has been specified on the command line before it. For example,
1. When `-h` or `--help` appears immediately after the application name (eg. `application.exe --help`), then the help displayed is a high-level summary of the application, including any command line examples and a listing of all possible commands the user can execute. 1. When `-h` or `--help` appears immediately after the application name (eg. `application.exe --help`), then the help displayed is a high-level summary of the application, including any command line examples and a listing of all possible commands the user can execute.
2. When `-h` or `--help` appears immediately after a command has been specified (eg. `application.exe command --help`), then the help displayed is specific to the command and includes information about command specific switches and any default values. 2. When `-h` or `--help` appears immediately after a command has been specified (eg. `application.exe command --help`), then the help displayed is specific to the command and includes information about command specific switches and any default values.
`HelpProvider` is the `Spectre.Console` class responsible for determining context and preparing the help text to write to the console. It is an implementation of the public interface `IHelpProvider`. `HelpProvider` is the `Spectre.Console` class responsible for determining context and preparing the help text to write to the console. It is an implementation of the public interface `IHelpProvider`.
## Custom help providers ## Custom help providers
Whilst it shouldn't be common place to implement your own help provider, it is however possible. Whilst it shouldn't be common place to implement your own help provider, it is however possible.
You are able to implement your own `IHelpProvider` and configure a `CommandApp` to use that instead of the Spectre.Console help provider. You are able to implement your own `IHelpProvider` and configure a `CommandApp` to use that instead of the Spectre.Console help provider.
```csharp ```csharp
using Spectre.Console.Cli; using Spectre.Console.Cli;
namespace Help; namespace Help;
public static class Program public static class Program
{ {
public static int Main(string[] args) public static int Main(string[] args)
{ {
var app = new CommandApp<DefaultCommand>(); var app = new CommandApp<DefaultCommand>();
app.Configure(config => app.Configure(config =>
{ {
// Register the custom help provider // Register the custom help provider
config.SetHelpProvider(new CustomHelpProvider(config.Settings)); config.SetHelpProvider(new CustomHelpProvider(config.Settings));
}); });
return app.Run(args); return app.Run(args);
} }
} }
``` ```
There is a working [example of a custom help provider](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Help) demonstrating this. There is a working [example of a custom help provider](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Help) demonstrating this.

View File

@ -1,119 +1,119 @@
Title: Markup Title: Markup
Order: 30 Order: 30
Description: The Markup class allows you to output rich text to the console. Description: The Markup class allows you to output rich text to the console.
Highlights: Highlights:
- Easily add *color*. - Easily add *color*.
- Add hyperlinks to for supported terminals. - Add hyperlinks to for supported terminals.
- Emoji 🚀 parsing. - Emoji 🚀 parsing.
Reference: Reference:
- M:Spectre.Console.AnsiConsole.Markup(System.String) - M:Spectre.Console.AnsiConsole.Markup(System.String)
- M:Spectre.Console.AnsiConsole.MarkupLine(System.String) - M:Spectre.Console.AnsiConsole.MarkupLine(System.String)
- T:Spectre.Console.Markup - T:Spectre.Console.Markup
--- ---
The `Markup` class allows you to output rich text to the console. The `Markup` class allows you to output rich text to the console.
## Syntax ## Syntax
Console markup uses a syntax inspired by bbcode. If you write the style (see [Styles](xref:styles)) Console markup uses a syntax inspired by bbcode. If you write the style (see [Styles](xref:styles))
in square brackets, e.g. `[bold red]`, that style will apply until it is closed with a `[/]`. in square brackets, e.g. `[bold red]`, that style will apply until it is closed with a `[/]`.
```csharp ```csharp
AnsiConsole.Write(new Markup("[bold yellow]Hello[/] [red]World![/]")); AnsiConsole.Write(new Markup("[bold yellow]Hello[/] [red]World![/]"));
``` ```
The `Markup` class implements `IRenderable` which means that you The `Markup` class implements `IRenderable` which means that you
can use this in tables, grids, and panels. Most classes that support can use this in tables, grids, and panels. Most classes that support
rendering of `IRenderable` also have overloads for rendering rich text. rendering of `IRenderable` also have overloads for rendering rich text.
```csharp ```csharp
var table = new Table(); var table = new Table();
table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]"))); table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]")));
table.AddColumn(new TableColumn("[blue]Bar[/]")); table.AddColumn(new TableColumn("[blue]Bar[/]"));
AnsiConsole.Write(table); AnsiConsole.Write(table);
``` ```
## Convenience methods ## Convenience methods
There are also convenience methods on `AnsiConsole` that can be used There are also convenience methods on `AnsiConsole` that can be used
to write markup text to the console without instantiating a new `Markup` to write markup text to the console without instantiating a new `Markup`
instance. instance.
```csharp ```csharp
AnsiConsole.Markup("[underline green]Hello[/] "); AnsiConsole.Markup("[underline green]Hello[/] ");
AnsiConsole.MarkupLine("[bold]World[/]"); AnsiConsole.MarkupLine("[bold]World[/]");
``` ```
## Escaping format characters ## Escaping format characters
To output a `[` you use `[[`, and to output a `]` you use `]]`. To output a `[` you use `[[`, and to output a `]` you use `]]`.
```csharp ```csharp
AnsiConsole.Markup("[[Hello]] "); // [Hello] AnsiConsole.Markup("[[Hello]] "); // [Hello]
AnsiConsole.Markup("[red][[World]][/]"); // [World] AnsiConsole.Markup("[red][[World]][/]"); // [World]
``` ```
You can also use the `EscapeMarkup` extension method. You can also use the `EscapeMarkup` extension method.
```csharp ```csharp
AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".EscapeMarkup()); AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".EscapeMarkup());
``` ```
You can also use the `Markup.Escape` method. You can also use the `Markup.Escape` method.
```csharp ```csharp
AnsiConsole.Markup("[red]{0}[/]", Markup.Escape("Hello [World]")); AnsiConsole.Markup("[red]{0}[/]", Markup.Escape("Hello [World]"));
``` ```
## Escaping Interpolated Strings ## Escaping Interpolated Strings
When working with interpolated strings, you can use the `MarkupInterpolated` and `MarkupLineInterpolated` methods to automatically escape the values in the interpolated string "holes". When working with interpolated strings, you can use the `MarkupInterpolated` and `MarkupLineInterpolated` methods to automatically escape the values in the interpolated string "holes".
```csharp ```csharp
string hello = "Hello [World]"; string hello = "Hello [World]";
AnsiConsole.MarkupInterpolated($"[red]{hello}[/]"); AnsiConsole.MarkupInterpolated($"[red]{hello}[/]");
``` ```
## Setting background color ## Setting background color
You can set the background color in markup by prefixing the color with `on`. You can set the background color in markup by prefixing the color with `on`.
```csharp ```csharp
AnsiConsole.Markup("[bold yellow on blue]Hello[/]"); AnsiConsole.Markup("[bold yellow on blue]Hello[/]");
AnsiConsole.Markup("[default on blue]World[/]"); AnsiConsole.Markup("[default on blue]World[/]");
``` ```
## Rendering emojis ## Rendering emojis
To output an emoji as part of markup, you can use emoji shortcodes. To output an emoji as part of markup, you can use emoji shortcodes.
```csharp ```csharp
AnsiConsole.Markup("Hello :globe_showing_europe_africa:!"); AnsiConsole.Markup("Hello :globe_showing_europe_africa:!");
``` ```
For a list of emoji, see the [Emojis](xref:emojis) appendix section. For a list of emoji, see the [Emojis](xref:emojis) appendix section.
## Colors ## Colors
In the examples above, all colors were referenced by their name, In the examples above, all colors were referenced by their name,
but you can also use the hex or rgb representation for colors in markdown. but you can also use the hex or rgb representation for colors in markdown.
```csharp ```csharp
AnsiConsole.Markup("[red]Foo[/] "); AnsiConsole.Markup("[red]Foo[/] ");
AnsiConsole.Markup("[#ff0000]Bar[/] "); AnsiConsole.Markup("[#ff0000]Bar[/] ");
AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] "); AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] ");
``` ```
For a list of colors, see the [Colors](xref:colors) appendix section. For a list of colors, see the [Colors](xref:colors) appendix section.
## Links ## Links
To output a clickable link, you can use the `[link]` style. To output a clickable link, you can use the `[link]` style.
```csharp ```csharp
AnsiConsole.Markup("[link]https://spectreconsole.net[/]"); AnsiConsole.Markup("[link]https://spectreconsole.net[/]");
AnsiConsole.Markup("[link=https://spectreconsole.net]Spectre Console Documentation[/]"); AnsiConsole.Markup("[link=https://spectreconsole.net]Spectre Console Documentation[/]");
``` ```
## Styles ## Styles
For a list of styles, see the [Styles](xref:styles) appendix section. For a list of styles, see the [Styles](xref:styles) appendix section.

View File

@ -63,22 +63,22 @@ What's the secret number? _
```text ```text
Enter password: ************_ Enter password: ************_
``` ```
## Masks ## Masks
<?# Example symbol="M:Prompt.Program.AskPasswordWithCustomMask" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskPasswordWithCustomMask" project="Prompt" /?>
```text ```text
Enter password: ------------_ Enter password: ------------_
``` ```
You can utilize a null character to completely hide input. You can utilize a null character to completely hide input.
<?# Example symbol="M:Prompt.Program.AskPasswordWithNullMask" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskPasswordWithNullMask" project="Prompt" /?>
```text ```text
Enter password: _ Enter password: _
``` ```

View File

@ -1,4 +1,4 @@
Title: Calendar Title: Calendar
Order: 40 Order: 40
RedirectFrom: calendar RedirectFrom: calendar
Description: "The **Calendar** is used to render a calendar to the terminal." Description: "The **Calendar** is used to render a calendar to the terminal."

View File

@ -1,4 +1,4 @@
using Statiq.App; using Statiq.App;
using Statiq.Common; using Statiq.Common;
using Statiq.Web; using Statiq.Web;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;

View File

@ -1,4 +1,4 @@
namespace Docs.Extensions namespace Docs.Extensions
{ {
public static class StringExtensions public static class StringExtensions
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Docs.Models namespace Docs.Models
{ {

View File

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using Docs.Utilities; using Docs.Utilities;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -1,4 +1,4 @@
using Statiq.Common; using Statiq.Common;
using Statiq.Web.GitHub; using Statiq.Web.GitHub;
using Statiq.Web.Netlify; using Statiq.Web.Netlify;

View File

@ -1,119 +1,119 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Playwright; using Microsoft.Playwright;
using Statiq.Common; using Statiq.Common;
using Statiq.Core; using Statiq.Core;
using Statiq.Web; using Statiq.Web;
using Statiq.Web.Modules; using Statiq.Web.Modules;
using Statiq.Web.Pipelines; using Statiq.Web.Pipelines;
namespace Docs.Pipelines namespace Docs.Pipelines
{ {
public class SocialImages : Pipeline public class SocialImages : Pipeline
{ {
public SocialImages() public SocialImages()
{ {
Dependencies.AddRange(nameof(Inputs)); Dependencies.AddRange(nameof(Inputs));
ProcessModules = new ModuleList ProcessModules = new ModuleList
{ {
new GetPipelineDocuments(ContentType.Content), new GetPipelineDocuments(ContentType.Content),
// Filter to non-archive content // Filter to non-archive content
new FilterDocuments(Config.FromDocument(doc => !Archives.IsArchive(doc))), new FilterDocuments(Config.FromDocument(doc => !Archives.IsArchive(doc))),
// Process the content // Process the content
new CacheDocuments new CacheDocuments
{ {
new AddTitle(), new AddTitle(),
new SetDestination(true), new SetDestination(true),
new ExecuteIf(Config.FromSetting(WebKeys.OptimizeContentFileNames, true)) new ExecuteIf(Config.FromSetting(WebKeys.OptimizeContentFileNames, true))
{ {
new OptimizeFileName() new OptimizeFileName()
}, },
new GenerateSocialImage(), new GenerateSocialImage(),
} }
}; };
OutputModules = new ModuleList { new WriteFiles() }; OutputModules = new ModuleList { new WriteFiles() };
} }
} }
class GenerateSocialImage : ParallelModule class GenerateSocialImage : ParallelModule
{ {
private IPlaywright _playwright; private IPlaywright _playwright;
private IBrowser _browser; private IBrowser _browser;
private WebApplication _app; private WebApplication _app;
private IBrowserContext _context; private IBrowserContext _context;
protected override async Task BeforeExecutionAsync(IExecutionContext context) protected override async Task BeforeExecutionAsync(IExecutionContext context)
{ {
var builder = WebApplication.CreateBuilder(); var builder = WebApplication.CreateBuilder();
builder.Logging.ClearProviders(); builder.Logging.ClearProviders();
builder.Services builder.Services
.AddRazorPages() .AddRazorPages()
.WithRazorPagesRoot("/src/SocialCards/"); .WithRazorPagesRoot("/src/SocialCards/");
_app = builder.Build(); _app = builder.Build();
_app.MapRazorPages(); _app.MapRazorPages();
_app.UseStaticFiles(new StaticFileOptions _app.UseStaticFiles(new StaticFileOptions
{ {
FileProvider = new PhysicalFileProvider( FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "src/SocialCards")), Path.Combine(builder.Environment.ContentRootPath, "src/SocialCards")),
RequestPath = "/static" RequestPath = "/static"
}); });
await _app.StartAsync().ConfigureAwait(false); await _app.StartAsync().ConfigureAwait(false);
_playwright = await Playwright.CreateAsync().ConfigureAwait(false); _playwright = await Playwright.CreateAsync().ConfigureAwait(false);
_browser = await _playwright.Chromium.LaunchAsync().ConfigureAwait(false); _browser = await _playwright.Chromium.LaunchAsync().ConfigureAwait(false);
_context = await _browser.NewContextAsync(new BrowserNewContextOptions { _context = await _browser.NewContextAsync(new BrowserNewContextOptions {
ViewportSize = new ViewportSize { Width = 1200, Height = 618 }, ViewportSize = new ViewportSize { Width = 1200, Height = 618 },
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
protected override async Task FinallyAsync(IExecutionContext context) protected override async Task FinallyAsync(IExecutionContext context)
{ {
await _context.DisposeAsync().ConfigureAwait(false); await _context.DisposeAsync().ConfigureAwait(false);
await _browser.DisposeAsync().ConfigureAwait(false); await _browser.DisposeAsync().ConfigureAwait(false);
_playwright.Dispose(); _playwright.Dispose();
await _app.DisposeAsync().ConfigureAwait(false); await _app.DisposeAsync().ConfigureAwait(false);
await base.FinallyAsync(context); await base.FinallyAsync(context);
} }
protected override async Task<IEnumerable<IDocument>> ExecuteInputAsync(IDocument input, IExecutionContext context) protected override async Task<IEnumerable<IDocument>> ExecuteInputAsync(IDocument input, IExecutionContext context)
{ {
var url = _app.Urls.FirstOrDefault(u => u.StartsWith("http://")); var url = _app.Urls.FirstOrDefault(u => u.StartsWith("http://"));
var page = await _context.NewPageAsync().ConfigureAwait(false); var page = await _context.NewPageAsync().ConfigureAwait(false);
var title = input.GetString("Title"); var title = input.GetString("Title");
var description = input.GetString("Description"); var description = input.GetString("Description");
var highlights = input.GetList<string>("Highlights") ?? Array.Empty<string>(); var highlights = input.GetList<string>("Highlights") ?? Array.Empty<string>();
await page.GotoAsync($"{url}/?title={title}&desc={description}&highlights={string.Join("||", highlights)}"); await page.GotoAsync($"{url}/?title={title}&desc={description}&highlights={string.Join("||", highlights)}");
// This will not just wait for the page to load over the network, but it'll also give // This will not just wait for the page to load over the network, but it'll also give
// chrome a chance to complete rendering of the fonts while the wait timeout completes. // chrome a chance to complete rendering of the fonts while the wait timeout completes.
await page.WaitForLoadStateAsync(LoadState.NetworkIdle).ConfigureAwait(false); await page.WaitForLoadStateAsync(LoadState.NetworkIdle).ConfigureAwait(false);
var bytes = await page.ScreenshotAsync().ConfigureAwait(false); var bytes = await page.ScreenshotAsync().ConfigureAwait(false);
await page.CloseAsync().ConfigureAwait(false); await page.CloseAsync().ConfigureAwait(false);
var destination = input.Destination.InsertSuffix("-social").ChangeExtension("png"); var destination = input.Destination.InsertSuffix("-social").ChangeExtension("png");
var doc = context.CreateDocument( var doc = context.CreateDocument(
input.Source, input.Source,
destination, destination,
new MetadataItems { { "DocId", input.Id }}, new MetadataItems { { "DocId", input.Id }},
context.GetContentProvider(bytes)); context.GetContentProvider(bytes));
return new[] { doc }; return new[] { doc };
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Docs.Extensions; using Docs.Extensions;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;

View File

@ -15,22 +15,22 @@ public static class Program
{ {
config.SetApplicationName("fake-dotnet"); config.SetApplicationName("fake-dotnet");
config.ValidateExamples(); config.ValidateExamples();
config.AddExample("run", "--no-build"); config.AddExample("run", "--no-build");
// Run // Run
config.AddCommand<RunCommand>("run"); config.AddCommand<RunCommand>("run");
// Add // Add
config.AddBranch<AddSettings>("add", add => config.AddBranch<AddSettings>("add", add =>
{ {
add.SetDescription("Add a package or reference to a .NET project"); add.SetDescription("Add a package or reference to a .NET project");
add.AddCommand<AddPackageCommand>("package"); add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference"); add.AddCommand<AddReferenceCommand>("reference");
}); });
// Serve // Serve
config.AddCommand<ServeCommand>("serve") config.AddCommand<ServeCommand>("serve")
.WithExample("serve", "-o", "firefox") .WithExample("serve", "-o", "firefox")
.WithExample("serve", "--port", "80", "-o", "firefox"); .WithExample("serve", "--port", "80", "-o", "firefox");
}); });

View File

@ -1,30 +1,30 @@
using System.Linq; using System.Linq;
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli; using Spectre.Console.Cli;
using Spectre.Console.Cli.Help; using Spectre.Console.Cli.Help;
using Spectre.Console.Rendering; using Spectre.Console.Rendering;
namespace Help; namespace Help;
/// <summary> /// <summary>
/// Example showing how to extend the built-in Spectre.Console help provider /// Example showing how to extend the built-in Spectre.Console help provider
/// by rendering a custom banner at the top of the help information /// by rendering a custom banner at the top of the help information
/// </summary> /// </summary>
internal class CustomHelpProvider : HelpProvider internal class CustomHelpProvider : HelpProvider
{ {
public CustomHelpProvider(ICommandAppSettings settings) public CustomHelpProvider(ICommandAppSettings settings)
: base(settings) : base(settings)
{ {
} }
public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command) public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command)
{ {
return new[] return new[]
{ {
new Text("--------------------------------------"), Text.NewLine, new Text("--------------------------------------"), Text.NewLine,
new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine,
new Text("--------------------------------------"), Text.NewLine, new Text("--------------------------------------"), Text.NewLine,
Text.NewLine, Text.NewLine,
}; };
} }
} }

View File

@ -1,20 +1,20 @@
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Cli; using Spectre.Console.Cli;
namespace Help; namespace Help;
public sealed class DefaultCommand : Command public sealed class DefaultCommand : Command
{ {
private IAnsiConsole _console; private IAnsiConsole _console;
public DefaultCommand(IAnsiConsole console) public DefaultCommand(IAnsiConsole console)
{ {
_console = console; _console = console;
} }
public override int Execute(CommandContext context) public override int Execute(CommandContext context)
{ {
_console.WriteLine("Hello world"); _console.WriteLine("Hello world");
return 0; return 0;
} }
} }

View File

@ -1,19 +1,19 @@
using Spectre.Console.Cli; using Spectre.Console.Cli;
namespace Help; namespace Help;
public static class Program public static class Program
{ {
public static int Main(string[] args) public static int Main(string[] args)
{ {
var app = new CommandApp<DefaultCommand>(); var app = new CommandApp<DefaultCommand>();
app.Configure(config => app.Configure(config =>
{ {
// Register the custom help provider // Register the custom help provider
config.SetHelpProvider(new CustomHelpProvider(config.Settings)); config.SetHelpProvider(new CustomHelpProvider(config.Settings));
}); });
return app.Run(args); return app.Run(args);
} }
} }

View File

@ -33,11 +33,11 @@ namespace Prompt
var age = AskAge(); var age = AskAge();
WriteDivider("Secrets"); WriteDivider("Secrets");
var password = AskPassword(); var password = AskPassword();
WriteDivider("Mask"); WriteDivider("Mask");
var mask = AskPasswordWithCustomMask(); var mask = AskPasswordWithCustomMask();
WriteDivider("Null Mask"); WriteDivider("Null Mask");
var nullMask = AskPasswordWithNullMask(); var nullMask = AskPasswordWithNullMask();
@ -54,8 +54,8 @@ namespace Prompt
.AddRow("[grey]Favorite fruit[/]", fruit) .AddRow("[grey]Favorite fruit[/]", fruit)
.AddRow("[grey]Favorite sport[/]", sport) .AddRow("[grey]Favorite sport[/]", sport)
.AddRow("[grey]Age[/]", age.ToString()) .AddRow("[grey]Age[/]", age.ToString())
.AddRow("[grey]Password[/]", password) .AddRow("[grey]Password[/]", password)
.AddRow("[grey]Mask[/]", mask) .AddRow("[grey]Mask[/]", mask)
.AddRow("[grey]Null Mask[/]", nullMask) .AddRow("[grey]Null Mask[/]", nullMask)
.AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color)); .AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color));
} }
@ -153,22 +153,22 @@ namespace Prompt
new TextPrompt<string>("Enter [green]password[/]?") new TextPrompt<string>("Enter [green]password[/]?")
.PromptStyle("red") .PromptStyle("red")
.Secret()); .Secret());
} }
public static string AskPasswordWithCustomMask() public static string AskPasswordWithCustomMask()
{ {
return AnsiConsole.Prompt( return AnsiConsole.Prompt(
new TextPrompt<string>("Enter [green]password[/]?") new TextPrompt<string>("Enter [green]password[/]?")
.PromptStyle("red") .PromptStyle("red")
.Secret('-')); .Secret('-'));
} }
public static string AskPasswordWithNullMask() public static string AskPasswordWithNullMask()
{ {
return AnsiConsole.Prompt( return AnsiConsole.Prompt(
new TextPrompt<string>("Enter [green]password[/]?") new TextPrompt<string>("Enter [green]password[/]?")
.PromptStyle("red") .PromptStyle("red")
.Secret(null)); .Secret(null));
} }
public static string AskColor() public static string AskColor()

View File

@ -1,4 +1,4 @@
using Spectre.Console; using Spectre.Console;
namespace Generator.Commands namespace Generator.Commands
{ {

View File

@ -1,4 +1,4 @@
using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Simplification;
namespace Spectre.Console.Analyzer.CodeActions; namespace Spectre.Console.Analyzer.CodeActions;
@ -32,171 +32,171 @@ public class SwitchToAnsiConsoleAction : CodeAction
/// <inheritdoc /> /// <inheritdoc />
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken) protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{ {
var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false); var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
var compilation = editor.SemanticModel.Compilation; var compilation = editor.SemanticModel.Compilation;
var operation = editor.SemanticModel.GetOperation(_originalInvocation, cancellationToken) as IInvocationOperation; var operation = editor.SemanticModel.GetOperation(_originalInvocation, cancellationToken) as IInvocationOperation;
if (operation == null) if (operation == null)
{ {
return _document; return _document;
} }
// If there is an IAnsiConsole passed into the method then we'll use it. // If there is an IAnsiConsole passed into the method then we'll use it.
// otherwise we'll check for a field level instance. // otherwise we'll check for a field level instance.
// if neither of those exist we'll fall back to the static param. // if neither of those exist we'll fall back to the static param.
var spectreConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole"); var spectreConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
var iansiConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.IAnsiConsole"); var iansiConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.IAnsiConsole");
ISymbol? accessibleConsoleSymbol = spectreConsoleSymbol; ISymbol? accessibleConsoleSymbol = spectreConsoleSymbol;
if (iansiConsoleSymbol != null) if (iansiConsoleSymbol != null)
{ {
var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition); var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition);
foreach (var symbol in editor.SemanticModel.LookupSymbols(operation.Syntax.GetLocation().SourceSpan.Start)) foreach (var symbol in editor.SemanticModel.LookupSymbols(operation.Syntax.GetLocation().SourceSpan.Start))
{ {
// LookupSymbols check the accessibility of the symbol, but it can // LookupSymbols check the accessibility of the symbol, but it can
// suggest instance members when the current context is static. // suggest instance members when the current context is static.
var symbolType = symbol switch var symbolType = symbol switch
{ {
IParameterSymbol parameter => parameter.Type, IParameterSymbol parameter => parameter.Type,
IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type, IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type,
IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type, IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type,
ILocalSymbol local => local.Type, ILocalSymbol local => local.Type,
_ => null, _ => null,
}; };
// Locals can be returned even if there are not valid in the current context. For instance, // Locals can be returned even if there are not valid in the current context. For instance,
// it can return locals declared after the current location. Or it can return locals that // it can return locals declared after the current location. Or it can return locals that
// should not be accessible in a static local function. // should not be accessible in a static local function.
// //
// void Sample() // void Sample()
// { // {
// int local = 0; // int local = 0;
// static void LocalFunction() => local; <-- local is invalid here but LookupSymbols suggests it // static void LocalFunction() => local; <-- local is invalid here but LookupSymbols suggests it
// } // }
// //
// Parameters from the ancestor methods or local functions are also returned even if the operation is in a static local function. // Parameters from the ancestor methods or local functions are also returned even if the operation is in a static local function.
if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter) if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter)
{ {
var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start; var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start;
// The local is not part of the source tree // The local is not part of the source tree
if (localPosition == null) if (localPosition == null)
{ {
break; break;
} }
// The local is declared after the current expression // The local is declared after the current expression
if (localPosition > _originalInvocation.Span.Start) if (localPosition > _originalInvocation.Span.Start)
{ {
break; break;
} }
// The local is declared outside the static local function // The local is declared outside the static local function
if (isInStaticContext && localPosition < parentStaticMemberStartPosition) if (isInStaticContext && localPosition < parentStaticMemberStartPosition)
{ {
break; break;
} }
} }
if (IsOrImplementSymbol(symbolType, iansiConsoleSymbol)) if (IsOrImplementSymbol(symbolType, iansiConsoleSymbol))
{ {
accessibleConsoleSymbol = symbol; accessibleConsoleSymbol = symbol;
break; break;
} }
} }
} }
if (accessibleConsoleSymbol == null) if (accessibleConsoleSymbol == null)
{ {
return _document; return _document;
} }
// Replace the original invocation // Replace the original invocation
var generator = editor.Generator; var generator = editor.Generator;
var consoleExpression = accessibleConsoleSymbol switch var consoleExpression = accessibleConsoleSymbol switch
{ {
ITypeSymbol typeSymbol => generator.TypeExpression(typeSymbol, addImport: true).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation), ITypeSymbol typeSymbol => generator.TypeExpression(typeSymbol, addImport: true).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation),
_ => generator.IdentifierName(accessibleConsoleSymbol.Name), _ => generator.IdentifierName(accessibleConsoleSymbol.Name),
}; };
var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(consoleExpression, operation.TargetMethod.Name), _originalInvocation.ArgumentList.Arguments) var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(consoleExpression, operation.TargetMethod.Name), _originalInvocation.ArgumentList.Arguments)
.WithLeadingTrivia(_originalInvocation.GetLeadingTrivia()) .WithLeadingTrivia(_originalInvocation.GetLeadingTrivia())
.WithTrailingTrivia(_originalInvocation.GetTrailingTrivia()); .WithTrailingTrivia(_originalInvocation.GetTrailingTrivia());
editor.ReplaceNode(_originalInvocation, newExpression); editor.ReplaceNode(_originalInvocation, newExpression);
return editor.GetChangedDocument(); return editor.GetChangedDocument();
} }
private static bool IsOrImplementSymbol(ITypeSymbol? symbol, ITypeSymbol interfaceSymbol) private static bool IsOrImplementSymbol(ITypeSymbol? symbol, ITypeSymbol interfaceSymbol)
{ {
if (symbol == null) if (symbol == null)
{ {
return false; return false;
} }
if (SymbolEqualityComparer.Default.Equals(symbol, interfaceSymbol)) if (SymbolEqualityComparer.Default.Equals(symbol, interfaceSymbol))
{ {
return true; return true;
} }
foreach (var iface in symbol.AllInterfaces) foreach (var iface in symbol.AllInterfaces)
{ {
if (SymbolEqualityComparer.Default.Equals(iface, interfaceSymbol)) if (SymbolEqualityComparer.Default.Equals(iface, interfaceSymbol))
{ {
return true; return true;
} }
} }
return false; return false;
} }
private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition) private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition)
{ {
// Local functions can be nested, and an instance local function can be declared // Local functions can be nested, and an instance local function can be declared
// in a static local function. So, you need to continue to check ancestors when a // in a static local function. So, you need to continue to check ancestors when a
// local function is not static. // local function is not static.
foreach (var member in operation.Syntax.Ancestors()) foreach (var member in operation.Syntax.Ancestors())
{ {
if (member is LocalFunctionStatementSyntax localFunction) if (member is LocalFunctionStatementSyntax localFunction)
{ {
var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken); var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken);
if (symbol != null && symbol.IsStatic) if (symbol != null && symbol.IsStatic)
{ {
parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start; parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start;
return true; return true;
} }
} }
else if (member is LambdaExpressionSyntax lambdaExpression) else if (member is LambdaExpressionSyntax lambdaExpression)
{ {
var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol; var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol;
if (symbol != null && symbol.IsStatic) if (symbol != null && symbol.IsStatic)
{ {
parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start; parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start;
return true; return true;
} }
} }
else if (member is AnonymousMethodExpressionSyntax anonymousMethod) else if (member is AnonymousMethodExpressionSyntax anonymousMethod)
{ {
var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol; var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol;
if (symbol != null && symbol.IsStatic) if (symbol != null && symbol.IsStatic)
{ {
parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start; parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start;
return true; return true;
} }
} }
else if (member is MethodDeclarationSyntax methodDeclaration) else if (member is MethodDeclarationSyntax methodDeclaration)
{ {
parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start; parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start;
var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken); var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken);
return symbol != null && symbol.IsStatic; return symbol != null && symbol.IsStatic;
} }
} }
parentStaticMemberStartPosition = -1; parentStaticMemberStartPosition = -1;
return false; return false;
} }
} }

View File

@ -1,5 +1,5 @@
using Spectre.Console.Cli.Internal.Configuration; using Spectre.Console.Cli.Internal.Configuration;
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
/// <summary> /// <summary>

View File

@ -1,5 +1,5 @@
using Spectre.Console.Cli.Internal.Configuration; using Spectre.Console.Cli.Internal.Configuration;
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
/// <summary> /// <summary>
@ -49,8 +49,8 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
public Task<int> RunAsync(IEnumerable<string> args) public Task<int> RunAsync(IEnumerable<string> args)
{ {
return _app.RunAsync(args); return _app.RunAsync(args);
} }
internal Configurator GetConfigurator() internal Configurator GetConfigurator()
{ {
return _app.GetConfigurator(); return _app.GetConfigurator();

View File

@ -5,40 +5,40 @@ namespace Spectre.Console.Cli;
/// and <see cref="IConfigurator{TSettings}"/>. /// and <see cref="IConfigurator{TSettings}"/>.
/// </summary> /// </summary>
public static class ConfiguratorExtensions public static class ConfiguratorExtensions
{ {
/// <summary> /// <summary>
/// Sets the help provider for the application. /// Sets the help provider for the application.
/// </summary> /// </summary>
/// <param name="configurator">The configurator.</param> /// <param name="configurator">The configurator.</param>
/// <param name="helpProvider">The help provider to use.</param> /// <param name="helpProvider">The help provider to use.</param>
/// <returns>A configurator that can be used to configure the application further.</returns> /// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider) public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider)
{ {
if (configurator == null) if (configurator == null)
{ {
throw new ArgumentNullException(nameof(configurator)); throw new ArgumentNullException(nameof(configurator));
} }
configurator.SetHelpProvider(helpProvider); configurator.SetHelpProvider(helpProvider);
return configurator; return configurator;
} }
/// <summary> /// <summary>
/// Sets the help provider for the application. /// Sets the help provider for the application.
/// </summary> /// </summary>
/// <param name="configurator">The configurator.</param> /// <param name="configurator">The configurator.</param>
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam> /// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
/// <returns>A configurator that can be used to configure the application further.</returns> /// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator SetHelpProvider<T>(this IConfigurator configurator) public static IConfigurator SetHelpProvider<T>(this IConfigurator configurator)
where T : IHelpProvider where T : IHelpProvider
{ {
if (configurator == null) if (configurator == null)
{ {
throw new ArgumentNullException(nameof(configurator)); throw new ArgumentNullException(nameof(configurator));
} }
configurator.SetHelpProvider<T>(); configurator.SetHelpProvider<T>();
return configurator; return configurator;
} }
/// <summary> /// <summary>

View File

@ -11,22 +11,22 @@ namespace Spectre.Console.Cli.Help;
public class HelpProvider : IHelpProvider public class HelpProvider : IHelpProvider
{ {
private HelpProviderResources resources; private HelpProviderResources resources;
/// <summary> /// <summary>
/// Gets a value indicating how many examples from direct children to show in the help text. /// Gets a value indicating how many examples from direct children to show in the help text.
/// </summary> /// </summary>
protected virtual int MaximumIndirectExamples { get; } protected virtual int MaximumIndirectExamples { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether any default values for command options are shown in the help text. /// Gets a value indicating whether any default values for command options are shown in the help text.
/// </summary> /// </summary>
protected virtual bool ShowOptionDefaultValues { get; } protected virtual bool ShowOptionDefaultValues { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether a trailing period of a command description is trimmed in the help text. /// Gets a value indicating whether a trailing period of a command description is trimmed in the help text.
/// </summary> /// </summary>
protected virtual bool TrimTrailingPeriod { get; } protected virtual bool TrimTrailingPeriod { get; }
private sealed class HelpArgument private sealed class HelpArgument
{ {
public string Name { get; } public string Name { get; }
@ -74,14 +74,14 @@ public class HelpProvider : IHelpProvider
public static IReadOnlyList<HelpOption> Get(ICommandInfo? command, HelpProviderResources resources) public static IReadOnlyList<HelpOption> Get(ICommandInfo? command, HelpProviderResources resources)
{ {
var parameters = new List<HelpOption>(); var parameters = new List<HelpOption>();
parameters.Add(new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null)); parameters.Add(new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null));
// Version information applies to the entire application // Version information applies to the entire application
// Include the "-v" option in the help when at the root of the command line application // Include the "-v" option in the help when at the root of the command line application
// Don't allow the "-v" option if users have specified one or more sub-commands // Don't allow the "-v" option if users have specified one or more sub-commands
if ((command == null || command?.Parent == null) && !(command?.IsBranch ?? false)) if ((command == null || command?.Parent == null) && !(command?.IsBranch ?? false))
{ {
parameters.Add(new HelpOption("v", "version", null, null, resources.PrintVersionDescription, null)); parameters.Add(new HelpOption("v", "version", null, null, resources.PrintVersionDescription, null));
} }
parameters.AddRange(command?.Parameters.OfType<ICommandOption>().Where(o => !o.IsHidden).Select(o => parameters.AddRange(command?.Parameters.OfType<ICommandOption>().Where(o => !o.IsHidden).Select(o =>
@ -92,55 +92,55 @@ public class HelpProvider : IHelpProvider
?? Array.Empty<HelpOption>()); ?? Array.Empty<HelpOption>());
return parameters; return parameters;
} }
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HelpProvider"/> class. /// Initializes a new instance of the <see cref="HelpProvider"/> class.
/// </summary> /// </summary>
/// <param name="settings">The command line application settings used for configuration.</param> /// <param name="settings">The command line application settings used for configuration.</param>
public HelpProvider(ICommandAppSettings settings) public HelpProvider(ICommandAppSettings settings)
{ {
this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues; this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues;
this.MaximumIndirectExamples = settings.MaximumIndirectExamples; this.MaximumIndirectExamples = settings.MaximumIndirectExamples;
this.TrimTrailingPeriod = settings.TrimTrailingPeriod; this.TrimTrailingPeriod = settings.TrimTrailingPeriod;
resources = new HelpProviderResources(settings.Culture); resources = new HelpProviderResources(settings.Culture);
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
{ {
var result = new List<IRenderable>(); var result = new List<IRenderable>();
result.AddRange(GetHeader(model, command)); result.AddRange(GetHeader(model, command));
result.AddRange(GetDescription(model, command)); result.AddRange(GetDescription(model, command));
result.AddRange(GetUsage(model, command)); result.AddRange(GetUsage(model, command));
result.AddRange(GetExamples(model, command)); result.AddRange(GetExamples(model, command));
result.AddRange(GetArguments(model, command)); result.AddRange(GetArguments(model, command));
result.AddRange(GetOptions(model, command)); result.AddRange(GetOptions(model, command));
result.AddRange(GetCommands(model, command)); result.AddRange(GetCommands(model, command));
result.AddRange(GetFooter(model, command)); result.AddRange(GetFooter(model, command));
return result; return result;
} }
/// <summary> /// <summary>
/// Gets the header for the help information. /// Gets the header for the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command)
{ {
yield break; yield break;
} }
/// <summary> /// <summary>
/// Gets the description section of the help information. /// Gets the description section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetDescription(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetDescription(ICommandModel model, ICommandInfo? command)
{ {
if (command?.Description == null) if (command?.Description == null)
@ -152,14 +152,14 @@ public class HelpProvider : IHelpProvider
composer.Style("yellow", $"{resources.Description}:").LineBreak(); composer.Style("yellow", $"{resources.Description}:").LineBreak();
composer.Text(command.Description).LineBreak(); composer.Text(command.Description).LineBreak();
yield return composer.LineBreak(); yield return composer.LineBreak();
} }
/// <summary> /// <summary>
/// Gets the usage section of the help information. /// Gets the usage section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetUsage(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetUsage(ICommandModel model, ICommandInfo? command)
{ {
var composer = new Composer(); var composer = new Composer();
@ -219,26 +219,26 @@ public class HelpProvider : IHelpProvider
} }
if (command.IsBranch && command.DefaultCommand == null) if (command.IsBranch && command.DefaultCommand == null)
{ {
// The user must specify the command // The user must specify the command
parameters.Add($"[aqua]<{resources.Command}>[/]"); parameters.Add($"[aqua]<{resources.Command}>[/]");
} }
else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0) else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0)
{ {
// We are on a branch with a default command // We are on a branch with a default command
// The user can optionally specify the command // The user can optionally specify the command
parameters.Add($"[aqua][[{resources.Command}]][/]"); parameters.Add($"[aqua][[{resources.Command}]][/]");
} }
else if (command.IsDefaultCommand) else if (command.IsDefaultCommand)
{ {
var commands = model.Commands.Where(x => !x.IsHidden && !x.IsDefaultCommand).ToList(); var commands = model.Commands.Where(x => !x.IsHidden && !x.IsDefaultCommand).ToList();
if (commands.Count > 0) if (commands.Count > 0)
{ {
// Commands other than the default are present // Commands other than the default are present
// So make these optional in the usage statement // So make these optional in the usage statement
parameters.Add($"[aqua][[{resources.Command}]][/]"); parameters.Add($"[aqua][[{resources.Command}]][/]");
} }
} }
} }
@ -249,19 +249,19 @@ public class HelpProvider : IHelpProvider
{ {
composer, composer,
}; };
} }
/// <summary> /// <summary>
/// Gets the examples section of the help information. /// Gets the examples section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
/// <remarks> /// <remarks>
/// Examples from the command's direct children are used /// Examples from the command's direct children are used
/// if no examples have been set on the specified command or model. /// if no examples have been set on the specified command or model.
/// </remarks> /// </remarks>
public virtual IEnumerable<IRenderable> GetExamples(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetExamples(ICommandModel model, ICommandInfo? command)
{ {
var maxExamples = int.MaxValue; var maxExamples = int.MaxValue;
@ -272,12 +272,12 @@ public class HelpProvider : IHelpProvider
// make sure that we limit the number of examples. // make sure that we limit the number of examples.
maxExamples = MaximumIndirectExamples; maxExamples = MaximumIndirectExamples;
// Start at the current command (if exists) // Start at the current command (if exists)
// or alternatively commence at the model. // or alternatively commence at the model.
var commandContainer = command ?? (ICommandContainer)model; var commandContainer = command ?? (ICommandContainer)model;
var queue = new Queue<ICommandContainer>(new[] { commandContainer }); var queue = new Queue<ICommandContainer>(new[] { commandContainer });
// Traverse the command tree and look for examples. // Traverse the command tree and look for examples.
// As soon as a node contains commands, bail. // As soon as a node contains commands, bail.
while (queue.Count > 0) while (queue.Count > 0)
{ {
@ -317,14 +317,14 @@ public class HelpProvider : IHelpProvider
} }
return Array.Empty<IRenderable>(); return Array.Empty<IRenderable>();
} }
/// <summary> /// <summary>
/// Gets the arguments section of the help information. /// Gets the arguments section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetArguments(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetArguments(ICommandModel model, ICommandInfo? command)
{ {
var arguments = HelpArgument.Get(command); var arguments = HelpArgument.Get(command);
@ -361,13 +361,13 @@ public class HelpProvider : IHelpProvider
result.Add(grid); result.Add(grid);
return result; return result;
} }
/// <summary> /// <summary>
/// Gets the options section of the help information. /// Gets the options section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetOptions(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetOptions(ICommandModel model, ICommandInfo? command)
{ {
@ -469,18 +469,18 @@ public class HelpProvider : IHelpProvider
result.Add(grid); result.Add(grid);
return result; return result;
} }
/// <summary> /// <summary>
/// Gets the commands section of the help information. /// Gets the commands section of the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetCommands(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetCommands(ICommandModel model, ICommandInfo? command)
{ {
var commandContainer = command ?? (ICommandContainer)model; var commandContainer = command ?? (ICommandContainer)model;
bool isDefaultCommand = command?.IsDefaultCommand ?? false; bool isDefaultCommand = command?.IsDefaultCommand ?? false;
var commands = isDefaultCommand ? model.Commands : commandContainer.Commands; var commands = isDefaultCommand ? model.Commands : commandContainer.Commands;
commands = commands.Where(x => !x.IsHidden).ToList(); commands = commands.Where(x => !x.IsHidden).ToList();
@ -530,16 +530,16 @@ public class HelpProvider : IHelpProvider
result.Add(grid); result.Add(grid);
return result; return result;
} }
/// <summary> /// <summary>
/// Gets the footer for the help information. /// Gets the footer for the help information.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
public virtual IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo? command)
{ {
yield break; yield break;
} }
} }

View File

@ -4,7 +4,7 @@ namespace Spectre.Console.Cli.Help;
/// Represents a command container. /// Represents a command container.
/// </summary> /// </summary>
public interface ICommandContainer public interface ICommandContainer
{ {
/// <summary> /// <summary>
/// Gets all the examples for the container. /// Gets all the examples for the container.
/// </summary> /// </summary>

View File

@ -1,42 +1,42 @@
namespace Spectre.Console.Cli.Help; namespace Spectre.Console.Cli.Help;
/// <summary> /// <summary>
/// Represents an executable command. /// Represents an executable command.
/// </summary> /// </summary>
public interface ICommandInfo : ICommandContainer public interface ICommandInfo : ICommandContainer
{ {
/// <summary> /// <summary>
/// Gets the name of the command. /// Gets the name of the command.
/// </summary> /// </summary>
string Name { get; } string Name { get; }
/// <summary> /// <summary>
/// Gets the description of the command. /// Gets the description of the command.
/// </summary> /// </summary>
string? Description { get; } string? Description { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the command is a branch. /// Gets a value indicating whether the command is a branch.
/// </summary> /// </summary>
bool IsBranch { get; } bool IsBranch { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the command is the default command within its container. /// Gets a value indicating whether the command is the default command within its container.
/// </summary> /// </summary>
bool IsDefaultCommand { get; } bool IsDefaultCommand { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the command is hidden. /// Gets a value indicating whether the command is hidden.
/// </summary> /// </summary>
bool IsHidden { get; } bool IsHidden { get; }
/// <summary> /// <summary>
/// Gets the parameters associated with the command. /// Gets the parameters associated with the command.
/// </summary> /// </summary>
IReadOnlyList<ICommandParameter> Parameters { get; } IReadOnlyList<ICommandParameter> Parameters { get; }
/// <summary> /// <summary>
/// Gets the parent command, if any. /// Gets the parent command, if any.
/// </summary> /// </summary>
ICommandInfo? Parent { get; } ICommandInfo? Parent { get; }
} }

View File

@ -1,23 +1,23 @@
namespace Spectre.Console.Cli.Help; namespace Spectre.Console.Cli.Help;
internal static class ICommandInfoExtensions internal static class ICommandInfoExtensions
{ {
/// <summary> /// <summary>
/// Walks up the command.Parent tree, adding each command into a list as it goes. /// Walks up the command.Parent tree, adding each command into a list as it goes.
/// </summary> /// </summary>
/// <remarks>The first command added to the list is the current (ie. this one).</remarks> /// <remarks>The first command added to the list is the current (ie. this one).</remarks>
/// <returns>The list of commands from current to root, as traversed by <see cref="CommandInfo.Parent"/>.</returns> /// <returns>The list of commands from current to root, as traversed by <see cref="CommandInfo.Parent"/>.</returns>
public static List<ICommandInfo> Flatten(this ICommandInfo commandInfo) public static List<ICommandInfo> Flatten(this ICommandInfo commandInfo)
{ {
var result = new Stack<Help.ICommandInfo>(); var result = new Stack<Help.ICommandInfo>();
var current = commandInfo; var current = commandInfo;
while (current != null) while (current != null)
{ {
result.Push(current); result.Push(current);
current = current.Parent; current = current.Parent;
} }
return result.ToList(); return result.ToList();
} }
} }

View File

@ -1,27 +1,27 @@
namespace Spectre.Console.Cli.Help; namespace Spectre.Console.Cli.Help;
/// <summary> /// <summary>
/// Represents a command option. /// Represents a command option.
/// </summary> /// </summary>
public interface ICommandOption : ICommandParameter public interface ICommandOption : ICommandParameter
{ {
/// <summary> /// <summary>
/// Gets the long names of the option. /// Gets the long names of the option.
/// </summary> /// </summary>
IReadOnlyList<string> LongNames { get; } IReadOnlyList<string> LongNames { get; }
/// <summary> /// <summary>
/// Gets the short names of the option. /// Gets the short names of the option.
/// </summary> /// </summary>
IReadOnlyList<string> ShortNames { get; } IReadOnlyList<string> ShortNames { get; }
/// <summary> /// <summary>
/// Gets the value name of the option, if applicable. /// Gets the value name of the option, if applicable.
/// </summary> /// </summary>
string? ValueName { get; } string? ValueName { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the option value is optional. /// Gets a value indicating whether the option value is optional.
/// </summary> /// </summary>
bool ValueIsOptional { get; } bool ValueIsOptional { get; }
} }

View File

@ -1,32 +1,32 @@
namespace Spectre.Console.Cli.Help; namespace Spectre.Console.Cli.Help;
/// <summary> /// <summary>
/// Represents a command parameter. /// Represents a command parameter.
/// </summary> /// </summary>
public interface ICommandParameter public interface ICommandParameter
{ {
/// <summary> /// <summary>
/// Gets a value indicating whether the parameter is a flag. /// Gets a value indicating whether the parameter is a flag.
/// </summary> /// </summary>
bool IsFlag { get; } bool IsFlag { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the parameter is required. /// Gets a value indicating whether the parameter is required.
/// </summary> /// </summary>
bool Required { get; } bool Required { get; }
/// <summary> /// <summary>
/// Gets the description of the parameter. /// Gets the description of the parameter.
/// </summary> /// </summary>
string? Description { get; } string? Description { get; }
/// <summary> /// <summary>
/// Gets the default value of the parameter, if specified. /// Gets the default value of the parameter, if specified.
/// </summary> /// </summary>
DefaultValueAttribute? DefaultValue { get; } DefaultValueAttribute? DefaultValue { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether the parameter is hidden. /// Gets a value indicating whether the parameter is hidden.
/// </summary> /// </summary>
bool IsHidden { get; } bool IsHidden { get; }
} }

View File

@ -1,20 +1,20 @@
namespace Spectre.Console.Cli.Help; namespace Spectre.Console.Cli.Help;
/// <summary> /// <summary>
/// The help provider interface for Spectre.Console. /// The help provider interface for Spectre.Console.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Implementations of this interface are responsbile /// Implementations of this interface are responsbile
/// for writing command help to the terminal when the /// for writing command help to the terminal when the
/// `-h` or `--help` has been specified on the command line. /// `-h` or `--help` has been specified on the command line.
/// </remarks> /// </remarks>
public interface IHelpProvider public interface IHelpProvider
{ {
/// <summary> /// <summary>
/// Writes help information for the application. /// Writes help information for the application.
/// </summary> /// </summary>
/// <param name="model">The command model to write help for.</param> /// <param name="model">The command model to write help for.</param>
/// <param name="command">The command for which to write help information (optional).</param> /// <param name="command">The command for which to write help information (optional).</param>
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects representing the help information.</returns> /// <returns>An enumerable collection of <see cref="IRenderable"/> objects representing the help information.</returns>
IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command); IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command);
} }

View File

@ -24,22 +24,22 @@ public interface ICommandAppSettings
/// <summary> /// <summary>
/// Gets or sets the application version (use it to override auto-detected value). /// Gets or sets the application version (use it to override auto-detected value).
/// </summary> /// </summary>
string? ApplicationVersion { get; set; } string? ApplicationVersion { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating how many examples from direct children to show in the help text. /// Gets or sets a value indicating how many examples from direct children to show in the help text.
/// </summary> /// </summary>
int MaximumIndirectExamples { get; set; } int MaximumIndirectExamples { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether any default values for command options are shown in the help text. /// Gets or sets a value indicating whether any default values for command options are shown in the help text.
/// </summary> /// </summary>
bool ShowOptionDefaultValues { get; set; } bool ShowOptionDefaultValues { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text. /// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text.
/// </summary> /// </summary>
bool TrimTrailingPeriod { get; set; } bool TrimTrailingPeriod { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="IAnsiConsole"/>. /// Gets or sets the <see cref="IAnsiConsole"/>.
@ -65,14 +65,14 @@ public interface ICommandAppSettings
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not parsing is strict. /// Gets or sets a value indicating whether or not parsing is strict.
/// </summary> /// </summary>
bool StrictParsing { get; set; } bool StrictParsing { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not flags found on the commnd line /// Gets or sets a value indicating whether or not flags found on the commnd line
/// that would normally result in a <see cref="CommandParseException"/> being thrown /// that would normally result in a <see cref="CommandParseException"/> being thrown
/// during parsing with the message "Flags cannot be assigned a value." /// during parsing with the message "Flags cannot be assigned a value."
/// should instead be added to the remaining arguments collection. /// should instead be added to the remaining arguments collection.
/// </summary> /// </summary>
bool ConvertFlagsToRemainingArguments { get; set; } bool ConvertFlagsToRemainingArguments { get; set; }
/// <summary> /// <summary>

View File

@ -4,19 +4,19 @@ namespace Spectre.Console.Cli;
/// Represents a configurator. /// Represents a configurator.
/// </summary> /// </summary>
public interface IConfigurator public interface IConfigurator
{ {
/// <summary> /// <summary>
/// Sets the help provider for the application. /// Sets the help provider for the application.
/// </summary> /// </summary>
/// <param name="helpProvider">The help provider to use.</param> /// <param name="helpProvider">The help provider to use.</param>
public void SetHelpProvider(IHelpProvider helpProvider); public void SetHelpProvider(IHelpProvider helpProvider);
/// <summary> /// <summary>
/// Sets the help provider for the application. /// Sets the help provider for the application.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam> /// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
public void SetHelpProvider<T>() public void SetHelpProvider<T>()
where T : IHelpProvider; where T : IHelpProvider;
/// <summary> /// <summary>
/// Gets the command app settings. /// Gets the command app settings.
@ -66,5 +66,5 @@ public interface IConfigurator
/// <param name="action">The command branch configurator.</param> /// <param name="action">The command branch configurator.</param>
/// <returns>A branch configurator that can be used to configure the branch further.</returns> /// <returns>A branch configurator that can be used to configure the branch further.</returns>
IBranchConfigurator AddBranch<TSettings>(string name, Action<IConfigurator<TSettings>> action) IBranchConfigurator AddBranch<TSettings>(string name, Action<IConfigurator<TSettings>> action)
where TSettings : CommandSettings; where TSettings : CommandSettings;
} }

View File

@ -1,146 +1,146 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal sealed class CommandExecutor internal sealed class CommandExecutor
{ {
private readonly ITypeRegistrar _registrar; private readonly ITypeRegistrar _registrar;
public CommandExecutor(ITypeRegistrar registrar) public CommandExecutor(ITypeRegistrar registrar)
{ {
_registrar = registrar ?? throw new ArgumentNullException(nameof(registrar)); _registrar = registrar ?? throw new ArgumentNullException(nameof(registrar));
_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> Execute(IConfiguration configuration, IEnumerable<string> args)
{ {
if (configuration == null) if (configuration == null)
{ {
throw new ArgumentNullException(nameof(configuration)); throw new ArgumentNullException(nameof(configuration));
} }
args ??= new List<string>(); args ??= new List<string>();
_registrar.RegisterInstance(typeof(IConfiguration), configuration); _registrar.RegisterInstance(typeof(IConfiguration), configuration);
_registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole()); _registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole());
// Create the command model. // Create the command model.
var model = CommandModelBuilder.Build(configuration); var model = CommandModelBuilder.Build(configuration);
_registrar.RegisterInstance(typeof(CommandModel), model); _registrar.RegisterInstance(typeof(CommandModel), model);
_registrar.RegisterDependencies(model); _registrar.RegisterDependencies(model);
// Asking for version? Kind of a hack, but it's alright. // Asking for version? Kind of a hack, but it's alright.
// We should probably make this a bit better in the future. // We should probably make this a bit better in the future.
if (args.Contains("-v") || args.Contains("--version")) if (args.Contains("-v") || args.Contains("--version"))
{ {
var console = configuration.Settings.Console.GetConsole(); var console = configuration.Settings.Console.GetConsole();
console.WriteLine(ResolveApplicationVersion(configuration)); console.WriteLine(ResolveApplicationVersion(configuration));
return 0; return 0;
} }
// Parse and map the model against the arguments. // Parse and map the model against the arguments.
var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args); var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args);
// Register the arguments with the container. // Register the arguments with the container.
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult); _registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
_registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining); _registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining);
// Create the resolver. // Create the resolver.
using (var resolver = new TypeResolverAdapter(_registrar.Build())) using (var resolver = new TypeResolverAdapter(_registrar.Build()))
{ {
// Get the registered help provider, falling back to the default provider // Get the registered help provider, falling back to the default provider
// if no custom implementations have been registered. // if no custom implementations have been registered.
var helpProviders = resolver.Resolve(typeof(IEnumerable<IHelpProvider>)) as IEnumerable<IHelpProvider>; var helpProviders = resolver.Resolve(typeof(IEnumerable<IHelpProvider>)) as IEnumerable<IHelpProvider>;
var helpProvider = helpProviders?.LastOrDefault() ?? new HelpProvider(configuration.Settings); var helpProvider = helpProviders?.LastOrDefault() ?? new HelpProvider(configuration.Settings);
// Currently the root? // Currently the root?
if (parsedResult?.Tree == null) if (parsedResult?.Tree == null)
{ {
// Display help. // Display help.
configuration.Settings.Console.SafeRender(helpProvider.Write(model, null)); configuration.Settings.Console.SafeRender(helpProvider.Write(model, null));
return 0; return 0;
} }
// Get the command to execute. // Get the command to execute.
var leaf = parsedResult.Tree.GetLeafCommand(); var leaf = parsedResult.Tree.GetLeafCommand();
if (leaf.Command.IsBranch || leaf.ShowHelp) if (leaf.Command.IsBranch || leaf.ShowHelp)
{ {
// Branches can't be executed. Show help. // Branches can't be executed. Show help.
configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command));
return leaf.ShowHelp ? 0 : 1; return leaf.ShowHelp ? 0 : 1;
} }
// Is this the default and is it called without arguments when there are required arguments? // Is this the default and is it called without arguments when there are required arguments?
if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required)) if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required))
{ {
// Display help for default command. // Display help for default command.
configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command));
return 1; return 1;
} }
// Create the content. // Create the content.
var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data); var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data);
// Execute the command tree. // Execute the command tree.
return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false); return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false);
} }
} }
#pragma warning disable CS8603 // Possible null reference return. #pragma warning disable CS8603 // Possible null reference return.
private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable<string> args) private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable<string> args)
{ {
var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments); var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments);
var parserContext = new CommandTreeParserContext(args, settings.ParsingMode); var parserContext = new CommandTreeParserContext(args, settings.ParsingMode);
var tokenizerResult = CommandTreeTokenizer.Tokenize(args); var tokenizerResult = CommandTreeTokenizer.Tokenize(args);
var parsedResult = parser.Parse(parserContext, tokenizerResult); var parsedResult = parser.Parse(parserContext, tokenizerResult);
var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand(); var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand();
var lastParsedCommand = lastParsedLeaf?.Command; var lastParsedCommand = lastParsedLeaf?.Command;
if (lastParsedLeaf != null && lastParsedCommand != null && if (lastParsedLeaf != null && lastParsedCommand != null &&
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp && lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
lastParsedCommand.DefaultCommand != null) lastParsedCommand.DefaultCommand != null)
{ {
// Insert this branch's default command into the command line // Insert this branch's default command into the command line
// arguments and try again to see if it will parse. // arguments and try again to see if it will parse.
var argsWithDefaultCommand = new List<string>(args); var argsWithDefaultCommand = new List<string>(args);
argsWithDefaultCommand.Insert(tokenizerResult.Tokens.Position, lastParsedCommand.DefaultCommand.Name); argsWithDefaultCommand.Insert(tokenizerResult.Tokens.Position, lastParsedCommand.DefaultCommand.Name);
parserContext = new CommandTreeParserContext(argsWithDefaultCommand, settings.ParsingMode); parserContext = new CommandTreeParserContext(argsWithDefaultCommand, settings.ParsingMode);
tokenizerResult = CommandTreeTokenizer.Tokenize(argsWithDefaultCommand); tokenizerResult = CommandTreeTokenizer.Tokenize(argsWithDefaultCommand);
parsedResult = parser.Parse(parserContext, tokenizerResult); parsedResult = parser.Parse(parserContext, tokenizerResult);
} }
return parsedResult; return parsedResult;
} }
#pragma warning restore CS8603 // Possible null reference return. #pragma warning restore CS8603 // Possible null reference return.
private static string ResolveApplicationVersion(IConfiguration configuration) private static string ResolveApplicationVersion(IConfiguration configuration)
{ {
return return
configuration.Settings.ApplicationVersion ?? // potential override configuration.Settings.ApplicationVersion ?? // potential override
VersionHelper.GetVersion(Assembly.GetEntryAssembly()); VersionHelper.GetVersion(Assembly.GetEntryAssembly());
} }
private static Task<int> Execute( private static Task<int> Execute(
CommandTree leaf, CommandTree leaf,
CommandTree tree, CommandTree tree,
CommandContext context, CommandContext context,
ITypeResolver resolver, ITypeResolver resolver,
IConfiguration configuration) IConfiguration configuration)
{ {
// Bind the command tree against the settings. // Bind the command tree against the settings.
var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver);
configuration.Settings.Interceptor?.Intercept(context, settings); configuration.Settings.Interceptor?.Intercept(context, settings);
// Create and validate the command. // Create and validate the command.
var command = leaf.CreateCommand(resolver); var command = leaf.CreateCommand(resolver);
var validationResult = command.Validate(context, settings); var validationResult = command.Validate(context, settings);
if (!validationResult.Successful) if (!validationResult.Successful)
{ {
throw CommandRuntimeException.ValidationFailed(validationResult); throw CommandRuntimeException.ValidationFailed(validationResult);
} }
// Execute the command. // Execute the command.
return command.Execute(context, settings); return command.Execute(context, settings);
} }
} }

View File

@ -1,4 +1,4 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal sealed class BranchConfigurator : IBranchConfigurator internal sealed class BranchConfigurator : IBranchConfigurator
{ {

View File

@ -5,16 +5,16 @@ internal sealed class CommandAppSettings : ICommandAppSettings
public CultureInfo? Culture { get; set; } public CultureInfo? Culture { get; set; }
public string? ApplicationName { get; set; } public string? ApplicationName { get; set; }
public string? ApplicationVersion { get; set; } public string? ApplicationVersion { get; set; }
public int MaximumIndirectExamples { get; set; } public int MaximumIndirectExamples { get; set; }
public bool ShowOptionDefaultValues { get; set; } public bool ShowOptionDefaultValues { get; set; }
public IAnsiConsole? Console { get; set; } public IAnsiConsole? Console { get; set; }
public ICommandInterceptor? Interceptor { get; set; } public ICommandInterceptor? Interceptor { get; set; }
public ITypeRegistrarFrontend Registrar { get; set; } public ITypeRegistrarFrontend Registrar { get; set; }
public CaseSensitivity CaseSensitivity { get; set; } public CaseSensitivity CaseSensitivity { get; set; }
public bool PropagateExceptions { get; set; } public bool PropagateExceptions { get; set; }
public bool ValidateExamples { get; set; } public bool ValidateExamples { get; set; }
public bool TrimTrailingPeriod { get; set; } = true; public bool TrimTrailingPeriod { get; set; } = true;
public bool StrictParsing { get; set; } public bool StrictParsing { get; set; }
public bool ConvertFlagsToRemainingArguments { get; set; } = false; public bool ConvertFlagsToRemainingArguments { get; set; } = false;
public ParsingMode ParsingMode => public ParsingMode ParsingMode =>
@ -26,7 +26,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
{ {
Registrar = new TypeRegistrar(registrar); Registrar = new TypeRegistrar(registrar);
CaseSensitivity = CaseSensitivity.All; CaseSensitivity = CaseSensitivity.All;
ShowOptionDefaultValues = true; ShowOptionDefaultValues = true;
MaximumIndirectExamples = 5; MaximumIndirectExamples = 5;
} }

View File

@ -19,19 +19,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
Settings = new CommandAppSettings(registrar); Settings = new CommandAppSettings(registrar);
Examples = new List<string[]>(); Examples = new List<string[]>();
} }
public void SetHelpProvider(IHelpProvider helpProvider) public void SetHelpProvider(IHelpProvider helpProvider)
{ {
// Register the help provider // Register the help provider
_registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); _registrar.RegisterInstance(typeof(IHelpProvider), helpProvider);
} }
public void SetHelpProvider<T>() public void SetHelpProvider<T>()
where T : IHelpProvider where T : IHelpProvider
{ {
// Register the help provider // Register the help provider
_registrar.Register(typeof(IHelpProvider), typeof(T)); _registrar.Register(typeof(IHelpProvider), typeof(T));
} }
public void AddExample(params string[] args) public void AddExample(params string[] args)
{ {

View File

@ -1,5 +1,5 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal sealed class CommandInfo : ICommandContainer, ICommandInfo internal sealed class CommandInfo : ICommandContainer, ICommandInfo
{ {
public string Name { get; } public string Name { get; }
@ -20,14 +20,14 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo
// only branches can have a default command // only branches can have a default command
public CommandInfo? DefaultCommand => IsBranch ? Children.FirstOrDefault(c => c.IsDefaultCommand) : null; public CommandInfo? DefaultCommand => IsBranch ? Children.FirstOrDefault(c => c.IsDefaultCommand) : null;
public bool IsHidden { get; } public bool IsHidden { get; }
IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Children.Cast<ICommandInfo>().ToList(); IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Children.Cast<ICommandInfo>().ToList();
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
IReadOnlyList<ICommandParameter> ICommandInfo.Parameters => Parameters.Cast<ICommandParameter>().ToList(); IReadOnlyList<ICommandParameter> ICommandInfo.Parameters => Parameters.Cast<ICommandParameter>().ToList();
ICommandInfo? ICommandInfo.Parent => Parent; ICommandInfo? ICommandInfo.Parent => Parent;
IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples; IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples;
public CommandInfo(CommandInfo? parent, ConfiguredCommand prototype) public CommandInfo(CommandInfo? parent, ConfiguredCommand prototype)
{ {
Parent = parent; Parent = parent;
@ -54,5 +54,5 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo
Description = description.Description; Description = description.Description;
} }
} }
} }
} }

View File

@ -1,17 +1,17 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal sealed class CommandModel : ICommandContainer, ICommandModel internal sealed class CommandModel : ICommandContainer, ICommandModel
{ {
public string? ApplicationName { get; } public string? ApplicationName { get; }
public ParsingMode ParsingMode { get; } public ParsingMode ParsingMode { get; }
public IList<CommandInfo> Commands { get; } public IList<CommandInfo> Commands { get; }
public IList<string[]> Examples { get; } public IList<string[]> Examples { get; }
public CommandInfo? DefaultCommand => Commands.FirstOrDefault(c => c.IsDefaultCommand); public CommandInfo? DefaultCommand => Commands.FirstOrDefault(c => c.IsDefaultCommand);
string ICommandModel.ApplicationName => GetApplicationName(ApplicationName); string ICommandModel.ApplicationName => GetApplicationName(ApplicationName);
IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Commands.Cast<ICommandInfo>().ToList(); IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Commands.Cast<ICommandInfo>().ToList();
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples; IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples;
public CommandModel( public CommandModel(
@ -22,18 +22,18 @@ internal sealed class CommandModel : ICommandContainer, ICommandModel
ApplicationName = settings.ApplicationName; ApplicationName = settings.ApplicationName;
ParsingMode = settings.ParsingMode; ParsingMode = settings.ParsingMode;
Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>()); Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>());
Examples = new List<string[]>(examples ?? Array.Empty<string[]>()); Examples = new List<string[]>(examples ?? Array.Empty<string[]>());
} }
/// <summary> /// <summary>
/// Gets the name of the application. /// Gets the name of the application.
/// If the provided <paramref name="applicationName"/> is not null or empty, /// If the provided <paramref name="applicationName"/> is not null or empty,
/// it is returned. Otherwise the name of the current application /// it is returned. Otherwise the name of the current application
/// is determined based on the executable file's name. /// is determined based on the executable file's name.
/// </summary> /// </summary>
/// <param name="applicationName">The optional name of the application.</param> /// <param name="applicationName">The optional name of the application.</param>
/// <returns> /// <returns>
/// The name of the application, or a default value of "?" if no valid application name can be determined. /// The name of the application, or a default value of "?" if no valid application name can be determined.
/// </returns> /// </returns>
private static string GetApplicationName(string? applicationName) private static string GetApplicationName(string? applicationName)
{ {
@ -45,7 +45,7 @@ internal sealed class CommandModel : ICommandContainer, ICommandModel
private static string? GetApplicationFile() private static string? GetApplicationFile()
{ {
var location = Assembly.GetEntryAssembly()?.Location; var location = Assembly.GetEntryAssembly()?.Location;
if (string.IsNullOrWhiteSpace(location)) if (string.IsNullOrWhiteSpace(location))
{ {

View File

@ -1,5 +1,5 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal static class CommandModelBuilder internal static class CommandModelBuilder
{ {
// Consider removing this in favor for value tuples at some point. // Consider removing this in favor for value tuples at some point.
@ -31,8 +31,8 @@ internal static class CommandModelBuilder
configuration.DefaultCommand.Examples.AddRange(configuration.Examples); configuration.DefaultCommand.Examples.AddRange(configuration.Examples);
// Build the default command. // Build the default command.
var defaultCommand = Build(null, configuration.DefaultCommand); var defaultCommand = Build(null, configuration.DefaultCommand);
result.Add(defaultCommand); result.Add(defaultCommand);
} }
@ -55,7 +55,7 @@ internal static class CommandModelBuilder
foreach (var childCommand in command.Children) foreach (var childCommand in command.Children)
{ {
var child = Build(info, childCommand); var child = Build(info, childCommand);
info.Children.Add(child); info.Children.Add(child);
} }
// Normalize argument positions. // Normalize argument positions.

View File

@ -1,5 +1,5 @@
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal abstract class CommandParameter : ICommandParameterInfo, ICommandParameter internal abstract class CommandParameter : ICommandParameterInfo, ICommandParameter
{ {
public Guid Id { get; } public Guid Id { get; }
@ -17,10 +17,10 @@ internal abstract class CommandParameter : ICommandParameterInfo, ICommandParame
public string PropertyName => Property.Name; public string PropertyName => Property.Name;
public virtual bool WantRawValue => ParameterType.IsPairDeconstructable() public virtual bool WantRawValue => ParameterType.IsPairDeconstructable()
&& (PairDeconstructor != null || Converter == null); && (PairDeconstructor != null || Converter == null);
public bool IsFlag => ParameterKind == ParameterKind.Flag; public bool IsFlag => ParameterKind == ParameterKind.Flag;
protected CommandParameter( protected CommandParameter(
Type parameterType, ParameterKind parameterKind, PropertyInfo property, Type parameterType, ParameterKind parameterKind, PropertyInfo property,
string? description, TypeConverterAttribute? converter, string? description, TypeConverterAttribute? converter,

View File

@ -8,13 +8,13 @@ internal interface ICommandContainer
/// <summary> /// <summary>
/// Gets all commands in the container. /// Gets all commands in the container.
/// </summary> /// </summary>
IList<CommandInfo> Commands { get; } IList<CommandInfo> Commands { get; }
/// <summary> /// <summary>
/// Gets the default command for the container. /// Gets the default command for the container.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Returns null if a default command has not been set. /// Returns null if a default command has not been set.
/// </remarks> /// </remarks>
CommandInfo? DefaultCommand { get; } CommandInfo? DefaultCommand { get; }
} }

View File

@ -1,12 +1,12 @@
using static Spectre.Console.Cli.CommandTreeTokenizer; using static Spectre.Console.Cli.CommandTreeTokenizer;
namespace Spectre.Console.Cli; namespace Spectre.Console.Cli;
internal class CommandTreeParser internal class CommandTreeParser
{ {
private readonly CommandModel _configuration; private readonly CommandModel _configuration;
private readonly ParsingMode _parsingMode; private readonly ParsingMode _parsingMode;
private readonly CommandOptionAttribute _help; private readonly CommandOptionAttribute _help;
private readonly bool _convertFlagsToRemainingArguments; private readonly bool _convertFlagsToRemainingArguments;
public CaseSensitivity CaseSensitivity { get; } public CaseSensitivity CaseSensitivity { get; }
@ -21,20 +21,20 @@ internal class CommandTreeParser
{ {
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_parsingMode = parsingMode ?? _configuration.ParsingMode; _parsingMode = parsingMode ?? _configuration.ParsingMode;
_help = new CommandOptionAttribute("-h|--help"); _help = new CommandOptionAttribute("-h|--help");
_convertFlagsToRemainingArguments = convertFlagsToRemainingArguments ?? false; _convertFlagsToRemainingArguments = convertFlagsToRemainingArguments ?? false;
CaseSensitivity = caseSensitivity; CaseSensitivity = caseSensitivity;
} }
public CommandTreeParserResult Parse(IEnumerable<string> args) public CommandTreeParserResult Parse(IEnumerable<string> args)
{ {
var parserContext = new CommandTreeParserContext(args, _parsingMode); var parserContext = new CommandTreeParserContext(args, _parsingMode);
var tokenizerResult = CommandTreeTokenizer.Tokenize(args); var tokenizerResult = CommandTreeTokenizer.Tokenize(args);
return Parse(parserContext, tokenizerResult); return Parse(parserContext, tokenizerResult);
} }
public CommandTreeParserResult Parse(CommandTreeParserContext context, CommandTreeTokenizerResult tokenizerResult) public CommandTreeParserResult Parse(CommandTreeParserContext context, CommandTreeTokenizerResult tokenizerResult)
{ {
var tokens = tokenizerResult.Tokens; var tokens = tokenizerResult.Tokens;
@ -258,8 +258,8 @@ internal class CommandTreeParser
// Find the option. // Find the option.
var option = node.FindOption(token.Value, isLongOption, CaseSensitivity); var option = node.FindOption(token.Value, isLongOption, CaseSensitivity);
if (option != null) if (option != null)
{ {
ParseOptionValue(context, stream, token, node, option); ParseOptionValue(context, stream, token, node, option);
return; return;
} }
@ -291,10 +291,10 @@ internal class CommandTreeParser
CommandTreeParserContext context, CommandTreeParserContext context,
CommandTreeTokenStream stream, CommandTreeTokenStream stream,
CommandTreeToken token, CommandTreeToken token,
CommandTree current, CommandTree current,
CommandParameter? parameter = null) CommandParameter? parameter = null)
{ {
bool addToMappedCommandParameters = parameter != null; bool addToMappedCommandParameters = parameter != null;
var value = default(string); var value = default(string);
@ -312,49 +312,49 @@ internal class CommandTreeParser
{ {
// Is this a command? // Is this a command?
if (current.Command.FindCommand(valueToken.Value, CaseSensitivity) == null) if (current.Command.FindCommand(valueToken.Value, CaseSensitivity) == null)
{ {
if (parameter != null) if (parameter != null)
{ {
if (parameter.ParameterKind == ParameterKind.Flag) if (parameter.ParameterKind == ParameterKind.Flag)
{ {
if (!CliConstants.AcceptedBooleanValues.Contains(valueToken.Value, StringComparer.OrdinalIgnoreCase)) if (!CliConstants.AcceptedBooleanValues.Contains(valueToken.Value, StringComparer.OrdinalIgnoreCase))
{ {
if (!valueToken.HadSeparator) if (!valueToken.HadSeparator)
{ {
// Do nothing // Do nothing
// - assume valueToken is unrelated to the flag parameter (ie. we've parsed it unnecessarily) // - assume valueToken is unrelated to the flag parameter (ie. we've parsed it unnecessarily)
// - rely on the "No value?" code below to set the flag to its default value // - rely on the "No value?" code below to set the flag to its default value
// - valueToken will be handled on the next pass of the parser // - valueToken will be handled on the next pass of the parser
} }
else else
{ {
// Flags cannot be assigned a value. // Flags cannot be assigned a value.
if (_convertFlagsToRemainingArguments) if (_convertFlagsToRemainingArguments)
{ {
value = stream.Consume(CommandTreeToken.Kind.String)?.Value; value = stream.Consume(CommandTreeToken.Kind.String)?.Value;
context.AddRemainingArgument(token.Value, value); context.AddRemainingArgument(token.Value, value);
// Prevent the option and it's non-boolean value from being added to // Prevent the option and it's non-boolean value from being added to
// mapped parameters (otherwise an exception will be thrown later // mapped parameters (otherwise an exception will be thrown later
// when binding the value to the flag in the comand settings) // when binding the value to the flag in the comand settings)
addToMappedCommandParameters = false; addToMappedCommandParameters = false;
} }
else else
{ {
throw CommandParseException.CannotAssignValueToFlag(context.Arguments, token); throw CommandParseException.CannotAssignValueToFlag(context.Arguments, token);
} }
} }
} }
else else
{ {
value = stream.Consume(CommandTreeToken.Kind.String)?.Value; value = stream.Consume(CommandTreeToken.Kind.String)?.Value;
} }
} }
else else
{ {
value = stream.Consume(CommandTreeToken.Kind.String)?.Value; value = stream.Consume(CommandTreeToken.Kind.String)?.Value;
} }
} }
else else
{ {
@ -370,13 +370,13 @@ internal class CommandTreeParser
} }
} }
else else
{ {
context.AddRemainingArgument(token.Value, parseValue ? valueToken.Value : null); context.AddRemainingArgument(token.Value, parseValue ? valueToken.Value : null);
} }
} }
else else
{ {
if (parameter == null && // Only add tokens which have not been matched to a command parameter if (parameter == null && // Only add tokens which have not been matched to a command parameter
(context.State == State.Remaining || context.ParsingMode == ParsingMode.Relaxed)) (context.State == State.Remaining || context.ParsingMode == ParsingMode.Relaxed))
{ {
context.AddRemainingArgument(token.Value, null); context.AddRemainingArgument(token.Value, null);
@ -399,10 +399,10 @@ internal class CommandTreeParser
if (parameter.IsFlagValue()) if (parameter.IsFlagValue())
{ {
value = null; value = null;
} }
else else
{ {
throw CommandParseException.OptionHasNoValue(context.Arguments, token, option); throw CommandParseException.OptionHasNoValue(context.Arguments, token, option);
} }
} }
else else
@ -415,9 +415,9 @@ internal class CommandTreeParser
} }
} }
if (parameter != null && addToMappedCommandParameters) if (parameter != null && addToMappedCommandParameters)
{ {
current.Mapped.Add(new MappedCommandParameter(parameter, value)); current.Mapped.Add(new MappedCommandParameter(parameter, value));
} }
} }
} }

View File

@ -26,15 +26,15 @@ internal class CommandTreeParserContext
public void IncreaseArgumentPosition() public void IncreaseArgumentPosition()
{ {
CurrentArgumentPosition++; CurrentArgumentPosition++;
} }
public void AddRemainingArgument(string key, string? value) public void AddRemainingArgument(string key, string? value)
{ {
if (!_remaining.ContainsKey(key)) if (!_remaining.ContainsKey(key))
{ {
_remaining.Add(key, new List<string?>()); _remaining.Add(key, new List<string?>());
} }
_remaining[key].Add(value); _remaining[key].Add(value);
} }

View File

@ -6,11 +6,11 @@ internal sealed class CommandTreeToken
public int Position { get; } public int Position { get; }
public string Value { get; } public string Value { get; }
public string Representation { get; } public string Representation { get; }
public bool IsGrouped { get; set; } public bool IsGrouped { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether a separater was encountered immediately before the <see cref="CommandTreeToken.Value"/>. /// Gets or sets a value indicating whether a separater was encountered immediately before the <see cref="CommandTreeToken.Value"/>.
/// </summary> /// </summary>
public bool HadSeparator { get; set; } public bool HadSeparator { get; set; }
public enum Kind public enum Kind

View File

@ -5,7 +5,7 @@ internal sealed class CommandTreeTokenStream : IReadOnlyList<CommandTreeToken>
private readonly List<CommandTreeToken> _tokens; private readonly List<CommandTreeToken> _tokens;
private int _position; private int _position;
public int Count => _tokens.Count; public int Count => _tokens.Count;
public int Position => _position; public int Position => _position;
public CommandTreeToken this[int index] => _tokens[index]; public CommandTreeToken this[int index] => _tokens[index];

View File

@ -29,13 +29,13 @@ internal static class CommandTreeTokenizer
var context = new CommandTreeTokenizerContext(); var context = new CommandTreeTokenizerContext();
foreach (var arg in args) foreach (var arg in args)
{ {
if (string.IsNullOrEmpty(arg)) if (string.IsNullOrEmpty(arg))
{ {
// Null strings in the args array are still represented as tokens // Null strings in the args array are still represented as tokens
tokens.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, string.Empty, string.Empty)); tokens.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, string.Empty, string.Empty));
continue; continue;
} }
var start = position; var start = position;
var reader = new TextBuffer(previousReader, arg); var reader = new TextBuffer(previousReader, arg);
@ -55,30 +55,30 @@ internal static class CommandTreeTokenizer
} }
private static int ParseToken(CommandTreeTokenizerContext context, TextBuffer reader, int position, int start, List<CommandTreeToken> tokens) private static int ParseToken(CommandTreeTokenizerContext context, TextBuffer reader, int position, int start, List<CommandTreeToken> tokens)
{ {
if (!reader.ReachedEnd && reader.Peek() == '-') if (!reader.ReachedEnd && reader.Peek() == '-')
{ {
// Option // Option
tokens.AddRange(ScanOptions(context, reader)); tokens.AddRange(ScanOptions(context, reader));
} }
else else
{ {
// Command or argument // Command or argument
while (reader.Peek() != -1) while (reader.Peek() != -1)
{ {
if (reader.ReachedEnd) if (reader.ReachedEnd)
{ {
position += reader.Position - start; position += reader.Position - start;
break; break;
} }
tokens.Add(ScanString(context, reader)); tokens.Add(ScanString(context, reader));
// Flush remaining tokens // Flush remaining tokens
context.FlushRemaining(); context.FlushRemaining();
} }
} }
return position; return position;
} }
@ -102,7 +102,7 @@ internal static class CommandTreeTokenizer
builder.Append(current); builder.Append(current);
} }
var value = builder.ToString(); var value = builder.ToString();
return new CommandTreeToken(CommandTreeToken.Kind.String, position, value, value); return new CommandTreeToken(CommandTreeToken.Kind.String, position, value, value);
} }
@ -149,8 +149,8 @@ internal static class CommandTreeTokenizer
var token = new CommandTreeToken(CommandTreeToken.Kind.String, reader.Position, "=", "="); var token = new CommandTreeToken(CommandTreeToken.Kind.String, reader.Position, "=", "=");
throw CommandParseException.OptionValueWasExpected(reader.Original, token); throw CommandParseException.OptionValueWasExpected(reader.Original, token);
} }
var tokenValue = ScanString(context, reader); var tokenValue = ScanString(context, reader);
tokenValue.HadSeparator = true; tokenValue.HadSeparator = true;
result.Add(tokenValue); result.Add(tokenValue);
} }
@ -193,7 +193,7 @@ internal static class CommandTreeTokenizer
// be tokenized as strings. This block handles parsing those cases, but we only allow this // be tokenized as strings. This block handles parsing those cases, but we only allow this
// when the digit is the first character in the token (i.e. "-a1" is always an error), hence the // when the digit is the first character in the token (i.e. "-a1" is always an error), hence the
// result.Count == 0 check above. // result.Count == 0 check above.
string value = string.Empty; string value = string.Empty;
while (!reader.ReachedEnd) while (!reader.ReachedEnd)
{ {
@ -212,12 +212,12 @@ internal static class CommandTreeTokenizer
result.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, value, value)); result.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, value, value));
} }
else else
{ {
// Create a token representing the short option. // Create a token representing the short option.
var representation = current.ToString(CultureInfo.InvariantCulture); var representation = current.ToString(CultureInfo.InvariantCulture);
var tokenPosition = position + 1 + result.Count; var tokenPosition = position + 1 + result.Count;
var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, tokenPosition, representation, representation); var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, tokenPosition, representation, representation);
throw CommandParseException.InvalidShortOptionName(reader.Original, token); throw CommandParseException.InvalidShortOptionName(reader.Original, token);
} }
} }

View File

@ -10,7 +10,7 @@ global using System.Linq;
global using System.Reflection; global using System.Reflection;
global using System.Text; global using System.Text;
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;
global using Spectre.Console.Cli.Unsafe; global using Spectre.Console.Cli.Unsafe;
global using Spectre.Console.Rendering; global using Spectre.Console.Rendering;

View File

@ -1,153 +1,153 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.42000 // Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace Spectre.Console.Cli.Resources { namespace Spectre.Console.Cli.Resources {
using System; using System;
/// <summary> /// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc. /// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary> /// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder // This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class HelpProvider { internal class HelpProvider {
private static global::System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture; private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal HelpProvider() { internal HelpProvider() {
} }
/// <summary> /// <summary>
/// Returns the cached ResourceManager instance used by this class. /// Returns the cached ResourceManager instance used by this class.
/// </summary> /// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager { internal static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.ReferenceEquals(resourceMan, null)) { if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Spectre.Console.Cli.Resources.HelpProvider", typeof(HelpProvider).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Spectre.Console.Cli.Resources.HelpProvider", typeof(HelpProvider).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;
} }
} }
/// <summary> /// <summary>
/// Overrides the current thread's CurrentUICulture property for all /// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class. /// resource lookups using this strongly typed resource class.
/// </summary> /// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture { internal static global::System.Globalization.CultureInfo Culture {
get { get {
return resourceCulture; return resourceCulture;
} }
set { set {
resourceCulture = value; resourceCulture = value;
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to ARGUMENTS. /// Looks up a localized string similar to ARGUMENTS.
/// </summary> /// </summary>
internal static string Arguments { internal static string Arguments {
get { get {
return ResourceManager.GetString("Arguments", resourceCulture); return ResourceManager.GetString("Arguments", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to COMMAND. /// Looks up a localized string similar to COMMAND.
/// </summary> /// </summary>
internal static string Command { internal static string Command {
get { get {
return ResourceManager.GetString("Command", resourceCulture); return ResourceManager.GetString("Command", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to COMMANDS. /// Looks up a localized string similar to COMMANDS.
/// </summary> /// </summary>
internal static string Commands { internal static string Commands {
get { get {
return ResourceManager.GetString("Commands", resourceCulture); return ResourceManager.GetString("Commands", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to DEFAULT. /// Looks up a localized string similar to DEFAULT.
/// </summary> /// </summary>
internal static string Default { internal static string Default {
get { get {
return ResourceManager.GetString("Default", resourceCulture); return ResourceManager.GetString("Default", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to DESCRIPTION. /// Looks up a localized string similar to DESCRIPTION.
/// </summary> /// </summary>
internal static string Description { internal static string Description {
get { get {
return ResourceManager.GetString("Description", resourceCulture); return ResourceManager.GetString("Description", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to EXAMPLES. /// Looks up a localized string similar to EXAMPLES.
/// </summary> /// </summary>
internal static string Examples { internal static string Examples {
get { get {
return ResourceManager.GetString("Examples", resourceCulture); return ResourceManager.GetString("Examples", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to OPTIONS. /// Looks up a localized string similar to OPTIONS.
/// </summary> /// </summary>
internal static string Options { internal static string Options {
get { get {
return ResourceManager.GetString("Options", resourceCulture); return ResourceManager.GetString("Options", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Prints help information. /// Looks up a localized string similar to Prints help information.
/// </summary> /// </summary>
internal static string PrintHelpDescription { internal static string PrintHelpDescription {
get { get {
return ResourceManager.GetString("PrintHelpDescription", resourceCulture); return ResourceManager.GetString("PrintHelpDescription", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Prints version information. /// Looks up a localized string similar to Prints version information.
/// </summary> /// </summary>
internal static string PrintVersionDescription { internal static string PrintVersionDescription {
get { get {
return ResourceManager.GetString("PrintVersionDescription", resourceCulture); return ResourceManager.GetString("PrintVersionDescription", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to USAGE. /// Looks up a localized string similar to USAGE.
/// </summary> /// </summary>
internal static string Usage { internal static string Usage {
get { get {
return ResourceManager.GetString("Usage", resourceCulture); return ResourceManager.GetString("Usage", resourceCulture);
} }
} }
} }
} }

View File

@ -141,8 +141,8 @@ public sealed class CommandAppTester
.Trim(); .Trim();
return new CommandAppResult(result, output, context, settings); return new CommandAppResult(result, output, context, settings);
} }
/// <summary> /// <summary>
/// Runs the command application asynchronously. /// Runs the command application asynchronously.
/// </summary> /// </summary>

View File

@ -60,7 +60,7 @@ public static partial class AnsiConsoleExtensions
if (!char.IsControl(key.KeyChar)) if (!char.IsControl(key.KeyChar))
{ {
text += key.KeyChar.ToString(); text += key.KeyChar.ToString();
var output = key.KeyChar.ToString(); var output = key.KeyChar.ToString();
console.Write(secret ? output.Mask(mask) : output, style); console.Write(secret ? output.Mask(mask) : output, style);
} }

View File

@ -185,28 +185,28 @@ public static class StringExtensions
#else #else
return text.Contains(value, StringComparison.Ordinal); return text.Contains(value, StringComparison.Ordinal);
#endif #endif
} }
/// <summary> /// <summary>
/// "Masks" every character in a string. /// "Masks" every character in a string.
/// </summary> /// </summary>
/// <param name="value">String value to mask.</param> /// <param name="value">String value to mask.</param>
/// <param name="mask">Character to use for masking.</param> /// <param name="mask">Character to use for masking.</param>
/// <returns>Masked string.</returns> /// <returns>Masked string.</returns>
public static string Mask(this string value, char? mask) public static string Mask(this string value, char? mask)
{ {
var output = string.Empty; var output = string.Empty;
if (mask is null) if (mask is null)
{ {
return output; return output;
} }
foreach (var c in value) foreach (var c in value)
{ {
output += mask; output += mask;
} }
return output; return output;
} }
} }

View File

@ -1,4 +1,4 @@
namespace Spectre.Console; namespace Spectre.Console;
/// <summary> /// <summary>
/// Represents horizontal alignment. /// Represents horizontal alignment.

View File

@ -286,25 +286,25 @@ public static class TextPromptExtensions
obj.IsSecret = true; obj.IsSecret = true;
return obj; return obj;
} }
/// <summary> /// <summary>
/// Replaces prompt user input with mask in the console. /// Replaces prompt user input with mask in the console.
/// </summary> /// </summary>
/// <typeparam name="T">The prompt type.</typeparam> /// <typeparam name="T">The prompt type.</typeparam>
/// <param name="obj">The prompt.</param> /// <param name="obj">The prompt.</param>
/// <param name="mask">The masking character to use for the secret.</param> /// <param name="mask">The masking character to use for the secret.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns> /// <returns>The same instance so that multiple calls can be chained.</returns>
public static TextPrompt<T> Secret<T>(this TextPrompt<T> obj, char? mask) public static TextPrompt<T> Secret<T>(this TextPrompt<T> obj, char? mask)
{ {
if (obj is null) if (obj is null)
{ {
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
} }
obj.IsSecret = true; obj.IsSecret = true;
obj.Mask = mask; obj.Mask = mask;
return obj; return obj;
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,4 @@
namespace Spectre.Console; namespace Spectre.Console;
/// <summary> /// <summary>
/// Represents vertical alignment. /// Represents vertical alignment.

View File

@ -1,4 +1,4 @@
namespace Spectre.Console; namespace Spectre.Console;
internal static class TypeNameHelper internal static class TypeNameHelper
{ {

View File

@ -1,4 +1,4 @@
namespace Spectre.Console; namespace Spectre.Console;
[DebuggerDisplay("{Region,nq}")] [DebuggerDisplay("{Region,nq}")]
internal sealed class LayoutRender internal sealed class LayoutRender

View File

@ -5,7 +5,7 @@ public static class SpectreAnalyzerVerifier<TAnalyzer>
{ {
public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
=> VerifyCodeFixAsync(source, OutputKind.DynamicallyLinkedLibrary, new[] { expected }, fixedSource); => VerifyCodeFixAsync(source, OutputKind.DynamicallyLinkedLibrary, new[] { expected }, fixedSource);
public static Task VerifyCodeFixAsync(string source, OutputKind outputKind, DiagnosticResult expected, string fixedSource) public static Task VerifyCodeFixAsync(string source, OutputKind outputKind, DiagnosticResult expected, string fixedSource)
=> VerifyCodeFixAsync(source, outputKind, new[] { expected }, fixedSource); => VerifyCodeFixAsync(source, outputKind, new[] { expected }, fixedSource);
@ -13,10 +13,10 @@ public static class SpectreAnalyzerVerifier<TAnalyzer>
{ {
var test = new Test var test = new Test
{ {
TestCode = source, TestCode = source,
TestState = TestState =
{ {
OutputKind = outputKind, OutputKind = outputKind,
}, },
FixedCode = fixedSource, FixedCode = fixedSource,
}; };

View File

@ -1,28 +1,28 @@
namespace Spectre.Console.Tests.Data; namespace Spectre.Console.Tests.Data;
public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettings> public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettings>
{ {
private readonly IAnsiConsole _console; private readonly IAnsiConsole _console;
public AsynchronousCommand(IAnsiConsole console) public AsynchronousCommand(IAnsiConsole console)
{ {
_console = console; _console = console;
} }
public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings) public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings)
{ {
// Simulate a long running asynchronous task // Simulate a long running asynchronous task
await Task.Delay(200); await Task.Delay(200);
if (settings.ThrowException) if (settings.ThrowException)
{ {
throw new Exception($"Throwing exception asynchronously"); throw new Exception($"Throwing exception asynchronously");
} }
else else
{ {
_console.WriteLine($"Finished executing asynchronously"); _console.WriteLine($"Finished executing asynchronously");
} }
return 0; return 0;
} }
} }

View File

@ -1,17 +1,17 @@
using Spectre.Console; using Spectre.Console;
public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings> public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings>
{ {
private readonly IAnsiConsole _console; private readonly IAnsiConsole _console;
public GreeterCommand(IAnsiConsole console) public GreeterCommand(IAnsiConsole console)
{ {
_console = console; _console = console;
} }
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings) public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
{ {
_console.WriteLine(settings.Greeting); _console.WriteLine(settings.Greeting);
return 0; return 0;
} }
} }

View File

@ -1,34 +1,34 @@
using Spectre.Console.Rendering; using Spectre.Console.Rendering;
namespace Spectre.Console.Cli.Tests.Data.Help; namespace Spectre.Console.Cli.Tests.Data.Help;
internal class CustomHelpProvider : HelpProvider internal class CustomHelpProvider : HelpProvider
{ {
private readonly string version; private readonly string version;
public CustomHelpProvider(ICommandAppSettings settings, string version) public CustomHelpProvider(ICommandAppSettings settings, string version)
: base(settings) : base(settings)
{ {
this.version = version; this.version = version;
} }
public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo command) public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo command)
{ {
return new IRenderable[] return new IRenderable[]
{ {
new Text("--------------------------------------"), Text.NewLine, new Text("--------------------------------------"), Text.NewLine,
new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine,
new Text("--------------------------------------"), Text.NewLine, new Text("--------------------------------------"), Text.NewLine,
Text.NewLine, Text.NewLine,
}; };
} }
public override IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo command) public override IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo command)
{ {
return new IRenderable[] return new IRenderable[]
{ {
Text.NewLine, Text.NewLine,
new Text($"Version {version}"), new Text($"Version {version}"),
}; };
} }
} }

View File

@ -1,21 +1,21 @@
using Spectre.Console.Rendering; using Spectre.Console.Rendering;
namespace Spectre.Console.Cli.Tests.Data.Help; namespace Spectre.Console.Cli.Tests.Data.Help;
internal class RedirectHelpProvider : IHelpProvider internal class RedirectHelpProvider : IHelpProvider
{ {
public virtual IEnumerable<IRenderable> Write(ICommandModel model) public virtual IEnumerable<IRenderable> Write(ICommandModel model)
{ {
return Write(model, null); return Write(model, null);
} }
#nullable enable #nullable enable
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command) public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
#nullable disable #nullable disable
{ {
return new[] return new[]
{ {
new Text("Help has moved online. Please see: http://www.example.com"), new Text("Help has moved online. Please see: http://www.example.com"),
Text.NewLine, Text.NewLine,
}; };
} }
} }

View File

@ -1,8 +1,8 @@
namespace Spectre.Console.Tests.Data; namespace Spectre.Console.Tests.Data;
public sealed class AsynchronousCommandSettings : CommandSettings public sealed class AsynchronousCommandSettings : CommandSettings
{ {
[CommandOption("--ThrowException")] [CommandOption("--ThrowException")]
[DefaultValue(false)] [DefaultValue(false)]
public bool ThrowException { get; set; } public bool ThrowException { get; set; }
} }

View File

@ -1,4 +1,4 @@
namespace Spectre.Console.Tests.Data; namespace Spectre.Console.Tests.Data;
public class ReptileSettings : AnimalSettings public class ReptileSettings : AnimalSettings
{ {

View File

@ -1,5 +1,5 @@
namespace Spectre.Console.Tests.Data; namespace Spectre.Console.Tests.Data;
public sealed class ThrowingCommandSettings : CommandSettings public sealed class ThrowingCommandSettings : CommandSettings
{ {
} }

View File

@ -7,7 +7,7 @@ global using System.Linq;
global using System.Runtime.CompilerServices; global using System.Runtime.CompilerServices;
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;
global using Spectre.Console.Cli.Help; global using Spectre.Console.Cli.Help;
global using Spectre.Console.Cli.Unsafe; global using Spectre.Console.Cli.Unsafe;
global using Spectre.Console.Testing; global using Spectre.Console.Testing;

View File

@ -1,70 +1,70 @@
namespace Spectre.Console.Tests.Unit.Cli; namespace Spectre.Console.Tests.Unit.Cli;
public sealed partial class CommandAppTests public sealed partial class CommandAppTests
{ {
public sealed class Async public sealed class Async
{ {
[Fact] [Fact]
public async void Should_Execute_Command_Asynchronously() public async void Should_Execute_Command_Asynchronously()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<AsynchronousCommand>(); app.SetDefaultCommand<AsynchronousCommand>();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
}); });
// When // When
var result = await app.RunAsync(); var result = await app.RunAsync();
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Output.ShouldBe("Finished executing asynchronously"); result.Output.ShouldBe("Finished executing asynchronously");
} }
[Fact] [Fact]
public async void Should_Handle_Exception_Asynchronously() public async void Should_Handle_Exception_Asynchronously()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<AsynchronousCommand>(); app.SetDefaultCommand<AsynchronousCommand>();
// When // When
var result = await app.RunAsync(new[] var result = await app.RunAsync(new[]
{ {
"--ThrowException", "--ThrowException",
"true", "true",
}); });
// Then // Then
result.ExitCode.ShouldBe(-1); result.ExitCode.ShouldBe(-1);
} }
[Fact] [Fact]
public async void Should_Throw_Exception_Asynchronously() public async void Should_Throw_Exception_Asynchronously()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<AsynchronousCommand>(); app.SetDefaultCommand<AsynchronousCommand>();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
}); });
// When // When
var result = await Record.ExceptionAsync(async () => var result = await Record.ExceptionAsync(async () =>
await app.RunAsync(new[] await app.RunAsync(new[]
{ {
"--ThrowException", "--ThrowException",
"true", "true",
})); }));
// Then // Then
result.ShouldBeOfType<Exception>().And(ex => result.ShouldBeOfType<Exception>().And(ex =>
{ {
ex.Message.ShouldBe("Throwing exception asynchronously"); ex.Message.ShouldBe("Throwing exception asynchronously");
}); });
} }
} }
} }

View File

@ -3,133 +3,133 @@ namespace Spectre.Console.Tests.Unit.Cli;
public sealed partial class CommandAppTests public sealed partial class CommandAppTests
{ {
public sealed class Branches public sealed class Branches
{ {
[Fact] [Fact]
public void Should_Run_The_Default_Command_On_Branch() public void Should_Run_The_Default_Command_On_Branch()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => config.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDefaultCommand<CatCommand>(); animal.SetDefaultCommand<CatCommand>();
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"animal", "4", "animal", "4",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>(); result.Settings.ShouldBeOfType<CatSettings>();
} }
[Fact] [Fact]
public void Should_Throw_When_No_Default_Command_On_Branch() public void Should_Throw_When_No_Default_Command_On_Branch()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => { }); config.AddBranch<AnimalSettings>("animal", animal => { });
}); });
// When // When
var result = Record.Exception(() => var result = Record.Exception(() =>
{ {
app.Run(new[] app.Run(new[]
{ {
"animal", "4", "animal", "4",
}); });
}); });
// Then // Then
result.ShouldBeOfType<CommandConfigurationException>().And(ex => result.ShouldBeOfType<CommandConfigurationException>().And(ex =>
{ {
ex.Message.ShouldBe("The branch 'animal' does not define any commands."); ex.Message.ShouldBe("The branch 'animal' does not define any commands.");
}); });
} }
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:SingleLineCommentMustBePrecededByBlankLine", Justification = "Helps to illustrate the expected behaviour of this unit test.")] [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:SingleLineCommentMustBePrecededByBlankLine", Justification = "Helps to illustrate the expected behaviour of this unit test.")]
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:SingleLineCommentsMustBeginWithSingleSpace", Justification = "Helps to illustrate the expected behaviour of this unit test.")] [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:SingleLineCommentsMustBeginWithSingleSpace", Justification = "Helps to illustrate the expected behaviour of this unit test.")]
[Fact] [Fact]
public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Relaxed_Parsing() public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Relaxed_Parsing()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => config.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDefaultCommand<CatCommand>(); animal.SetDefaultCommand<CatCommand>();
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
// The CommandTreeParser should be unable to determine which command line // The CommandTreeParser should be unable to determine which command line
// arguments belong to the branch and which belong to the branch's // arguments belong to the branch and which belong to the branch's
// default command (once inserted). // default command (once inserted).
"animal", "4", "--name", "Kitty", "animal", "4", "--name", "Kitty",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>().And(cat => result.Settings.ShouldBeOfType<CatSettings>().And(cat =>
{ {
cat.Legs.ShouldBe(4); cat.Legs.ShouldBe(4);
//cat.Name.ShouldBe("Kitty"); //<-- Should normally be correct, but instead name will be added to the remaining arguments (see below). //cat.Name.ShouldBe("Kitty"); //<-- Should normally be correct, but instead name will be added to the remaining arguments (see below).
}); });
result.Context.Remaining.Parsed.Count.ShouldBe(1); result.Context.Remaining.Parsed.Count.ShouldBe(1);
result.Context.ShouldHaveRemainingArgument("name", values: new[] { "Kitty", }); result.Context.ShouldHaveRemainingArgument("name", values: new[] { "Kitty", });
} }
[Fact] [Fact]
public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Strict_Parsing() public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Strict_Parsing()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.UseStrictParsing(); config.UseStrictParsing();
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => config.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDefaultCommand<CatCommand>(); animal.SetDefaultCommand<CatCommand>();
}); });
}); });
// When // When
var result = Record.Exception(() => var result = Record.Exception(() =>
{ {
app.Run(new[] app.Run(new[]
{ {
// The CommandTreeParser should be unable to determine which command line // The CommandTreeParser should be unable to determine which command line
// arguments belong to the branch and which belong to the branch's // arguments belong to the branch and which belong to the branch's
// default command (once inserted). // default command (once inserted).
"animal", "4", "--name", "Kitty", "animal", "4", "--name", "Kitty",
}); });
}); });
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe("Unknown option 'name'."); ex.Message.ShouldBe("Unknown option 'name'.");
}); });
} }
[Fact] [Fact]
public void Should_Run_The_Default_Command_On_Branch_On_Branch() public void Should_Run_The_Default_Command_On_Branch_On_Branch()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
@ -141,23 +141,23 @@ public sealed partial class CommandAppTests
mammal.SetDefaultCommand<CatCommand>(); mammal.SetDefaultCommand<CatCommand>();
}); });
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"animal", "4", "mammal", "animal", "4", "mammal",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>(); result.Settings.ShouldBeOfType<CatSettings>();
} }
[Fact] [Fact]
public void Should_Run_The_Default_Command_On_Branch_On_Branch_With_Arguments() public void Should_Run_The_Default_Command_On_Branch_On_Branch_With_Arguments()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
@ -169,83 +169,83 @@ public sealed partial class CommandAppTests
mammal.SetDefaultCommand<CatCommand>(); mammal.SetDefaultCommand<CatCommand>();
}); });
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"animal", "4", "mammal", "--name", "Kitty", "animal", "4", "mammal", "--name", "Kitty",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>().And(cat => result.Settings.ShouldBeOfType<CatSettings>().And(cat =>
{ {
cat.Legs.ShouldBe(4); cat.Legs.ShouldBe(4);
cat.Name.ShouldBe("Kitty"); cat.Name.ShouldBe("Kitty");
}); });
} }
[Fact] [Fact]
public void Should_Run_The_Default_Command_Not_The_Named_Command_On_Branch() public void Should_Run_The_Default_Command_Not_The_Named_Command_On_Branch()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => config.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.AddCommand<DogCommand>("dog"); animal.AddCommand<DogCommand>("dog");
animal.SetDefaultCommand<CatCommand>(); animal.SetDefaultCommand<CatCommand>();
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"animal", "4", "animal", "4",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<CatSettings>(); result.Settings.ShouldBeOfType<CatSettings>();
} }
[Fact] [Fact]
public void Should_Run_The_Named_Command_Not_The_Default_Command_On_Branch() public void Should_Run_The_Named_Command_Not_The_Default_Command_On_Branch()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddBranch<AnimalSettings>("animal", animal => config.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.AddCommand<DogCommand>("dog"); animal.AddCommand<DogCommand>("dog");
animal.SetDefaultCommand<LionCommand>(); animal.SetDefaultCommand<LionCommand>();
}); });
}); });
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"animal", "4", "dog", "12", "--good-boy", "--name", "Rufus", "animal", "4", "dog", "12", "--good-boy", "--name", "Rufus",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog => result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{ {
dog.Legs.ShouldBe(4); dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12); dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true); dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus"); dog.Name.ShouldBe("Rufus");
}); });
} }
[Fact] [Fact]
public void Should_Allow_Multiple_Branches_Multiple_Commands() public void Should_Allow_Multiple_Branches_Multiple_Commands()
{ {

View File

@ -1,5 +1,5 @@
using Spectre.Console.Cli.Tests.Data.Help; using Spectre.Console.Cli.Tests.Data.Help;
namespace Spectre.Console.Tests.Unit.Cli; namespace Spectre.Console.Tests.Unit.Cli;
public sealed partial class CommandAppTests public sealed partial class CommandAppTests
@ -93,12 +93,12 @@ public sealed partial class CommandAppTests
}); });
// When // When
var result = fixture.Run("cat", "--help"); var result = fixture.Run("cat", "--help");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Branch_Called_Without_Help")] [Expectation("Branch_Called_Without_Help")]
public Task Should_Output_Branch_When_Called_Without_Help_Option() public Task Should_Output_Branch_When_Called_Without_Help_Option()
@ -110,27 +110,27 @@ public sealed partial class CommandAppTests
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<CatSettings>("cat", animal => configurator.AddBranch<CatSettings>("cat", animal =>
{ {
animal.SetDescription("Contains settings for a cat."); animal.SetDescription("Contains settings for a cat.");
animal.AddCommand<LionCommand>("lion"); animal.AddCommand<LionCommand>("lion");
}); });
}); });
// When // When
var result = fixture.Run("cat"); var result = fixture.Run("cat");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Branch_Default_Greeter")] [Expectation("Branch_Default_Greeter")]
public Task Should_Output_Branch_With_Default_Correctly() public Task Should_Output_Branch_With_Default_Correctly()
{ {
// Given // Given
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<OptionalArgumentWithDefaultValueSettings>("branch", animal => configurator.AddBranch<OptionalArgumentWithDefaultValueSettings>("branch", animal =>
{ {
animal.SetDefaultCommand<GreeterCommand>(); animal.SetDefaultCommand<GreeterCommand>();
@ -138,8 +138,8 @@ public sealed partial class CommandAppTests
}); });
}); });
// When // When
var result = fixture.Run("branch", "--help"); var result = fixture.Run("branch", "--help");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
@ -186,7 +186,7 @@ public sealed partial class CommandAppTests
}); });
// When // When
var result = fixture.Run("cat", "lion", "--help"); var result = fixture.Run("cat", "lion", "--help");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
@ -233,13 +233,13 @@ public sealed partial class CommandAppTests
[Theory] [Theory]
[InlineData(null, "EN")] [InlineData(null, "EN")]
[InlineData("", "EN")] [InlineData("", "EN")]
[InlineData("en", "EN")] [InlineData("en", "EN")]
[InlineData("en-EN", "EN")] [InlineData("en-EN", "EN")]
[InlineData("fr", "FR")] [InlineData("fr", "FR")]
[InlineData("fr-FR", "FR")] [InlineData("fr-FR", "FR")]
[InlineData("sv", "SV")] [InlineData("sv", "SV")]
[InlineData("sv-SE", "SV")] [InlineData("sv-SE", "SV")]
[InlineData("de", "DE")] [InlineData("de", "DE")]
[InlineData("de-DE", "DE")] [InlineData("de-DE", "DE")]
[Expectation("Default_Without_Args_Additional")] [Expectation("Default_Without_Args_Additional")]
public Task Should_Output_Default_Command_And_Additional_Commands_When_Default_Command_Has_Required_Parameters_And_Is_Called_Without_Args_Localised(string culture, string expectationPrefix) public Task Should_Output_Default_Command_And_Additional_Commands_When_Default_Command_Has_Required_Parameters_And_Is_Called_Without_Args_Localised(string culture, string expectationPrefix)
@ -281,106 +281,106 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Custom_Help_Registered_By_Instance")] [Expectation("Custom_Help_Registered_By_Instance")]
public Task Should_Output_Custom_Help_When_Registered_By_Instance() public Task Should_Output_Custom_Help_When_Registered_By_Instance()
{ {
var registrar = new DefaultTypeRegistrar(); var registrar = new DefaultTypeRegistrar();
// Given // Given
var fixture = new CommandAppTester(registrar); var fixture = new CommandAppTester(registrar);
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
// Create the custom help provider // Create the custom help provider
var helpProvider = new CustomHelpProvider(configurator.Settings, "1.0"); var helpProvider = new CustomHelpProvider(configurator.Settings, "1.0");
// Register the custom help provider instance // Register the custom help provider instance
registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); registrar.RegisterInstance(typeof(IHelpProvider), helpProvider);
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddCommand<DogCommand>("dog"); configurator.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = fixture.Run(); var result = fixture.Run();
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Custom_Help_Registered_By_Type")] [Expectation("Custom_Help_Registered_By_Type")]
public Task Should_Output_Custom_Help_When_Registered_By_Type() public Task Should_Output_Custom_Help_When_Registered_By_Type()
{ {
var registrar = new DefaultTypeRegistrar(); var registrar = new DefaultTypeRegistrar();
// Given // Given
var fixture = new CommandAppTester(registrar); var fixture = new CommandAppTester(registrar);
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
// Register the custom help provider type // Register the custom help provider type
registrar.Register(typeof(IHelpProvider), typeof(RedirectHelpProvider)); registrar.Register(typeof(IHelpProvider), typeof(RedirectHelpProvider));
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddCommand<DogCommand>("dog"); configurator.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = fixture.Run(); var result = fixture.Run();
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Custom_Help_Configured_By_Instance")] [Expectation("Custom_Help_Configured_By_Instance")]
public Task Should_Output_Custom_Help_When_Configured_By_Instance() public Task Should_Output_Custom_Help_When_Configured_By_Instance()
{ {
var registrar = new DefaultTypeRegistrar(); var registrar = new DefaultTypeRegistrar();
// Given // Given
var fixture = new CommandAppTester(registrar); var fixture = new CommandAppTester(registrar);
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
// Configure the custom help provider instance // Configure the custom help provider instance
configurator.SetHelpProvider(new CustomHelpProvider(configurator.Settings, "1.0")); configurator.SetHelpProvider(new CustomHelpProvider(configurator.Settings, "1.0"));
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddCommand<DogCommand>("dog"); configurator.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = fixture.Run(); var result = fixture.Run();
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Custom_Help_Configured_By_Type")] [Expectation("Custom_Help_Configured_By_Type")]
public Task Should_Output_Custom_Help_When_Configured_By_Type() public Task Should_Output_Custom_Help_When_Configured_By_Type()
{ {
var registrar = new DefaultTypeRegistrar(); var registrar = new DefaultTypeRegistrar();
// Given // Given
var fixture = new CommandAppTester(registrar); var fixture = new CommandAppTester(registrar);
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
// Configure the custom help provider type // Configure the custom help provider type
configurator.SetHelpProvider<RedirectHelpProvider>(); configurator.SetHelpProvider<RedirectHelpProvider>();
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddCommand<DogCommand>("dog"); configurator.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = fixture.Run(); var result = fixture.Run();
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Root_Examples")] [Expectation("Root_Examples")]
@ -391,20 +391,20 @@ public sealed partial class CommandAppTests
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
// All root examples should be shown // All root examples should be shown
configurator.AddExample("dog", "--name", "Rufus", "--age", "12", "--good-boy"); configurator.AddExample("dog", "--name", "Rufus", "--age", "12", "--good-boy");
configurator.AddExample("dog", "--name", "Luna"); configurator.AddExample("dog", "--name", "Luna");
configurator.AddExample("dog", "--name", "Charlie"); configurator.AddExample("dog", "--name", "Charlie");
configurator.AddExample("dog", "--name", "Bella"); configurator.AddExample("dog", "--name", "Bella");
configurator.AddExample("dog", "--name", "Daisy"); configurator.AddExample("dog", "--name", "Daisy");
configurator.AddExample("dog", "--name", "Milo"); configurator.AddExample("dog", "--name", "Milo");
configurator.AddExample("horse", "--name", "Brutus"); configurator.AddExample("horse", "--name", "Brutus");
configurator.AddExample("horse", "--name", "Sugar", "--IsAlive", "false"); configurator.AddExample("horse", "--name", "Sugar", "--IsAlive", "false");
configurator.AddExample("horse", "--name", "Cash"); configurator.AddExample("horse", "--name", "Cash");
configurator.AddExample("horse", "--name", "Dakota"); configurator.AddExample("horse", "--name", "Dakota");
configurator.AddExample("horse", "--name", "Cisco"); configurator.AddExample("horse", "--name", "Cisco");
configurator.AddExample("horse", "--name", "Spirit"); configurator.AddExample("horse", "--name", "Spirit");
configurator.AddCommand<DogCommand>("dog"); configurator.AddCommand<DogCommand>("dog");
configurator.AddCommand<HorseCommand>("horse"); configurator.AddCommand<HorseCommand>("horse");
@ -415,10 +415,10 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Root_Examples_Children")] [Expectation("Root_Examples_Children")]
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Single line comment is relevant to several code blocks that follow.")] [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Single line comment is relevant to several code blocks that follow.")]
public Task Should_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples() public Task Should_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{ {
@ -426,135 +426,24 @@ public sealed partial class CommandAppTests
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
// It should be capped to the first 5 examples by default // It should be capped to the first 5 examples by default
configurator.AddCommand<DogCommand>("dog") configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy") .WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna") .WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie") .WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella") .WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy") .WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo"); .WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse") configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus") .WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false") .WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash") .WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota") .WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco") .WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Children_Eight")]
public Task Should_Output_Eight_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Show the first 8 examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 8;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Children_Twelve")]
public Task Should_Output_All_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Show all examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = int.MaxValue;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Children_None")]
public Task Should_Not_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Do not show examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 0;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit"); .WithExample("horse", "--name", "Spirit");
}); });
@ -566,7 +455,118 @@ public sealed partial class CommandAppTests
} }
[Fact] [Fact]
[Expectation("Root_Examples_Leafs")] [Expectation("Root_Examples_Children_Eight")]
public Task Should_Output_Eight_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Show the first 8 examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 8;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Children_Twelve")]
public Task Should_Output_All_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Show all examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = int.MaxValue;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Children_None")]
public Task Should_Not_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
{
// Given
var fixture = new CommandAppTester();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
// Do not show examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 0;
configurator.AddCommand<DogCommand>("dog")
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("dog", "--name", "Luna")
.WithExample("dog", "--name", "Charlie")
.WithExample("dog", "--name", "Bella")
.WithExample("dog", "--name", "Daisy")
.WithExample("dog", "--name", "Milo");
configurator.AddCommand<HorseCommand>("horse")
.WithExample("horse", "--name", "Brutus")
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("horse", "--name", "Cash")
.WithExample("horse", "--name", "Dakota")
.WithExample("horse", "--name", "Cisco")
.WithExample("horse", "--name", "Spirit");
});
// When
var result = fixture.Run("--help");
// Then
return Verifier.Verify(result.Output);
}
[Fact]
[Expectation("Root_Examples_Leafs")]
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Single line comment is relevant to several code blocks that follow.")] [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Single line comment is relevant to several code blocks that follow.")]
public Task Should_Output_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found() public Task Should_Output_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found()
{ {
@ -577,24 +577,24 @@ public sealed partial class CommandAppTests
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
// It should be capped to the first 5 examples by default // It should be capped to the first 5 examples by default
animal.AddCommand<DogCommand>("dog") animal.AddCommand<DogCommand>("dog")
.WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("animal", "dog", "--name", "Luna") .WithExample("animal", "dog", "--name", "Luna")
.WithExample("animal", "dog", "--name", "Charlie") .WithExample("animal", "dog", "--name", "Charlie")
.WithExample("animal", "dog", "--name", "Bella") .WithExample("animal", "dog", "--name", "Bella")
.WithExample("animal", "dog", "--name", "Daisy") .WithExample("animal", "dog", "--name", "Daisy")
.WithExample("animal", "dog", "--name", "Milo"); .WithExample("animal", "dog", "--name", "Milo");
animal.AddCommand<HorseCommand>("horse") animal.AddCommand<HorseCommand>("horse")
.WithExample("animal", "horse", "--name", "Brutus") .WithExample("animal", "horse", "--name", "Brutus")
.WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("animal", "horse", "--name", "Cash") .WithExample("animal", "horse", "--name", "Cash")
.WithExample("animal", "horse", "--name", "Dakota") .WithExample("animal", "horse", "--name", "Dakota")
.WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Cisco")
.WithExample("animal", "horse", "--name", "Spirit"); .WithExample("animal", "horse", "--name", "Spirit");
}); });
}); });
@ -604,10 +604,10 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Root_Examples_Leafs_Eight")] [Expectation("Root_Examples_Leafs_Eight")]
public Task Should_Output_Eight_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found() public Task Should_Output_Eight_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found()
{ {
// Given // Given
@ -617,25 +617,25 @@ public sealed partial class CommandAppTests
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
// Show the first 8 examples defined on the direct children // Show the first 8 examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 8; configurator.Settings.MaximumIndirectExamples = 8;
animal.AddCommand<DogCommand>("dog") animal.AddCommand<DogCommand>("dog")
.WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("animal", "dog", "--name", "Luna") .WithExample("animal", "dog", "--name", "Luna")
.WithExample("animal", "dog", "--name", "Charlie") .WithExample("animal", "dog", "--name", "Charlie")
.WithExample("animal", "dog", "--name", "Bella") .WithExample("animal", "dog", "--name", "Bella")
.WithExample("animal", "dog", "--name", "Daisy") .WithExample("animal", "dog", "--name", "Daisy")
.WithExample("animal", "dog", "--name", "Milo"); .WithExample("animal", "dog", "--name", "Milo");
animal.AddCommand<HorseCommand>("horse") animal.AddCommand<HorseCommand>("horse")
.WithExample("animal", "horse", "--name", "Brutus") .WithExample("animal", "horse", "--name", "Brutus")
.WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("animal", "horse", "--name", "Cash") .WithExample("animal", "horse", "--name", "Cash")
.WithExample("animal", "horse", "--name", "Dakota") .WithExample("animal", "horse", "--name", "Dakota")
.WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Cisco")
.WithExample("animal", "horse", "--name", "Spirit"); .WithExample("animal", "horse", "--name", "Spirit");
}); });
}); });
@ -645,10 +645,10 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Root_Examples_Leafs_Twelve")] [Expectation("Root_Examples_Leafs_Twelve")]
public Task Should_Output_All_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found() public Task Should_Output_All_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found()
{ {
// Given // Given
@ -658,25 +658,25 @@ public sealed partial class CommandAppTests
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
// Show all examples defined on the direct children // Show all examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = int.MaxValue; configurator.Settings.MaximumIndirectExamples = int.MaxValue;
animal.AddCommand<DogCommand>("dog") animal.AddCommand<DogCommand>("dog")
.WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("animal", "dog", "--name", "Luna") .WithExample("animal", "dog", "--name", "Luna")
.WithExample("animal", "dog", "--name", "Charlie") .WithExample("animal", "dog", "--name", "Charlie")
.WithExample("animal", "dog", "--name", "Bella") .WithExample("animal", "dog", "--name", "Bella")
.WithExample("animal", "dog", "--name", "Daisy") .WithExample("animal", "dog", "--name", "Daisy")
.WithExample("animal", "dog", "--name", "Milo"); .WithExample("animal", "dog", "--name", "Milo");
animal.AddCommand<HorseCommand>("horse") animal.AddCommand<HorseCommand>("horse")
.WithExample("animal", "horse", "--name", "Brutus") .WithExample("animal", "horse", "--name", "Brutus")
.WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("animal", "horse", "--name", "Cash") .WithExample("animal", "horse", "--name", "Cash")
.WithExample("animal", "horse", "--name", "Dakota") .WithExample("animal", "horse", "--name", "Dakota")
.WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Cisco")
.WithExample("animal", "horse", "--name", "Spirit"); .WithExample("animal", "horse", "--name", "Spirit");
}); });
}); });
@ -686,10 +686,10 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Root_Examples_Leafs_None")] [Expectation("Root_Examples_Leafs_None")]
public Task Should_Not_Output_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found() public Task Should_Not_Output_Examples_Defined_On_Leaves_If_No_Other_Examples_Are_Found()
{ {
// Given // Given
@ -699,25 +699,25 @@ public sealed partial class CommandAppTests
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
// Do not show examples defined on the direct children // Do not show examples defined on the direct children
configurator.Settings.MaximumIndirectExamples = 0; configurator.Settings.MaximumIndirectExamples = 0;
animal.AddCommand<DogCommand>("dog") animal.AddCommand<DogCommand>("dog")
.WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy")
.WithExample("animal", "dog", "--name", "Luna") .WithExample("animal", "dog", "--name", "Luna")
.WithExample("animal", "dog", "--name", "Charlie") .WithExample("animal", "dog", "--name", "Charlie")
.WithExample("animal", "dog", "--name", "Bella") .WithExample("animal", "dog", "--name", "Bella")
.WithExample("animal", "dog", "--name", "Daisy") .WithExample("animal", "dog", "--name", "Daisy")
.WithExample("animal", "dog", "--name", "Milo"); .WithExample("animal", "dog", "--name", "Milo");
animal.AddCommand<HorseCommand>("horse") animal.AddCommand<HorseCommand>("horse")
.WithExample("animal", "horse", "--name", "Brutus") .WithExample("animal", "horse", "--name", "Brutus")
.WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false")
.WithExample("animal", "horse", "--name", "Cash") .WithExample("animal", "horse", "--name", "Cash")
.WithExample("animal", "horse", "--name", "Dakota") .WithExample("animal", "horse", "--name", "Dakota")
.WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Cisco")
.WithExample("animal", "horse", "--name", "Spirit"); .WithExample("animal", "horse", "--name", "Spirit");
}); });
}); });
@ -737,23 +737,23 @@ public sealed partial class CommandAppTests
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
configurator.AddBranch<AnimalSettings>("animal", animal => configurator.AddBranch<AnimalSettings>("animal", animal =>
{ {
animal.SetDescription("The animal command."); animal.SetDescription("The animal command.");
// All branch examples should be shown // All branch examples should be shown
animal.AddExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy"); animal.AddExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy");
animal.AddExample("animal", "dog", "--name", "Luna"); animal.AddExample("animal", "dog", "--name", "Luna");
animal.AddExample("animal", "dog", "--name", "Charlie"); animal.AddExample("animal", "dog", "--name", "Charlie");
animal.AddExample("animal", "dog", "--name", "Bella"); animal.AddExample("animal", "dog", "--name", "Bella");
animal.AddExample("animal", "dog", "--name", "Daisy"); animal.AddExample("animal", "dog", "--name", "Daisy");
animal.AddExample("animal", "dog", "--name", "Milo"); animal.AddExample("animal", "dog", "--name", "Milo");
animal.AddExample("animal", "horse", "--name", "Brutus"); animal.AddExample("animal", "horse", "--name", "Brutus");
animal.AddExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false"); animal.AddExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false");
animal.AddExample("animal", "horse", "--name", "Cash"); animal.AddExample("animal", "horse", "--name", "Cash");
animal.AddExample("animal", "horse", "--name", "Dakota"); animal.AddExample("animal", "horse", "--name", "Dakota");
animal.AddExample("animal", "horse", "--name", "Cisco"); animal.AddExample("animal", "horse", "--name", "Cisco");
animal.AddExample("animal", "horse", "--name", "Spirit"); animal.AddExample("animal", "horse", "--name", "Spirit");
animal.AddCommand<DogCommand>("dog") animal.AddCommand<DogCommand>("dog")
@ -768,7 +768,7 @@ public sealed partial class CommandAppTests
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
[Fact] [Fact]
[Expectation("Default_Examples")] [Expectation("Default_Examples")]
@ -780,13 +780,13 @@ public sealed partial class CommandAppTests
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationName("myapp"); configurator.SetApplicationName("myapp");
// All root examples should be shown // All root examples should be shown
configurator.AddExample("--name", "Rufus", "--age", "12", "--good-boy"); configurator.AddExample("--name", "Rufus", "--age", "12", "--good-boy");
configurator.AddExample("--name", "Luna"); configurator.AddExample("--name", "Luna");
configurator.AddExample("--name", "Charlie"); configurator.AddExample("--name", "Charlie");
configurator.AddExample("--name", "Bella"); configurator.AddExample("--name", "Bella");
configurator.AddExample("--name", "Daisy"); configurator.AddExample("--name", "Daisy");
configurator.AddExample("--name", "Milo"); configurator.AddExample("--name", "Milo");
}); });

View File

@ -352,8 +352,8 @@ public sealed partial class CommandAppTests
var result = app.Run("dog", "-u"); var result = app.Run("dog", "-u");
// Then // Then
return Verifier.Verify(result.Output); return Verifier.Verify(result.Output);
} }
} }
[UsesVerify] [UsesVerify]
@ -584,7 +584,7 @@ public sealed partial class CommandAppTests
// Then // Then
result.Output.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'."); result.Output.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'.");
} }
} }
} }
} }

View File

@ -3,16 +3,16 @@ namespace Spectre.Console.Tests.Unit.Cli;
public sealed partial class CommandAppTests public sealed partial class CommandAppTests
{ {
public sealed class Remaining public sealed class Remaining
{ {
[Theory] [Theory]
[InlineData("-a")] [InlineData("-a")]
[InlineData("--alive")] [InlineData("--alive")]
public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_RelaxedParsing(string knownFlag) public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_RelaxedParsing(string knownFlag)
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
@ -20,29 +20,29 @@ public sealed partial class CommandAppTests
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
knownFlag, knownFlag,
}); });
// Then
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.IsAlive.ShouldBe(true);
});
result.Context.Remaining.Parsed.Count.ShouldBe(0); // Then
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.IsAlive.ShouldBe(true);
});
result.Context.Remaining.Parsed.Count.ShouldBe(0);
result.Context.Remaining.Raw.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0);
} }
[Theory] [Theory]
[InlineData("-r")] [InlineData("-r")]
[InlineData("--romeo")] [InlineData("--romeo")]
public void Should_Add_Unknown_Flags_To_Remaining_Arguments_RelaxedParsing(string unknownFlag) public void Should_Add_Unknown_Flags_To_Remaining_Arguments_RelaxedParsing(string unknownFlag)
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
@ -50,23 +50,23 @@ public sealed partial class CommandAppTests
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
unknownFlag, unknownFlag,
}); });
// Then // Then
result.Context.Remaining.Parsed.Count.ShouldBe(1); result.Context.Remaining.Parsed.Count.ShouldBe(1);
result.Context.ShouldHaveRemainingArgument(unknownFlag.TrimStart('-'), values: new[] { (string)null }); result.Context.ShouldHaveRemainingArgument(unknownFlag.TrimStart('-'), values: new[] { (string)null });
result.Context.Remaining.Raw.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0);
} }
[Fact] [Fact]
public void Should_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_RelaxedParsing() public void Should_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_RelaxedParsing()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
@ -74,25 +74,25 @@ public sealed partial class CommandAppTests
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
"-agr", "-agr",
}); });
// Then // Then
result.Context.Remaining.Parsed.Count.ShouldBe(1); result.Context.Remaining.Parsed.Count.ShouldBe(1);
result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null }); result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null });
result.Context.Remaining.Raw.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0);
} }
[Theory] [Theory]
[InlineData("-a")] [InlineData("-a")]
[InlineData("--alive")] [InlineData("--alive")]
public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_StrictParsing(string knownFlag) public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_StrictParsing(string knownFlag)
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.UseStrictParsing(); config.UseStrictParsing();
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
@ -101,68 +101,68 @@ public sealed partial class CommandAppTests
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
knownFlag, knownFlag,
}); });
// Then // Then
result.Context.Remaining.Parsed.Count.ShouldBe(0); result.Context.Remaining.Parsed.Count.ShouldBe(0);
result.Context.Remaining.Raw.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0);
} }
[Theory] [Theory]
[InlineData("-r")] [InlineData("-r")]
[InlineData("--romeo")] [InlineData("--romeo")]
public void Should_Not_Add_Unknown_Flags_To_Remaining_Arguments_StrictParsing(string unknownFlag) public void Should_Not_Add_Unknown_Flags_To_Remaining_Arguments_StrictParsing(string unknownFlag)
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.UseStrictParsing(); config.UseStrictParsing();
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = Record.Exception(() => app.Run(new[] var result = Record.Exception(() => app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
unknownFlag, unknownFlag,
})); }));
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe($"Unknown option '{unknownFlag.TrimStart('-')}'."); ex.Message.ShouldBe($"Unknown option '{unknownFlag.TrimStart('-')}'.");
}); });
} }
[Fact] [Fact]
public void Should_Not_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_StrictParsing() public void Should_Not_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_StrictParsing()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.UseStrictParsing(); config.UseStrictParsing();
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
// When // When
var result = Record.Exception(() => app.Run(new[] var result = Record.Exception(() => app.Run(new[]
{ {
"dog", "12", "4", "dog", "12", "4",
"-agr", "-agr",
})); }));
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe($"Unknown option 'r'."); ex.Message.ShouldBe($"Unknown option 'r'.");
}); });
} }
[Fact] [Fact]
public void Should_Register_Remaining_Parsed_Arguments_With_Context() public void Should_Register_Remaining_Parsed_Arguments_With_Context()
@ -254,19 +254,19 @@ public sealed partial class CommandAppTests
result.Context.Remaining.Raw[0].ShouldBe("/c"); result.Context.Remaining.Raw[0].ShouldBe("/c");
result.Context.Remaining.Raw[1].ShouldBe("\"set && pause\""); result.Context.Remaining.Raw[1].ShouldBe("\"set && pause\"");
result.Context.Remaining.Raw[2].ShouldBe("Name=\" -Rufus --' "); result.Context.Remaining.Raw[2].ShouldBe("Name=\" -Rufus --' ");
} }
[Theory] [Theory]
[InlineData(true)] [InlineData(true)]
[InlineData(false)] [InlineData(false)]
public void Should_Convert_Flags_To_Remaining_Arguments_If_Cannot_Be_Assigned(bool useStrictParsing) public void Should_Convert_Flags_To_Remaining_Arguments_If_Cannot_Be_Assigned(bool useStrictParsing)
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.Configure(config => app.Configure(config =>
{ {
config.Settings.ConvertFlagsToRemainingArguments = true; config.Settings.ConvertFlagsToRemainingArguments = true;
config.Settings.StrictParsing = useStrictParsing; config.Settings.StrictParsing = useStrictParsing;
config.PropagateExceptions(); config.PropagateExceptions();
config.AddCommand<DogCommand>("dog"); config.AddCommand<DogCommand>("dog");
}); });
@ -280,8 +280,8 @@ public sealed partial class CommandAppTests
// Then // Then
result.Context.Remaining.Parsed.Count.ShouldBe(1); result.Context.Remaining.Parsed.Count.ShouldBe(1);
result.Context.ShouldHaveRemainingArgument("good-boy", values: new[] { "Please be good Rufus!" }); result.Context.ShouldHaveRemainingArgument("good-boy", values: new[] { "Please be good Rufus!" });
result.Context.Remaining.Raw.Count.ShouldBe(0); // nb. there are no "raw" remaining arguments on the command line result.Context.Remaining.Raw.Count.ShouldBe(0); // nb. there are no "raw" remaining arguments on the command line
} }
} }

View File

@ -15,8 +15,8 @@ public sealed partial class CommandAppTests
// Then // Then
result.Output.ShouldStartWith("Spectre.Cli version "); result.Output.ShouldStartWith("Spectre.Cli version ");
} }
[Fact] [Fact]
public void Should_Output_Application_Version_To_The_Console_With_No_Command() public void Should_Output_Application_Version_To_The_Console_With_No_Command()
{ {
@ -24,7 +24,7 @@ public sealed partial class CommandAppTests
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationVersion("1.0"); configurator.SetApplicationVersion("1.0");
}); });
// When // When
@ -32,8 +32,8 @@ public sealed partial class CommandAppTests
// Then // Then
result.Output.ShouldBe("1.0"); result.Output.ShouldBe("1.0");
} }
[Fact] [Fact]
public void Should_Output_Application_Version_To_The_Console_With_Command() public void Should_Output_Application_Version_To_The_Console_With_Command()
{ {
@ -41,8 +41,8 @@ public sealed partial class CommandAppTests
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationVersion("1.0"); configurator.SetApplicationVersion("1.0");
configurator.AddCommand<EmptyCommand>("empty"); configurator.AddCommand<EmptyCommand>("empty");
}); });
@ -51,8 +51,8 @@ public sealed partial class CommandAppTests
// Then // Then
result.Output.ShouldBe("1.0"); result.Output.ShouldBe("1.0");
} }
[Fact] [Fact]
public void Should_Output_Application_Version_To_The_Console_With_Default_Command() public void Should_Output_Application_Version_To_The_Console_With_Default_Command()
{ {
@ -69,8 +69,8 @@ public sealed partial class CommandAppTests
// Then // Then
result.Output.ShouldBe("1.0"); result.Output.ShouldBe("1.0");
} }
[Fact] [Fact]
public void Should_Output_Application_Version_To_The_Console_With_Branch_Default_Command() public void Should_Output_Application_Version_To_The_Console_With_Branch_Default_Command()
{ {
@ -78,11 +78,11 @@ public sealed partial class CommandAppTests
var fixture = new CommandAppTester(); var fixture = new CommandAppTester();
fixture.Configure(configurator => fixture.Configure(configurator =>
{ {
configurator.SetApplicationVersion("1.0"); configurator.SetApplicationVersion("1.0");
configurator.AddBranch<EmptyCommandSettings>("branch", branch => configurator.AddBranch<EmptyCommandSettings>("branch", branch =>
{ {
branch.SetDefaultCommand<EmptyCommand>(); branch.SetDefaultCommand<EmptyCommand>();
}); });
}); });

View File

@ -1041,66 +1041,66 @@ public sealed partial class CommandAppTests
// Then // Then
result.Context.ShouldNotBeNull(); result.Context.ShouldNotBeNull();
result.Context.Data.ShouldBe(123); result.Context.Data.ShouldBe(123);
} }
public sealed class Default_Command public sealed class Default_Command
{ {
[Fact] [Fact]
public void Should_Be_Able_To_Set_The_Default_Command() public void Should_Be_Able_To_Set_The_Default_Command()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<DogCommand>(); app.SetDefaultCommand<DogCommand>();
// When // When
var result = app.Run(new[] var result = app.Run(new[]
{ {
"4", "12", "--good-boy", "--name", "Rufus", "4", "12", "--good-boy", "--name", "Rufus",
}); });
// Then // Then
result.ExitCode.ShouldBe(0); result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog => result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{ {
dog.Legs.ShouldBe(4); dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12); dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true); dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus"); dog.Name.ShouldBe("Rufus");
}); });
} }
[Fact] [Fact]
public void Should_Set_The_Default_Command_Description_Data_CommandApp() public void Should_Set_The_Default_Command_Description_Data_CommandApp()
{ {
// Given // Given
var app = new CommandApp(); var app = new CommandApp();
app.SetDefaultCommand<DogCommand>() app.SetDefaultCommand<DogCommand>()
.WithDescription("The default command") .WithDescription("The default command")
.WithData(new string[] { "foo", "bar" }); .WithData(new string[] { "foo", "bar" });
// When // When
// Then // Then
app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); app.GetConfigurator().DefaultCommand.ShouldNotBeNull();
app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command");
app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" });
} }
[Fact] [Fact]
public void Should_Set_The_Default_Command_Description_Data_CommandAppOfT() public void Should_Set_The_Default_Command_Description_Data_CommandAppOfT()
{ {
// Given // Given
var app = new CommandApp<DogCommand>() var app = new CommandApp<DogCommand>()
.WithDescription("The default command") .WithDescription("The default command")
.WithData(new string[] { "foo", "bar" }); .WithData(new string[] { "foo", "bar" });
// When // When
// Then // Then
app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); app.GetConfigurator().DefaultCommand.ShouldNotBeNull();
app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command");
app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" });
} }
} }
public sealed class Delegate_Commands public sealed class Delegate_Commands
@ -1114,7 +1114,7 @@ public sealed partial class CommandAppTests
var app = new CommandApp(); var app = new CommandApp();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddDelegate<DogSettings>( config.AddDelegate<DogSettings>(
"foo", (context, settings) => "foo", (context, settings) =>
@ -1134,8 +1134,8 @@ public sealed partial class CommandAppTests
dog.Age.ShouldBe(12); dog.Age.ShouldBe(12);
dog.Legs.ShouldBe(4); dog.Legs.ShouldBe(4);
data.ShouldBe(2); data.ShouldBe(2);
} }
[Fact] [Fact]
public async void Should_Execute_Async_Delegate_Command_At_Root_Level() public async void Should_Execute_Async_Delegate_Command_At_Root_Level()
{ {
@ -1145,7 +1145,7 @@ public sealed partial class CommandAppTests
var app = new CommandApp(); var app = new CommandApp();
app.Configure(config => app.Configure(config =>
{ {
config.PropagateExceptions(); config.PropagateExceptions();
config.AddAsyncDelegate<DogSettings>( config.AddAsyncDelegate<DogSettings>(
"foo", (context, settings) => "foo", (context, settings) =>
@ -1199,8 +1199,8 @@ public sealed partial class CommandAppTests
dog.Age.ShouldBe(12); dog.Age.ShouldBe(12);
dog.Legs.ShouldBe(4); dog.Legs.ShouldBe(4);
data.ShouldBe(2); data.ShouldBe(2);
} }
[Fact] [Fact]
public async void Should_Execute_Nested_Async_Delegate_Command() public async void Should_Execute_Nested_Async_Delegate_Command()
{ {

View File

@ -1,314 +1,314 @@
namespace Spectre.Console.Tests.Unit.Cli.Parsing; namespace Spectre.Console.Tests.Unit.Cli.Parsing;
public class CommandTreeTokenizerTests public class CommandTreeTokenizerTests
{ {
public sealed class ScanString public sealed class ScanString
{ {
[Theory] [Theory]
[InlineData("")] [InlineData("")]
[InlineData(" ")] [InlineData(" ")]
[InlineData(" ")] [InlineData(" ")]
[InlineData("\t")] [InlineData("\t")]
[InlineData("\r\n\t")] [InlineData("\r\n\t")]
[InlineData("👋🏻")] [InlineData("👋🏻")]
[InlineData("🐎👋🏻🔥❤️")] [InlineData("🐎👋🏻🔥❤️")]
[InlineData("\"🐎👋🏻🔥❤️\" is an emoji sequence")] [InlineData("\"🐎👋🏻🔥❤️\" is an emoji sequence")]
public void Should_Preserve_Edgecase_Inputs(string actualAndExpected) public void Should_Preserve_Edgecase_Inputs(string actualAndExpected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(actualAndExpected); result.Tokens[0].Value.ShouldBe(actualAndExpected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
} }
[Theory] [Theory]
// Double-quote handling // Double-quote handling
[InlineData("\"")] [InlineData("\"")]
[InlineData("\"\"")] [InlineData("\"\"")]
[InlineData("\"Rufus\"")] [InlineData("\"Rufus\"")]
[InlineData("\" Rufus\"")] [InlineData("\" Rufus\"")]
[InlineData("\"-R\"")] [InlineData("\"-R\"")]
[InlineData("\"-Rufus\"")] [InlineData("\"-Rufus\"")]
[InlineData("\" -Rufus\"")] [InlineData("\" -Rufus\"")]
// Single-quote handling // Single-quote handling
[InlineData("'")] [InlineData("'")]
[InlineData("''")] [InlineData("''")]
[InlineData("'Rufus'")] [InlineData("'Rufus'")]
[InlineData("' Rufus'")] [InlineData("' Rufus'")]
[InlineData("'-R'")] [InlineData("'-R'")]
[InlineData("'-Rufus'")] [InlineData("'-Rufus'")]
[InlineData("' -Rufus'")] [InlineData("' -Rufus'")]
public void Should_Preserve_Quotes(string actualAndExpected) public void Should_Preserve_Quotes(string actualAndExpected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(actualAndExpected); result.Tokens[0].Value.ShouldBe(actualAndExpected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
} }
[Theory] [Theory]
[InlineData("Rufus-")] [InlineData("Rufus-")]
[InlineData("Rufus--")] [InlineData("Rufus--")]
[InlineData("R-u-f-u-s")] [InlineData("R-u-f-u-s")]
public void Should_Preserve_Hyphen_Delimiters(string actualAndExpected) public void Should_Preserve_Hyphen_Delimiters(string actualAndExpected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(actualAndExpected); result.Tokens[0].Value.ShouldBe(actualAndExpected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
} }
[Theory] [Theory]
[InlineData(" Rufus")] [InlineData(" Rufus")]
[InlineData("Rufus ")] [InlineData("Rufus ")]
[InlineData(" Rufus ")] [InlineData(" Rufus ")]
public void Should_Preserve_Spaces(string actualAndExpected) public void Should_Preserve_Spaces(string actualAndExpected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(actualAndExpected); result.Tokens[0].Value.ShouldBe(actualAndExpected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
} }
[Theory] [Theory]
[InlineData(" \" -Rufus -- ")] [InlineData(" \" -Rufus -- ")]
[InlineData("Name=\" -Rufus --' ")] [InlineData("Name=\" -Rufus --' ")]
public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces(string actualAndExpected) public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces(string actualAndExpected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(actualAndExpected); result.Tokens[0].Value.ShouldBe(actualAndExpected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
} }
} }
public sealed class ScanLongOption public sealed class ScanLongOption
{ {
[Theory] [Theory]
[InlineData("--Name-", "Name-")] [InlineData("--Name-", "Name-")]
[InlineData("--Name_", "Name_")] [InlineData("--Name_", "Name_")]
public void Should_Allow_Hyphens_And_Underscores_In_Option_Name(string actual, string expected) public void Should_Allow_Hyphens_And_Underscores_In_Option_Name(string actual, string expected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); var result = CommandTreeTokenizer.Tokenize(new string[] { actual });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(expected); result.Tokens[0].Value.ShouldBe(expected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.LongOption); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.LongOption);
} }
[Theory] [Theory]
[InlineData("-- ")] [InlineData("-- ")]
[InlineData("--Name ")] [InlineData("--Name ")]
[InlineData("--Name\"")] [InlineData("--Name\"")]
[InlineData("--Nam\"e")] [InlineData("--Nam\"e")]
public void Should_Throw_On_Invalid_Option_Name(string actual) public void Should_Throw_On_Invalid_Option_Name(string actual)
{ {
// Given // Given
// When // When
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe("Invalid long option name."); ex.Message.ShouldBe("Invalid long option name.");
}); });
} }
} }
public sealed class ScanShortOptions public sealed class ScanShortOptions
{ {
[Fact] [Fact]
public void Should_Accept_Option_Without_Value() public void Should_Accept_Option_Without_Value()
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new[] { "-a" }); var result = CommandTreeTokenizer.Tokenize(new[] { "-a" });
// Then // Then
result.Remaining.ShouldBeEmpty(); result.Remaining.ShouldBeEmpty();
result.Tokens.ShouldHaveSingleItem(); result.Tokens.ShouldHaveSingleItem();
var t = result.Tokens[0]; var t = result.Tokens[0];
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(0); t.Position.ShouldBe(0);
t.Value.ShouldBe("a"); t.Value.ShouldBe("a");
t.Representation.ShouldBe("-a"); t.Representation.ShouldBe("-a");
} }
[Theory] [Theory]
[InlineData("-a:foo")] [InlineData("-a:foo")]
[InlineData("-a=foo")] [InlineData("-a=foo")]
public void Should_Accept_Option_With_Value(string param) public void Should_Accept_Option_With_Value(string param)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new[] { param }); var result = CommandTreeTokenizer.Tokenize(new[] { param });
// Then // Then
result.Remaining.ShouldBeEmpty(); result.Remaining.ShouldBeEmpty();
result.Tokens.Count.ShouldBe(2); result.Tokens.Count.ShouldBe(2);
var t = result.Tokens.Consume(); var t = result.Tokens.Consume();
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(0); t.Position.ShouldBe(0);
t.Value.ShouldBe("a"); t.Value.ShouldBe("a");
t.Representation.ShouldBe("-a"); t.Representation.ShouldBe("-a");
t = result.Tokens.Consume(); t = result.Tokens.Consume();
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(3); t.Position.ShouldBe(3);
t.Value.ShouldBe("foo"); t.Value.ShouldBe("foo");
t.Representation.ShouldBe("foo"); t.Representation.ShouldBe("foo");
} }
[Theory] [Theory]
// Positive values // Positive values
[InlineData("-a:1.5", null, "1.5")] [InlineData("-a:1.5", null, "1.5")]
[InlineData("-a=1.5", null, "1.5")] [InlineData("-a=1.5", null, "1.5")]
[InlineData("-a", "1.5", "1.5")] [InlineData("-a", "1.5", "1.5")]
// Negative values // Negative values
[InlineData("-a:-1.5", null, "-1.5")] [InlineData("-a:-1.5", null, "-1.5")]
[InlineData("-a=-1.5", null, "-1.5")] [InlineData("-a=-1.5", null, "-1.5")]
[InlineData("-a", "-1.5", "-1.5")] [InlineData("-a", "-1.5", "-1.5")]
public void Should_Accept_Option_With_Numeric_Value(string firstArg, string secondArg, string expectedValue) public void Should_Accept_Option_With_Numeric_Value(string firstArg, string secondArg, string expectedValue)
{ {
// Given // Given
List<string> args = new List<string>(); List<string> args = new List<string>();
args.Add(firstArg); args.Add(firstArg);
if (secondArg != null) if (secondArg != null)
{ {
args.Add(secondArg); args.Add(secondArg);
} }
// When // When
var result = CommandTreeTokenizer.Tokenize(args); var result = CommandTreeTokenizer.Tokenize(args);
// Then // Then
result.Remaining.ShouldBeEmpty(); result.Remaining.ShouldBeEmpty();
result.Tokens.Count.ShouldBe(2); result.Tokens.Count.ShouldBe(2);
var t = result.Tokens.Consume(); var t = result.Tokens.Consume();
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(0); t.Position.ShouldBe(0);
t.Value.ShouldBe("a"); t.Value.ShouldBe("a");
t.Representation.ShouldBe("-a"); t.Representation.ShouldBe("-a");
t = result.Tokens.Consume(); t = result.Tokens.Consume();
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(3); t.Position.ShouldBe(3);
t.Value.ShouldBe(expectedValue); t.Value.ShouldBe(expectedValue);
t.Representation.ShouldBe(expectedValue); t.Representation.ShouldBe(expectedValue);
} }
[Fact] [Fact]
public void Should_Accept_Option_With_Negative_Numeric_Prefixed_String_Value() public void Should_Accept_Option_With_Negative_Numeric_Prefixed_String_Value()
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " }); var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " });
// Then // Then
result.Remaining.ShouldBeEmpty(); result.Remaining.ShouldBeEmpty();
result.Tokens.ShouldHaveSingleItem(); result.Tokens.ShouldHaveSingleItem();
var t = result.Tokens[0]; var t = result.Tokens[0];
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
t.IsGrouped.ShouldBe(false); t.IsGrouped.ShouldBe(false);
t.Position.ShouldBe(0); t.Position.ShouldBe(0);
t.Value.ShouldBe("-6..2"); t.Value.ShouldBe("-6..2");
t.Representation.ShouldBe("-6..2"); t.Representation.ShouldBe("-6..2");
} }
[Theory] [Theory]
[InlineData("-N ", "N")] [InlineData("-N ", "N")]
public void Should_Remove_Trailing_Spaces_In_Option_Name(string actual, string expected) public void Should_Remove_Trailing_Spaces_In_Option_Name(string actual, string expected)
{ {
// Given // Given
// When // When
var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); var result = CommandTreeTokenizer.Tokenize(new string[] { actual });
// Then // Then
result.Tokens.Count.ShouldBe(1); result.Tokens.Count.ShouldBe(1);
result.Tokens[0].Value.ShouldBe(expected); result.Tokens[0].Value.ShouldBe(expected);
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
} }
[Theory] [Theory]
[InlineData("-N-")] [InlineData("-N-")]
[InlineData("-N\"")] [InlineData("-N\"")]
[InlineData("-a1")] [InlineData("-a1")]
public void Should_Throw_On_Invalid_Option_Name(string actual) public void Should_Throw_On_Invalid_Option_Name(string actual)
{ {
// Given // Given
// When // When
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe("Short option does not have a valid name."); ex.Message.ShouldBe("Short option does not have a valid name.");
}); });
} }
} }
[Theory] [Theory]
[InlineData("-")] [InlineData("-")]
[InlineData("- ")] [InlineData("- ")]
public void Should_Throw_On_Missing_Option_Name(string actual) public void Should_Throw_On_Missing_Option_Name(string actual)
{ {
// Given // Given
// When // When
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
// Then // Then
result.ShouldBeOfType<CommandParseException>().And(ex => result.ShouldBeOfType<CommandParseException>().And(ex =>
{ {
ex.Message.ShouldBe("Option does not have a name."); ex.Message.ShouldBe("Option does not have a name.");
}); });
} }
} }

View File

@ -1,4 +1,4 @@
namespace Spectre.Console.Tests.Unit.Cli.Testing; namespace Spectre.Console.Tests.Unit.Cli.Testing;
public class FakeTypeRegistrarTests public class FakeTypeRegistrarTests
{ {

View File

@ -1,20 +1,20 @@
namespace Spectre.Console.Tests.Unit; namespace Spectre.Console.Tests.Unit;
public sealed class StyleTests public sealed class StyleTests
{ {
[Fact] [Fact]
public void Should_Convert_From_Color_As_Expected() public void Should_Convert_From_Color_As_Expected()
{ {
// Given // Given
Style style; Style style;
// When // When
style = Color.Red; style = Color.Red;
// Then // Then
style.Foreground.ShouldBe(Color.Red); style.Foreground.ShouldBe(Color.Red);
} }
[Fact] [Fact]
public void Should_Combine_Two_Styles_As_Expected() public void Should_Combine_Two_Styles_As_Expected()
{ {

View File

@ -1,4 +1,4 @@
namespace Spectre.Console.Tests; namespace Spectre.Console.Tests;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class GitHubIssueAttribute : Attribute public sealed class GitHubIssueAttribute : Attribute