From 44300c871fb412b34ae1d8d3c937b4fa50c0c2b9 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Mon, 27 Nov 2023 12:41:08 +0100 Subject: [PATCH] fixed line-endings --- docs/Program.cs | 154 ++-- docs/input/appendix/styles.md | 10 +- docs/input/cli/command-help.md | 94 +-- docs/input/markup.md | 238 +++--- docs/input/prompts/text.md | 20 +- docs/input/widgets/calendar.md | 2 +- docs/src/Extensions/BootstrapperExtensions.cs | 2 +- .../Extensions/IExecutionContextExtensions.cs | 2 +- docs/src/Extensions/StringExtensions.cs | 2 +- docs/src/Models/Color.cs | 2 +- docs/src/Models/ColorModel.cs | 2 +- docs/src/Pipelines/CodePipeline.cs | 2 +- docs/src/Pipelines/DeploymentPipeline.cs | 2 +- docs/src/Pipelines/SocialCardPipeline.cs | 236 +++--- docs/src/Shortcodes/ExampleSnippet.cs | 2 +- docs/src/Utilities/HighlightService.cs | 2 +- examples/Cli/Demo/Program.cs | 32 +- examples/Cli/Help/CustomHelpProvider.cs | 58 +- examples/Cli/Help/DefaultCommand.cs | 38 +- examples/Cli/Help/Program.cs | 38 +- examples/Console/Prompt/Program.cs | 32 +- .../Commands/AsciiCast/AsciiCastExtensions.cs | 2 +- .../CodeActions/SwitchToAnsiConsoleAction.cs | 334 ++++----- src/Spectre.Console.Cli/CommandApp.cs | 4 +- src/Spectre.Console.Cli/CommandAppOfT.cs | 8 +- .../ConfiguratorExtensions.cs | 56 +- src/Spectre.Console.Cli/Help/HelpProvider.cs | 272 +++---- .../Help/ICommandContainer.cs | 2 +- src/Spectre.Console.Cli/Help/ICommandInfo.cs | 78 +- .../Help/ICommandInfoExtensions.cs | 46 +- .../Help/ICommandOption.cs | 48 +- .../Help/ICommandParameter.cs | 58 +- src/Spectre.Console.Cli/Help/IHelpProvider.cs | 28 +- .../ICommandAppSettings.cs | 44 +- src/Spectre.Console.Cli/IConfigurator.cs | 28 +- .../Internal/CommandExecutor.cs | 290 ++++---- .../Configuration/BranchConfigurator.cs | 2 +- .../Configuration/CommandAppSettings.cs | 10 +- .../Internal/Configuration/Configurator.cs | 26 +- .../Internal/Modelling/CommandInfo.cs | 20 +- .../Internal/Modelling/CommandModel.cs | 34 +- .../Internal/Modelling/CommandModelBuilder.cs | 8 +- .../Internal/Modelling/CommandParameter.cs | 10 +- .../Internal/Modelling/ICommandContainer.cs | 12 +- .../Internal/Parsing/CommandTreeParser.cs | 140 ++-- .../Parsing/CommandTreeParserContext.cs | 16 +- .../Internal/Parsing/CommandTreeToken.cs | 10 +- .../Parsing/CommandTreeTokenStream.cs | 2 +- .../Internal/Parsing/CommandTreeTokenizer.cs | 80 +-- src/Spectre.Console.Cli/Properties/Usings.cs | 2 +- .../Resources/HelpProvider.Designer.cs | 306 ++++---- .../Cli/CommandAppTester.cs | 4 +- .../Extensions/AnsiConsoleExtensions.Input.cs | 2 +- .../Extensions/StringExtensions.cs | 46 +- src/Spectre.Console/HorizontalAlignment.cs | 2 +- .../Prompts/TextPromptExtensions.cs | 28 +- src/Spectre.Console/VerticalAlignment.cs | 2 +- .../Widgets/Exceptions/TypeNameHelper.cs | 2 +- .../Widgets/Layout/LayoutRender.cs | 2 +- .../SpectreAnalyzerVerifier.cs | 10 +- .../Data/Commands/AsynchronousCommand.cs | 56 +- .../Data/Commands/GreeterCommand.cs | 32 +- .../Data/Help/CustomHelpProvider.cs | 68 +- .../Data/Help/RedirectHelpProvider.cs | 40 +- .../Settings/AsynchronousCommandSettings.cs | 14 +- .../Data/Settings/ReptileSettings.cs | 2 +- .../Data/Settings/ThrowingCommandSettings.cs | 10 +- .../Properties/Usings.cs | 2 +- .../Unit/CommandAppTests.Async.cs | 138 ++-- .../Unit/CommandAppTests.Branches.cs | 312 ++++---- .../Unit/CommandAppTests.Help.cs | 680 +++++++++--------- .../Unit/CommandAppTests.Parsing.cs | 6 +- .../Unit/CommandAppTests.Remaining.cs | 174 ++--- .../Unit/CommandAppTests.Version.cs | 32 +- .../Unit/CommandAppTests.cs | 126 ++-- .../Unit/Parsing/CommandTreeTokenizerTests.cs | 628 ++++++++-------- .../Unit/Testing/FakeTypeRegistrarTests.cs | 2 +- test/Spectre.Console.Tests/Unit/StyleTests.cs | 24 +- .../Utilities/GitHubIssueAttribute.cs | 2 +- 79 files changed, 2696 insertions(+), 2696 deletions(-) diff --git a/docs/Program.cs b/docs/Program.cs index f5f7fa0..abb1945 100644 --- a/docs/Program.cs +++ b/docs/Program.cs @@ -1,77 +1,77 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Docs.Extensions; -using Docs.Shortcodes; -using Docs.Utilities; -using Microsoft.Extensions.DependencyInjection; -using Statiq.App; -using Statiq.Common; -using Statiq.Core; -using Statiq.Web; - -namespace Docs -{ - public static class Program - { - public static async Task Main(string[] args) => - await Bootstrapper.Factory - .CreateWeb(args) - .AddSetting(Keys.Host, "spectreconsole.net") - .AddSetting(Keys.LinksUseHttps, true) - .AddSetting(Constants.EditLink, ConfigureEditLink()) - .AddSetting(Constants.SourceFiles, new List - { - "../../src/Spectre.Console/**/{!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.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs" - }) - .AddSetting(Constants.ExampleSourceFiles, new List - { - "../../examples/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", - } - ) - .ConfigureServices(i => - { - i.AddSingleton(new TypeNameLinks()); - }) - .ConfigureSite("spectreconsole", "spectre.console", "main") - .AddShortcode("Children", typeof(ChildrenShortcode)) - .AddShortcode("ColorTable", typeof(ColorTableShortcode)) - .AddShortcode("EmojiTable", typeof(EmojiTableShortcode)) - .AddShortcode("Alert", typeof(AlertShortcode)) - .AddShortcode("Info", typeof(InfoShortcode)) - .AddShortcode("AsciiCast", typeof(AsciiCastShortcode)) - .AddShortcode("Example", typeof(ExampleSnippet)) - .AddPipelines() - .BuildPipeline( - "Bootstrap", - builder => builder - .WithInputReadFiles("../node_modules/asciinema-player/dist/bundle/asciinema-player.js") - .WithProcessModules(new SetDestination(Config.FromDocument(doc => new NormalizedPath($"./assets/{doc.Source.FileName}")), true)) - .WithOutputWriteFiles() - ) - .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("npm", "install --audit false --fund false --progress false") - { - LogErrors = false - }) - .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("dotnet", "playwright install chromium")) - .AddProcess(ProcessTiming.BeforeDeployment, _ => new ProcessLauncher("npm", "run build:tailwind") - { - LogErrors = false - }) - .RunAsync(); - - private static Config ConfigureEditLink() - { - return Config.FromDocument((doc, ctx) => - { - return string.Format("https://github.com/{0}/{1}/edit/{2}/docs/input/{3}", - ctx.GetString(Constants.Site.Owner), - ctx.GetString(Constants.Site.Repository), - ctx.GetString(Constants.Site.Branch), - doc.Source.GetRelativeInputPath()); - }); - } - } -} +using System.Collections.Generic; +using System.Threading.Tasks; +using Docs.Extensions; +using Docs.Shortcodes; +using Docs.Utilities; +using Microsoft.Extensions.DependencyInjection; +using Statiq.App; +using Statiq.Common; +using Statiq.Core; +using Statiq.Web; + +namespace Docs +{ + public static class Program + { + public static async Task Main(string[] args) => + await Bootstrapper.Factory + .CreateWeb(args) + .AddSetting(Keys.Host, "spectreconsole.net") + .AddSetting(Keys.LinksUseHttps, true) + .AddSetting(Constants.EditLink, ConfigureEditLink()) + .AddSetting(Constants.SourceFiles, new List + { + "../../src/Spectre.Console/**/{!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.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs" + }) + .AddSetting(Constants.ExampleSourceFiles, new List + { + "../../examples/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", + } + ) + .ConfigureServices(i => + { + i.AddSingleton(new TypeNameLinks()); + }) + .ConfigureSite("spectreconsole", "spectre.console", "main") + .AddShortcode("Children", typeof(ChildrenShortcode)) + .AddShortcode("ColorTable", typeof(ColorTableShortcode)) + .AddShortcode("EmojiTable", typeof(EmojiTableShortcode)) + .AddShortcode("Alert", typeof(AlertShortcode)) + .AddShortcode("Info", typeof(InfoShortcode)) + .AddShortcode("AsciiCast", typeof(AsciiCastShortcode)) + .AddShortcode("Example", typeof(ExampleSnippet)) + .AddPipelines() + .BuildPipeline( + "Bootstrap", + builder => builder + .WithInputReadFiles("../node_modules/asciinema-player/dist/bundle/asciinema-player.js") + .WithProcessModules(new SetDestination(Config.FromDocument(doc => new NormalizedPath($"./assets/{doc.Source.FileName}")), true)) + .WithOutputWriteFiles() + ) + .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("npm", "install --audit false --fund false --progress false") + { + LogErrors = false + }) + .AddProcess(ProcessTiming.Initialization, _ => new ProcessLauncher("dotnet", "playwright install chromium")) + .AddProcess(ProcessTiming.BeforeDeployment, _ => new ProcessLauncher("npm", "run build:tailwind") + { + LogErrors = false + }) + .RunAsync(); + + private static Config ConfigureEditLink() + { + return Config.FromDocument((doc, ctx) => + { + return string.Format("https://github.com/{0}/{1}/edit/{2}/docs/input/{3}", + ctx.GetString(Constants.Site.Owner), + ctx.GetString(Constants.Site.Repository), + ctx.GetString(Constants.Site.Branch), + doc.Source.GetRelativeInputPath()); + }); + } + } +} diff --git a/docs/input/appendix/styles.md b/docs/input/appendix/styles.md index 5e47022..a13e99e 100644 --- a/docs/input/appendix/styles.md +++ b/docs/input/appendix/styles.md @@ -4,7 +4,7 @@ Description: "*Spectre.Console* makes it easy to write text with different style Highlights: - Bold, Italic, Underline, strikethrough - Dim, Invert - - Conceal, slowblink, rapidblink + - Conceal, slowblink, rapidblink - Links --- @@ -46,9 +46,9 @@ Note that what styles that can be used is defined by the system or your terminal strikethrough Shows text with a horizontal line through the center - - - link - Creates a clickable link within text + + + link + Creates a clickable link within text \ No newline at end of file diff --git a/docs/input/cli/command-help.md b/docs/input/cli/command-help.md index adc7a25..b121bb3 100644 --- a/docs/input/cli/command-help.md +++ b/docs/input/cli/command-help.md @@ -1,47 +1,47 @@ -Title: Command Help -Order: 13 -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. - -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, - -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. - -`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 - -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. - -```csharp -using Spectre.Console.Cli; - -namespace Help; - -public static class Program -{ - public static int Main(string[] args) - { - var app = new CommandApp(); - - app.Configure(config => - { - // Register the custom help provider - config.SetHelpProvider(new CustomHelpProvider(config.Settings)); - }); - - 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. - +Title: Command Help +Order: 13 +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. + +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, + +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. + +`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 + +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. + +```csharp +using Spectre.Console.Cli; + +namespace Help; + +public static class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + + app.Configure(config => + { + // Register the custom help provider + config.SetHelpProvider(new CustomHelpProvider(config.Settings)); + }); + + 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. + diff --git a/docs/input/markup.md b/docs/input/markup.md index 2998cd7..6aed0b8 100644 --- a/docs/input/markup.md +++ b/docs/input/markup.md @@ -1,119 +1,119 @@ -Title: Markup -Order: 30 -Description: The Markup class allows you to output rich text to the console. -Highlights: - - Easily add *color*. - - Add hyperlinks to for supported terminals. - - Emoji 🚀 parsing. -Reference: - - M:Spectre.Console.AnsiConsole.Markup(System.String) - - M:Spectre.Console.AnsiConsole.MarkupLine(System.String) - - T:Spectre.Console.Markup ---- - -The `Markup` class allows you to output rich text to the console. - -## Syntax - -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 `[/]`. - -```csharp -AnsiConsole.Write(new Markup("[bold yellow]Hello[/] [red]World![/]")); -``` - -The `Markup` class implements `IRenderable` which means that you -can use this in tables, grids, and panels. Most classes that support -rendering of `IRenderable` also have overloads for rendering rich text. - -```csharp -var table = new Table(); -table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]"))); -table.AddColumn(new TableColumn("[blue]Bar[/]")); -AnsiConsole.Write(table); -``` - -## Convenience methods - -There are also convenience methods on `AnsiConsole` that can be used -to write markup text to the console without instantiating a new `Markup` -instance. - -```csharp -AnsiConsole.Markup("[underline green]Hello[/] "); -AnsiConsole.MarkupLine("[bold]World[/]"); -``` - -## Escaping format characters - -To output a `[` you use `[[`, and to output a `]` you use `]]`. - -```csharp -AnsiConsole.Markup("[[Hello]] "); // [Hello] -AnsiConsole.Markup("[red][[World]][/]"); // [World] -``` - -You can also use the `EscapeMarkup` extension method. - -```csharp -AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".EscapeMarkup()); -``` -You can also use the `Markup.Escape` method. - -```csharp -AnsiConsole.Markup("[red]{0}[/]", Markup.Escape("Hello [World]")); -``` - -## 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". - -```csharp -string hello = "Hello [World]"; -AnsiConsole.MarkupInterpolated($"[red]{hello}[/]"); -``` - -## Setting background color - -You can set the background color in markup by prefixing the color with `on`. - -```csharp -AnsiConsole.Markup("[bold yellow on blue]Hello[/]"); -AnsiConsole.Markup("[default on blue]World[/]"); -``` - -## Rendering emojis - -To output an emoji as part of markup, you can use emoji shortcodes. - -```csharp -AnsiConsole.Markup("Hello :globe_showing_europe_africa:!"); -``` - -For a list of emoji, see the [Emojis](xref:emojis) appendix section. - -## Colors - -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. - -```csharp -AnsiConsole.Markup("[red]Foo[/] "); -AnsiConsole.Markup("[#ff0000]Bar[/] "); -AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] "); -``` - -For a list of colors, see the [Colors](xref:colors) appendix section. - -## Links - -To output a clickable link, you can use the `[link]` style. - -```csharp -AnsiConsole.Markup("[link]https://spectreconsole.net[/]"); -AnsiConsole.Markup("[link=https://spectreconsole.net]Spectre Console Documentation[/]"); -``` - -## Styles - -For a list of styles, see the [Styles](xref:styles) appendix section. +Title: Markup +Order: 30 +Description: The Markup class allows you to output rich text to the console. +Highlights: + - Easily add *color*. + - Add hyperlinks to for supported terminals. + - Emoji 🚀 parsing. +Reference: + - M:Spectre.Console.AnsiConsole.Markup(System.String) + - M:Spectre.Console.AnsiConsole.MarkupLine(System.String) + - T:Spectre.Console.Markup +--- + +The `Markup` class allows you to output rich text to the console. + +## Syntax + +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 `[/]`. + +```csharp +AnsiConsole.Write(new Markup("[bold yellow]Hello[/] [red]World![/]")); +``` + +The `Markup` class implements `IRenderable` which means that you +can use this in tables, grids, and panels. Most classes that support +rendering of `IRenderable` also have overloads for rendering rich text. + +```csharp +var table = new Table(); +table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]"))); +table.AddColumn(new TableColumn("[blue]Bar[/]")); +AnsiConsole.Write(table); +``` + +## Convenience methods + +There are also convenience methods on `AnsiConsole` that can be used +to write markup text to the console without instantiating a new `Markup` +instance. + +```csharp +AnsiConsole.Markup("[underline green]Hello[/] "); +AnsiConsole.MarkupLine("[bold]World[/]"); +``` + +## Escaping format characters + +To output a `[` you use `[[`, and to output a `]` you use `]]`. + +```csharp +AnsiConsole.Markup("[[Hello]] "); // [Hello] +AnsiConsole.Markup("[red][[World]][/]"); // [World] +``` + +You can also use the `EscapeMarkup` extension method. + +```csharp +AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".EscapeMarkup()); +``` +You can also use the `Markup.Escape` method. + +```csharp +AnsiConsole.Markup("[red]{0}[/]", Markup.Escape("Hello [World]")); +``` + +## 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". + +```csharp +string hello = "Hello [World]"; +AnsiConsole.MarkupInterpolated($"[red]{hello}[/]"); +``` + +## Setting background color + +You can set the background color in markup by prefixing the color with `on`. + +```csharp +AnsiConsole.Markup("[bold yellow on blue]Hello[/]"); +AnsiConsole.Markup("[default on blue]World[/]"); +``` + +## Rendering emojis + +To output an emoji as part of markup, you can use emoji shortcodes. + +```csharp +AnsiConsole.Markup("Hello :globe_showing_europe_africa:!"); +``` + +For a list of emoji, see the [Emojis](xref:emojis) appendix section. + +## Colors + +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. + +```csharp +AnsiConsole.Markup("[red]Foo[/] "); +AnsiConsole.Markup("[#ff0000]Bar[/] "); +AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] "); +``` + +For a list of colors, see the [Colors](xref:colors) appendix section. + +## Links + +To output a clickable link, you can use the `[link]` style. + +```csharp +AnsiConsole.Markup("[link]https://spectreconsole.net[/]"); +AnsiConsole.Markup("[link=https://spectreconsole.net]Spectre Console Documentation[/]"); +``` + +## Styles + +For a list of styles, see the [Styles](xref:styles) appendix section. diff --git a/docs/input/prompts/text.md b/docs/input/prompts/text.md index 88ddd43..953fd54 100644 --- a/docs/input/prompts/text.md +++ b/docs/input/prompts/text.md @@ -63,22 +63,22 @@ What's the secret number? _ ```text Enter password: ************_ -``` - -## Masks - +``` + +## Masks + -```text +```text Enter password: ------------_ -``` - -You can utilize a null character to completely hide input. - +``` + +You can utilize a null character to completely hide input. + -```text +```text Enter password: _ ``` diff --git a/docs/input/widgets/calendar.md b/docs/input/widgets/calendar.md index d2c3595..0e098bd 100644 --- a/docs/input/widgets/calendar.md +++ b/docs/input/widgets/calendar.md @@ -1,4 +1,4 @@ -Title: Calendar +Title: Calendar Order: 40 RedirectFrom: calendar Description: "The **Calendar** is used to render a calendar to the terminal." diff --git a/docs/src/Extensions/BootstrapperExtensions.cs b/docs/src/Extensions/BootstrapperExtensions.cs index cea29c0..795b7bf 100644 --- a/docs/src/Extensions/BootstrapperExtensions.cs +++ b/docs/src/Extensions/BootstrapperExtensions.cs @@ -1,4 +1,4 @@ -using Statiq.App; +using Statiq.App; using Statiq.Common; using Statiq.Web; diff --git a/docs/src/Extensions/IExecutionContextExtensions.cs b/docs/src/Extensions/IExecutionContextExtensions.cs index 30f74bb..131c475 100644 --- a/docs/src/Extensions/IExecutionContextExtensions.cs +++ b/docs/src/Extensions/IExecutionContextExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/docs/src/Extensions/StringExtensions.cs b/docs/src/Extensions/StringExtensions.cs index fdaa80f..3dd1e4d 100644 --- a/docs/src/Extensions/StringExtensions.cs +++ b/docs/src/Extensions/StringExtensions.cs @@ -1,4 +1,4 @@ -namespace Docs.Extensions +namespace Docs.Extensions { public static class StringExtensions { diff --git a/docs/src/Models/Color.cs b/docs/src/Models/Color.cs index ad0be57..dc82681 100644 --- a/docs/src/Models/Color.cs +++ b/docs/src/Models/Color.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; diff --git a/docs/src/Models/ColorModel.cs b/docs/src/Models/ColorModel.cs index ddd5046..279d3d8 100644 --- a/docs/src/Models/ColorModel.cs +++ b/docs/src/Models/ColorModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Docs.Models { diff --git a/docs/src/Pipelines/CodePipeline.cs b/docs/src/Pipelines/CodePipeline.cs index 0a5b195..d505080 100644 --- a/docs/src/Pipelines/CodePipeline.cs +++ b/docs/src/Pipelines/CodePipeline.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net; using Docs.Utilities; using Microsoft.Extensions.DependencyInjection; diff --git a/docs/src/Pipelines/DeploymentPipeline.cs b/docs/src/Pipelines/DeploymentPipeline.cs index 298804a..affe406 100644 --- a/docs/src/Pipelines/DeploymentPipeline.cs +++ b/docs/src/Pipelines/DeploymentPipeline.cs @@ -1,4 +1,4 @@ -using Statiq.Common; +using Statiq.Common; using Statiq.Web.GitHub; using Statiq.Web.Netlify; diff --git a/docs/src/Pipelines/SocialCardPipeline.cs b/docs/src/Pipelines/SocialCardPipeline.cs index 1ef0f70..bf471f6 100644 --- a/docs/src/Pipelines/SocialCardPipeline.cs +++ b/docs/src/Pipelines/SocialCardPipeline.cs @@ -1,119 +1,119 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; -using Microsoft.Playwright; -using Statiq.Common; -using Statiq.Core; -using Statiq.Web; -using Statiq.Web.Modules; -using Statiq.Web.Pipelines; - -namespace Docs.Pipelines -{ - public class SocialImages : Pipeline - { - public SocialImages() - { - Dependencies.AddRange(nameof(Inputs)); - - ProcessModules = new ModuleList - { - new GetPipelineDocuments(ContentType.Content), - - // Filter to non-archive content - new FilterDocuments(Config.FromDocument(doc => !Archives.IsArchive(doc))), - - // Process the content - new CacheDocuments - { - new AddTitle(), - new SetDestination(true), - new ExecuteIf(Config.FromSetting(WebKeys.OptimizeContentFileNames, true)) - { - new OptimizeFileName() - }, - new GenerateSocialImage(), - } - }; - - OutputModules = new ModuleList { new WriteFiles() }; - } - } - - class GenerateSocialImage : ParallelModule - { - private IPlaywright _playwright; - private IBrowser _browser; - private WebApplication _app; - private IBrowserContext _context; - - protected override async Task BeforeExecutionAsync(IExecutionContext context) - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - - builder.Services - .AddRazorPages() - .WithRazorPagesRoot("/src/SocialCards/"); - - _app = builder.Build(); - _app.MapRazorPages(); - _app.UseStaticFiles(new StaticFileOptions - { - FileProvider = new PhysicalFileProvider( - Path.Combine(builder.Environment.ContentRootPath, "src/SocialCards")), - RequestPath = "/static" - }); - - await _app.StartAsync().ConfigureAwait(false); - - _playwright = await Playwright.CreateAsync().ConfigureAwait(false); - _browser = await _playwright.Chromium.LaunchAsync().ConfigureAwait(false); - _context = await _browser.NewContextAsync(new BrowserNewContextOptions { - ViewportSize = new ViewportSize { Width = 1200, Height = 618 }, - }).ConfigureAwait(false); - } - - protected override async Task FinallyAsync(IExecutionContext context) - { - await _context.DisposeAsync().ConfigureAwait(false); - await _browser.DisposeAsync().ConfigureAwait(false); - _playwright.Dispose(); - await _app.DisposeAsync().ConfigureAwait(false); - await base.FinallyAsync(context); - } - - protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) - { - var url = _app.Urls.FirstOrDefault(u => u.StartsWith("http://")); - var page = await _context.NewPageAsync().ConfigureAwait(false); - - var title = input.GetString("Title"); - var description = input.GetString("Description"); - var highlights = input.GetList("Highlights") ?? Array.Empty(); - - 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 - // chrome a chance to complete rendering of the fonts while the wait timeout completes. - await page.WaitForLoadStateAsync(LoadState.NetworkIdle).ConfigureAwait(false); - var bytes = await page.ScreenshotAsync().ConfigureAwait(false); - await page.CloseAsync().ConfigureAwait(false); - - var destination = input.Destination.InsertSuffix("-social").ChangeExtension("png"); - var doc = context.CreateDocument( - input.Source, - destination, - new MetadataItems { { "DocId", input.Id }}, - context.GetContentProvider(bytes)); - - return new[] { doc }; - } - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Playwright; +using Statiq.Common; +using Statiq.Core; +using Statiq.Web; +using Statiq.Web.Modules; +using Statiq.Web.Pipelines; + +namespace Docs.Pipelines +{ + public class SocialImages : Pipeline + { + public SocialImages() + { + Dependencies.AddRange(nameof(Inputs)); + + ProcessModules = new ModuleList + { + new GetPipelineDocuments(ContentType.Content), + + // Filter to non-archive content + new FilterDocuments(Config.FromDocument(doc => !Archives.IsArchive(doc))), + + // Process the content + new CacheDocuments + { + new AddTitle(), + new SetDestination(true), + new ExecuteIf(Config.FromSetting(WebKeys.OptimizeContentFileNames, true)) + { + new OptimizeFileName() + }, + new GenerateSocialImage(), + } + }; + + OutputModules = new ModuleList { new WriteFiles() }; + } + } + + class GenerateSocialImage : ParallelModule + { + private IPlaywright _playwright; + private IBrowser _browser; + private WebApplication _app; + private IBrowserContext _context; + + protected override async Task BeforeExecutionAsync(IExecutionContext context) + { + var builder = WebApplication.CreateBuilder(); + builder.Logging.ClearProviders(); + + builder.Services + .AddRazorPages() + .WithRazorPagesRoot("/src/SocialCards/"); + + _app = builder.Build(); + _app.MapRazorPages(); + _app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider( + Path.Combine(builder.Environment.ContentRootPath, "src/SocialCards")), + RequestPath = "/static" + }); + + await _app.StartAsync().ConfigureAwait(false); + + _playwright = await Playwright.CreateAsync().ConfigureAwait(false); + _browser = await _playwright.Chromium.LaunchAsync().ConfigureAwait(false); + _context = await _browser.NewContextAsync(new BrowserNewContextOptions { + ViewportSize = new ViewportSize { Width = 1200, Height = 618 }, + }).ConfigureAwait(false); + } + + protected override async Task FinallyAsync(IExecutionContext context) + { + await _context.DisposeAsync().ConfigureAwait(false); + await _browser.DisposeAsync().ConfigureAwait(false); + _playwright.Dispose(); + await _app.DisposeAsync().ConfigureAwait(false); + await base.FinallyAsync(context); + } + + protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context) + { + var url = _app.Urls.FirstOrDefault(u => u.StartsWith("http://")); + var page = await _context.NewPageAsync().ConfigureAwait(false); + + var title = input.GetString("Title"); + var description = input.GetString("Description"); + var highlights = input.GetList("Highlights") ?? Array.Empty(); + + 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 + // chrome a chance to complete rendering of the fonts while the wait timeout completes. + await page.WaitForLoadStateAsync(LoadState.NetworkIdle).ConfigureAwait(false); + var bytes = await page.ScreenshotAsync().ConfigureAwait(false); + await page.CloseAsync().ConfigureAwait(false); + + var destination = input.Destination.InsertSuffix("-social").ChangeExtension("png"); + var doc = context.CreateDocument( + input.Source, + destination, + new MetadataItems { { "DocId", input.Id }}, + context.GetContentProvider(bytes)); + + return new[] { doc }; + } + } } \ No newline at end of file diff --git a/docs/src/Shortcodes/ExampleSnippet.cs b/docs/src/Shortcodes/ExampleSnippet.cs index 2f0c1e9..fcd8145 100644 --- a/docs/src/Shortcodes/ExampleSnippet.cs +++ b/docs/src/Shortcodes/ExampleSnippet.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Docs.Extensions; diff --git a/docs/src/Utilities/HighlightService.cs b/docs/src/Utilities/HighlightService.cs index 3ef6b88..cb77687 100644 --- a/docs/src/Utilities/HighlightService.cs +++ b/docs/src/Utilities/HighlightService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/examples/Cli/Demo/Program.cs b/examples/Cli/Demo/Program.cs index 8b87954..f6c3403 100644 --- a/examples/Cli/Demo/Program.cs +++ b/examples/Cli/Demo/Program.cs @@ -15,22 +15,22 @@ public static class Program { config.SetApplicationName("fake-dotnet"); config.ValidateExamples(); - config.AddExample("run", "--no-build"); - - // Run - config.AddCommand("run"); - - // Add - config.AddBranch("add", add => - { - add.SetDescription("Add a package or reference to a .NET project"); - add.AddCommand("package"); - add.AddCommand("reference"); - }); - - // Serve - config.AddCommand("serve") - .WithExample("serve", "-o", "firefox") + config.AddExample("run", "--no-build"); + + // Run + config.AddCommand("run"); + + // Add + config.AddBranch("add", add => + { + add.SetDescription("Add a package or reference to a .NET project"); + add.AddCommand("package"); + add.AddCommand("reference"); + }); + + // Serve + config.AddCommand("serve") + .WithExample("serve", "-o", "firefox") .WithExample("serve", "--port", "80", "-o", "firefox"); }); diff --git a/examples/Cli/Help/CustomHelpProvider.cs b/examples/Cli/Help/CustomHelpProvider.cs index 36b213b..fd9c01f 100644 --- a/examples/Cli/Help/CustomHelpProvider.cs +++ b/examples/Cli/Help/CustomHelpProvider.cs @@ -1,30 +1,30 @@ -using System.Linq; -using Spectre.Console; -using Spectre.Console.Cli; -using Spectre.Console.Cli.Help; -using Spectre.Console.Rendering; - -namespace Help; - -/// -/// Example showing how to extend the built-in Spectre.Console help provider -/// by rendering a custom banner at the top of the help information -/// -internal class CustomHelpProvider : HelpProvider -{ - public CustomHelpProvider(ICommandAppSettings settings) - : base(settings) - { - } - - public override IEnumerable GetHeader(ICommandModel model, ICommandInfo? command) - { - return new[] - { - new Text("--------------------------------------"), Text.NewLine, - new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, - new Text("--------------------------------------"), Text.NewLine, - Text.NewLine, - }; - } +using System.Linq; +using Spectre.Console; +using Spectre.Console.Cli; +using Spectre.Console.Cli.Help; +using Spectre.Console.Rendering; + +namespace Help; + +/// +/// Example showing how to extend the built-in Spectre.Console help provider +/// by rendering a custom banner at the top of the help information +/// +internal class CustomHelpProvider : HelpProvider +{ + public CustomHelpProvider(ICommandAppSettings settings) + : base(settings) + { + } + + public override IEnumerable GetHeader(ICommandModel model, ICommandInfo? command) + { + return new[] + { + new Text("--------------------------------------"), Text.NewLine, + new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, + new Text("--------------------------------------"), Text.NewLine, + Text.NewLine, + }; + } } \ No newline at end of file diff --git a/examples/Cli/Help/DefaultCommand.cs b/examples/Cli/Help/DefaultCommand.cs index 51d1420..71613d0 100644 --- a/examples/Cli/Help/DefaultCommand.cs +++ b/examples/Cli/Help/DefaultCommand.cs @@ -1,20 +1,20 @@ -using Spectre.Console; -using Spectre.Console.Cli; - -namespace Help; - -public sealed class DefaultCommand : Command -{ - private IAnsiConsole _console; - - public DefaultCommand(IAnsiConsole console) - { - _console = console; - } - - public override int Execute(CommandContext context) - { - _console.WriteLine("Hello world"); - return 0; - } +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Help; + +public sealed class DefaultCommand : Command +{ + private IAnsiConsole _console; + + public DefaultCommand(IAnsiConsole console) + { + _console = console; + } + + public override int Execute(CommandContext context) + { + _console.WriteLine("Hello world"); + return 0; + } } \ No newline at end of file diff --git a/examples/Cli/Help/Program.cs b/examples/Cli/Help/Program.cs index 1d4675f..039625e 100644 --- a/examples/Cli/Help/Program.cs +++ b/examples/Cli/Help/Program.cs @@ -1,19 +1,19 @@ -using Spectre.Console.Cli; - -namespace Help; - -public static class Program -{ - public static int Main(string[] args) - { - var app = new CommandApp(); - - app.Configure(config => - { - // Register the custom help provider - config.SetHelpProvider(new CustomHelpProvider(config.Settings)); - }); - - return app.Run(args); - } -} +using Spectre.Console.Cli; + +namespace Help; + +public static class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + + app.Configure(config => + { + // Register the custom help provider + config.SetHelpProvider(new CustomHelpProvider(config.Settings)); + }); + + return app.Run(args); + } +} diff --git a/examples/Console/Prompt/Program.cs b/examples/Console/Prompt/Program.cs index 0008bfa..ab2cee4 100644 --- a/examples/Console/Prompt/Program.cs +++ b/examples/Console/Prompt/Program.cs @@ -33,11 +33,11 @@ namespace Prompt var age = AskAge(); WriteDivider("Secrets"); - var password = AskPassword(); - + var password = AskPassword(); + WriteDivider("Mask"); - var mask = AskPasswordWithCustomMask(); - + var mask = AskPasswordWithCustomMask(); + WriteDivider("Null Mask"); var nullMask = AskPasswordWithNullMask(); @@ -54,8 +54,8 @@ namespace Prompt .AddRow("[grey]Favorite fruit[/]", fruit) .AddRow("[grey]Favorite sport[/]", sport) .AddRow("[grey]Age[/]", age.ToString()) - .AddRow("[grey]Password[/]", password) - .AddRow("[grey]Mask[/]", mask) + .AddRow("[grey]Password[/]", password) + .AddRow("[grey]Mask[/]", mask) .AddRow("[grey]Null Mask[/]", nullMask) .AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color)); } @@ -153,22 +153,22 @@ namespace Prompt new TextPrompt("Enter [green]password[/]?") .PromptStyle("red") .Secret()); - } - - public static string AskPasswordWithCustomMask() - { + } + + public static string AskPasswordWithCustomMask() + { return AnsiConsole.Prompt( new TextPrompt("Enter [green]password[/]?") .PromptStyle("red") - .Secret('-')); - } - - public static string AskPasswordWithNullMask() - { + .Secret('-')); + } + + public static string AskPasswordWithNullMask() + { return AnsiConsole.Prompt( new TextPrompt("Enter [green]password[/]?") .PromptStyle("red") - .Secret(null)); + .Secret(null)); } public static string AskColor() diff --git a/resources/scripts/Generator/Commands/AsciiCast/AsciiCastExtensions.cs b/resources/scripts/Generator/Commands/AsciiCast/AsciiCastExtensions.cs index 50e592e..307e1e6 100644 --- a/resources/scripts/Generator/Commands/AsciiCast/AsciiCastExtensions.cs +++ b/resources/scripts/Generator/Commands/AsciiCast/AsciiCastExtensions.cs @@ -1,4 +1,4 @@ -using Spectre.Console; +using Spectre.Console; namespace Generator.Commands { diff --git a/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs b/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs index 6a0833d..922524f 100644 --- a/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs +++ b/src/Spectre.Console.Analyzer/Fixes/CodeActions/SwitchToAnsiConsoleAction.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Simplification; namespace Spectre.Console.Analyzer.CodeActions; @@ -32,171 +32,171 @@ public class SwitchToAnsiConsoleAction : CodeAction /// protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) - { - var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false); - var compilation = editor.SemanticModel.Compilation; - - var operation = editor.SemanticModel.GetOperation(_originalInvocation, cancellationToken) as IInvocationOperation; - if (operation == null) - { - return _document; - } - - // If there is an IAnsiConsole passed into the method then we'll use it. - // otherwise we'll check for a field level instance. - // if neither of those exist we'll fall back to the static param. - var spectreConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole"); - var iansiConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.IAnsiConsole"); - - ISymbol? accessibleConsoleSymbol = spectreConsoleSymbol; - if (iansiConsoleSymbol != null) - { - var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition); - - foreach (var symbol in editor.SemanticModel.LookupSymbols(operation.Syntax.GetLocation().SourceSpan.Start)) - { - // LookupSymbols check the accessibility of the symbol, but it can - // suggest instance members when the current context is static. - var symbolType = symbol switch - { - IParameterSymbol parameter => parameter.Type, - IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type, - IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type, - ILocalSymbol local => local.Type, - _ => null, - }; - - // 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 - // should not be accessible in a static local function. - // - // void Sample() - // { - // int local = 0; - // 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. - if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter) - { - var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start; - - // The local is not part of the source tree - if (localPosition == null) - { - break; - } - - // The local is declared after the current expression - if (localPosition > _originalInvocation.Span.Start) - { - break; - } - - // The local is declared outside the static local function - if (isInStaticContext && localPosition < parentStaticMemberStartPosition) - { - break; - } - } - - if (IsOrImplementSymbol(symbolType, iansiConsoleSymbol)) - { - accessibleConsoleSymbol = symbol; - break; - } - } - } - - if (accessibleConsoleSymbol == null) - { - return _document; - } - - // Replace the original invocation - var generator = editor.Generator; - var consoleExpression = accessibleConsoleSymbol switch - { - ITypeSymbol typeSymbol => generator.TypeExpression(typeSymbol, addImport: true).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation), - _ => generator.IdentifierName(accessibleConsoleSymbol.Name), - }; - - var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(consoleExpression, operation.TargetMethod.Name), _originalInvocation.ArgumentList.Arguments) - .WithLeadingTrivia(_originalInvocation.GetLeadingTrivia()) - .WithTrailingTrivia(_originalInvocation.GetTrailingTrivia()); - - editor.ReplaceNode(_originalInvocation, newExpression); - - return editor.GetChangedDocument(); - } - - private static bool IsOrImplementSymbol(ITypeSymbol? symbol, ITypeSymbol interfaceSymbol) - { - if (symbol == null) - { - return false; - } - - if (SymbolEqualityComparer.Default.Equals(symbol, interfaceSymbol)) - { - return true; - } - - foreach (var iface in symbol.AllInterfaces) - { - if (SymbolEqualityComparer.Default.Equals(iface, interfaceSymbol)) - { - return true; - } - } - - return false; - } - - private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition) - { - // 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 - // local function is not static. - foreach (var member in operation.Syntax.Ancestors()) - { - if (member is LocalFunctionStatementSyntax localFunction) - { - var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken); - if (symbol != null && symbol.IsStatic) - { - parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start; - return true; - } - } - else if (member is LambdaExpressionSyntax lambdaExpression) - { - var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol; - if (symbol != null && symbol.IsStatic) - { - parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start; - return true; - } - } - else if (member is AnonymousMethodExpressionSyntax anonymousMethod) - { - var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol; - if (symbol != null && symbol.IsStatic) - { - parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start; - return true; - } - } - else if (member is MethodDeclarationSyntax methodDeclaration) - { - parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start; - - var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken); - return symbol != null && symbol.IsStatic; - } - } - - parentStaticMemberStartPosition = -1; - return false; + { + var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false); + var compilation = editor.SemanticModel.Compilation; + + var operation = editor.SemanticModel.GetOperation(_originalInvocation, cancellationToken) as IInvocationOperation; + if (operation == null) + { + return _document; + } + + // If there is an IAnsiConsole passed into the method then we'll use it. + // otherwise we'll check for a field level instance. + // if neither of those exist we'll fall back to the static param. + var spectreConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole"); + var iansiConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.IAnsiConsole"); + + ISymbol? accessibleConsoleSymbol = spectreConsoleSymbol; + if (iansiConsoleSymbol != null) + { + var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition); + + foreach (var symbol in editor.SemanticModel.LookupSymbols(operation.Syntax.GetLocation().SourceSpan.Start)) + { + // LookupSymbols check the accessibility of the symbol, but it can + // suggest instance members when the current context is static. + var symbolType = symbol switch + { + IParameterSymbol parameter => parameter.Type, + IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type, + IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type, + ILocalSymbol local => local.Type, + _ => null, + }; + + // 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 + // should not be accessible in a static local function. + // + // void Sample() + // { + // int local = 0; + // 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. + if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter) + { + var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start; + + // The local is not part of the source tree + if (localPosition == null) + { + break; + } + + // The local is declared after the current expression + if (localPosition > _originalInvocation.Span.Start) + { + break; + } + + // The local is declared outside the static local function + if (isInStaticContext && localPosition < parentStaticMemberStartPosition) + { + break; + } + } + + if (IsOrImplementSymbol(symbolType, iansiConsoleSymbol)) + { + accessibleConsoleSymbol = symbol; + break; + } + } + } + + if (accessibleConsoleSymbol == null) + { + return _document; + } + + // Replace the original invocation + var generator = editor.Generator; + var consoleExpression = accessibleConsoleSymbol switch + { + ITypeSymbol typeSymbol => generator.TypeExpression(typeSymbol, addImport: true).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation), + _ => generator.IdentifierName(accessibleConsoleSymbol.Name), + }; + + var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(consoleExpression, operation.TargetMethod.Name), _originalInvocation.ArgumentList.Arguments) + .WithLeadingTrivia(_originalInvocation.GetLeadingTrivia()) + .WithTrailingTrivia(_originalInvocation.GetTrailingTrivia()); + + editor.ReplaceNode(_originalInvocation, newExpression); + + return editor.GetChangedDocument(); + } + + private static bool IsOrImplementSymbol(ITypeSymbol? symbol, ITypeSymbol interfaceSymbol) + { + if (symbol == null) + { + return false; + } + + if (SymbolEqualityComparer.Default.Equals(symbol, interfaceSymbol)) + { + return true; + } + + foreach (var iface in symbol.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(iface, interfaceSymbol)) + { + return true; + } + } + + return false; + } + + private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition) + { + // 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 + // local function is not static. + foreach (var member in operation.Syntax.Ancestors()) + { + if (member is LocalFunctionStatementSyntax localFunction) + { + var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken); + if (symbol != null && symbol.IsStatic) + { + parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start; + return true; + } + } + else if (member is LambdaExpressionSyntax lambdaExpression) + { + var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol; + if (symbol != null && symbol.IsStatic) + { + parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start; + return true; + } + } + else if (member is AnonymousMethodExpressionSyntax anonymousMethod) + { + var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol; + if (symbol != null && symbol.IsStatic) + { + parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start; + return true; + } + } + else if (member is MethodDeclarationSyntax methodDeclaration) + { + parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start; + + var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken); + return symbol != null && symbol.IsStatic; + } + } + + parentStaticMemberStartPosition = -1; + return false; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/CommandApp.cs b/src/Spectre.Console.Cli/CommandApp.cs index 4882fc0..07077f2 100644 --- a/src/Spectre.Console.Cli/CommandApp.cs +++ b/src/Spectre.Console.Cli/CommandApp.cs @@ -1,5 +1,5 @@ -using Spectre.Console.Cli.Internal.Configuration; - +using Spectre.Console.Cli.Internal.Configuration; + namespace Spectre.Console.Cli; /// diff --git a/src/Spectre.Console.Cli/CommandAppOfT.cs b/src/Spectre.Console.Cli/CommandAppOfT.cs index 295011e..5e80663 100644 --- a/src/Spectre.Console.Cli/CommandAppOfT.cs +++ b/src/Spectre.Console.Cli/CommandAppOfT.cs @@ -1,5 +1,5 @@ -using Spectre.Console.Cli.Internal.Configuration; - +using Spectre.Console.Cli.Internal.Configuration; + namespace Spectre.Console.Cli; /// @@ -49,8 +49,8 @@ public sealed class CommandApp : ICommandApp public Task RunAsync(IEnumerable args) { return _app.RunAsync(args); - } - + } + internal Configurator GetConfigurator() { return _app.GetConfigurator(); diff --git a/src/Spectre.Console.Cli/ConfiguratorExtensions.cs b/src/Spectre.Console.Cli/ConfiguratorExtensions.cs index 9c62194..6170164 100644 --- a/src/Spectre.Console.Cli/ConfiguratorExtensions.cs +++ b/src/Spectre.Console.Cli/ConfiguratorExtensions.cs @@ -5,40 +5,40 @@ namespace Spectre.Console.Cli; /// and . /// public static class ConfiguratorExtensions -{ - /// - /// Sets the help provider for the application. - /// - /// The configurator. - /// The help provider to use. - /// A configurator that can be used to configure the application further. - public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider) - { +{ + /// + /// Sets the help provider for the application. + /// + /// The configurator. + /// The help provider to use. + /// A configurator that can be used to configure the application further. + public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider) + { if (configurator == null) { throw new ArgumentNullException(nameof(configurator)); - } - - configurator.SetHelpProvider(helpProvider); - return configurator; - } - - /// - /// Sets the help provider for the application. - /// - /// The configurator. - /// The type of the help provider to instantiate at runtime and use. - /// A configurator that can be used to configure the application further. - public static IConfigurator SetHelpProvider(this IConfigurator configurator) - where T : IHelpProvider - { + } + + configurator.SetHelpProvider(helpProvider); + return configurator; + } + + /// + /// Sets the help provider for the application. + /// + /// The configurator. + /// The type of the help provider to instantiate at runtime and use. + /// A configurator that can be used to configure the application further. + public static IConfigurator SetHelpProvider(this IConfigurator configurator) + where T : IHelpProvider + { if (configurator == null) { throw new ArgumentNullException(nameof(configurator)); - } - - configurator.SetHelpProvider(); - return configurator; + } + + configurator.SetHelpProvider(); + return configurator; } /// diff --git a/src/Spectre.Console.Cli/Help/HelpProvider.cs b/src/Spectre.Console.Cli/Help/HelpProvider.cs index 0c8ad67..889e5b9 100644 --- a/src/Spectre.Console.Cli/Help/HelpProvider.cs +++ b/src/Spectre.Console.Cli/Help/HelpProvider.cs @@ -11,22 +11,22 @@ namespace Spectre.Console.Cli.Help; public class HelpProvider : IHelpProvider { private HelpProviderResources resources; - - /// - /// Gets a value indicating how many examples from direct children to show in the help text. - /// - protected virtual int MaximumIndirectExamples { get; } - - /// - /// Gets a value indicating whether any default values for command options are shown in the help text. - /// - protected virtual bool ShowOptionDefaultValues { get; } - - /// - /// Gets a value indicating whether a trailing period of a command description is trimmed in the help text. - /// - protected virtual bool TrimTrailingPeriod { get; } - + + /// + /// Gets a value indicating how many examples from direct children to show in the help text. + /// + protected virtual int MaximumIndirectExamples { get; } + + /// + /// Gets a value indicating whether any default values for command options are shown in the help text. + /// + protected virtual bool ShowOptionDefaultValues { get; } + + /// + /// Gets a value indicating whether a trailing period of a command description is trimmed in the help text. + /// + protected virtual bool TrimTrailingPeriod { get; } + private sealed class HelpArgument { public string Name { get; } @@ -74,14 +74,14 @@ public class HelpProvider : IHelpProvider public static IReadOnlyList Get(ICommandInfo? command, HelpProviderResources resources) { var parameters = new List(); - parameters.Add(new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null)); - - // Version information applies to the entire 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 - if ((command == null || command?.Parent == null) && !(command?.IsBranch ?? false)) - { - parameters.Add(new HelpOption("v", "version", null, null, resources.PrintVersionDescription, null)); + parameters.Add(new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null)); + + // Version information applies to the entire 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 + if ((command == null || command?.Parent == null) && !(command?.IsBranch ?? false)) + { + parameters.Add(new HelpOption("v", "version", null, null, resources.PrintVersionDescription, null)); } parameters.AddRange(command?.Parameters.OfType().Where(o => !o.IsHidden).Select(o => @@ -92,55 +92,55 @@ public class HelpProvider : IHelpProvider ?? Array.Empty()); return parameters; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The command line application settings used for configuration. - public HelpProvider(ICommandAppSettings settings) - { - this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues; - this.MaximumIndirectExamples = settings.MaximumIndirectExamples; + } + + /// + /// Initializes a new instance of the class. + /// + /// The command line application settings used for configuration. + public HelpProvider(ICommandAppSettings settings) + { + this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues; + this.MaximumIndirectExamples = settings.MaximumIndirectExamples; this.TrimTrailingPeriod = settings.TrimTrailingPeriod; - resources = new HelpProviderResources(settings.Culture); - } - - /// + resources = new HelpProviderResources(settings.Culture); + } + + /// public virtual IEnumerable Write(ICommandModel model, ICommandInfo? command) { - var result = new List(); - - result.AddRange(GetHeader(model, command)); + var result = new List(); + + result.AddRange(GetHeader(model, command)); result.AddRange(GetDescription(model, command)); result.AddRange(GetUsage(model, command)); result.AddRange(GetExamples(model, command)); result.AddRange(GetArguments(model, command)); result.AddRange(GetOptions(model, command)); - result.AddRange(GetCommands(model, command)); + result.AddRange(GetCommands(model, command)); result.AddRange(GetFooter(model, command)); return result; - } - - /// - /// Gets the header for the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. + } + + /// + /// Gets the header for the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. public virtual IEnumerable GetHeader(ICommandModel model, ICommandInfo? command) - { - yield break; - } - - /// - /// Gets the description section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. + { + yield break; + } + + /// + /// Gets the description section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. public virtual IEnumerable GetDescription(ICommandModel model, ICommandInfo? command) { if (command?.Description == null) @@ -152,14 +152,14 @@ public class HelpProvider : IHelpProvider composer.Style("yellow", $"{resources.Description}:").LineBreak(); composer.Text(command.Description).LineBreak(); yield return composer.LineBreak(); - } - - /// - /// Gets the usage section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. + } + + /// + /// Gets the usage section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. public virtual IEnumerable GetUsage(ICommandModel model, ICommandInfo? command) { var composer = new Composer(); @@ -219,26 +219,26 @@ public class HelpProvider : IHelpProvider } if (command.IsBranch && command.DefaultCommand == null) - { + { // The user must specify the command parameters.Add($"[aqua]<{resources.Command}>[/]"); - } - else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0) - { - // We are on a branch with a default command + } + else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0) + { + // We are on a branch with a default command // The user can optionally specify the command - parameters.Add($"[aqua][[{resources.Command}]][/]"); - } - else if (command.IsDefaultCommand) - { - var commands = model.Commands.Where(x => !x.IsHidden && !x.IsDefaultCommand).ToList(); - - if (commands.Count > 0) - { - // Commands other than the default are present - // So make these optional in the usage statement - parameters.Add($"[aqua][[{resources.Command}]][/]"); - } + parameters.Add($"[aqua][[{resources.Command}]][/]"); + } + else if (command.IsDefaultCommand) + { + var commands = model.Commands.Where(x => !x.IsHidden && !x.IsDefaultCommand).ToList(); + + if (commands.Count > 0) + { + // Commands other than the default are present + // So make these optional in the usage statement + parameters.Add($"[aqua][[{resources.Command}]][/]"); + } } } @@ -249,19 +249,19 @@ public class HelpProvider : IHelpProvider { composer, }; - } - - /// - /// Gets the examples section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. - /// - /// Examples from the command's direct children are used - /// if no examples have been set on the specified command or model. - /// - public virtual IEnumerable GetExamples(ICommandModel model, ICommandInfo? command) + } + + /// + /// Gets the examples section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. + /// + /// Examples from the command's direct children are used + /// if no examples have been set on the specified command or model. + /// + public virtual IEnumerable GetExamples(ICommandModel model, ICommandInfo? command) { var maxExamples = int.MaxValue; @@ -272,12 +272,12 @@ public class HelpProvider : IHelpProvider // make sure that we limit the number of examples. maxExamples = MaximumIndirectExamples; - // Start at the current command (if exists) - // or alternatively commence at the model. + // Start at the current command (if exists) + // or alternatively commence at the model. var commandContainer = command ?? (ICommandContainer)model; var queue = new Queue(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. while (queue.Count > 0) { @@ -317,14 +317,14 @@ public class HelpProvider : IHelpProvider } return Array.Empty(); - } - - /// - /// Gets the arguments section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. + } + + /// + /// Gets the arguments section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. public virtual IEnumerable GetArguments(ICommandModel model, ICommandInfo? command) { var arguments = HelpArgument.Get(command); @@ -361,13 +361,13 @@ public class HelpProvider : IHelpProvider result.Add(grid); return result; - } - - /// - /// Gets the options section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). + } + + /// + /// Gets the options section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). /// An enumerable collection of objects. public virtual IEnumerable GetOptions(ICommandModel model, ICommandInfo? command) { @@ -469,18 +469,18 @@ public class HelpProvider : IHelpProvider result.Add(grid); return result; - } - - /// - /// Gets the commands section of the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). + } + + /// + /// Gets the commands section of the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). /// An enumerable collection of objects. public virtual IEnumerable GetCommands(ICommandModel model, ICommandInfo? command) - { - var commandContainer = command ?? (ICommandContainer)model; - bool isDefaultCommand = command?.IsDefaultCommand ?? false; + { + var commandContainer = command ?? (ICommandContainer)model; + bool isDefaultCommand = command?.IsDefaultCommand ?? false; var commands = isDefaultCommand ? model.Commands : commandContainer.Commands; commands = commands.Where(x => !x.IsHidden).ToList(); @@ -530,16 +530,16 @@ public class HelpProvider : IHelpProvider result.Add(grid); return result; - } - - /// - /// Gets the footer for the help information. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects. + } + + /// + /// Gets the footer for the help information. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects. public virtual IEnumerable GetFooter(ICommandModel model, ICommandInfo? command) - { - yield break; + { + yield break; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Help/ICommandContainer.cs b/src/Spectre.Console.Cli/Help/ICommandContainer.cs index 21eb4b8..6eccb8d 100644 --- a/src/Spectre.Console.Cli/Help/ICommandContainer.cs +++ b/src/Spectre.Console.Cli/Help/ICommandContainer.cs @@ -4,7 +4,7 @@ namespace Spectre.Console.Cli.Help; /// Represents a command container. /// public interface ICommandContainer -{ +{ /// /// Gets all the examples for the container. /// diff --git a/src/Spectre.Console.Cli/Help/ICommandInfo.cs b/src/Spectre.Console.Cli/Help/ICommandInfo.cs index aaba5ea..9adb16f 100644 --- a/src/Spectre.Console.Cli/Help/ICommandInfo.cs +++ b/src/Spectre.Console.Cli/Help/ICommandInfo.cs @@ -1,42 +1,42 @@ namespace Spectre.Console.Cli.Help; -/// -/// Represents an executable command. -/// -public interface ICommandInfo : ICommandContainer -{ - /// - /// Gets the name of the command. - /// - string Name { get; } - - /// - /// Gets the description of the command. - /// - string? Description { get; } - - /// - /// Gets a value indicating whether the command is a branch. - /// - bool IsBranch { get; } - - /// - /// Gets a value indicating whether the command is the default command within its container. - /// - bool IsDefaultCommand { get; } - - /// - /// Gets a value indicating whether the command is hidden. - /// - bool IsHidden { get; } - - /// - /// Gets the parameters associated with the command. - /// - IReadOnlyList Parameters { get; } - - /// - /// Gets the parent command, if any. - /// - ICommandInfo? Parent { get; } +/// +/// Represents an executable command. +/// +public interface ICommandInfo : ICommandContainer +{ + /// + /// Gets the name of the command. + /// + string Name { get; } + + /// + /// Gets the description of the command. + /// + string? Description { get; } + + /// + /// Gets a value indicating whether the command is a branch. + /// + bool IsBranch { get; } + + /// + /// Gets a value indicating whether the command is the default command within its container. + /// + bool IsDefaultCommand { get; } + + /// + /// Gets a value indicating whether the command is hidden. + /// + bool IsHidden { get; } + + /// + /// Gets the parameters associated with the command. + /// + IReadOnlyList Parameters { get; } + + /// + /// Gets the parent command, if any. + /// + ICommandInfo? Parent { get; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs b/src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs index e91c886..3f54f8d 100644 --- a/src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs +++ b/src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs @@ -1,23 +1,23 @@ -namespace Spectre.Console.Cli.Help; - -internal static class ICommandInfoExtensions -{ - /// - /// Walks up the command.Parent tree, adding each command into a list as it goes. - /// - /// The first command added to the list is the current (ie. this one). - /// The list of commands from current to root, as traversed by . - public static List Flatten(this ICommandInfo commandInfo) - { - var result = new Stack(); - - var current = commandInfo; - while (current != null) - { - result.Push(current); - current = current.Parent; - } - - return result.ToList(); - } -} +namespace Spectre.Console.Cli.Help; + +internal static class ICommandInfoExtensions +{ + /// + /// Walks up the command.Parent tree, adding each command into a list as it goes. + /// + /// The first command added to the list is the current (ie. this one). + /// The list of commands from current to root, as traversed by . + public static List Flatten(this ICommandInfo commandInfo) + { + var result = new Stack(); + + var current = commandInfo; + while (current != null) + { + result.Push(current); + current = current.Parent; + } + + return result.ToList(); + } +} diff --git a/src/Spectre.Console.Cli/Help/ICommandOption.cs b/src/Spectre.Console.Cli/Help/ICommandOption.cs index 1247df8..e6c3517 100644 --- a/src/Spectre.Console.Cli/Help/ICommandOption.cs +++ b/src/Spectre.Console.Cli/Help/ICommandOption.cs @@ -1,27 +1,27 @@ namespace Spectre.Console.Cli.Help; -/// -/// Represents a command option. -/// -public interface ICommandOption : ICommandParameter -{ - /// - /// Gets the long names of the option. - /// - IReadOnlyList LongNames { get; } - - /// - /// Gets the short names of the option. - /// - IReadOnlyList ShortNames { get; } - - /// - /// Gets the value name of the option, if applicable. - /// - string? ValueName { get; } - - /// - /// Gets a value indicating whether the option value is optional. - /// - bool ValueIsOptional { get; } +/// +/// Represents a command option. +/// +public interface ICommandOption : ICommandParameter +{ + /// + /// Gets the long names of the option. + /// + IReadOnlyList LongNames { get; } + + /// + /// Gets the short names of the option. + /// + IReadOnlyList ShortNames { get; } + + /// + /// Gets the value name of the option, if applicable. + /// + string? ValueName { get; } + + /// + /// Gets a value indicating whether the option value is optional. + /// + bool ValueIsOptional { get; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Help/ICommandParameter.cs b/src/Spectre.Console.Cli/Help/ICommandParameter.cs index 03bd7b6..161fa73 100644 --- a/src/Spectre.Console.Cli/Help/ICommandParameter.cs +++ b/src/Spectre.Console.Cli/Help/ICommandParameter.cs @@ -1,32 +1,32 @@ namespace Spectre.Console.Cli.Help; -/// -/// Represents a command parameter. -/// -public interface ICommandParameter -{ - /// - /// Gets a value indicating whether the parameter is a flag. - /// - bool IsFlag { get; } - - /// - /// Gets a value indicating whether the parameter is required. - /// - bool Required { get; } - - /// - /// Gets the description of the parameter. - /// - string? Description { get; } - - /// - /// Gets the default value of the parameter, if specified. - /// - DefaultValueAttribute? DefaultValue { get; } - - /// - /// Gets a value indicating whether the parameter is hidden. - /// - bool IsHidden { get; } +/// +/// Represents a command parameter. +/// +public interface ICommandParameter +{ + /// + /// Gets a value indicating whether the parameter is a flag. + /// + bool IsFlag { get; } + + /// + /// Gets a value indicating whether the parameter is required. + /// + bool Required { get; } + + /// + /// Gets the description of the parameter. + /// + string? Description { get; } + + /// + /// Gets the default value of the parameter, if specified. + /// + DefaultValueAttribute? DefaultValue { get; } + + /// + /// Gets a value indicating whether the parameter is hidden. + /// + bool IsHidden { get; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Help/IHelpProvider.cs b/src/Spectre.Console.Cli/Help/IHelpProvider.cs index 420dd0f..bca3c87 100644 --- a/src/Spectre.Console.Cli/Help/IHelpProvider.cs +++ b/src/Spectre.Console.Cli/Help/IHelpProvider.cs @@ -1,20 +1,20 @@ namespace Spectre.Console.Cli.Help; - + /// -/// The help provider interface for Spectre.Console. -/// -/// -/// Implementations of this interface are responsbile -/// for writing command help to the terminal when the -/// `-h` or `--help` has been specified on the command line. +/// The help provider interface for Spectre.Console. +/// +/// +/// Implementations of this interface are responsbile +/// for writing command help to the terminal when the +/// `-h` or `--help` has been specified on the command line. /// public interface IHelpProvider -{ - /// - /// Writes help information for the application. - /// - /// The command model to write help for. - /// The command for which to write help information (optional). - /// An enumerable collection of objects representing the help information. +{ + /// + /// Writes help information for the application. + /// + /// The command model to write help for. + /// The command for which to write help information (optional). + /// An enumerable collection of objects representing the help information. IEnumerable Write(ICommandModel model, ICommandInfo? command); } diff --git a/src/Spectre.Console.Cli/ICommandAppSettings.cs b/src/Spectre.Console.Cli/ICommandAppSettings.cs index a1c59a7..7455c8d 100644 --- a/src/Spectre.Console.Cli/ICommandAppSettings.cs +++ b/src/Spectre.Console.Cli/ICommandAppSettings.cs @@ -24,22 +24,22 @@ public interface ICommandAppSettings /// /// Gets or sets the application version (use it to override auto-detected value). /// - string? ApplicationVersion { get; set; } - - /// - /// Gets or sets a value indicating how many examples from direct children to show in the help text. - /// - int MaximumIndirectExamples { get; set; } - - /// - /// Gets or sets a value indicating whether any default values for command options are shown in the help text. - /// - bool ShowOptionDefaultValues { get; set; } - - /// - /// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text. - /// - bool TrimTrailingPeriod { get; set; } + string? ApplicationVersion { get; set; } + + /// + /// Gets or sets a value indicating how many examples from direct children to show in the help text. + /// + int MaximumIndirectExamples { get; set; } + + /// + /// Gets or sets a value indicating whether any default values for command options are shown in the help text. + /// + bool ShowOptionDefaultValues { get; set; } + + /// + /// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text. + /// + bool TrimTrailingPeriod { get; set; } /// /// Gets or sets the . @@ -65,14 +65,14 @@ public interface ICommandAppSettings /// /// Gets or sets a value indicating whether or not parsing is strict. /// - bool StrictParsing { get; set; } - + bool StrictParsing { get; set; } + /// - /// Gets or sets a value indicating whether or not flags found on the commnd line - /// that would normally result in a being thrown - /// during parsing with the message "Flags cannot be assigned a value." + /// Gets or sets a value indicating whether or not flags found on the commnd line + /// that would normally result in a being thrown + /// during parsing with the message "Flags cannot be assigned a value." /// should instead be added to the remaining arguments collection. - /// + /// bool ConvertFlagsToRemainingArguments { get; set; } /// diff --git a/src/Spectre.Console.Cli/IConfigurator.cs b/src/Spectre.Console.Cli/IConfigurator.cs index 4f00c0b..ccac8d0 100644 --- a/src/Spectre.Console.Cli/IConfigurator.cs +++ b/src/Spectre.Console.Cli/IConfigurator.cs @@ -4,19 +4,19 @@ namespace Spectre.Console.Cli; /// Represents a configurator. /// public interface IConfigurator -{ - /// - /// Sets the help provider for the application. - /// - /// The help provider to use. - public void SetHelpProvider(IHelpProvider helpProvider); - - /// - /// Sets the help provider for the application. - /// - /// The type of the help provider to instantiate at runtime and use. - public void SetHelpProvider() - where T : IHelpProvider; +{ + /// + /// Sets the help provider for the application. + /// + /// The help provider to use. + public void SetHelpProvider(IHelpProvider helpProvider); + + /// + /// Sets the help provider for the application. + /// + /// The type of the help provider to instantiate at runtime and use. + public void SetHelpProvider() + where T : IHelpProvider; /// /// Gets the command app settings. @@ -66,5 +66,5 @@ public interface IConfigurator /// The command branch configurator. /// A branch configurator that can be used to configure the branch further. IBranchConfigurator AddBranch(string name, Action> action) - where TSettings : CommandSettings; + where TSettings : CommandSettings; } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/CommandExecutor.cs b/src/Spectre.Console.Cli/Internal/CommandExecutor.cs index db64733..1e844d8 100644 --- a/src/Spectre.Console.Cli/Internal/CommandExecutor.cs +++ b/src/Spectre.Console.Cli/Internal/CommandExecutor.cs @@ -1,146 +1,146 @@ -namespace Spectre.Console.Cli; - -internal sealed class CommandExecutor -{ - private readonly ITypeRegistrar _registrar; - - public CommandExecutor(ITypeRegistrar registrar) - { - _registrar = registrar ?? throw new ArgumentNullException(nameof(registrar)); - _registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor)); - } - - public async Task Execute(IConfiguration configuration, IEnumerable args) - { - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - args ??= new List(); - - _registrar.RegisterInstance(typeof(IConfiguration), configuration); - _registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole()); - - // Create the command model. - var model = CommandModelBuilder.Build(configuration); - _registrar.RegisterInstance(typeof(CommandModel), model); - _registrar.RegisterDependencies(model); - - // Asking for version? Kind of a hack, but it's alright. - // We should probably make this a bit better in the future. - if (args.Contains("-v") || args.Contains("--version")) - { - var console = configuration.Settings.Console.GetConsole(); - console.WriteLine(ResolveApplicationVersion(configuration)); - return 0; - } - - // Parse and map the model against the arguments. - var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args); - - // Register the arguments with the container. - _registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult); - _registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining); - - // Create the resolver. - using (var resolver = new TypeResolverAdapter(_registrar.Build())) - { - // Get the registered help provider, falling back to the default provider - // if no custom implementations have been registered. - var helpProviders = resolver.Resolve(typeof(IEnumerable)) as IEnumerable; - var helpProvider = helpProviders?.LastOrDefault() ?? new HelpProvider(configuration.Settings); - - // Currently the root? - if (parsedResult?.Tree == null) - { - // Display help. - configuration.Settings.Console.SafeRender(helpProvider.Write(model, null)); - return 0; - } - - // Get the command to execute. - var leaf = parsedResult.Tree.GetLeafCommand(); - if (leaf.Command.IsBranch || leaf.ShowHelp) - { - // Branches can't be executed. Show help. - configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); - return leaf.ShowHelp ? 0 : 1; - } - - // 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)) - { - // Display help for default command. - configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); - return 1; - } - - // Create the content. - var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data); - - // Execute the command tree. - return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false); - } - } - -#pragma warning disable CS8603 // Possible null reference return. - private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable args) - { - var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments); - - var parserContext = new CommandTreeParserContext(args, settings.ParsingMode); - var tokenizerResult = CommandTreeTokenizer.Tokenize(args); - var parsedResult = parser.Parse(parserContext, tokenizerResult); - - var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand(); - var lastParsedCommand = lastParsedLeaf?.Command; - if (lastParsedLeaf != null && lastParsedCommand != null && - lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp && - lastParsedCommand.DefaultCommand != null) - { - // Insert this branch's default command into the command line - // arguments and try again to see if it will parse. - var argsWithDefaultCommand = new List(args); - - argsWithDefaultCommand.Insert(tokenizerResult.Tokens.Position, lastParsedCommand.DefaultCommand.Name); - - parserContext = new CommandTreeParserContext(argsWithDefaultCommand, settings.ParsingMode); - tokenizerResult = CommandTreeTokenizer.Tokenize(argsWithDefaultCommand); - parsedResult = parser.Parse(parserContext, tokenizerResult); - } - - return parsedResult; - } -#pragma warning restore CS8603 // Possible null reference return. - - private static string ResolveApplicationVersion(IConfiguration configuration) - { - return - configuration.Settings.ApplicationVersion ?? // potential override - VersionHelper.GetVersion(Assembly.GetEntryAssembly()); - } - - private static Task Execute( - CommandTree leaf, - CommandTree tree, - CommandContext context, - ITypeResolver resolver, - IConfiguration configuration) - { - // Bind the command tree against the settings. - var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); - configuration.Settings.Interceptor?.Intercept(context, settings); - - // Create and validate the command. - var command = leaf.CreateCommand(resolver); - var validationResult = command.Validate(context, settings); - if (!validationResult.Successful) - { - throw CommandRuntimeException.ValidationFailed(validationResult); - } - - // Execute the command. - return command.Execute(context, settings); - } +namespace Spectre.Console.Cli; + +internal sealed class CommandExecutor +{ + private readonly ITypeRegistrar _registrar; + + public CommandExecutor(ITypeRegistrar registrar) + { + _registrar = registrar ?? throw new ArgumentNullException(nameof(registrar)); + _registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor)); + } + + public async Task Execute(IConfiguration configuration, IEnumerable args) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + args ??= new List(); + + _registrar.RegisterInstance(typeof(IConfiguration), configuration); + _registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole()); + + // Create the command model. + var model = CommandModelBuilder.Build(configuration); + _registrar.RegisterInstance(typeof(CommandModel), model); + _registrar.RegisterDependencies(model); + + // Asking for version? Kind of a hack, but it's alright. + // We should probably make this a bit better in the future. + if (args.Contains("-v") || args.Contains("--version")) + { + var console = configuration.Settings.Console.GetConsole(); + console.WriteLine(ResolveApplicationVersion(configuration)); + return 0; + } + + // Parse and map the model against the arguments. + var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args); + + // Register the arguments with the container. + _registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult); + _registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining); + + // Create the resolver. + using (var resolver = new TypeResolverAdapter(_registrar.Build())) + { + // Get the registered help provider, falling back to the default provider + // if no custom implementations have been registered. + var helpProviders = resolver.Resolve(typeof(IEnumerable)) as IEnumerable; + var helpProvider = helpProviders?.LastOrDefault() ?? new HelpProvider(configuration.Settings); + + // Currently the root? + if (parsedResult?.Tree == null) + { + // Display help. + configuration.Settings.Console.SafeRender(helpProvider.Write(model, null)); + return 0; + } + + // Get the command to execute. + var leaf = parsedResult.Tree.GetLeafCommand(); + if (leaf.Command.IsBranch || leaf.ShowHelp) + { + // Branches can't be executed. Show help. + configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); + return leaf.ShowHelp ? 0 : 1; + } + + // 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)) + { + // Display help for default command. + configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command)); + return 1; + } + + // Create the content. + var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data); + + // Execute the command tree. + return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false); + } + } + +#pragma warning disable CS8603 // Possible null reference return. + private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable args) + { + var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments); + + var parserContext = new CommandTreeParserContext(args, settings.ParsingMode); + var tokenizerResult = CommandTreeTokenizer.Tokenize(args); + var parsedResult = parser.Parse(parserContext, tokenizerResult); + + var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand(); + var lastParsedCommand = lastParsedLeaf?.Command; + if (lastParsedLeaf != null && lastParsedCommand != null && + lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp && + lastParsedCommand.DefaultCommand != null) + { + // Insert this branch's default command into the command line + // arguments and try again to see if it will parse. + var argsWithDefaultCommand = new List(args); + + argsWithDefaultCommand.Insert(tokenizerResult.Tokens.Position, lastParsedCommand.DefaultCommand.Name); + + parserContext = new CommandTreeParserContext(argsWithDefaultCommand, settings.ParsingMode); + tokenizerResult = CommandTreeTokenizer.Tokenize(argsWithDefaultCommand); + parsedResult = parser.Parse(parserContext, tokenizerResult); + } + + return parsedResult; + } +#pragma warning restore CS8603 // Possible null reference return. + + private static string ResolveApplicationVersion(IConfiguration configuration) + { + return + configuration.Settings.ApplicationVersion ?? // potential override + VersionHelper.GetVersion(Assembly.GetEntryAssembly()); + } + + private static Task Execute( + CommandTree leaf, + CommandTree tree, + CommandContext context, + ITypeResolver resolver, + IConfiguration configuration) + { + // Bind the command tree against the settings. + var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver); + configuration.Settings.Interceptor?.Intercept(context, settings); + + // Create and validate the command. + var command = leaf.CreateCommand(resolver); + var validationResult = command.Validate(context, settings); + if (!validationResult.Successful) + { + throw CommandRuntimeException.ValidationFailed(validationResult); + } + + // Execute the command. + return command.Execute(context, settings); + } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Configuration/BranchConfigurator.cs b/src/Spectre.Console.Cli/Internal/Configuration/BranchConfigurator.cs index ba26f08..04c20a3 100644 --- a/src/Spectre.Console.Cli/Internal/Configuration/BranchConfigurator.cs +++ b/src/Spectre.Console.Cli/Internal/Configuration/BranchConfigurator.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console.Cli; +namespace Spectre.Console.Cli; internal sealed class BranchConfigurator : IBranchConfigurator { diff --git a/src/Spectre.Console.Cli/Internal/Configuration/CommandAppSettings.cs b/src/Spectre.Console.Cli/Internal/Configuration/CommandAppSettings.cs index f50daae..815e165 100644 --- a/src/Spectre.Console.Cli/Internal/Configuration/CommandAppSettings.cs +++ b/src/Spectre.Console.Cli/Internal/Configuration/CommandAppSettings.cs @@ -5,16 +5,16 @@ internal sealed class CommandAppSettings : ICommandAppSettings public CultureInfo? Culture { get; set; } public string? ApplicationName { get; set; } public string? ApplicationVersion { get; set; } - public int MaximumIndirectExamples { get; set; } - public bool ShowOptionDefaultValues { get; set; } + public int MaximumIndirectExamples { get; set; } + public bool ShowOptionDefaultValues { get; set; } public IAnsiConsole? Console { get; set; } public ICommandInterceptor? Interceptor { get; set; } public ITypeRegistrarFrontend Registrar { get; set; } public CaseSensitivity CaseSensitivity { get; set; } public bool PropagateExceptions { get; set; } public bool ValidateExamples { get; set; } - public bool TrimTrailingPeriod { get; set; } = true; - public bool StrictParsing { get; set; } + public bool TrimTrailingPeriod { get; set; } = true; + public bool StrictParsing { get; set; } public bool ConvertFlagsToRemainingArguments { get; set; } = false; public ParsingMode ParsingMode => @@ -26,7 +26,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings { Registrar = new TypeRegistrar(registrar); CaseSensitivity = CaseSensitivity.All; - ShowOptionDefaultValues = true; + ShowOptionDefaultValues = true; MaximumIndirectExamples = 5; } diff --git a/src/Spectre.Console.Cli/Internal/Configuration/Configurator.cs b/src/Spectre.Console.Cli/Internal/Configuration/Configurator.cs index 2726a2c..0bb22c8 100644 --- a/src/Spectre.Console.Cli/Internal/Configuration/Configurator.cs +++ b/src/Spectre.Console.Cli/Internal/Configuration/Configurator.cs @@ -19,19 +19,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig Settings = new CommandAppSettings(registrar); Examples = new List(); } - - public void SetHelpProvider(IHelpProvider helpProvider) - { - // Register the help provider - _registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); - } - - public void SetHelpProvider() - where T : IHelpProvider - { - // Register the help provider - _registrar.Register(typeof(IHelpProvider), typeof(T)); - } + + public void SetHelpProvider(IHelpProvider helpProvider) + { + // Register the help provider + _registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); + } + + public void SetHelpProvider() + where T : IHelpProvider + { + // Register the help provider + _registrar.Register(typeof(IHelpProvider), typeof(T)); + } public void AddExample(params string[] args) { diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs index ed59e41..08bad48 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs @@ -1,5 +1,5 @@ namespace Spectre.Console.Cli; - + internal sealed class CommandInfo : ICommandContainer, ICommandInfo { public string Name { get; } @@ -20,14 +20,14 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo // only branches can have a default command public CommandInfo? DefaultCommand => IsBranch ? Children.FirstOrDefault(c => c.IsDefaultCommand) : null; - public bool IsHidden { get; } - - IReadOnlyList Help.ICommandContainer.Commands => Children.Cast().ToList(); - ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; - IReadOnlyList ICommandInfo.Parameters => Parameters.Cast().ToList(); - ICommandInfo? ICommandInfo.Parent => Parent; - IReadOnlyList Help.ICommandContainer.Examples => (IReadOnlyList)Examples; - + public bool IsHidden { get; } + + IReadOnlyList Help.ICommandContainer.Commands => Children.Cast().ToList(); + ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; + IReadOnlyList ICommandInfo.Parameters => Parameters.Cast().ToList(); + ICommandInfo? ICommandInfo.Parent => Parent; + IReadOnlyList Help.ICommandContainer.Examples => (IReadOnlyList)Examples; + public CommandInfo(CommandInfo? parent, ConfiguredCommand prototype) { Parent = parent; @@ -54,5 +54,5 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo Description = description.Description; } } - } + } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandModel.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandModel.cs index 81a4c5c..3da02da 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandModel.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandModel.cs @@ -1,17 +1,17 @@ namespace Spectre.Console.Cli; - + internal sealed class CommandModel : ICommandContainer, ICommandModel { public string? ApplicationName { get; } public ParsingMode ParsingMode { get; } public IList Commands { get; } - public IList Examples { get; } - + public IList Examples { get; } + public CommandInfo? DefaultCommand => Commands.FirstOrDefault(c => c.IsDefaultCommand); string ICommandModel.ApplicationName => GetApplicationName(ApplicationName); IReadOnlyList Help.ICommandContainer.Commands => Commands.Cast().ToList(); - ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; + ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand; IReadOnlyList Help.ICommandContainer.Examples => (IReadOnlyList)Examples; public CommandModel( @@ -22,18 +22,18 @@ internal sealed class CommandModel : ICommandContainer, ICommandModel ApplicationName = settings.ApplicationName; ParsingMode = settings.ParsingMode; Commands = new List(commands ?? Array.Empty()); - Examples = new List(examples ?? Array.Empty()); - } - - /// - /// Gets the name of the application. - /// If the provided is not null or empty, - /// it is returned. Otherwise the name of the current application - /// is determined based on the executable file's name. - /// - /// The optional name of the application. - /// - /// The name of the application, or a default value of "?" if no valid application name can be determined. + Examples = new List(examples ?? Array.Empty()); + } + + /// + /// Gets the name of the application. + /// If the provided is not null or empty, + /// it is returned. Otherwise the name of the current application + /// is determined based on the executable file's name. + /// + /// The optional name of the application. + /// + /// The name of the application, or a default value of "?" if no valid application name can be determined. /// private static string GetApplicationName(string? applicationName) { @@ -45,7 +45,7 @@ internal sealed class CommandModel : ICommandContainer, ICommandModel private static string? GetApplicationFile() { - var location = Assembly.GetEntryAssembly()?.Location; + var location = Assembly.GetEntryAssembly()?.Location; if (string.IsNullOrWhiteSpace(location)) { diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs index c31a9bf..4845ab0 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs @@ -1,5 +1,5 @@ namespace Spectre.Console.Cli; - + internal static class CommandModelBuilder { // 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); // Build the default command. - var defaultCommand = Build(null, configuration.DefaultCommand); - + var defaultCommand = Build(null, configuration.DefaultCommand); + result.Add(defaultCommand); } @@ -55,7 +55,7 @@ internal static class CommandModelBuilder foreach (var childCommand in command.Children) { var child = Build(info, childCommand); - info.Children.Add(child); + info.Children.Add(child); } // Normalize argument positions. diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandParameter.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandParameter.cs index 74d6626..e461d50 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandParameter.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandParameter.cs @@ -1,5 +1,5 @@ namespace Spectre.Console.Cli; - + internal abstract class CommandParameter : ICommandParameterInfo, ICommandParameter { public Guid Id { get; } @@ -17,10 +17,10 @@ internal abstract class CommandParameter : ICommandParameterInfo, ICommandParame public string PropertyName => Property.Name; public virtual bool WantRawValue => ParameterType.IsPairDeconstructable() - && (PairDeconstructor != null || Converter == null); - - public bool IsFlag => ParameterKind == ParameterKind.Flag; - + && (PairDeconstructor != null || Converter == null); + + public bool IsFlag => ParameterKind == ParameterKind.Flag; + protected CommandParameter( Type parameterType, ParameterKind parameterKind, PropertyInfo property, string? description, TypeConverterAttribute? converter, diff --git a/src/Spectre.Console.Cli/Internal/Modelling/ICommandContainer.cs b/src/Spectre.Console.Cli/Internal/Modelling/ICommandContainer.cs index e96b564..8685ecf 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/ICommandContainer.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/ICommandContainer.cs @@ -8,13 +8,13 @@ internal interface ICommandContainer /// /// Gets all commands in the container. /// - IList Commands { get; } - + IList Commands { get; } + /// /// Gets the default command for the container. - /// - /// - /// Returns null if a default command has not been set. - /// + /// + /// + /// Returns null if a default command has not been set. + /// CommandInfo? DefaultCommand { get; } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParser.cs b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParser.cs index 899b117..54152a0 100644 --- a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParser.cs +++ b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParser.cs @@ -1,12 +1,12 @@ -using static Spectre.Console.Cli.CommandTreeTokenizer; - +using static Spectre.Console.Cli.CommandTreeTokenizer; + namespace Spectre.Console.Cli; internal class CommandTreeParser { private readonly CommandModel _configuration; private readonly ParsingMode _parsingMode; - private readonly CommandOptionAttribute _help; + private readonly CommandOptionAttribute _help; private readonly bool _convertFlagsToRemainingArguments; public CaseSensitivity CaseSensitivity { get; } @@ -21,20 +21,20 @@ internal class CommandTreeParser { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _parsingMode = parsingMode ?? _configuration.ParsingMode; - _help = new CommandOptionAttribute("-h|--help"); + _help = new CommandOptionAttribute("-h|--help"); _convertFlagsToRemainingArguments = convertFlagsToRemainingArguments ?? false; CaseSensitivity = caseSensitivity; - } - + } + public CommandTreeParserResult Parse(IEnumerable args) - { - var parserContext = new CommandTreeParserContext(args, _parsingMode); - var tokenizerResult = CommandTreeTokenizer.Tokenize(args); - - return Parse(parserContext, tokenizerResult); - } - + { + var parserContext = new CommandTreeParserContext(args, _parsingMode); + var tokenizerResult = CommandTreeTokenizer.Tokenize(args); + + return Parse(parserContext, tokenizerResult); + } + public CommandTreeParserResult Parse(CommandTreeParserContext context, CommandTreeTokenizerResult tokenizerResult) { var tokens = tokenizerResult.Tokens; @@ -258,8 +258,8 @@ internal class CommandTreeParser // Find the option. var option = node.FindOption(token.Value, isLongOption, CaseSensitivity); if (option != null) - { - ParseOptionValue(context, stream, token, node, option); + { + ParseOptionValue(context, stream, token, node, option); return; } @@ -291,10 +291,10 @@ internal class CommandTreeParser CommandTreeParserContext context, CommandTreeTokenStream stream, CommandTreeToken token, - CommandTree current, + CommandTree current, CommandParameter? parameter = null) - { - bool addToMappedCommandParameters = parameter != null; + { + bool addToMappedCommandParameters = parameter != null; var value = default(string); @@ -312,49 +312,49 @@ internal class CommandTreeParser { // Is this a command? if (current.Command.FindCommand(valueToken.Value, CaseSensitivity) == null) - { - if (parameter != null) - { - if (parameter.ParameterKind == ParameterKind.Flag) - { - if (!CliConstants.AcceptedBooleanValues.Contains(valueToken.Value, StringComparer.OrdinalIgnoreCase)) - { - if (!valueToken.HadSeparator) - { - // Do nothing - // - 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 - // - valueToken will be handled on the next pass of the parser - } - else - { - // Flags cannot be assigned a value. - if (_convertFlagsToRemainingArguments) - { - value = stream.Consume(CommandTreeToken.Kind.String)?.Value; - - context.AddRemainingArgument(token.Value, value); - - // Prevent the option and it's non-boolean value from being added to - // mapped parameters (otherwise an exception will be thrown later - // when binding the value to the flag in the comand settings) - addToMappedCommandParameters = false; - } - else - { - throw CommandParseException.CannotAssignValueToFlag(context.Arguments, token); - } - } - } - else - { - value = stream.Consume(CommandTreeToken.Kind.String)?.Value; - } - } - else - { - value = stream.Consume(CommandTreeToken.Kind.String)?.Value; - } + { + if (parameter != null) + { + if (parameter.ParameterKind == ParameterKind.Flag) + { + if (!CliConstants.AcceptedBooleanValues.Contains(valueToken.Value, StringComparer.OrdinalIgnoreCase)) + { + if (!valueToken.HadSeparator) + { + // Do nothing + // - 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 + // - valueToken will be handled on the next pass of the parser + } + else + { + // Flags cannot be assigned a value. + if (_convertFlagsToRemainingArguments) + { + value = stream.Consume(CommandTreeToken.Kind.String)?.Value; + + context.AddRemainingArgument(token.Value, value); + + // Prevent the option and it's non-boolean value from being added to + // mapped parameters (otherwise an exception will be thrown later + // when binding the value to the flag in the comand settings) + addToMappedCommandParameters = false; + } + else + { + throw CommandParseException.CannotAssignValueToFlag(context.Arguments, token); + } + } + } + else + { + value = stream.Consume(CommandTreeToken.Kind.String)?.Value; + } + } + else + { + value = stream.Consume(CommandTreeToken.Kind.String)?.Value; + } } else { @@ -370,13 +370,13 @@ internal class CommandTreeParser } } else - { + { context.AddRemainingArgument(token.Value, parseValue ? valueToken.Value : null); } } 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.AddRemainingArgument(token.Value, null); @@ -399,10 +399,10 @@ internal class CommandTreeParser if (parameter.IsFlagValue()) { value = null; - } - else - { - throw CommandParseException.OptionHasNoValue(context.Arguments, token, option); + } + else + { + throw CommandParseException.OptionHasNoValue(context.Arguments, token, option); } } else @@ -415,9 +415,9 @@ internal class CommandTreeParser } } - if (parameter != null && addToMappedCommandParameters) - { - current.Mapped.Add(new MappedCommandParameter(parameter, value)); + if (parameter != null && addToMappedCommandParameters) + { + current.Mapped.Add(new MappedCommandParameter(parameter, value)); } } } \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParserContext.cs b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParserContext.cs index 4aaff0c..bce7718 100644 --- a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParserContext.cs +++ b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParserContext.cs @@ -26,15 +26,15 @@ internal class CommandTreeParserContext public void IncreaseArgumentPosition() { CurrentArgumentPosition++; - } - + } + public void AddRemainingArgument(string key, string? value) - { - if (!_remaining.ContainsKey(key)) - { - _remaining.Add(key, new List()); - } - + { + if (!_remaining.ContainsKey(key)) + { + _remaining.Add(key, new List()); + } + _remaining[key].Add(value); } diff --git a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeToken.cs b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeToken.cs index 8f30072..f6ea26c 100644 --- a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeToken.cs +++ b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeToken.cs @@ -6,11 +6,11 @@ internal sealed class CommandTreeToken public int Position { get; } public string Value { get; } public string Representation { get; } - public bool IsGrouped { get; set; } - - /// - /// Gets or sets a value indicating whether a separater was encountered immediately before the . - /// + public bool IsGrouped { get; set; } + + /// + /// Gets or sets a value indicating whether a separater was encountered immediately before the . + /// public bool HadSeparator { get; set; } public enum Kind diff --git a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenStream.cs b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenStream.cs index 1db9979..e1c2f1d 100644 --- a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenStream.cs +++ b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenStream.cs @@ -5,7 +5,7 @@ internal sealed class CommandTreeTokenStream : IReadOnlyList private readonly List _tokens; private int _position; - public int Count => _tokens.Count; + public int Count => _tokens.Count; public int Position => _position; public CommandTreeToken this[int index] => _tokens[index]; diff --git a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenizer.cs b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenizer.cs index 9006928..78f01eb 100644 --- a/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenizer.cs +++ b/src/Spectre.Console.Cli/Internal/Parsing/CommandTreeTokenizer.cs @@ -29,13 +29,13 @@ internal static class CommandTreeTokenizer var context = new CommandTreeTokenizerContext(); foreach (var arg in args) - { - if (string.IsNullOrEmpty(arg)) - { - // Null strings in the args array are still represented as tokens - tokens.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, string.Empty, string.Empty)); - continue; - } + { + if (string.IsNullOrEmpty(arg)) + { + // Null strings in the args array are still represented as tokens + tokens.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, string.Empty, string.Empty)); + continue; + } var start = position; 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 tokens) - { - if (!reader.ReachedEnd && reader.Peek() == '-') - { - // Option - tokens.AddRange(ScanOptions(context, reader)); - } - else - { - // Command or argument - while (reader.Peek() != -1) - { - if (reader.ReachedEnd) - { - position += reader.Position - start; - break; - } - - tokens.Add(ScanString(context, reader)); - - // Flush remaining tokens - context.FlushRemaining(); - } - } - + { + if (!reader.ReachedEnd && reader.Peek() == '-') + { + // Option + tokens.AddRange(ScanOptions(context, reader)); + } + else + { + // Command or argument + while (reader.Peek() != -1) + { + if (reader.ReachedEnd) + { + position += reader.Position - start; + break; + } + + tokens.Add(ScanString(context, reader)); + + // Flush remaining tokens + context.FlushRemaining(); + } + } + return position; } @@ -102,7 +102,7 @@ internal static class CommandTreeTokenizer builder.Append(current); } - var value = builder.ToString(); + var value = builder.ToString(); 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, "=", "="); throw CommandParseException.OptionValueWasExpected(reader.Original, token); } - - var tokenValue = ScanString(context, reader); + + var tokenValue = ScanString(context, reader); tokenValue.HadSeparator = true; 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 // when the digit is the first character in the token (i.e. "-a1" is always an error), hence the // result.Count == 0 check above. - string value = string.Empty; + string value = string.Empty; while (!reader.ReachedEnd) { @@ -212,12 +212,12 @@ internal static class CommandTreeTokenizer result.Add(new CommandTreeToken(CommandTreeToken.Kind.String, position, value, value)); } else - { + { // Create a token representing the short option. - var representation = current.ToString(CultureInfo.InvariantCulture); - var tokenPosition = position + 1 + result.Count; - var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, tokenPosition, representation, representation); - + var representation = current.ToString(CultureInfo.InvariantCulture); + var tokenPosition = position + 1 + result.Count; + var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, tokenPosition, representation, representation); + throw CommandParseException.InvalidShortOptionName(reader.Original, token); } } diff --git a/src/Spectre.Console.Cli/Properties/Usings.cs b/src/Spectre.Console.Cli/Properties/Usings.cs index 70a6d6a..48d442f 100644 --- a/src/Spectre.Console.Cli/Properties/Usings.cs +++ b/src/Spectre.Console.Cli/Properties/Usings.cs @@ -10,7 +10,7 @@ global using System.Linq; global using System.Reflection; global using System.Text; global using System.Threading.Tasks; -global using System.Xml; +global using System.Xml; global using Spectre.Console.Cli.Help; global using Spectre.Console.Cli.Unsafe; global using Spectre.Console.Rendering; \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Resources/HelpProvider.Designer.cs b/src/Spectre.Console.Cli/Resources/HelpProvider.Designer.cs index 94ba7cf..b9fe53a 100644 --- a/src/Spectre.Console.Cli/Resources/HelpProvider.Designer.cs +++ b/src/Spectre.Console.Cli/Resources/HelpProvider.Designer.cs @@ -1,153 +1,153 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Spectre.Console.Cli.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class HelpProvider { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal HelpProvider() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Spectre.Console.Cli.Resources.HelpProvider", typeof(HelpProvider).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to ARGUMENTS. - /// - internal static string Arguments { - get { - return ResourceManager.GetString("Arguments", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to COMMAND. - /// - internal static string Command { - get { - return ResourceManager.GetString("Command", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to COMMANDS. - /// - internal static string Commands { - get { - return ResourceManager.GetString("Commands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DEFAULT. - /// - internal static string Default { - get { - return ResourceManager.GetString("Default", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DESCRIPTION. - /// - internal static string Description { - get { - return ResourceManager.GetString("Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to EXAMPLES. - /// - internal static string Examples { - get { - return ResourceManager.GetString("Examples", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to OPTIONS. - /// - internal static string Options { - get { - return ResourceManager.GetString("Options", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prints help information. - /// - internal static string PrintHelpDescription { - get { - return ResourceManager.GetString("PrintHelpDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prints version information. - /// - internal static string PrintVersionDescription { - get { - return ResourceManager.GetString("PrintVersionDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to USAGE. - /// - internal static string Usage { - get { - return ResourceManager.GetString("Usage", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Spectre.Console.Cli.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class HelpProvider { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal HelpProvider() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Spectre.Console.Cli.Resources.HelpProvider", typeof(HelpProvider).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to ARGUMENTS. + /// + internal static string Arguments { + get { + return ResourceManager.GetString("Arguments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to COMMAND. + /// + internal static string Command { + get { + return ResourceManager.GetString("Command", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to COMMANDS. + /// + internal static string Commands { + get { + return ResourceManager.GetString("Commands", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DEFAULT. + /// + internal static string Default { + get { + return ResourceManager.GetString("Default", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DESCRIPTION. + /// + internal static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to EXAMPLES. + /// + internal static string Examples { + get { + return ResourceManager.GetString("Examples", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OPTIONS. + /// + internal static string Options { + get { + return ResourceManager.GetString("Options", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prints help information. + /// + internal static string PrintHelpDescription { + get { + return ResourceManager.GetString("PrintHelpDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prints version information. + /// + internal static string PrintVersionDescription { + get { + return ResourceManager.GetString("PrintVersionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to USAGE. + /// + internal static string Usage { + get { + return ResourceManager.GetString("Usage", resourceCulture); + } + } + } +} diff --git a/src/Spectre.Console.Testing/Cli/CommandAppTester.cs b/src/Spectre.Console.Testing/Cli/CommandAppTester.cs index f53f60c..314054a 100644 --- a/src/Spectre.Console.Testing/Cli/CommandAppTester.cs +++ b/src/Spectre.Console.Testing/Cli/CommandAppTester.cs @@ -141,8 +141,8 @@ public sealed class CommandAppTester .Trim(); return new CommandAppResult(result, output, context, settings); - } - + } + /// /// Runs the command application asynchronously. /// diff --git a/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Input.cs b/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Input.cs index 31f6f16..f7ec487 100644 --- a/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Input.cs +++ b/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Input.cs @@ -60,7 +60,7 @@ public static partial class AnsiConsoleExtensions if (!char.IsControl(key.KeyChar)) { - text += key.KeyChar.ToString(); + text += key.KeyChar.ToString(); var output = key.KeyChar.ToString(); console.Write(secret ? output.Mask(mask) : output, style); } diff --git a/src/Spectre.Console/Extensions/StringExtensions.cs b/src/Spectre.Console/Extensions/StringExtensions.cs index e6485e9..d3b54ea 100644 --- a/src/Spectre.Console/Extensions/StringExtensions.cs +++ b/src/Spectre.Console/Extensions/StringExtensions.cs @@ -185,28 +185,28 @@ public static class StringExtensions #else return text.Contains(value, StringComparison.Ordinal); #endif - } - - /// - /// "Masks" every character in a string. - /// - /// String value to mask. - /// Character to use for masking. - /// Masked string. - public static string Mask(this string value, char? mask) - { - var output = string.Empty; - - if (mask is null) - { - return output; - } - - foreach (var c in value) - { - output += mask; - } - - return output; + } + + /// + /// "Masks" every character in a string. + /// + /// String value to mask. + /// Character to use for masking. + /// Masked string. + public static string Mask(this string value, char? mask) + { + var output = string.Empty; + + if (mask is null) + { + return output; + } + + foreach (var c in value) + { + output += mask; + } + + return output; } } \ No newline at end of file diff --git a/src/Spectre.Console/HorizontalAlignment.cs b/src/Spectre.Console/HorizontalAlignment.cs index e8f6c21..97b7c13 100644 --- a/src/Spectre.Console/HorizontalAlignment.cs +++ b/src/Spectre.Console/HorizontalAlignment.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console; +namespace Spectre.Console; /// /// Represents horizontal alignment. diff --git a/src/Spectre.Console/Prompts/TextPromptExtensions.cs b/src/Spectre.Console/Prompts/TextPromptExtensions.cs index 4fad784..d3cd851 100644 --- a/src/Spectre.Console/Prompts/TextPromptExtensions.cs +++ b/src/Spectre.Console/Prompts/TextPromptExtensions.cs @@ -286,25 +286,25 @@ public static class TextPromptExtensions obj.IsSecret = true; return obj; - } - + } + /// /// Replaces prompt user input with mask in the console. /// /// The prompt type. - /// The prompt. + /// The prompt. /// The masking character to use for the secret. - /// The same instance so that multiple calls can be chained. - public static TextPrompt Secret(this TextPrompt obj, char? mask) - { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - obj.IsSecret = true; - obj.Mask = mask; - return obj; + /// The same instance so that multiple calls can be chained. + public static TextPrompt Secret(this TextPrompt obj, char? mask) + { + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + obj.IsSecret = true; + obj.Mask = mask; + return obj; } /// diff --git a/src/Spectre.Console/VerticalAlignment.cs b/src/Spectre.Console/VerticalAlignment.cs index dfffb77..5d983c3 100644 --- a/src/Spectre.Console/VerticalAlignment.cs +++ b/src/Spectre.Console/VerticalAlignment.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console; +namespace Spectre.Console; /// /// Represents vertical alignment. diff --git a/src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs b/src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs index 41eb78a..0b824a6 100644 --- a/src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs +++ b/src/Spectre.Console/Widgets/Exceptions/TypeNameHelper.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console; +namespace Spectre.Console; internal static class TypeNameHelper { diff --git a/src/Spectre.Console/Widgets/Layout/LayoutRender.cs b/src/Spectre.Console/Widgets/Layout/LayoutRender.cs index a608be7..5ed22f1 100644 --- a/src/Spectre.Console/Widgets/Layout/LayoutRender.cs +++ b/src/Spectre.Console/Widgets/Layout/LayoutRender.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console; +namespace Spectre.Console; [DebuggerDisplay("{Region,nq}")] internal sealed class LayoutRender diff --git a/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs b/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs index 677c8d0..eb333f2 100644 --- a/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs +++ b/test/Spectre.Console.Analyzer.Tests/SpectreAnalyzerVerifier.cs @@ -5,7 +5,7 @@ public static class SpectreAnalyzerVerifier { public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) => VerifyCodeFixAsync(source, OutputKind.DynamicallyLinkedLibrary, new[] { expected }, fixedSource); - + public static Task VerifyCodeFixAsync(string source, OutputKind outputKind, DiagnosticResult expected, string fixedSource) => VerifyCodeFixAsync(source, outputKind, new[] { expected }, fixedSource); @@ -13,10 +13,10 @@ public static class SpectreAnalyzerVerifier { var test = new Test { - TestCode = source, - TestState = - { - OutputKind = outputKind, + TestCode = source, + TestState = + { + OutputKind = outputKind, }, FixedCode = fixedSource, }; diff --git a/test/Spectre.Console.Cli.Tests/Data/Commands/AsynchronousCommand.cs b/test/Spectre.Console.Cli.Tests/Data/Commands/AsynchronousCommand.cs index 100d187..550f2cc 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Commands/AsynchronousCommand.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Commands/AsynchronousCommand.cs @@ -1,28 +1,28 @@ -namespace Spectre.Console.Tests.Data; - -public sealed class AsynchronousCommand : AsyncCommand -{ - private readonly IAnsiConsole _console; - - public AsynchronousCommand(IAnsiConsole console) - { - _console = console; - } - - public async override Task ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings) - { - // Simulate a long running asynchronous task - await Task.Delay(200); - - if (settings.ThrowException) - { - throw new Exception($"Throwing exception asynchronously"); - } - else - { - _console.WriteLine($"Finished executing asynchronously"); - } - - return 0; - } -} +namespace Spectre.Console.Tests.Data; + +public sealed class AsynchronousCommand : AsyncCommand +{ + private readonly IAnsiConsole _console; + + public AsynchronousCommand(IAnsiConsole console) + { + _console = console; + } + + public async override Task ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings) + { + // Simulate a long running asynchronous task + await Task.Delay(200); + + if (settings.ThrowException) + { + throw new Exception($"Throwing exception asynchronously"); + } + else + { + _console.WriteLine($"Finished executing asynchronously"); + } + + return 0; + } +} diff --git a/test/Spectre.Console.Cli.Tests/Data/Commands/GreeterCommand.cs b/test/Spectre.Console.Cli.Tests/Data/Commands/GreeterCommand.cs index cb7bdda..d466f46 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Commands/GreeterCommand.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Commands/GreeterCommand.cs @@ -1,17 +1,17 @@ -using Spectre.Console; - -public class GreeterCommand : Command -{ - private readonly IAnsiConsole _console; - - public GreeterCommand(IAnsiConsole console) - { - _console = console; - } - - public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings) - { - _console.WriteLine(settings.Greeting); - return 0; - } +using Spectre.Console; + +public class GreeterCommand : Command +{ + private readonly IAnsiConsole _console; + + public GreeterCommand(IAnsiConsole console) + { + _console = console; + } + + public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings) + { + _console.WriteLine(settings.Greeting); + return 0; + } } \ No newline at end of file diff --git a/test/Spectre.Console.Cli.Tests/Data/Help/CustomHelpProvider.cs b/test/Spectre.Console.Cli.Tests/Data/Help/CustomHelpProvider.cs index 78c6d97..a7be226 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Help/CustomHelpProvider.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Help/CustomHelpProvider.cs @@ -1,34 +1,34 @@ -using Spectre.Console.Rendering; - -namespace Spectre.Console.Cli.Tests.Data.Help; - -internal class CustomHelpProvider : HelpProvider -{ - private readonly string version; - - public CustomHelpProvider(ICommandAppSettings settings, string version) - : base(settings) - { - this.version = version; - } - - public override IEnumerable GetHeader(ICommandModel model, ICommandInfo command) - { - return new IRenderable[] - { - new Text("--------------------------------------"), Text.NewLine, - new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, - new Text("--------------------------------------"), Text.NewLine, - Text.NewLine, - }; - } - - public override IEnumerable GetFooter(ICommandModel model, ICommandInfo command) - { - return new IRenderable[] - { - Text.NewLine, - new Text($"Version {version}"), - }; - } -} +using Spectre.Console.Rendering; + +namespace Spectre.Console.Cli.Tests.Data.Help; + +internal class CustomHelpProvider : HelpProvider +{ + private readonly string version; + + public CustomHelpProvider(ICommandAppSettings settings, string version) + : base(settings) + { + this.version = version; + } + + public override IEnumerable GetHeader(ICommandModel model, ICommandInfo command) + { + return new IRenderable[] + { + new Text("--------------------------------------"), Text.NewLine, + new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, + new Text("--------------------------------------"), Text.NewLine, + Text.NewLine, + }; + } + + public override IEnumerable GetFooter(ICommandModel model, ICommandInfo command) + { + return new IRenderable[] + { + Text.NewLine, + new Text($"Version {version}"), + }; + } +} diff --git a/test/Spectre.Console.Cli.Tests/Data/Help/RedirectHelpProvider.cs b/test/Spectre.Console.Cli.Tests/Data/Help/RedirectHelpProvider.cs index f017bdf..b8c1919 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Help/RedirectHelpProvider.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Help/RedirectHelpProvider.cs @@ -1,21 +1,21 @@ -using Spectre.Console.Rendering; - -namespace Spectre.Console.Cli.Tests.Data.Help; - -internal class RedirectHelpProvider : IHelpProvider -{ - public virtual IEnumerable Write(ICommandModel model) - { - return Write(model, null); - } -#nullable enable - public virtual IEnumerable Write(ICommandModel model, ICommandInfo? command) -#nullable disable - { - return new[] - { - new Text("Help has moved online. Please see: http://www.example.com"), - Text.NewLine, - }; - } +using Spectre.Console.Rendering; + +namespace Spectre.Console.Cli.Tests.Data.Help; + +internal class RedirectHelpProvider : IHelpProvider +{ + public virtual IEnumerable Write(ICommandModel model) + { + return Write(model, null); + } +#nullable enable + public virtual IEnumerable Write(ICommandModel model, ICommandInfo? command) +#nullable disable + { + return new[] + { + new Text("Help has moved online. Please see: http://www.example.com"), + Text.NewLine, + }; + } } \ No newline at end of file diff --git a/test/Spectre.Console.Cli.Tests/Data/Settings/AsynchronousCommandSettings.cs b/test/Spectre.Console.Cli.Tests/Data/Settings/AsynchronousCommandSettings.cs index b7bb8e2..b41e10d 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Settings/AsynchronousCommandSettings.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Settings/AsynchronousCommandSettings.cs @@ -1,8 +1,8 @@ -namespace Spectre.Console.Tests.Data; - -public sealed class AsynchronousCommandSettings : CommandSettings -{ - [CommandOption("--ThrowException")] - [DefaultValue(false)] - public bool ThrowException { get; set; } +namespace Spectre.Console.Tests.Data; + +public sealed class AsynchronousCommandSettings : CommandSettings +{ + [CommandOption("--ThrowException")] + [DefaultValue(false)] + public bool ThrowException { get; set; } } \ No newline at end of file diff --git a/test/Spectre.Console.Cli.Tests/Data/Settings/ReptileSettings.cs b/test/Spectre.Console.Cli.Tests/Data/Settings/ReptileSettings.cs index 4ec4314..04b6f45 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Settings/ReptileSettings.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Settings/ReptileSettings.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console.Tests.Data; +namespace Spectre.Console.Tests.Data; public class ReptileSettings : AnimalSettings { diff --git a/test/Spectre.Console.Cli.Tests/Data/Settings/ThrowingCommandSettings.cs b/test/Spectre.Console.Cli.Tests/Data/Settings/ThrowingCommandSettings.cs index 88a28ff..4d0c0ea 100644 --- a/test/Spectre.Console.Cli.Tests/Data/Settings/ThrowingCommandSettings.cs +++ b/test/Spectre.Console.Cli.Tests/Data/Settings/ThrowingCommandSettings.cs @@ -1,5 +1,5 @@ -namespace Spectre.Console.Tests.Data; - -public sealed class ThrowingCommandSettings : CommandSettings -{ -} +namespace Spectre.Console.Tests.Data; + +public sealed class ThrowingCommandSettings : CommandSettings +{ +} diff --git a/test/Spectre.Console.Cli.Tests/Properties/Usings.cs b/test/Spectre.Console.Cli.Tests/Properties/Usings.cs index c8e60a1..7436882 100644 --- a/test/Spectre.Console.Cli.Tests/Properties/Usings.cs +++ b/test/Spectre.Console.Cli.Tests/Properties/Usings.cs @@ -7,7 +7,7 @@ global using System.Linq; global using System.Runtime.CompilerServices; global using System.Threading.Tasks; global using Shouldly; -global using Spectre.Console.Cli; +global using Spectre.Console.Cli; global using Spectre.Console.Cli.Help; global using Spectre.Console.Cli.Unsafe; global using Spectre.Console.Testing; diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Async.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Async.cs index ee88b0b..8fe9020 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Async.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Async.cs @@ -1,70 +1,70 @@ -namespace Spectre.Console.Tests.Unit.Cli; - -public sealed partial class CommandAppTests -{ - public sealed class Async - { - [Fact] - public async void Should_Execute_Command_Asynchronously() - { - // Given - var app = new CommandAppTester(); - app.SetDefaultCommand(); - app.Configure(config => - { - config.PropagateExceptions(); - }); - - // When - var result = await app.RunAsync(); - - // Then - result.ExitCode.ShouldBe(0); - result.Output.ShouldBe("Finished executing asynchronously"); - } - - [Fact] - public async void Should_Handle_Exception_Asynchronously() - { - // Given - var app = new CommandAppTester(); - app.SetDefaultCommand(); - - // When - var result = await app.RunAsync(new[] - { - "--ThrowException", - "true", - }); - - // Then - result.ExitCode.ShouldBe(-1); - } - - [Fact] - public async void Should_Throw_Exception_Asynchronously() - { - // Given - var app = new CommandAppTester(); - app.SetDefaultCommand(); - app.Configure(config => - { - config.PropagateExceptions(); - }); - - // When - var result = await Record.ExceptionAsync(async () => - await app.RunAsync(new[] - { - "--ThrowException", - "true", - })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe("Throwing exception asynchronously"); - }); - } - } +namespace Spectre.Console.Tests.Unit.Cli; + +public sealed partial class CommandAppTests +{ + public sealed class Async + { + [Fact] + public async void Should_Execute_Command_Asynchronously() + { + // Given + var app = new CommandAppTester(); + app.SetDefaultCommand(); + app.Configure(config => + { + config.PropagateExceptions(); + }); + + // When + var result = await app.RunAsync(); + + // Then + result.ExitCode.ShouldBe(0); + result.Output.ShouldBe("Finished executing asynchronously"); + } + + [Fact] + public async void Should_Handle_Exception_Asynchronously() + { + // Given + var app = new CommandAppTester(); + app.SetDefaultCommand(); + + // When + var result = await app.RunAsync(new[] + { + "--ThrowException", + "true", + }); + + // Then + result.ExitCode.ShouldBe(-1); + } + + [Fact] + public async void Should_Throw_Exception_Asynchronously() + { + // Given + var app = new CommandAppTester(); + app.SetDefaultCommand(); + app.Configure(config => + { + config.PropagateExceptions(); + }); + + // When + var result = await Record.ExceptionAsync(async () => + await app.RunAsync(new[] + { + "--ThrowException", + "true", + })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe("Throwing exception asynchronously"); + }); + } + } } \ No newline at end of file diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Branches.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Branches.cs index 5df7feb..716bd31 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Branches.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Branches.cs @@ -3,133 +3,133 @@ namespace Spectre.Console.Tests.Unit.Cli; public sealed partial class CommandAppTests { public sealed class Branches - { + { [Fact] public void Should_Run_The_Default_Command_On_Branch() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddBranch("animal", animal => { - animal.SetDefaultCommand(); + animal.SetDefaultCommand(); }); - }); - - // When - var result = app.Run(new[] + }); + + // When + var result = app.Run(new[] { "animal", "4", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType(); - } - + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType(); + } + [Fact] public void Should_Throw_When_No_Default_Command_On_Branch() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddBranch("animal", animal => { }); - }); - - // When - var result = Record.Exception(() => - { - app.Run(new[] - { - "animal", "4", - }); - }); - - // Then - result.ShouldBeOfType().And(ex => - { - 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.SpacingRules", "SA1005:SingleLineCommentsMustBeginWithSingleSpace", Justification = "Helps to illustrate the expected behaviour of this unit test.")] - [Fact] + }); + + // When + var result = Record.Exception(() => + { + app.Run(new[] + { + "animal", "4", + }); + }); + + // Then + result.ShouldBeOfType().And(ex => + { + 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.SpacingRules", "SA1005:SingleLineCommentsMustBeginWithSingleSpace", Justification = "Helps to illustrate the expected behaviour of this unit test.")] + [Fact] public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Relaxed_Parsing() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddBranch("animal", animal => { - animal.SetDefaultCommand(); + animal.SetDefaultCommand(); }); - }); - - // When - var result = app.Run(new[] - { - // The CommandTreeParser should be unable to determine which command line - // arguments belong to the branch and which belong to the branch's + }); + + // When + var result = app.Run(new[] + { + // The CommandTreeParser should be unable to determine which command line + // arguments belong to the branch and which belong to the branch's // default command (once inserted). "animal", "4", "--name", "Kitty", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType().And(cat => - { - cat.Legs.ShouldBe(4); - //cat.Name.ShouldBe("Kitty"); //<-- Should normally be correct, but instead name will be added to the remaining arguments (see below). - }); + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType().And(cat => + { + cat.Legs.ShouldBe(4); + //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.ShouldHaveRemainingArgument("name", values: new[] { "Kitty", }); - } - - [Fact] + result.Context.ShouldHaveRemainingArgument("name", values: new[] { "Kitty", }); + } + + [Fact] public void Should_Be_Unable_To_Parse_Default_Command_Arguments_Strict_Parsing() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => - { - config.UseStrictParsing(); + { + config.UseStrictParsing(); config.PropagateExceptions(); config.AddBranch("animal", animal => { - animal.SetDefaultCommand(); + animal.SetDefaultCommand(); }); - }); - - // When - var result = Record.Exception(() => - { - app.Run(new[] - { - // The CommandTreeParser should be unable to determine which command line - // arguments belong to the branch and which belong to the branch's + }); + + // When + var result = Record.Exception(() => + { + app.Run(new[] + { + // The CommandTreeParser should be unable to determine which command line + // arguments belong to the branch and which belong to the branch's // default command (once inserted). - "animal", "4", "--name", "Kitty", - }); - }); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe("Unknown option 'name'."); - }); - } - + "animal", "4", "--name", "Kitty", + }); + }); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe("Unknown option 'name'."); + }); + } + [Fact] public void Should_Run_The_Default_Command_On_Branch_On_Branch() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => { @@ -141,23 +141,23 @@ public sealed partial class CommandAppTests mammal.SetDefaultCommand(); }); }); - }); - - // When - var result = app.Run(new[] + }); + + // When + var result = app.Run(new[] { "animal", "4", "mammal", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType(); - } - + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType(); + } + [Fact] public void Should_Run_The_Default_Command_On_Branch_On_Branch_With_Arguments() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => { @@ -169,83 +169,83 @@ public sealed partial class CommandAppTests mammal.SetDefaultCommand(); }); }); - }); - - // When - var result = app.Run(new[] + }); + + // When + var result = app.Run(new[] { "animal", "4", "mammal", "--name", "Kitty", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType().And(cat => - { - cat.Legs.ShouldBe(4); - cat.Name.ShouldBe("Kitty"); - }); - } - + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType().And(cat => + { + cat.Legs.ShouldBe(4); + cat.Name.ShouldBe("Kitty"); + }); + } + [Fact] public void Should_Run_The_Default_Command_Not_The_Named_Command_On_Branch() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddBranch("animal", animal => { - animal.AddCommand("dog"); + animal.AddCommand("dog"); - animal.SetDefaultCommand(); + animal.SetDefaultCommand(); }); - }); - - // When - var result = app.Run(new[] + }); + + // When + var result = app.Run(new[] { "animal", "4", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType(); - } - + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType(); + } + [Fact] public void Should_Run_The_Named_Command_Not_The_Default_Command_On_Branch() - { - // Given + { + // Given var app = new CommandAppTester(); app.Configure(config => { config.PropagateExceptions(); config.AddBranch("animal", animal => { - animal.AddCommand("dog"); + animal.AddCommand("dog"); - animal.SetDefaultCommand(); + animal.SetDefaultCommand(); }); - }); - - // When - var result = app.Run(new[] + }); + + // When + var result = app.Run(new[] { "animal", "4", "dog", "12", "--good-boy", "--name", "Rufus", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType().And(dog => - { - dog.Legs.ShouldBe(4); - dog.Age.ShouldBe(12); - dog.GoodBoy.ShouldBe(true); - dog.Name.ShouldBe("Rufus"); - }); - } - + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType().And(dog => + { + dog.Legs.ShouldBe(4); + dog.Age.ShouldBe(12); + dog.GoodBoy.ShouldBe(true); + dog.Name.ShouldBe("Rufus"); + }); + } + [Fact] public void Should_Allow_Multiple_Branches_Multiple_Commands() { diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Help.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Help.cs index a684a8a..c6aa104 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Help.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Help.cs @@ -1,5 +1,5 @@ -using Spectre.Console.Cli.Tests.Data.Help; - +using Spectre.Console.Cli.Tests.Data.Help; + namespace Spectre.Console.Tests.Unit.Cli; public sealed partial class CommandAppTests @@ -93,12 +93,12 @@ public sealed partial class CommandAppTests }); // When - var result = fixture.Run("cat", "--help"); + var result = fixture.Run("cat", "--help"); // Then return Verifier.Verify(result.Output); - } - + } + [Fact] [Expectation("Branch_Called_Without_Help")] public Task Should_Output_Branch_When_Called_Without_Help_Option() @@ -110,27 +110,27 @@ public sealed partial class CommandAppTests configurator.SetApplicationName("myapp"); configurator.AddBranch("cat", animal => { - animal.SetDescription("Contains settings for a cat."); + animal.SetDescription("Contains settings for a cat."); animal.AddCommand("lion"); }); }); // When - var result = fixture.Run("cat"); + var result = fixture.Run("cat"); // Then return Verifier.Verify(result.Output); - } - + } + [Fact] - [Expectation("Branch_Default_Greeter")] + [Expectation("Branch_Default_Greeter")] public Task Should_Output_Branch_With_Default_Correctly() { // Given - var fixture = new CommandAppTester(); + var fixture = new CommandAppTester(); fixture.Configure(configurator => { - configurator.SetApplicationName("myapp"); + configurator.SetApplicationName("myapp"); configurator.AddBranch("branch", animal => { animal.SetDefaultCommand(); @@ -138,8 +138,8 @@ public sealed partial class CommandAppTests }); }); - // When - var result = fixture.Run("branch", "--help"); + // When + var result = fixture.Run("branch", "--help"); // Then return Verifier.Verify(result.Output); @@ -186,7 +186,7 @@ public sealed partial class CommandAppTests }); // When - var result = fixture.Run("cat", "lion", "--help"); + var result = fixture.Run("cat", "lion", "--help"); // Then return Verifier.Verify(result.Output); @@ -233,13 +233,13 @@ public sealed partial class CommandAppTests [Theory] [InlineData(null, "EN")] [InlineData("", "EN")] - [InlineData("en", "EN")] + [InlineData("en", "EN")] [InlineData("en-EN", "EN")] - [InlineData("fr", "FR")] + [InlineData("fr", "FR")] [InlineData("fr-FR", "FR")] - [InlineData("sv", "SV")] + [InlineData("sv", "SV")] [InlineData("sv-SE", "SV")] - [InlineData("de", "DE")] + [InlineData("de", "DE")] [InlineData("de-DE", "DE")] [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) @@ -281,106 +281,106 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } - + } + [Fact] [Expectation("Custom_Help_Registered_By_Instance")] public Task Should_Output_Custom_Help_When_Registered_By_Instance() - { - var registrar = new DefaultTypeRegistrar(); - - // Given - var fixture = new CommandAppTester(registrar); - fixture.Configure(configurator => - { - // Create the custom help provider - var helpProvider = new CustomHelpProvider(configurator.Settings, "1.0"); - - // Register the custom help provider instance - registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); - - configurator.SetApplicationName("myapp"); - configurator.AddCommand("dog"); - }); - - // When - var result = fixture.Run(); - - // Then - return Verifier.Verify(result.Output); - } - + { + var registrar = new DefaultTypeRegistrar(); + + // Given + var fixture = new CommandAppTester(registrar); + fixture.Configure(configurator => + { + // Create the custom help provider + var helpProvider = new CustomHelpProvider(configurator.Settings, "1.0"); + + // Register the custom help provider instance + registrar.RegisterInstance(typeof(IHelpProvider), helpProvider); + + configurator.SetApplicationName("myapp"); + configurator.AddCommand("dog"); + }); + + // When + var result = fixture.Run(); + + // Then + return Verifier.Verify(result.Output); + } + [Fact] [Expectation("Custom_Help_Registered_By_Type")] public Task Should_Output_Custom_Help_When_Registered_By_Type() - { - var registrar = new DefaultTypeRegistrar(); - - // Given - var fixture = new CommandAppTester(registrar); - fixture.Configure(configurator => - { - // Register the custom help provider type - registrar.Register(typeof(IHelpProvider), typeof(RedirectHelpProvider)); - - configurator.SetApplicationName("myapp"); - configurator.AddCommand("dog"); - }); - - // When - var result = fixture.Run(); - - // Then - return Verifier.Verify(result.Output); - } - + { + var registrar = new DefaultTypeRegistrar(); + + // Given + var fixture = new CommandAppTester(registrar); + fixture.Configure(configurator => + { + // Register the custom help provider type + registrar.Register(typeof(IHelpProvider), typeof(RedirectHelpProvider)); + + configurator.SetApplicationName("myapp"); + configurator.AddCommand("dog"); + }); + + // When + var result = fixture.Run(); + + // Then + return Verifier.Verify(result.Output); + } + [Fact] [Expectation("Custom_Help_Configured_By_Instance")] public Task Should_Output_Custom_Help_When_Configured_By_Instance() - { - var registrar = new DefaultTypeRegistrar(); - - // Given - var fixture = new CommandAppTester(registrar); - fixture.Configure(configurator => - { - // Configure the custom help provider instance - configurator.SetHelpProvider(new CustomHelpProvider(configurator.Settings, "1.0")); - - configurator.SetApplicationName("myapp"); - configurator.AddCommand("dog"); - }); - - // When - var result = fixture.Run(); - - // Then - return Verifier.Verify(result.Output); - } - + { + var registrar = new DefaultTypeRegistrar(); + + // Given + var fixture = new CommandAppTester(registrar); + fixture.Configure(configurator => + { + // Configure the custom help provider instance + configurator.SetHelpProvider(new CustomHelpProvider(configurator.Settings, "1.0")); + + configurator.SetApplicationName("myapp"); + configurator.AddCommand("dog"); + }); + + // When + var result = fixture.Run(); + + // Then + return Verifier.Verify(result.Output); + } + [Fact] [Expectation("Custom_Help_Configured_By_Type")] public Task Should_Output_Custom_Help_When_Configured_By_Type() - { - var registrar = new DefaultTypeRegistrar(); - - // Given - var fixture = new CommandAppTester(registrar); - fixture.Configure(configurator => - { - // Configure the custom help provider type - configurator.SetHelpProvider(); - - configurator.SetApplicationName("myapp"); - configurator.AddCommand("dog"); - }); - - // When - var result = fixture.Run(); - - // Then - return Verifier.Verify(result.Output); - } + { + var registrar = new DefaultTypeRegistrar(); + + // Given + var fixture = new CommandAppTester(registrar); + fixture.Configure(configurator => + { + // Configure the custom help provider type + configurator.SetHelpProvider(); + + configurator.SetApplicationName("myapp"); + configurator.AddCommand("dog"); + }); + + // When + var result = fixture.Run(); + + // Then + return Verifier.Verify(result.Output); + } [Fact] [Expectation("Root_Examples")] @@ -391,20 +391,20 @@ public sealed partial class CommandAppTests fixture.Configure(configurator => { configurator.SetApplicationName("myapp"); - - // All root examples should be shown - configurator.AddExample("dog", "--name", "Rufus", "--age", "12", "--good-boy"); - configurator.AddExample("dog", "--name", "Luna"); - configurator.AddExample("dog", "--name", "Charlie"); - configurator.AddExample("dog", "--name", "Bella"); - configurator.AddExample("dog", "--name", "Daisy"); + + // All root examples should be shown + configurator.AddExample("dog", "--name", "Rufus", "--age", "12", "--good-boy"); + configurator.AddExample("dog", "--name", "Luna"); + configurator.AddExample("dog", "--name", "Charlie"); + configurator.AddExample("dog", "--name", "Bella"); + configurator.AddExample("dog", "--name", "Daisy"); configurator.AddExample("dog", "--name", "Milo"); - configurator.AddExample("horse", "--name", "Brutus"); - configurator.AddExample("horse", "--name", "Sugar", "--IsAlive", "false"); - configurator.AddExample("horse", "--name", "Cash"); - configurator.AddExample("horse", "--name", "Dakota"); - configurator.AddExample("horse", "--name", "Cisco"); - configurator.AddExample("horse", "--name", "Spirit"); + configurator.AddExample("horse", "--name", "Brutus"); + configurator.AddExample("horse", "--name", "Sugar", "--IsAlive", "false"); + configurator.AddExample("horse", "--name", "Cash"); + configurator.AddExample("horse", "--name", "Dakota"); + configurator.AddExample("horse", "--name", "Cisco"); + configurator.AddExample("horse", "--name", "Spirit"); configurator.AddCommand("dog"); configurator.AddCommand("horse"); @@ -415,10 +415,10 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } + } [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.")] 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(); fixture.Configure(configurator => { - configurator.SetApplicationName("myapp"); - - // It should be capped to the first 5 examples by default - + configurator.SetApplicationName("myapp"); + + // It should be capped to the first 5 examples by default + configurator.AddCommand("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", "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("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_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("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("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("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("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("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("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", "Brutus") + .WithExample("horse", "--name", "Sugar", "--IsAlive", "false") + .WithExample("horse", "--name", "Cash") + .WithExample("horse", "--name", "Dakota") + .WithExample("horse", "--name", "Cisco") .WithExample("horse", "--name", "Spirit"); }); @@ -566,7 +455,118 @@ public sealed partial class CommandAppTests } [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("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("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("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("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("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("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.")] 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.AddBranch("animal", animal => { - animal.SetDescription("The animal command."); - - // It should be capped to the first 5 examples by default + animal.SetDescription("The animal command."); + + // It should be capped to the first 5 examples by default animal.AddCommand("dog") - .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") - .WithExample("animal", "dog", "--name", "Luna") - .WithExample("animal", "dog", "--name", "Charlie") - .WithExample("animal", "dog", "--name", "Bella") - .WithExample("animal", "dog", "--name", "Daisy") - .WithExample("animal", "dog", "--name", "Milo"); + .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") + .WithExample("animal", "dog", "--name", "Luna") + .WithExample("animal", "dog", "--name", "Charlie") + .WithExample("animal", "dog", "--name", "Bella") + .WithExample("animal", "dog", "--name", "Daisy") + .WithExample("animal", "dog", "--name", "Milo"); animal.AddCommand("horse") - .WithExample("animal", "horse", "--name", "Brutus") - .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") - .WithExample("animal", "horse", "--name", "Cash") - .WithExample("animal", "horse", "--name", "Dakota") - .WithExample("animal", "horse", "--name", "Cisco") + .WithExample("animal", "horse", "--name", "Brutus") + .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") + .WithExample("animal", "horse", "--name", "Cash") + .WithExample("animal", "horse", "--name", "Dakota") + .WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Spirit"); }); }); @@ -604,10 +604,10 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } - + } + [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() { // Given @@ -617,25 +617,25 @@ public sealed partial class CommandAppTests configurator.SetApplicationName("myapp"); configurator.AddBranch("animal", animal => { - animal.SetDescription("The animal command."); - - // Show the first 8 examples defined on the direct children - configurator.Settings.MaximumIndirectExamples = 8; + animal.SetDescription("The animal command."); + + // Show the first 8 examples defined on the direct children + configurator.Settings.MaximumIndirectExamples = 8; animal.AddCommand("dog") - .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") - .WithExample("animal", "dog", "--name", "Luna") - .WithExample("animal", "dog", "--name", "Charlie") - .WithExample("animal", "dog", "--name", "Bella") - .WithExample("animal", "dog", "--name", "Daisy") - .WithExample("animal", "dog", "--name", "Milo"); + .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") + .WithExample("animal", "dog", "--name", "Luna") + .WithExample("animal", "dog", "--name", "Charlie") + .WithExample("animal", "dog", "--name", "Bella") + .WithExample("animal", "dog", "--name", "Daisy") + .WithExample("animal", "dog", "--name", "Milo"); animal.AddCommand("horse") - .WithExample("animal", "horse", "--name", "Brutus") - .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") - .WithExample("animal", "horse", "--name", "Cash") - .WithExample("animal", "horse", "--name", "Dakota") - .WithExample("animal", "horse", "--name", "Cisco") + .WithExample("animal", "horse", "--name", "Brutus") + .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") + .WithExample("animal", "horse", "--name", "Cash") + .WithExample("animal", "horse", "--name", "Dakota") + .WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Spirit"); }); }); @@ -645,10 +645,10 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } - + } + [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() { // Given @@ -658,25 +658,25 @@ public sealed partial class CommandAppTests configurator.SetApplicationName("myapp"); configurator.AddBranch("animal", animal => { - animal.SetDescription("The animal command."); - - // Show all examples defined on the direct children - configurator.Settings.MaximumIndirectExamples = int.MaxValue; + animal.SetDescription("The animal command."); + + // Show all examples defined on the direct children + configurator.Settings.MaximumIndirectExamples = int.MaxValue; animal.AddCommand("dog") - .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") - .WithExample("animal", "dog", "--name", "Luna") - .WithExample("animal", "dog", "--name", "Charlie") - .WithExample("animal", "dog", "--name", "Bella") - .WithExample("animal", "dog", "--name", "Daisy") - .WithExample("animal", "dog", "--name", "Milo"); + .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") + .WithExample("animal", "dog", "--name", "Luna") + .WithExample("animal", "dog", "--name", "Charlie") + .WithExample("animal", "dog", "--name", "Bella") + .WithExample("animal", "dog", "--name", "Daisy") + .WithExample("animal", "dog", "--name", "Milo"); animal.AddCommand("horse") - .WithExample("animal", "horse", "--name", "Brutus") - .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") - .WithExample("animal", "horse", "--name", "Cash") - .WithExample("animal", "horse", "--name", "Dakota") - .WithExample("animal", "horse", "--name", "Cisco") + .WithExample("animal", "horse", "--name", "Brutus") + .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") + .WithExample("animal", "horse", "--name", "Cash") + .WithExample("animal", "horse", "--name", "Dakota") + .WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Spirit"); }); }); @@ -686,10 +686,10 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } - + } + [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() { // Given @@ -699,25 +699,25 @@ public sealed partial class CommandAppTests configurator.SetApplicationName("myapp"); configurator.AddBranch("animal", animal => { - animal.SetDescription("The animal command."); - - // Do not show examples defined on the direct children - configurator.Settings.MaximumIndirectExamples = 0; + animal.SetDescription("The animal command."); + + // Do not show examples defined on the direct children + configurator.Settings.MaximumIndirectExamples = 0; animal.AddCommand("dog") - .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") - .WithExample("animal", "dog", "--name", "Luna") - .WithExample("animal", "dog", "--name", "Charlie") - .WithExample("animal", "dog", "--name", "Bella") - .WithExample("animal", "dog", "--name", "Daisy") - .WithExample("animal", "dog", "--name", "Milo"); + .WithExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy") + .WithExample("animal", "dog", "--name", "Luna") + .WithExample("animal", "dog", "--name", "Charlie") + .WithExample("animal", "dog", "--name", "Bella") + .WithExample("animal", "dog", "--name", "Daisy") + .WithExample("animal", "dog", "--name", "Milo"); animal.AddCommand("horse") - .WithExample("animal", "horse", "--name", "Brutus") - .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") - .WithExample("animal", "horse", "--name", "Cash") - .WithExample("animal", "horse", "--name", "Dakota") - .WithExample("animal", "horse", "--name", "Cisco") + .WithExample("animal", "horse", "--name", "Brutus") + .WithExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false") + .WithExample("animal", "horse", "--name", "Cash") + .WithExample("animal", "horse", "--name", "Dakota") + .WithExample("animal", "horse", "--name", "Cisco") .WithExample("animal", "horse", "--name", "Spirit"); }); }); @@ -737,23 +737,23 @@ public sealed partial class CommandAppTests var fixture = new CommandAppTester(); fixture.Configure(configurator => { - configurator.SetApplicationName("myapp"); + configurator.SetApplicationName("myapp"); configurator.AddBranch("animal", animal => { - animal.SetDescription("The animal command."); - - // All branch examples should be shown - animal.AddExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy"); - animal.AddExample("animal", "dog", "--name", "Luna"); - animal.AddExample("animal", "dog", "--name", "Charlie"); - animal.AddExample("animal", "dog", "--name", "Bella"); - animal.AddExample("animal", "dog", "--name", "Daisy"); - animal.AddExample("animal", "dog", "--name", "Milo"); - animal.AddExample("animal", "horse", "--name", "Brutus"); - animal.AddExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false"); - animal.AddExample("animal", "horse", "--name", "Cash"); - animal.AddExample("animal", "horse", "--name", "Dakota"); - animal.AddExample("animal", "horse", "--name", "Cisco"); + animal.SetDescription("The animal command."); + + // All branch examples should be shown + animal.AddExample("animal", "dog", "--name", "Rufus", "--age", "12", "--good-boy"); + animal.AddExample("animal", "dog", "--name", "Luna"); + animal.AddExample("animal", "dog", "--name", "Charlie"); + animal.AddExample("animal", "dog", "--name", "Bella"); + animal.AddExample("animal", "dog", "--name", "Daisy"); + animal.AddExample("animal", "dog", "--name", "Milo"); + animal.AddExample("animal", "horse", "--name", "Brutus"); + animal.AddExample("animal", "horse", "--name", "Sugar", "--IsAlive", "false"); + animal.AddExample("animal", "horse", "--name", "Cash"); + animal.AddExample("animal", "horse", "--name", "Dakota"); + animal.AddExample("animal", "horse", "--name", "Cisco"); animal.AddExample("animal", "horse", "--name", "Spirit"); animal.AddCommand("dog") @@ -768,7 +768,7 @@ public sealed partial class CommandAppTests // Then return Verifier.Verify(result.Output); - } + } [Fact] [Expectation("Default_Examples")] @@ -780,13 +780,13 @@ public sealed partial class CommandAppTests fixture.Configure(configurator => { configurator.SetApplicationName("myapp"); - - // All root examples should be shown - configurator.AddExample("--name", "Rufus", "--age", "12", "--good-boy"); - configurator.AddExample("--name", "Luna"); - configurator.AddExample("--name", "Charlie"); - configurator.AddExample("--name", "Bella"); - configurator.AddExample("--name", "Daisy"); + + // All root examples should be shown + configurator.AddExample("--name", "Rufus", "--age", "12", "--good-boy"); + configurator.AddExample("--name", "Luna"); + configurator.AddExample("--name", "Charlie"); + configurator.AddExample("--name", "Bella"); + configurator.AddExample("--name", "Daisy"); configurator.AddExample("--name", "Milo"); }); diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Parsing.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Parsing.cs index c195ffd..e9e0f73 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Parsing.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Parsing.cs @@ -352,8 +352,8 @@ public sealed partial class CommandAppTests var result = app.Run("dog", "-u"); // Then - return Verifier.Verify(result.Output); - } + return Verifier.Verify(result.Output); + } } [UsesVerify] @@ -584,7 +584,7 @@ public sealed partial class CommandAppTests // Then result.Output.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'."); - } + } } } } diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Remaining.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Remaining.cs index 8a3eefe..92febae 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Remaining.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Remaining.cs @@ -3,16 +3,16 @@ namespace Spectre.Console.Tests.Unit.Cli; public sealed partial class CommandAppTests { public sealed class Remaining - { - [Theory] - [InlineData("-a")] + { + [Theory] + [InlineData("-a")] [InlineData("--alive")] public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_RelaxedParsing(string knownFlag) { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddCommand("dog"); }); @@ -20,29 +20,29 @@ public sealed partial class CommandAppTests // When var result = app.Run(new[] { - "dog", "12", "4", - knownFlag, - }); - - // Then - result.Settings.ShouldBeOfType().And(dog => - { - dog.IsAlive.ShouldBe(true); - }); + "dog", "12", "4", + knownFlag, + }); - result.Context.Remaining.Parsed.Count.ShouldBe(0); + // Then + result.Settings.ShouldBeOfType().And(dog => + { + dog.IsAlive.ShouldBe(true); + }); + + result.Context.Remaining.Parsed.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0); - } - - [Theory] - [InlineData("-r")] + } + + [Theory] + [InlineData("-r")] [InlineData("--romeo")] public void Should_Add_Unknown_Flags_To_Remaining_Arguments_RelaxedParsing(string unknownFlag) { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddCommand("dog"); }); @@ -50,23 +50,23 @@ public sealed partial class CommandAppTests // When var result = app.Run(new[] { - "dog", "12", "4", - unknownFlag, - }); - - // Then - result.Context.Remaining.Parsed.Count.ShouldBe(1); - result.Context.ShouldHaveRemainingArgument(unknownFlag.TrimStart('-'), values: new[] { (string)null }); + "dog", "12", "4", + unknownFlag, + }); + + // Then + result.Context.Remaining.Parsed.Count.ShouldBe(1); + result.Context.ShouldHaveRemainingArgument(unknownFlag.TrimStart('-'), values: new[] { (string)null }); result.Context.Remaining.Raw.Count.ShouldBe(0); - } - - [Fact] + } + + [Fact] public void Should_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_RelaxedParsing() { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddCommand("dog"); }); @@ -74,25 +74,25 @@ public sealed partial class CommandAppTests // When var result = app.Run(new[] { - "dog", "12", "4", - "-agr", - }); - - // Then - result.Context.Remaining.Parsed.Count.ShouldBe(1); - result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null }); + "dog", "12", "4", + "-agr", + }); + + // Then + result.Context.Remaining.Parsed.Count.ShouldBe(1); + result.Context.ShouldHaveRemainingArgument("r", values: new[] { (string)null }); result.Context.Remaining.Raw.Count.ShouldBe(0); - } - - [Theory] - [InlineData("-a")] + } + + [Theory] + [InlineData("-a")] [InlineData("--alive")] public void Should_Not_Add_Known_Flags_To_Remaining_Arguments_StrictParsing(string knownFlag) { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.UseStrictParsing(); config.PropagateExceptions(); config.AddCommand("dog"); @@ -101,68 +101,68 @@ public sealed partial class CommandAppTests // When var result = app.Run(new[] { - "dog", "12", "4", - knownFlag, - }); - - // Then - result.Context.Remaining.Parsed.Count.ShouldBe(0); + "dog", "12", "4", + knownFlag, + }); + + // Then + result.Context.Remaining.Parsed.Count.ShouldBe(0); result.Context.Remaining.Raw.Count.ShouldBe(0); - } - - [Theory] - [InlineData("-r")] + } + + [Theory] + [InlineData("-r")] [InlineData("--romeo")] public void Should_Not_Add_Unknown_Flags_To_Remaining_Arguments_StrictParsing(string unknownFlag) { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.UseStrictParsing(); config.PropagateExceptions(); config.AddCommand("dog"); }); - - // When + + // When var result = Record.Exception(() => app.Run(new[] { - "dog", "12", "4", - unknownFlag, - })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe($"Unknown option '{unknownFlag.TrimStart('-')}'."); + "dog", "12", "4", + unknownFlag, + })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe($"Unknown option '{unknownFlag.TrimStart('-')}'."); }); - } - - [Fact] + } + + [Fact] public void Should_Not_Add_Unknown_Flags_When_Grouped_To_Remaining_Arguments_StrictParsing() { // Given var app = new CommandAppTester(); app.Configure(config => - { + { config.UseStrictParsing(); config.PropagateExceptions(); config.AddCommand("dog"); }); - - // When + + // When var result = Record.Exception(() => app.Run(new[] { - "dog", "12", "4", - "-agr", - })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe($"Unknown option 'r'."); + "dog", "12", "4", + "-agr", + })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe($"Unknown option 'r'."); }); - } + } [Fact] 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[1].ShouldBe("\"set && pause\""); result.Context.Remaining.Raw[2].ShouldBe("Name=\" -Rufus --' "); - } - - [Theory] - [InlineData(true)] + } + + [Theory] + [InlineData(true)] [InlineData(false)] public void Should_Convert_Flags_To_Remaining_Arguments_If_Cannot_Be_Assigned(bool useStrictParsing) { // Given var app = new CommandAppTester(); app.Configure(config => - { - config.Settings.ConvertFlagsToRemainingArguments = true; - config.Settings.StrictParsing = useStrictParsing; + { + config.Settings.ConvertFlagsToRemainingArguments = true; + config.Settings.StrictParsing = useStrictParsing; config.PropagateExceptions(); config.AddCommand("dog"); }); @@ -280,8 +280,8 @@ public sealed partial class CommandAppTests // Then 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 } } diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Version.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Version.cs index 1a6d884..424a324 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Version.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Version.cs @@ -15,8 +15,8 @@ public sealed partial class CommandAppTests // Then result.Output.ShouldStartWith("Spectre.Cli version "); - } - + } + [Fact] 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(); fixture.Configure(configurator => { - configurator.SetApplicationVersion("1.0"); + configurator.SetApplicationVersion("1.0"); }); // When @@ -32,8 +32,8 @@ public sealed partial class CommandAppTests // Then result.Output.ShouldBe("1.0"); - } - + } + [Fact] public void Should_Output_Application_Version_To_The_Console_With_Command() { @@ -41,8 +41,8 @@ public sealed partial class CommandAppTests var fixture = new CommandAppTester(); fixture.Configure(configurator => { - configurator.SetApplicationVersion("1.0"); - + configurator.SetApplicationVersion("1.0"); + configurator.AddCommand("empty"); }); @@ -51,8 +51,8 @@ public sealed partial class CommandAppTests // Then result.Output.ShouldBe("1.0"); - } - + } + [Fact] public void Should_Output_Application_Version_To_The_Console_With_Default_Command() { @@ -69,8 +69,8 @@ public sealed partial class CommandAppTests // Then result.Output.ShouldBe("1.0"); - } - + } + [Fact] 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(); fixture.Configure(configurator => { - configurator.SetApplicationVersion("1.0"); - - configurator.AddBranch("branch", branch => - { - branch.SetDefaultCommand(); + configurator.SetApplicationVersion("1.0"); + + configurator.AddBranch("branch", branch => + { + branch.SetDefaultCommand(); }); }); diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs index dcac185..68be0f5 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs @@ -1041,66 +1041,66 @@ public sealed partial class CommandAppTests // Then result.Context.ShouldNotBeNull(); result.Context.Data.ShouldBe(123); - } - + } + public sealed class Default_Command - { - [Fact] - public void Should_Be_Able_To_Set_The_Default_Command() - { - // Given - var app = new CommandAppTester(); - app.SetDefaultCommand(); - - // When - var result = app.Run(new[] + { + [Fact] + public void Should_Be_Able_To_Set_The_Default_Command() + { + // Given + var app = new CommandAppTester(); + app.SetDefaultCommand(); + + // When + var result = app.Run(new[] { "4", "12", "--good-boy", "--name", "Rufus", - }); - - // Then - result.ExitCode.ShouldBe(0); - result.Settings.ShouldBeOfType().And(dog => - { - dog.Legs.ShouldBe(4); - dog.Age.ShouldBe(12); - dog.GoodBoy.ShouldBe(true); - dog.Name.ShouldBe("Rufus"); - }); - } - - [Fact] - public void Should_Set_The_Default_Command_Description_Data_CommandApp() - { - // Given - var app = new CommandApp(); - app.SetDefaultCommand() - .WithDescription("The default command") - .WithData(new string[] { "foo", "bar" }); - - // When - - // Then - app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); - app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); - app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); - } - - [Fact] - public void Should_Set_The_Default_Command_Description_Data_CommandAppOfT() - { - // Given - var app = new CommandApp() - .WithDescription("The default command") - .WithData(new string[] { "foo", "bar" }); - - // When - - // Then - app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); - app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); - app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); - } + }); + + // Then + result.ExitCode.ShouldBe(0); + result.Settings.ShouldBeOfType().And(dog => + { + dog.Legs.ShouldBe(4); + dog.Age.ShouldBe(12); + dog.GoodBoy.ShouldBe(true); + dog.Name.ShouldBe("Rufus"); + }); + } + + [Fact] + public void Should_Set_The_Default_Command_Description_Data_CommandApp() + { + // Given + var app = new CommandApp(); + app.SetDefaultCommand() + .WithDescription("The default command") + .WithData(new string[] { "foo", "bar" }); + + // When + + // Then + app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); + app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); + app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); + } + + [Fact] + public void Should_Set_The_Default_Command_Description_Data_CommandAppOfT() + { + // Given + var app = new CommandApp() + .WithDescription("The default command") + .WithData(new string[] { "foo", "bar" }); + + // When + + // Then + app.GetConfigurator().DefaultCommand.ShouldNotBeNull(); + app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command"); + app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" }); + } } public sealed class Delegate_Commands @@ -1114,7 +1114,7 @@ public sealed partial class CommandAppTests var app = new CommandApp(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddDelegate( "foo", (context, settings) => @@ -1134,8 +1134,8 @@ public sealed partial class CommandAppTests dog.Age.ShouldBe(12); dog.Legs.ShouldBe(4); data.ShouldBe(2); - } - + } + [Fact] public async void Should_Execute_Async_Delegate_Command_At_Root_Level() { @@ -1145,7 +1145,7 @@ public sealed partial class CommandAppTests var app = new CommandApp(); app.Configure(config => - { + { config.PropagateExceptions(); config.AddAsyncDelegate( "foo", (context, settings) => @@ -1199,8 +1199,8 @@ public sealed partial class CommandAppTests dog.Age.ShouldBe(12); dog.Legs.ShouldBe(4); data.ShouldBe(2); - } - + } + [Fact] public async void Should_Execute_Nested_Async_Delegate_Command() { diff --git a/test/Spectre.Console.Cli.Tests/Unit/Parsing/CommandTreeTokenizerTests.cs b/test/Spectre.Console.Cli.Tests/Unit/Parsing/CommandTreeTokenizerTests.cs index d968224..b5d25e4 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/Parsing/CommandTreeTokenizerTests.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/Parsing/CommandTreeTokenizerTests.cs @@ -1,314 +1,314 @@ -namespace Spectre.Console.Tests.Unit.Cli.Parsing; - -public class CommandTreeTokenizerTests -{ - public sealed class ScanString - { - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(" ")] - [InlineData("\t")] - [InlineData("\r\n\t")] - [InlineData("👋🏻")] - [InlineData("🐎👋🏻🔥❤️")] - [InlineData("\"🐎👋🏻🔥❤️\" is an emoji sequence")] - public void Should_Preserve_Edgecase_Inputs(string actualAndExpected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(actualAndExpected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); - } - - [Theory] - - // Double-quote handling - [InlineData("\"")] - [InlineData("\"\"")] - [InlineData("\"Rufus\"")] - [InlineData("\" Rufus\"")] - [InlineData("\"-R\"")] - [InlineData("\"-Rufus\"")] - [InlineData("\" -Rufus\"")] - - // Single-quote handling - [InlineData("'")] - [InlineData("''")] - [InlineData("'Rufus'")] - [InlineData("' Rufus'")] - [InlineData("'-R'")] - [InlineData("'-Rufus'")] - [InlineData("' -Rufus'")] - public void Should_Preserve_Quotes(string actualAndExpected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(actualAndExpected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); - } - - [Theory] - [InlineData("Rufus-")] - [InlineData("Rufus--")] - [InlineData("R-u-f-u-s")] - public void Should_Preserve_Hyphen_Delimiters(string actualAndExpected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(actualAndExpected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); - } - - [Theory] - [InlineData(" Rufus")] - [InlineData("Rufus ")] - [InlineData(" Rufus ")] - public void Should_Preserve_Spaces(string actualAndExpected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(actualAndExpected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); - } - - [Theory] - [InlineData(" \" -Rufus -- ")] - [InlineData("Name=\" -Rufus --' ")] - public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces(string actualAndExpected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(actualAndExpected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); - } - } - - public sealed class ScanLongOption - { - [Theory] - [InlineData("--Name-", "Name-")] - [InlineData("--Name_", "Name_")] - public void Should_Allow_Hyphens_And_Underscores_In_Option_Name(string actual, string expected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(expected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.LongOption); - } - - [Theory] - [InlineData("-- ")] - [InlineData("--Name ")] - [InlineData("--Name\"")] - [InlineData("--Nam\"e")] - public void Should_Throw_On_Invalid_Option_Name(string actual) - { - // Given - - // When - var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe("Invalid long option name."); - }); - } - } - - public sealed class ScanShortOptions - { - [Fact] - public void Should_Accept_Option_Without_Value() - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new[] { "-a" }); - - // Then - result.Remaining.ShouldBeEmpty(); - result.Tokens.ShouldHaveSingleItem(); - - var t = result.Tokens[0]; - t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(0); - t.Value.ShouldBe("a"); - t.Representation.ShouldBe("-a"); - } - - [Theory] - [InlineData("-a:foo")] - [InlineData("-a=foo")] - public void Should_Accept_Option_With_Value(string param) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new[] { param }); - - // Then - result.Remaining.ShouldBeEmpty(); - result.Tokens.Count.ShouldBe(2); - - var t = result.Tokens.Consume(); - t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(0); - t.Value.ShouldBe("a"); - t.Representation.ShouldBe("-a"); - - t = result.Tokens.Consume(); - t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(3); - t.Value.ShouldBe("foo"); - t.Representation.ShouldBe("foo"); - } - - [Theory] - - // Positive values - [InlineData("-a:1.5", null, "1.5")] - [InlineData("-a=1.5", null, "1.5")] - [InlineData("-a", "1.5", "1.5")] - - // Negative values - [InlineData("-a:-1.5", null, "-1.5")] - [InlineData("-a=-1.5", null, "-1.5")] - [InlineData("-a", "-1.5", "-1.5")] - public void Should_Accept_Option_With_Numeric_Value(string firstArg, string secondArg, string expectedValue) - { - // Given - List args = new List(); - args.Add(firstArg); - if (secondArg != null) - { - args.Add(secondArg); - } - - // When - var result = CommandTreeTokenizer.Tokenize(args); - - // Then - result.Remaining.ShouldBeEmpty(); - result.Tokens.Count.ShouldBe(2); - - var t = result.Tokens.Consume(); - t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(0); - t.Value.ShouldBe("a"); - t.Representation.ShouldBe("-a"); - - t = result.Tokens.Consume(); - t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(3); - t.Value.ShouldBe(expectedValue); - t.Representation.ShouldBe(expectedValue); - } - - [Fact] - public void Should_Accept_Option_With_Negative_Numeric_Prefixed_String_Value() - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " }); - - // Then - result.Remaining.ShouldBeEmpty(); - result.Tokens.ShouldHaveSingleItem(); - - var t = result.Tokens[0]; - t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); - t.IsGrouped.ShouldBe(false); - t.Position.ShouldBe(0); - t.Value.ShouldBe("-6..2"); - t.Representation.ShouldBe("-6..2"); - } - - [Theory] - [InlineData("-N ", "N")] - public void Should_Remove_Trailing_Spaces_In_Option_Name(string actual, string expected) - { - // Given - - // When - var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); - - // Then - result.Tokens.Count.ShouldBe(1); - result.Tokens[0].Value.ShouldBe(expected); - result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); - } - - [Theory] - [InlineData("-N-")] - [InlineData("-N\"")] - [InlineData("-a1")] - public void Should_Throw_On_Invalid_Option_Name(string actual) - { - // Given - - // When - var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe("Short option does not have a valid name."); - }); - } - } - - [Theory] - [InlineData("-")] - [InlineData("- ")] - public void Should_Throw_On_Missing_Option_Name(string actual) - { - // Given - - // When - var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); - - // Then - result.ShouldBeOfType().And(ex => - { - ex.Message.ShouldBe("Option does not have a name."); - }); - } -} +namespace Spectre.Console.Tests.Unit.Cli.Parsing; + +public class CommandTreeTokenizerTests +{ + public sealed class ScanString + { + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData("\t")] + [InlineData("\r\n\t")] + [InlineData("👋🏻")] + [InlineData("🐎👋🏻🔥❤️")] + [InlineData("\"🐎👋🏻🔥❤️\" is an emoji sequence")] + public void Should_Preserve_Edgecase_Inputs(string actualAndExpected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(actualAndExpected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); + } + + [Theory] + + // Double-quote handling + [InlineData("\"")] + [InlineData("\"\"")] + [InlineData("\"Rufus\"")] + [InlineData("\" Rufus\"")] + [InlineData("\"-R\"")] + [InlineData("\"-Rufus\"")] + [InlineData("\" -Rufus\"")] + + // Single-quote handling + [InlineData("'")] + [InlineData("''")] + [InlineData("'Rufus'")] + [InlineData("' Rufus'")] + [InlineData("'-R'")] + [InlineData("'-Rufus'")] + [InlineData("' -Rufus'")] + public void Should_Preserve_Quotes(string actualAndExpected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(actualAndExpected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); + } + + [Theory] + [InlineData("Rufus-")] + [InlineData("Rufus--")] + [InlineData("R-u-f-u-s")] + public void Should_Preserve_Hyphen_Delimiters(string actualAndExpected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(actualAndExpected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); + } + + [Theory] + [InlineData(" Rufus")] + [InlineData("Rufus ")] + [InlineData(" Rufus ")] + public void Should_Preserve_Spaces(string actualAndExpected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(actualAndExpected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); + } + + [Theory] + [InlineData(" \" -Rufus -- ")] + [InlineData("Name=\" -Rufus --' ")] + public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces(string actualAndExpected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(actualAndExpected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String); + } + } + + public sealed class ScanLongOption + { + [Theory] + [InlineData("--Name-", "Name-")] + [InlineData("--Name_", "Name_")] + public void Should_Allow_Hyphens_And_Underscores_In_Option_Name(string actual, string expected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(expected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.LongOption); + } + + [Theory] + [InlineData("-- ")] + [InlineData("--Name ")] + [InlineData("--Name\"")] + [InlineData("--Nam\"e")] + public void Should_Throw_On_Invalid_Option_Name(string actual) + { + // Given + + // When + var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe("Invalid long option name."); + }); + } + } + + public sealed class ScanShortOptions + { + [Fact] + public void Should_Accept_Option_Without_Value() + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new[] { "-a" }); + + // Then + result.Remaining.ShouldBeEmpty(); + result.Tokens.ShouldHaveSingleItem(); + + var t = result.Tokens[0]; + t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(0); + t.Value.ShouldBe("a"); + t.Representation.ShouldBe("-a"); + } + + [Theory] + [InlineData("-a:foo")] + [InlineData("-a=foo")] + public void Should_Accept_Option_With_Value(string param) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new[] { param }); + + // Then + result.Remaining.ShouldBeEmpty(); + result.Tokens.Count.ShouldBe(2); + + var t = result.Tokens.Consume(); + t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(0); + t.Value.ShouldBe("a"); + t.Representation.ShouldBe("-a"); + + t = result.Tokens.Consume(); + t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(3); + t.Value.ShouldBe("foo"); + t.Representation.ShouldBe("foo"); + } + + [Theory] + + // Positive values + [InlineData("-a:1.5", null, "1.5")] + [InlineData("-a=1.5", null, "1.5")] + [InlineData("-a", "1.5", "1.5")] + + // Negative values + [InlineData("-a:-1.5", null, "-1.5")] + [InlineData("-a=-1.5", null, "-1.5")] + [InlineData("-a", "-1.5", "-1.5")] + public void Should_Accept_Option_With_Numeric_Value(string firstArg, string secondArg, string expectedValue) + { + // Given + List args = new List(); + args.Add(firstArg); + if (secondArg != null) + { + args.Add(secondArg); + } + + // When + var result = CommandTreeTokenizer.Tokenize(args); + + // Then + result.Remaining.ShouldBeEmpty(); + result.Tokens.Count.ShouldBe(2); + + var t = result.Tokens.Consume(); + t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(0); + t.Value.ShouldBe("a"); + t.Representation.ShouldBe("-a"); + + t = result.Tokens.Consume(); + t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(3); + t.Value.ShouldBe(expectedValue); + t.Representation.ShouldBe(expectedValue); + } + + [Fact] + public void Should_Accept_Option_With_Negative_Numeric_Prefixed_String_Value() + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " }); + + // Then + result.Remaining.ShouldBeEmpty(); + result.Tokens.ShouldHaveSingleItem(); + + var t = result.Tokens[0]; + t.TokenKind.ShouldBe(CommandTreeToken.Kind.String); + t.IsGrouped.ShouldBe(false); + t.Position.ShouldBe(0); + t.Value.ShouldBe("-6..2"); + t.Representation.ShouldBe("-6..2"); + } + + [Theory] + [InlineData("-N ", "N")] + public void Should_Remove_Trailing_Spaces_In_Option_Name(string actual, string expected) + { + // Given + + // When + var result = CommandTreeTokenizer.Tokenize(new string[] { actual }); + + // Then + result.Tokens.Count.ShouldBe(1); + result.Tokens[0].Value.ShouldBe(expected); + result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption); + } + + [Theory] + [InlineData("-N-")] + [InlineData("-N\"")] + [InlineData("-a1")] + public void Should_Throw_On_Invalid_Option_Name(string actual) + { + // Given + + // When + var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe("Short option does not have a valid name."); + }); + } + } + + [Theory] + [InlineData("-")] + [InlineData("- ")] + public void Should_Throw_On_Missing_Option_Name(string actual) + { + // Given + + // When + var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual })); + + // Then + result.ShouldBeOfType().And(ex => + { + ex.Message.ShouldBe("Option does not have a name."); + }); + } +} diff --git a/test/Spectre.Console.Cli.Tests/Unit/Testing/FakeTypeRegistrarTests.cs b/test/Spectre.Console.Cli.Tests/Unit/Testing/FakeTypeRegistrarTests.cs index a5d3a42..61ae88d 100644 --- a/test/Spectre.Console.Cli.Tests/Unit/Testing/FakeTypeRegistrarTests.cs +++ b/test/Spectre.Console.Cli.Tests/Unit/Testing/FakeTypeRegistrarTests.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console.Tests.Unit.Cli.Testing; +namespace Spectre.Console.Tests.Unit.Cli.Testing; public class FakeTypeRegistrarTests { diff --git a/test/Spectre.Console.Tests/Unit/StyleTests.cs b/test/Spectre.Console.Tests/Unit/StyleTests.cs index b0b4660..379ab3e 100644 --- a/test/Spectre.Console.Tests/Unit/StyleTests.cs +++ b/test/Spectre.Console.Tests/Unit/StyleTests.cs @@ -1,20 +1,20 @@ namespace Spectre.Console.Tests.Unit; public sealed class StyleTests -{ +{ [Fact] public void Should_Convert_From_Color_As_Expected() - { - // Given - Style style; - - // When - style = Color.Red; - - // Then - style.Foreground.ShouldBe(Color.Red); - } - + { + // Given + Style style; + + // When + style = Color.Red; + + // Then + style.Foreground.ShouldBe(Color.Red); + } + [Fact] public void Should_Combine_Two_Styles_As_Expected() { diff --git a/test/Spectre.Console.Tests/Utilities/GitHubIssueAttribute.cs b/test/Spectre.Console.Tests/Utilities/GitHubIssueAttribute.cs index d7d30a7..4f1ede2 100644 --- a/test/Spectre.Console.Tests/Utilities/GitHubIssueAttribute.cs +++ b/test/Spectre.Console.Tests/Utilities/GitHubIssueAttribute.cs @@ -1,4 +1,4 @@ -namespace Spectre.Console.Tests; +namespace Spectre.Console.Tests; [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class GitHubIssueAttribute : Attribute