mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-13 15:42:50 +08:00
fixed line-endings
This commit is contained in:
parent
989c0b9904
commit
44300c871f
154
docs/Program.cs
154
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<int> 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<string>
|
||||
{
|
||||
"../../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<string>
|
||||
{
|
||||
"../../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<string> 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<int> 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<string>
|
||||
{
|
||||
"../../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<string>
|
||||
{
|
||||
"../../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<string> 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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
<tr>
|
||||
<td><code>strikethrough</code></td>
|
||||
<td>Shows text with a horizontal line through the center</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>link</link></td>
|
||||
<td>Creates a clickable link within text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>link</link></td>
|
||||
<td>Creates a clickable link within text</td>
|
||||
</tr>
|
||||
</table>
|
@ -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<DefaultCommand>();
|
||||
|
||||
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<DefaultCommand>();
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -63,22 +63,22 @@ What's the secret number? _
|
||||
|
||||
```text
|
||||
Enter password: ************_
|
||||
```
|
||||
|
||||
## Masks
|
||||
|
||||
```
|
||||
|
||||
## Masks
|
||||
|
||||
<?# Example symbol="M:Prompt.Program.AskPasswordWithCustomMask" project="Prompt" /?>
|
||||
|
||||
|
||||
```text
|
||||
```text
|
||||
Enter password: ------------_
|
||||
```
|
||||
|
||||
You can utilize a null character to completely hide input.
|
||||
|
||||
```
|
||||
|
||||
You can utilize a null character to completely hide input.
|
||||
|
||||
<?# Example symbol="M:Prompt.Program.AskPasswordWithNullMask" project="Prompt" /?>
|
||||
|
||||
```text
|
||||
```text
|
||||
Enter password: _
|
||||
```
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Title: Calendar
|
||||
Title: Calendar
|
||||
Order: 40
|
||||
RedirectFrom: calendar
|
||||
Description: "The **Calendar** is used to render a calendar to the terminal."
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Statiq.App;
|
||||
using Statiq.App;
|
||||
using Statiq.Common;
|
||||
using Statiq.Web;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Docs.Extensions
|
||||
namespace Docs.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Docs.Models
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Docs.Utilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Statiq.Common;
|
||||
using Statiq.Common;
|
||||
using Statiq.Web.GitHub;
|
||||
using Statiq.Web.Netlify;
|
||||
|
||||
|
@ -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<IEnumerable<IDocument>> 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<string>("Highlights") ?? Array.Empty<string>();
|
||||
|
||||
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<IEnumerable<IDocument>> 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<string>("Highlights") ?? Array.Empty<string>();
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Docs.Extensions;
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -15,22 +15,22 @@ public static class Program
|
||||
{
|
||||
config.SetApplicationName("fake-dotnet");
|
||||
config.ValidateExamples();
|
||||
config.AddExample("run", "--no-build");
|
||||
|
||||
// Run
|
||||
config.AddCommand<RunCommand>("run");
|
||||
|
||||
// Add
|
||||
config.AddBranch<AddSettings>("add", add =>
|
||||
{
|
||||
add.SetDescription("Add a package or reference to a .NET project");
|
||||
add.AddCommand<AddPackageCommand>("package");
|
||||
add.AddCommand<AddReferenceCommand>("reference");
|
||||
});
|
||||
|
||||
// Serve
|
||||
config.AddCommand<ServeCommand>("serve")
|
||||
.WithExample("serve", "-o", "firefox")
|
||||
config.AddExample("run", "--no-build");
|
||||
|
||||
// Run
|
||||
config.AddCommand<RunCommand>("run");
|
||||
|
||||
// Add
|
||||
config.AddBranch<AddSettings>("add", add =>
|
||||
{
|
||||
add.SetDescription("Add a package or reference to a .NET project");
|
||||
add.AddCommand<AddPackageCommand>("package");
|
||||
add.AddCommand<AddReferenceCommand>("reference");
|
||||
});
|
||||
|
||||
// Serve
|
||||
config.AddCommand<ServeCommand>("serve")
|
||||
.WithExample("serve", "-o", "firefox")
|
||||
.WithExample("serve", "--port", "80", "-o", "firefox");
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Example showing how to extend the built-in Spectre.Console help provider
|
||||
/// by rendering a custom banner at the top of the help information
|
||||
/// </summary>
|
||||
internal class CustomHelpProvider : HelpProvider
|
||||
{
|
||||
public CustomHelpProvider(ICommandAppSettings settings)
|
||||
: base(settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<IRenderable> 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;
|
||||
|
||||
/// <summary>
|
||||
/// Example showing how to extend the built-in Spectre.Console help provider
|
||||
/// by rendering a custom banner at the top of the help information
|
||||
/// </summary>
|
||||
internal class CustomHelpProvider : HelpProvider
|
||||
{
|
||||
public CustomHelpProvider(ICommandAppSettings settings)
|
||||
: base(settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new Text("--------------------------------------"), Text.NewLine,
|
||||
new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine,
|
||||
new Text("--------------------------------------"), Text.NewLine,
|
||||
Text.NewLine,
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace Help;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
var app = new CommandApp<DefaultCommand>();
|
||||
|
||||
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<DefaultCommand>();
|
||||
|
||||
app.Configure(config =>
|
||||
{
|
||||
// Register the custom help provider
|
||||
config.SetHelpProvider(new CustomHelpProvider(config.Settings));
|
||||
});
|
||||
|
||||
return app.Run(args);
|
||||
}
|
||||
}
|
||||
|
@ -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<string>("Enter [green]password[/]?")
|
||||
.PromptStyle("red")
|
||||
.Secret());
|
||||
}
|
||||
|
||||
public static string AskPasswordWithCustomMask()
|
||||
{
|
||||
}
|
||||
|
||||
public static string AskPasswordWithCustomMask()
|
||||
{
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("Enter [green]password[/]?")
|
||||
.PromptStyle("red")
|
||||
.Secret('-'));
|
||||
}
|
||||
|
||||
public static string AskPasswordWithNullMask()
|
||||
{
|
||||
.Secret('-'));
|
||||
}
|
||||
|
||||
public static string AskPasswordWithNullMask()
|
||||
{
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("Enter [green]password[/]?")
|
||||
.PromptStyle("red")
|
||||
.Secret(null));
|
||||
.Secret(null));
|
||||
}
|
||||
|
||||
public static string AskColor()
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Spectre.Console;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace Generator.Commands
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<Document> 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;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using Spectre.Console.Cli.Internal.Configuration;
|
||||
|
||||
using Spectre.Console.Cli.Internal.Configuration;
|
||||
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Spectre.Console.Cli.Internal.Configuration;
|
||||
|
||||
using Spectre.Console.Cli.Internal.Configuration;
|
||||
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
/// <summary>
|
||||
@ -49,8 +49,8 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
|
||||
public Task<int> RunAsync(IEnumerable<string> args)
|
||||
{
|
||||
return _app.RunAsync(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal Configurator GetConfigurator()
|
||||
{
|
||||
return _app.GetConfigurator();
|
||||
|
@ -5,40 +5,40 @@ namespace Spectre.Console.Cli;
|
||||
/// and <see cref="IConfigurator{TSettings}"/>.
|
||||
/// </summary>
|
||||
public static class ConfiguratorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <param name="helpProvider">The help provider to use.</param>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider)
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <param name="helpProvider">The help provider to use.</param>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator SetHelpProvider(this IConfigurator configurator, IHelpProvider helpProvider)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
configurator.SetHelpProvider(helpProvider);
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator SetHelpProvider<T>(this IConfigurator configurator)
|
||||
where T : IHelpProvider
|
||||
{
|
||||
}
|
||||
|
||||
configurator.SetHelpProvider(helpProvider);
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator SetHelpProvider<T>(this IConfigurator configurator)
|
||||
where T : IHelpProvider
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
configurator.SetHelpProvider<T>();
|
||||
return configurator;
|
||||
}
|
||||
|
||||
configurator.SetHelpProvider<T>();
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -11,22 +11,22 @@ namespace Spectre.Console.Cli.Help;
|
||||
public class HelpProvider : IHelpProvider
|
||||
{
|
||||
private HelpProviderResources resources;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating how many examples from direct children to show in the help text.
|
||||
/// </summary>
|
||||
protected virtual int MaximumIndirectExamples { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any default values for command options are shown in the help text.
|
||||
/// </summary>
|
||||
protected virtual bool ShowOptionDefaultValues { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a trailing period of a command description is trimmed in the help text.
|
||||
/// </summary>
|
||||
protected virtual bool TrimTrailingPeriod { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating how many examples from direct children to show in the help text.
|
||||
/// </summary>
|
||||
protected virtual int MaximumIndirectExamples { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any default values for command options are shown in the help text.
|
||||
/// </summary>
|
||||
protected virtual bool ShowOptionDefaultValues { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a trailing period of a command description is trimmed in the help text.
|
||||
/// </summary>
|
||||
protected virtual bool TrimTrailingPeriod { get; }
|
||||
|
||||
private sealed class HelpArgument
|
||||
{
|
||||
public string Name { get; }
|
||||
@ -74,14 +74,14 @@ public class HelpProvider : IHelpProvider
|
||||
public static IReadOnlyList<HelpOption> Get(ICommandInfo? command, HelpProviderResources resources)
|
||||
{
|
||||
var parameters = new List<HelpOption>();
|
||||
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<ICommandOption>().Where(o => !o.IsHidden).Select(o =>
|
||||
@ -92,55 +92,55 @@ public class HelpProvider : IHelpProvider
|
||||
?? Array.Empty<HelpOption>());
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HelpProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The command line application settings used for configuration.</param>
|
||||
public HelpProvider(ICommandAppSettings settings)
|
||||
{
|
||||
this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues;
|
||||
this.MaximumIndirectExamples = settings.MaximumIndirectExamples;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HelpProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The command line application settings used for configuration.</param>
|
||||
public HelpProvider(ICommandAppSettings settings)
|
||||
{
|
||||
this.ShowOptionDefaultValues = settings.ShowOptionDefaultValues;
|
||||
this.MaximumIndirectExamples = settings.MaximumIndirectExamples;
|
||||
this.TrimTrailingPeriod = settings.TrimTrailingPeriod;
|
||||
|
||||
resources = new HelpProviderResources(settings.Culture);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
resources = new HelpProviderResources(settings.Culture);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
var result = new List<IRenderable>();
|
||||
|
||||
result.AddRange(GetHeader(model, command));
|
||||
var result = new List<IRenderable>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header for the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header for the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the usage section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the usage section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> 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,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the examples section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
/// <remarks>
|
||||
/// Examples from the command's direct children are used
|
||||
/// if no examples have been set on the specified command or model.
|
||||
/// </remarks>
|
||||
public virtual IEnumerable<IRenderable> GetExamples(ICommandModel model, ICommandInfo? command)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the examples section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
/// <remarks>
|
||||
/// Examples from the command's direct children are used
|
||||
/// if no examples have been set on the specified command or model.
|
||||
/// </remarks>
|
||||
public virtual IEnumerable<IRenderable> 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<ICommandContainer>(new[] { commandContainer });
|
||||
|
||||
// Traverse the command tree and look for examples.
|
||||
// Traverse the command tree and look for examples.
|
||||
// As soon as a node contains commands, bail.
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
@ -317,14 +317,14 @@ public class HelpProvider : IHelpProvider
|
||||
}
|
||||
|
||||
return Array.Empty<IRenderable>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> GetArguments(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
var arguments = HelpArgument.Get(command);
|
||||
@ -361,13 +361,13 @@ public class HelpProvider : IHelpProvider
|
||||
result.Add(grid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> GetOptions(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
@ -469,18 +469,18 @@ public class HelpProvider : IHelpProvider
|
||||
result.Add(grid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the commands section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the commands section of the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the footer for the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the footer for the help information.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects.</returns>
|
||||
public virtual IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo? command)
|
||||
{
|
||||
yield break;
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace Spectre.Console.Cli.Help;
|
||||
/// Represents a command container.
|
||||
/// </summary>
|
||||
public interface ICommandContainer
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all the examples for the container.
|
||||
/// </summary>
|
||||
|
@ -1,42 +1,42 @@
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an executable command.
|
||||
/// </summary>
|
||||
public interface ICommandInfo : ICommandContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the command.
|
||||
/// </summary>
|
||||
string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is a branch.
|
||||
/// </summary>
|
||||
bool IsBranch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is the default command within its container.
|
||||
/// </summary>
|
||||
bool IsDefaultCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is hidden.
|
||||
/// </summary>
|
||||
bool IsHidden { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters associated with the command.
|
||||
/// </summary>
|
||||
IReadOnlyList<ICommandParameter> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent command, if any.
|
||||
/// </summary>
|
||||
ICommandInfo? Parent { get; }
|
||||
/// <summary>
|
||||
/// Represents an executable command.
|
||||
/// </summary>
|
||||
public interface ICommandInfo : ICommandContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the command.
|
||||
/// </summary>
|
||||
string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is a branch.
|
||||
/// </summary>
|
||||
bool IsBranch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is the default command within its container.
|
||||
/// </summary>
|
||||
bool IsDefaultCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the command is hidden.
|
||||
/// </summary>
|
||||
bool IsHidden { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters associated with the command.
|
||||
/// </summary>
|
||||
IReadOnlyList<ICommandParameter> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent command, if any.
|
||||
/// </summary>
|
||||
ICommandInfo? Parent { get; }
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
internal static class ICommandInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Walks up the command.Parent tree, adding each command into a list as it goes.
|
||||
/// </summary>
|
||||
/// <remarks>The first command added to the list is the current (ie. this one).</remarks>
|
||||
/// <returns>The list of commands from current to root, as traversed by <see cref="CommandInfo.Parent"/>.</returns>
|
||||
public static List<ICommandInfo> Flatten(this ICommandInfo commandInfo)
|
||||
{
|
||||
var result = new Stack<Help.ICommandInfo>();
|
||||
|
||||
var current = commandInfo;
|
||||
while (current != null)
|
||||
{
|
||||
result.Push(current);
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
internal static class ICommandInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Walks up the command.Parent tree, adding each command into a list as it goes.
|
||||
/// </summary>
|
||||
/// <remarks>The first command added to the list is the current (ie. this one).</remarks>
|
||||
/// <returns>The list of commands from current to root, as traversed by <see cref="CommandInfo.Parent"/>.</returns>
|
||||
public static List<ICommandInfo> Flatten(this ICommandInfo commandInfo)
|
||||
{
|
||||
var result = new Stack<Help.ICommandInfo>();
|
||||
|
||||
var current = commandInfo;
|
||||
while (current != null)
|
||||
{
|
||||
result.Push(current);
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command option.
|
||||
/// </summary>
|
||||
public interface ICommandOption : ICommandParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the long names of the option.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> LongNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the short names of the option.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> ShortNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value name of the option, if applicable.
|
||||
/// </summary>
|
||||
string? ValueName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the option value is optional.
|
||||
/// </summary>
|
||||
bool ValueIsOptional { get; }
|
||||
/// <summary>
|
||||
/// Represents a command option.
|
||||
/// </summary>
|
||||
public interface ICommandOption : ICommandParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the long names of the option.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> LongNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the short names of the option.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> ShortNames { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value name of the option, if applicable.
|
||||
/// </summary>
|
||||
string? ValueName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the option value is optional.
|
||||
/// </summary>
|
||||
bool ValueIsOptional { get; }
|
||||
}
|
@ -1,32 +1,32 @@
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a command parameter.
|
||||
/// </summary>
|
||||
public interface ICommandParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is a flag.
|
||||
/// </summary>
|
||||
bool IsFlag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is required.
|
||||
/// </summary>
|
||||
bool Required { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the parameter.
|
||||
/// </summary>
|
||||
string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default value of the parameter, if specified.
|
||||
/// </summary>
|
||||
DefaultValueAttribute? DefaultValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is hidden.
|
||||
/// </summary>
|
||||
bool IsHidden { get; }
|
||||
/// <summary>
|
||||
/// Represents a command parameter.
|
||||
/// </summary>
|
||||
public interface ICommandParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is a flag.
|
||||
/// </summary>
|
||||
bool IsFlag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is required.
|
||||
/// </summary>
|
||||
bool Required { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the parameter.
|
||||
/// </summary>
|
||||
string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default value of the parameter, if specified.
|
||||
/// </summary>
|
||||
DefaultValueAttribute? DefaultValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the parameter is hidden.
|
||||
/// </summary>
|
||||
bool IsHidden { get; }
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
namespace Spectre.Console.Cli.Help;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The help provider interface for Spectre.Console.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public interface IHelpProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes help information for the application.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects representing the help information.</returns>
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes help information for the application.
|
||||
/// </summary>
|
||||
/// <param name="model">The command model to write help for.</param>
|
||||
/// <param name="command">The command for which to write help information (optional).</param>
|
||||
/// <returns>An enumerable collection of <see cref="IRenderable"/> objects representing the help information.</returns>
|
||||
IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command);
|
||||
}
|
||||
|
@ -24,22 +24,22 @@ public interface ICommandAppSettings
|
||||
/// <summary>
|
||||
/// Gets or sets the application version (use it to override auto-detected value).
|
||||
/// </summary>
|
||||
string? ApplicationVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how many examples from direct children to show in the help text.
|
||||
/// </summary>
|
||||
int MaximumIndirectExamples { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether any default values for command options are shown in the help text.
|
||||
/// </summary>
|
||||
bool ShowOptionDefaultValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text.
|
||||
/// </summary>
|
||||
bool TrimTrailingPeriod { get; set; }
|
||||
string? ApplicationVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how many examples from direct children to show in the help text.
|
||||
/// </summary>
|
||||
int MaximumIndirectExamples { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether any default values for command options are shown in the help text.
|
||||
/// </summary>
|
||||
bool ShowOptionDefaultValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a trailing period of a command description is trimmed in the help text.
|
||||
/// </summary>
|
||||
bool TrimTrailingPeriod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
||||
@ -65,14 +65,14 @@ public interface ICommandAppSettings
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not parsing is strict.
|
||||
/// </summary>
|
||||
bool StrictParsing { get; set; }
|
||||
|
||||
bool StrictParsing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not flags found on the commnd line
|
||||
/// that would normally result in a <see cref="CommandParseException"/> being thrown
|
||||
/// 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 <see cref="CommandParseException"/> being thrown
|
||||
/// during parsing with the message "Flags cannot be assigned a value."
|
||||
/// should instead be added to the remaining arguments collection.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
bool ConvertFlagsToRemainingArguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -4,19 +4,19 @@ namespace Spectre.Console.Cli;
|
||||
/// Represents a configurator.
|
||||
/// </summary>
|
||||
public interface IConfigurator
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="helpProvider">The help provider to use.</param>
|
||||
public void SetHelpProvider(IHelpProvider helpProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
|
||||
public void SetHelpProvider<T>()
|
||||
where T : IHelpProvider;
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <param name="helpProvider">The help provider to use.</param>
|
||||
public void SetHelpProvider(IHelpProvider helpProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the help provider for the application.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the help provider to instantiate at runtime and use.</typeparam>
|
||||
public void SetHelpProvider<T>()
|
||||
where T : IHelpProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command app settings.
|
||||
@ -66,5 +66,5 @@ public interface IConfigurator
|
||||
/// <param name="action">The command branch configurator.</param>
|
||||
/// <returns>A branch configurator that can be used to configure the branch further.</returns>
|
||||
IBranchConfigurator AddBranch<TSettings>(string name, Action<IConfigurator<TSettings>> action)
|
||||
where TSettings : CommandSettings;
|
||||
where TSettings : CommandSettings;
|
||||
}
|
@ -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<int> Execute(IConfiguration configuration, IEnumerable<string> args)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
args ??= new List<string>();
|
||||
|
||||
_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<IHelpProvider>)) as IEnumerable<IHelpProvider>;
|
||||
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<string> 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<string>(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<int> 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<int> Execute(IConfiguration configuration, IEnumerable<string> args)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
args ??= new List<string>();
|
||||
|
||||
_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<IHelpProvider>)) as IEnumerable<IHelpProvider>;
|
||||
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<string> 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<string>(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<int> 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);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class BranchConfigurator : IBranchConfigurator
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -19,19 +19,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
||||
Settings = new CommandAppSettings(registrar);
|
||||
Examples = new List<string[]>();
|
||||
}
|
||||
|
||||
public void SetHelpProvider(IHelpProvider helpProvider)
|
||||
{
|
||||
// Register the help provider
|
||||
_registrar.RegisterInstance(typeof(IHelpProvider), helpProvider);
|
||||
}
|
||||
|
||||
public void SetHelpProvider<T>()
|
||||
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<T>()
|
||||
where T : IHelpProvider
|
||||
{
|
||||
// Register the help provider
|
||||
_registrar.Register(typeof(IHelpProvider), typeof(T));
|
||||
}
|
||||
|
||||
public void AddExample(params string[] args)
|
||||
{
|
||||
|
@ -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<ICommandInfo> Help.ICommandContainer.Commands => Children.Cast<ICommandInfo>().ToList();
|
||||
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
|
||||
IReadOnlyList<ICommandParameter> ICommandInfo.Parameters => Parameters.Cast<ICommandParameter>().ToList();
|
||||
ICommandInfo? ICommandInfo.Parent => Parent;
|
||||
IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples;
|
||||
|
||||
public bool IsHidden { get; }
|
||||
|
||||
IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Children.Cast<ICommandInfo>().ToList();
|
||||
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
|
||||
IReadOnlyList<ICommandParameter> ICommandInfo.Parameters => Parameters.Cast<ICommandParameter>().ToList();
|
||||
ICommandInfo? ICommandInfo.Parent => Parent;
|
||||
IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples;
|
||||
|
||||
public CommandInfo(CommandInfo? parent, ConfiguredCommand prototype)
|
||||
{
|
||||
Parent = parent;
|
||||
@ -54,5 +54,5 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo
|
||||
Description = description.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
|
||||
internal sealed class CommandModel : ICommandContainer, ICommandModel
|
||||
{
|
||||
public string? ApplicationName { get; }
|
||||
public ParsingMode ParsingMode { get; }
|
||||
public IList<CommandInfo> Commands { get; }
|
||||
public IList<string[]> Examples { get; }
|
||||
|
||||
public IList<string[]> Examples { get; }
|
||||
|
||||
public CommandInfo? DefaultCommand => Commands.FirstOrDefault(c => c.IsDefaultCommand);
|
||||
|
||||
string ICommandModel.ApplicationName => GetApplicationName(ApplicationName);
|
||||
IReadOnlyList<ICommandInfo> Help.ICommandContainer.Commands => Commands.Cast<ICommandInfo>().ToList();
|
||||
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
|
||||
ICommandInfo? Help.ICommandContainer.DefaultCommand => DefaultCommand;
|
||||
IReadOnlyList<string[]> Help.ICommandContainer.Examples => (IReadOnlyList<string[]>)Examples;
|
||||
|
||||
public CommandModel(
|
||||
@ -22,18 +22,18 @@ internal sealed class CommandModel : ICommandContainer, ICommandModel
|
||||
ApplicationName = settings.ApplicationName;
|
||||
ParsingMode = settings.ParsingMode;
|
||||
Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>());
|
||||
Examples = new List<string[]>(examples ?? Array.Empty<string[]>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the application.
|
||||
/// If the provided <paramref name="applicationName"/> is not null or empty,
|
||||
/// it is returned. Otherwise the name of the current application
|
||||
/// is determined based on the executable file's name.
|
||||
/// </summary>
|
||||
/// <param name="applicationName">The optional name of the application.</param>
|
||||
/// <returns>
|
||||
/// The name of the application, or a default value of "?" if no valid application name can be determined.
|
||||
Examples = new List<string[]>(examples ?? Array.Empty<string[]>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the application.
|
||||
/// If the provided <paramref name="applicationName"/> is not null or empty,
|
||||
/// it is returned. Otherwise the name of the current application
|
||||
/// is determined based on the executable file's name.
|
||||
/// </summary>
|
||||
/// <param name="applicationName">The optional name of the application.</param>
|
||||
/// <returns>
|
||||
/// The name of the application, or a default value of "?" if no valid application name can be determined.
|
||||
/// </returns>
|
||||
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))
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -8,13 +8,13 @@ internal interface ICommandContainer
|
||||
/// <summary>
|
||||
/// Gets all commands in the container.
|
||||
/// </summary>
|
||||
IList<CommandInfo> Commands { get; }
|
||||
|
||||
IList<CommandInfo> Commands { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default command for the container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns null if a default command has not been set.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns null if a default command has not been set.
|
||||
/// </remarks>
|
||||
CommandInfo? DefaultCommand { get; }
|
||||
}
|
@ -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<string> 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string?>());
|
||||
}
|
||||
|
||||
{
|
||||
if (!_remaining.ContainsKey(key))
|
||||
{
|
||||
_remaining.Add(key, new List<string?>());
|
||||
}
|
||||
|
||||
_remaining[key].Add(value);
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a separater was encountered immediately before the <see cref="CommandTreeToken.Value"/>.
|
||||
/// </summary>
|
||||
public bool IsGrouped { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a separater was encountered immediately before the <see cref="CommandTreeToken.Value"/>.
|
||||
/// </summary>
|
||||
public bool HadSeparator { get; set; }
|
||||
|
||||
public enum Kind
|
||||
|
@ -5,7 +5,7 @@ internal sealed class CommandTreeTokenStream : IReadOnlyList<CommandTreeToken>
|
||||
private readonly List<CommandTreeToken> _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];
|
||||
|
@ -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<CommandTreeToken> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
@ -1,153 +1,153 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Spectre.Console.Cli.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ARGUMENTS.
|
||||
/// </summary>
|
||||
internal static string Arguments {
|
||||
get {
|
||||
return ResourceManager.GetString("Arguments", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to COMMAND.
|
||||
/// </summary>
|
||||
internal static string Command {
|
||||
get {
|
||||
return ResourceManager.GetString("Command", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to COMMANDS.
|
||||
/// </summary>
|
||||
internal static string Commands {
|
||||
get {
|
||||
return ResourceManager.GetString("Commands", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DEFAULT.
|
||||
/// </summary>
|
||||
internal static string Default {
|
||||
get {
|
||||
return ResourceManager.GetString("Default", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DESCRIPTION.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to EXAMPLES.
|
||||
/// </summary>
|
||||
internal static string Examples {
|
||||
get {
|
||||
return ResourceManager.GetString("Examples", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OPTIONS.
|
||||
/// </summary>
|
||||
internal static string Options {
|
||||
get {
|
||||
return ResourceManager.GetString("Options", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Prints help information.
|
||||
/// </summary>
|
||||
internal static string PrintHelpDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PrintHelpDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Prints version information.
|
||||
/// </summary>
|
||||
internal static string PrintVersionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PrintVersionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to USAGE.
|
||||
/// </summary>
|
||||
internal static string Usage {
|
||||
get {
|
||||
return ResourceManager.GetString("Usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Spectre.Console.Cli.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ARGUMENTS.
|
||||
/// </summary>
|
||||
internal static string Arguments {
|
||||
get {
|
||||
return ResourceManager.GetString("Arguments", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to COMMAND.
|
||||
/// </summary>
|
||||
internal static string Command {
|
||||
get {
|
||||
return ResourceManager.GetString("Command", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to COMMANDS.
|
||||
/// </summary>
|
||||
internal static string Commands {
|
||||
get {
|
||||
return ResourceManager.GetString("Commands", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DEFAULT.
|
||||
/// </summary>
|
||||
internal static string Default {
|
||||
get {
|
||||
return ResourceManager.GetString("Default", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to DESCRIPTION.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to EXAMPLES.
|
||||
/// </summary>
|
||||
internal static string Examples {
|
||||
get {
|
||||
return ResourceManager.GetString("Examples", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OPTIONS.
|
||||
/// </summary>
|
||||
internal static string Options {
|
||||
get {
|
||||
return ResourceManager.GetString("Options", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Prints help information.
|
||||
/// </summary>
|
||||
internal static string PrintHelpDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PrintHelpDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Prints version information.
|
||||
/// </summary>
|
||||
internal static string PrintVersionDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("PrintVersionDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to USAGE.
|
||||
/// </summary>
|
||||
internal static string Usage {
|
||||
get {
|
||||
return ResourceManager.GetString("Usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,8 +141,8 @@ public sealed class CommandAppTester
|
||||
.Trim();
|
||||
|
||||
return new CommandAppResult(result, output, context, settings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command application asynchronously.
|
||||
/// </summary>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -185,28 +185,28 @@ public static class StringExtensions
|
||||
#else
|
||||
return text.Contains(value, StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Masks" every character in a string.
|
||||
/// </summary>
|
||||
/// <param name="value">String value to mask.</param>
|
||||
/// <param name="mask">Character to use for masking.</param>
|
||||
/// <returns>Masked string.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Masks" every character in a string.
|
||||
/// </summary>
|
||||
/// <param name="value">String value to mask.</param>
|
||||
/// <param name="mask">Character to use for masking.</param>
|
||||
/// <returns>Masked string.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console;
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents horizontal alignment.
|
||||
|
@ -286,25 +286,25 @@ public static class TextPromptExtensions
|
||||
|
||||
obj.IsSecret = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces prompt user input with mask in the console.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The prompt type.</typeparam>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="obj">The prompt.</param>
|
||||
/// <param name="mask">The masking character to use for the secret.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> Secret<T>(this TextPrompt<T> obj, char? mask)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.IsSecret = true;
|
||||
obj.Mask = mask;
|
||||
return obj;
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static TextPrompt<T> Secret<T>(this TextPrompt<T> obj, char? mask)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.IsSecret = true;
|
||||
obj.Mask = mask;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console;
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents vertical alignment.
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console;
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class TypeNameHelper
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console;
|
||||
namespace Spectre.Console;
|
||||
|
||||
[DebuggerDisplay("{Region,nq}")]
|
||||
internal sealed class LayoutRender
|
||||
|
@ -5,7 +5,7 @@ public static class SpectreAnalyzerVerifier<TAnalyzer>
|
||||
{
|
||||
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<TAnalyzer>
|
||||
{
|
||||
var test = new Test
|
||||
{
|
||||
TestCode = source,
|
||||
TestState =
|
||||
{
|
||||
OutputKind = outputKind,
|
||||
TestCode = source,
|
||||
TestState =
|
||||
{
|
||||
OutputKind = outputKind,
|
||||
},
|
||||
FixedCode = fixedSource,
|
||||
};
|
||||
|
@ -1,28 +1,28 @@
|
||||
namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettings>
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
|
||||
public AsynchronousCommand(IAnsiConsole console)
|
||||
{
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public async override Task<int> 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<AsynchronousCommandSettings>
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
|
||||
public AsynchronousCommand(IAnsiConsole console)
|
||||
{
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public async override Task<int> 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;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
using Spectre.Console;
|
||||
|
||||
public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings>
|
||||
{
|
||||
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<OptionalArgumentWithDefaultValueSettings>
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
|
||||
public GreeterCommand(IAnsiConsole console)
|
||||
{
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
|
||||
{
|
||||
_console.WriteLine(settings.Greeting);
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -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<IRenderable> 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<IRenderable> 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<IRenderable> 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<IRenderable> GetFooter(ICommandModel model, ICommandInfo command)
|
||||
{
|
||||
return new IRenderable[]
|
||||
{
|
||||
Text.NewLine,
|
||||
new Text($"Version {version}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Cli.Tests.Data.Help;
|
||||
|
||||
internal class RedirectHelpProvider : IHelpProvider
|
||||
{
|
||||
public virtual IEnumerable<IRenderable> Write(ICommandModel model)
|
||||
{
|
||||
return Write(model, null);
|
||||
}
|
||||
#nullable enable
|
||||
public virtual IEnumerable<IRenderable> 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<IRenderable> Write(ICommandModel model)
|
||||
{
|
||||
return Write(model, null);
|
||||
}
|
||||
#nullable enable
|
||||
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
|
||||
#nullable disable
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new Text("Help has moved online. Please see: http://www.example.com"),
|
||||
Text.NewLine,
|
||||
};
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console.Tests.Data;
|
||||
namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public class ReptileSettings : AnimalSettings
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class ThrowingCommandSettings : CommandSettings
|
||||
{
|
||||
}
|
||||
namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class ThrowingCommandSettings : CommandSettings
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<AsynchronousCommand>();
|
||||
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<AsynchronousCommand>();
|
||||
|
||||
// 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<AsynchronousCommand>();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await Record.ExceptionAsync(async () =>
|
||||
await app.RunAsync(new[]
|
||||
{
|
||||
"--ThrowException",
|
||||
"true",
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<Exception>().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<AsynchronousCommand>();
|
||||
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<AsynchronousCommand>();
|
||||
|
||||
// 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<AsynchronousCommand>();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await Record.ExceptionAsync(async () =>
|
||||
await app.RunAsync(new[]
|
||||
{
|
||||
"--ThrowException",
|
||||
"true",
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<Exception>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Throwing exception asynchronously");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "4",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_When_No_Default_Command_On_Branch()
|
||||
{
|
||||
// Given
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddBranch<AnimalSettings>("animal", animal => { });
|
||||
});
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() =>
|
||||
{
|
||||
app.Run(new[]
|
||||
{
|
||||
"animal", "4",
|
||||
});
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandConfigurationException>().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<CommandConfigurationException>().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<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
// 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<CatSettings>().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<CatSettings>().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<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
// 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<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Unknown option 'name'.");
|
||||
});
|
||||
}
|
||||
|
||||
"animal", "4", "--name", "Kitty",
|
||||
});
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().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<CatCommand>();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "4", "mammal",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
[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<CatCommand>();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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<CatSettings>().And(cat =>
|
||||
{
|
||||
cat.Legs.ShouldBe(4);
|
||||
cat.Name.ShouldBe("Kitty");
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>().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<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
animal.SetDefaultCommand<CatCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "4",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<CatSettings>();
|
||||
}
|
||||
|
||||
[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<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
|
||||
animal.SetDefaultCommand<LionCommand>();
|
||||
animal.SetDefaultCommand<LionCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
// 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<DogSettings>().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<DogSettings>().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()
|
||||
{
|
||||
|
@ -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<CatSettings>("cat", animal =>
|
||||
{
|
||||
animal.SetDescription("Contains settings for a cat.");
|
||||
animal.SetDescription("Contains settings for a cat.");
|
||||
animal.AddCommand<LionCommand>("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<OptionalArgumentWithDefaultValueSettings>("branch", animal =>
|
||||
{
|
||||
animal.SetDefaultCommand<GreeterCommand>();
|
||||
@ -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<DogCommand>("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<DogCommand>("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<DogCommand>("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<DogCommand>("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<DogCommand>("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<DogCommand>("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<RedirectHelpProvider>();
|
||||
|
||||
configurator.SetApplicationName("myapp");
|
||||
configurator.AddCommand<DogCommand>("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<RedirectHelpProvider>();
|
||||
|
||||
configurator.SetApplicationName("myapp");
|
||||
configurator.AddCommand<DogCommand>("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<DogCommand>("dog");
|
||||
configurator.AddCommand<HorseCommand>("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<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Children_Eight")]
|
||||
public Task Should_Output_Eight_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
|
||||
// Show the first 8 examples defined on the direct children
|
||||
configurator.Settings.MaximumIndirectExamples = 8;
|
||||
|
||||
configurator.AddCommand<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Children_Twelve")]
|
||||
public Task Should_Output_All_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
|
||||
// Show all examples defined on the direct children
|
||||
configurator.Settings.MaximumIndirectExamples = int.MaxValue;
|
||||
|
||||
configurator.AddCommand<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Children_None")]
|
||||
public Task Should_Not_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
|
||||
// Do not show examples defined on the direct children
|
||||
configurator.Settings.MaximumIndirectExamples = 0;
|
||||
|
||||
configurator.AddCommand<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "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<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Children_Twelve")]
|
||||
public Task Should_Output_All_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
|
||||
// Show all examples defined on the direct children
|
||||
configurator.Settings.MaximumIndirectExamples = int.MaxValue;
|
||||
|
||||
configurator.AddCommand<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Children_None")]
|
||||
public Task Should_Not_Output_Examples_Defined_On_Direct_Children_If_Root_Has_No_Examples()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
|
||||
// Do not show examples defined on the direct children
|
||||
configurator.Settings.MaximumIndirectExamples = 0;
|
||||
|
||||
configurator.AddCommand<DogCommand>("dog")
|
||||
.WithExample("dog", "--name", "Rufus", "--age", "12", "--good-boy")
|
||||
.WithExample("dog", "--name", "Luna")
|
||||
.WithExample("dog", "--name", "Charlie")
|
||||
.WithExample("dog", "--name", "Bella")
|
||||
.WithExample("dog", "--name", "Daisy")
|
||||
.WithExample("dog", "--name", "Milo");
|
||||
|
||||
configurator.AddCommand<HorseCommand>("horse")
|
||||
.WithExample("horse", "--name", "Brutus")
|
||||
.WithExample("horse", "--name", "Sugar", "--IsAlive", "false")
|
||||
.WithExample("horse", "--name", "Cash")
|
||||
.WithExample("horse", "--name", "Dakota")
|
||||
.WithExample("horse", "--name", "Cisco")
|
||||
.WithExample("horse", "--name", "Spirit");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Root_Examples_Leafs")]
|
||||
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Single line comment is relevant to several code blocks that follow.")]
|
||||
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<AnimalSettings>("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<DogCommand>("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<HorseCommand>("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<AnimalSettings>("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<DogCommand>("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<HorseCommand>("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<AnimalSettings>("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<DogCommand>("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<HorseCommand>("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<AnimalSettings>("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<DogCommand>("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<HorseCommand>("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<AnimalSettings>("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<DogCommand>("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");
|
||||
});
|
||||
|
||||
|
@ -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'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<DogCommand>("dog");
|
||||
});
|
||||
@ -20,29 +20,29 @@ public sealed partial class CommandAppTests
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"dog", "12", "4",
|
||||
knownFlag,
|
||||
});
|
||||
|
||||
// Then
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
|
||||
{
|
||||
dog.IsAlive.ShouldBe(true);
|
||||
});
|
||||
"dog", "12", "4",
|
||||
knownFlag,
|
||||
});
|
||||
|
||||
result.Context.Remaining.Parsed.Count.ShouldBe(0);
|
||||
// Then
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
|
||||
{
|
||||
dog.IsAlive.ShouldBe(true);
|
||||
});
|
||||
|
||||
result.Context.Remaining.Parsed.Count.ShouldBe(0);
|
||||
result.Context.Remaining.Raw.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[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<DogCommand>("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<DogCommand>("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<DogCommand>("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<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => app.Run(new[]
|
||||
{
|
||||
"dog", "12", "4",
|
||||
unknownFlag,
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe($"Unknown option '{unknownFlag.TrimStart('-')}'.");
|
||||
"dog", "12", "4",
|
||||
unknownFlag,
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().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<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => app.Run(new[]
|
||||
{
|
||||
"dog", "12", "4",
|
||||
"-agr",
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe($"Unknown option 'r'.");
|
||||
"dog", "12", "4",
|
||||
"-agr",
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().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<DogCommand>("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
|
||||
}
|
||||
}
|
||||
|
@ -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<EmptyCommand>("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<EmptyCommandSettings>("branch", branch =>
|
||||
{
|
||||
branch.SetDefaultCommand<EmptyCommand>();
|
||||
configurator.SetApplicationVersion("1.0");
|
||||
|
||||
configurator.AddBranch<EmptyCommandSettings>("branch", branch =>
|
||||
{
|
||||
branch.SetDefaultCommand<EmptyCommand>();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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<DogCommand>();
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Be_Able_To_Set_The_Default_Command()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<DogCommand>();
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"4", "12", "--good-boy", "--name", "Rufus",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().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<DogCommand>()
|
||||
.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<DogCommand>()
|
||||
.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<DogSettings>().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<DogCommand>()
|
||||
.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<DogCommand>()
|
||||
.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<DogSettings>(
|
||||
"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<DogSettings>(
|
||||
"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()
|
||||
{
|
||||
|
@ -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<CommandParseException>().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<string> args = new List<string>();
|
||||
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<CommandParseException>().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<CommandParseException>().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<CommandParseException>().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<string> args = new List<string>();
|
||||
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<CommandParseException>().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<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Option does not have a name.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console.Tests.Unit.Cli.Testing;
|
||||
namespace Spectre.Console.Tests.Unit.Cli.Testing;
|
||||
|
||||
public class FakeTypeRegistrarTests
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Spectre.Console.Tests;
|
||||
namespace Spectre.Console.Tests;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class GitHubIssueAttribute : Attribute
|
||||
|
Loading…
x
Reference in New Issue
Block a user