mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7397169a27 | ||
|
|
ffdb47d77f | ||
|
|
383bee0e3e | ||
|
|
4d6541dd14 | ||
|
|
b1e0896a0d | ||
|
|
e07ccd9f66 | ||
|
|
250b1f4c9c | ||
|
|
023c77ff09 | ||
|
|
dc402220f2 | ||
|
|
c448d0d5f6 | ||
|
|
3da367f29f | ||
|
|
ead7115cbe | ||
|
|
1fd028942f | ||
|
|
4219bbbf61 | ||
|
|
29a43686d4 | ||
|
|
63b940cf0e | ||
|
|
bbf58ee814 | ||
|
|
e2a674815d | ||
|
|
343b98944d | ||
|
|
2bbb7c1ab6 | ||
|
|
5296e56b1c | ||
|
|
943c045fab | ||
|
|
2af3f7faeb | ||
|
|
ed9e198d60 | ||
|
|
3bee7212b7 | ||
|
|
c82d8c4523 | ||
|
|
bef21e8a21 | ||
|
|
037e109699 | ||
|
|
f7befacd79 | ||
|
|
cec5fb4595 | ||
|
|
9c86391fb6 | ||
|
|
a3dcb31729 | ||
|
|
1002c6fe27 | ||
|
|
c64797d681 | ||
|
|
131b37fff8 | ||
|
|
813a53cdfa | ||
|
|
2af901a814 | ||
|
|
83afa97017 | ||
|
|
e0ded712e8 | ||
|
|
d484e832f5 | ||
|
|
71a5d83067 | ||
|
|
35ce60b596 | ||
|
|
0ec70a44db | ||
|
|
1ee2653cf8 | ||
|
|
4c0178cf9a | ||
|
|
296bc61837 | ||
|
|
766ccb1f1b | ||
|
|
f7314dc2e8 | ||
|
|
f453890d13 | ||
|
|
2c8f459806 |
@@ -2,7 +2,7 @@ root = true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = CRLF
|
end_of_line = LF
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
@@ -26,9 +26,6 @@ indent_size = 2
|
|||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.sh]
|
|
||||||
end_of_line = lf
|
|
||||||
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
# Prefer file scoped namespace declarations
|
# Prefer file scoped namespace declarations
|
||||||
csharp_style_namespace_declarations = file_scoped:warning
|
csharp_style_namespace_declarations = file_scoped:warning
|
||||||
|
|||||||
19
.github/pull_request_template.md
vendored
Normal file
19
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!--
|
||||||
|
Do NOT open a PR without discussing the changes on an open issue, first.
|
||||||
|
|
||||||
|
Add the issue number here. e.g. #123
|
||||||
|
-->
|
||||||
|
fixes #
|
||||||
|
|
||||||
|
<!-- formalities. These are not optional. -->
|
||||||
|
|
||||||
|
- [ ] I have read the [Contribution Guidelines](../CONTRIBUTING.md)
|
||||||
|
- [ ] I have commented on the issue above and discussed the intended changes
|
||||||
|
- [ ] A maintainer has signed off on the changes and the issue was assigned to me
|
||||||
|
- [ ] All newly added code is adequately covered by tests
|
||||||
|
- [ ] All existing tests are still running without errors
|
||||||
|
- [ ] The documentation was modified to reflect the changes _OR_ no documentation changes are required.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
<!-- describe the changes you made. -->
|
||||||
5
.github/workflows/ci.yaml
vendored
5
.github/workflows/ci.yaml
vendored
@@ -76,6 +76,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: |
|
||||||
|
6.0.x
|
||||||
|
7.0.x
|
||||||
|
8.0.x
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
5
.github/workflows/publish.yaml
vendored
5
.github/workflows/publish.yaml
vendored
@@ -107,6 +107,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: |
|
||||||
|
6.0.x
|
||||||
|
7.0.x
|
||||||
|
8.0.x
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
60
build.cake
60
build.cake
@@ -14,8 +14,21 @@ Task("Build")
|
|||||||
.IsDependentOn("Clean")
|
.IsDependentOn("Clean")
|
||||||
.Does(context =>
|
.Does(context =>
|
||||||
{
|
{
|
||||||
|
Information("Compiling generator...");
|
||||||
|
DotNetBuild("./resources/scripts/Generator/Generator.sln", new DotNetBuildSettings {
|
||||||
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
|
NoIncremental = context.HasArgument("rebuild"),
|
||||||
|
MSBuildSettings = new DotNetMSBuildSettings()
|
||||||
|
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||||
|
});
|
||||||
|
|
||||||
|
Information("\nCompiling Spectre.Console...");
|
||||||
DotNetBuild("./src/Spectre.Console.sln", new DotNetBuildSettings {
|
DotNetBuild("./src/Spectre.Console.sln", new DotNetBuildSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoIncremental = context.HasArgument("rebuild"),
|
NoIncremental = context.HasArgument("rebuild"),
|
||||||
MSBuildSettings = new DotNetMSBuildSettings()
|
MSBuildSettings = new DotNetMSBuildSettings()
|
||||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||||
@@ -28,6 +41,8 @@ Task("Build-Analyzer")
|
|||||||
{
|
{
|
||||||
DotNetBuild("./src/Spectre.Console.Analyzer.sln", new DotNetBuildSettings {
|
DotNetBuild("./src/Spectre.Console.Analyzer.sln", new DotNetBuildSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoIncremental = context.HasArgument("rebuild"),
|
NoIncremental = context.HasArgument("rebuild"),
|
||||||
MSBuildSettings = new DotNetMSBuildSettings()
|
MSBuildSettings = new DotNetMSBuildSettings()
|
||||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||||
@@ -40,6 +55,8 @@ Task("Build-Examples")
|
|||||||
{
|
{
|
||||||
DotNetBuild("./examples/Examples.sln", new DotNetBuildSettings {
|
DotNetBuild("./examples/Examples.sln", new DotNetBuildSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoIncremental = context.HasArgument("rebuild"),
|
NoIncremental = context.HasArgument("rebuild"),
|
||||||
MSBuildSettings = new DotNetMSBuildSettings()
|
MSBuildSettings = new DotNetMSBuildSettings()
|
||||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||||
@@ -54,18 +71,24 @@ Task("Test")
|
|||||||
{
|
{
|
||||||
DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
|
DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoRestore = true,
|
NoRestore = true,
|
||||||
NoBuild = true,
|
NoBuild = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
|
DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoRestore = true,
|
NoRestore = true,
|
||||||
NoBuild = true,
|
NoBuild = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
|
DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoRestore = true,
|
NoRestore = true,
|
||||||
NoBuild = true,
|
NoBuild = true,
|
||||||
});
|
});
|
||||||
@@ -77,6 +100,8 @@ Task("Package")
|
|||||||
{
|
{
|
||||||
context.DotNetPack($"./src/Spectre.Console.sln", new DotNetPackSettings {
|
context.DotNetPack($"./src/Spectre.Console.sln", new DotNetPackSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoRestore = true,
|
NoRestore = true,
|
||||||
NoBuild = true,
|
NoBuild = true,
|
||||||
OutputDirectory = "./.artifacts",
|
OutputDirectory = "./.artifacts",
|
||||||
@@ -86,6 +111,8 @@ Task("Package")
|
|||||||
|
|
||||||
context.DotNetPack($"./src/Spectre.Console.Analyzer.sln", new DotNetPackSettings {
|
context.DotNetPack($"./src/Spectre.Console.Analyzer.sln", new DotNetPackSettings {
|
||||||
Configuration = configuration,
|
Configuration = configuration,
|
||||||
|
Verbosity = DotNetVerbosity.Minimal,
|
||||||
|
NoLogo = true,
|
||||||
NoRestore = true,
|
NoRestore = true,
|
||||||
NoBuild = true,
|
NoBuild = true,
|
||||||
OutputDirectory = "./.artifacts",
|
OutputDirectory = "./.artifacts",
|
||||||
@@ -94,38 +121,6 @@ Task("Package")
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("Publish-GitHub")
|
|
||||||
.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
|
||||||
.IsDependentOn("Package")
|
|
||||||
.Does(context =>
|
|
||||||
{
|
|
||||||
var apiKey = Argument<string>("github-key", null);
|
|
||||||
if(string.IsNullOrWhiteSpace(apiKey)) {
|
|
||||||
throw new CakeException("No GitHub API key was provided.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish to GitHub Packages
|
|
||||||
var exitCode = 0;
|
|
||||||
foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
|
|
||||||
{
|
|
||||||
context.Information("Publishing {0}...", file.GetFilename().FullPath);
|
|
||||||
exitCode += StartProcess("dotnet",
|
|
||||||
new ProcessSettings {
|
|
||||||
Arguments = new ProcessArgumentBuilder()
|
|
||||||
.Append("gpr")
|
|
||||||
.Append("push")
|
|
||||||
.AppendQuoted(file.FullPath)
|
|
||||||
.AppendSwitchSecret("-k", " ", apiKey)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(exitCode != 0)
|
|
||||||
{
|
|
||||||
throw new CakeException("Could not push GitHub packages.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Task("Publish-NuGet")
|
Task("Publish-NuGet")
|
||||||
.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
||||||
.IsDependentOn("Package")
|
.IsDependentOn("Package")
|
||||||
@@ -152,7 +147,6 @@ Task("Publish-NuGet")
|
|||||||
// Targets
|
// Targets
|
||||||
|
|
||||||
Task("Publish")
|
Task("Publish")
|
||||||
.IsDependentOn("Publish-GitHub")
|
|
||||||
.IsDependentOn("Publish-NuGet");
|
.IsDependentOn("Publish-NuGet");
|
||||||
|
|
||||||
Task("Default")
|
Task("Default")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes>
|
<DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes>
|
||||||
<NoWarn>MVC1000</NoWarn>
|
<NoWarn>MVC1000</NoWarn>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "7.0.100",
|
"version": "8.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{"version": 2, "width": 122, "height": 5, "title": "breakdown-chart (plain)", "env": {"TERM": "Spectre.Console"}}
|
{"version": 2, "width": 84, "height": 5, "title": "breakdown-chart (plain)", "env": {"TERM": "Spectre.Console"}}
|
||||||
[0, "o", "\u001B[31m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[34m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[32m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[37m\u2588\u2588\u001B[0m\u001B[37m\u2588\u2588\u001B[0m\r\n\u001B[31m\u25A0\u001B[0m SCSS \u001B[37m80\u001B[0m \u001B[34m\u25A0\u001B[0m HTML \u001B[37m28.3\u001B[0m \u001B[32m\u25A0\u001B[0m C# \u001B[37m22.6\u001B[0m \u001B[37m\u25A0\u001B[0m JavaScript \u001B[37m6\u001B[0m \r\n\u001B[37m\u25A0\u001B[0m Ruby \u001B[37m6\u001B[0m \u001B[36m\u25A0\u001B[0m Shell \u001B[37m0.1\u001B[0m \r\n"]
|
[0, "o", "\u001B[31m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[34m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[32m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[37m\u2588\u2588\u001B[0m\u001B[37m\u2588\u2588\u001B[0m\r\n\u001B[31m\u25A0\u001B[0m SCSS \u001B[37m80\u001B[0m \u001B[34m\u25A0\u001B[0m HTML \u001B[37m28.3\u001B[0m \u001B[32m\u25A0\u001B[0m C# \u001B[37m22.6\u001B[0m \u001B[37m\u25A0\u001B[0m JavaScript \u001B[37m6\u001B[0m \r\n\u001B[37m\u25A0\u001B[0m Ruby \u001B[37m6\u001B[0m \u001B[36m\u25A0\u001B[0m Shell \u001B[37m0.1\u001B[0m \r\n"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{"version": 2, "width": 122, "height": 5, "title": "breakdown-chart (rich)", "env": {"TERM": "Spectre.Console"}}
|
{"version": 2, "width": 84, "height": 5, "title": "breakdown-chart (rich)", "env": {"TERM": "Spectre.Console"}}
|
||||||
[0, "o", "\u001B[38;5;9m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;12m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;2m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;11m\u2588\u2588\u001B[0m\u001B[38;5;119m\u2588\u2588\u001B[0m\r\n\u001B[38;5;9m\u25A0\u001B[0m SCSS \u001B[38;5;8m80\u001B[0m \u001B[38;5;12m\u25A0\u001B[0m HTML \u001B[38;5;8m28.3\u001B[0m \u001B[38;5;2m\u25A0\u001B[0m C# \u001B[38;5;8m22.6\u001B[0m \u001B[38;5;11m\u25A0\u001B[0m JavaScript \u001B[38;5;8m6\u001B[0m \r\n\u001B[38;5;119m\u25A0\u001B[0m Ruby \u001B[38;5;8m6\u001B[0m \u001B[38;5;14m\u25A0\u001B[0m Shell \u001B[38;5;8m0.1\u001B[0m \r\n"]
|
[0, "o", "\u001B[38;5;9m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;12m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;2m\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u001B[0m\u001B[38;5;11m\u2588\u2588\u001B[0m\u001B[38;5;119m\u2588\u2588\u001B[0m\r\n\u001B[38;5;9m\u25A0\u001B[0m SCSS \u001B[38;5;8m80\u001B[0m \u001B[38;5;12m\u25A0\u001B[0m HTML \u001B[38;5;8m28.3\u001B[0m \u001B[38;5;2m\u25A0\u001B[0m C# \u001B[38;5;8m22.6\u001B[0m \u001B[38;5;11m\u25A0\u001B[0m JavaScript \u001B[38;5;8m6\u001B[0m \r\n\u001B[38;5;119m\u25A0\u001B[0m Ruby \u001B[38;5;8m6\u001B[0m \u001B[38;5;14m\u25A0\u001B[0m Shell \u001B[38;5;8m0.1\u001B[0m \r\n"]
|
||||||
|
|
||||||
|
|||||||
43
docs/input/assets/casts/columns-plain.cast
Normal file
43
docs/input/assets/casts/columns-plain.cast
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{"version": 2, "width": 84, "height": 24, "title": "columns (plain)", "env": {"TERM": "Spectre.Console"}}
|
||||||
|
[0, "o", "\u001B[?25l"]
|
||||||
|
[0.094, "o", "\u001B[1;37mApple\u001B[0m "]
|
||||||
|
[0.297, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m "]
|
||||||
|
[0.5, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m "]
|
||||||
|
[0.719, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m "]
|
||||||
|
[0.922, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m "]
|
||||||
|
[1.125, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m "]
|
||||||
|
[1.344, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m "]
|
||||||
|
[1.563, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m"]
|
||||||
|
[1.766, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m"]
|
||||||
|
[1.969, "o", "\r\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m\r\n\u001B[1;37mCherry\u001B[0m "]
|
||||||
|
[2.172, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m\r\n\u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m "]
|
||||||
|
[2.375, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m\r\n\u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m "]
|
||||||
|
[2.594, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m\r\n\u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m "]
|
||||||
|
[2.797, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m\r\n\u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m "]
|
||||||
|
[3, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m\r\n\u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \u001B[1;37mGrape\u001B[0m "]
|
||||||
|
[3.219, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m\r\n\u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m"]
|
||||||
|
[3.422, "o", "\r\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m \u001B[1;37mBreadfruit\u001B[0m\r\n\u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m\r\n\u001B[1;37mJackfruit\u001B[0m "]
|
||||||
|
[3.625, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m "]
|
||||||
|
[3.828, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m "]
|
||||||
|
[4.047, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m "]
|
||||||
|
[4.25, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m "]
|
||||||
|
[4.453, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m "]
|
||||||
|
[4.672, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m "]
|
||||||
|
[4.875, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m "]
|
||||||
|
[5.094, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m "]
|
||||||
|
[5.297, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m "]
|
||||||
|
[5.516, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m "]
|
||||||
|
[5.734, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m "]
|
||||||
|
[5.953, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m "]
|
||||||
|
[6.172, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m "]
|
||||||
|
[6.375, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m "]
|
||||||
|
[6.594, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m "]
|
||||||
|
[6.797, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m "]
|
||||||
|
[7.016, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m \u001B[1;37mSalmonberry\u001B[0m "]
|
||||||
|
[7.234, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m \u001B[1;37mSalmonberry\u001B[0m \u001B[1;37mStrawberry\u001B[0m "]
|
||||||
|
[7.438, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m \u001B[1;37mSalmonberry\u001B[0m \u001B[1;37mStrawberry\u001B[0m \r\n\u001B[1;37mXimenia\u001B[0m "]
|
||||||
|
[7.641, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m \u001B[1;37mSalmonberry\u001B[0m \u001B[1;37mStrawberry\u001B[0m \r\n\u001B[1;37mXimenia\u001B[0m "]
|
||||||
|
[7.641, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K"]
|
||||||
|
[7.641, "o", "\u001B[?25h"]
|
||||||
|
[7.656, "o", "\u001B[1;37mApple\u001B[0m \u001B[1;37mApricot\u001B[0m \u001B[1;37mAvocado\u001B[0m \u001B[1;37mBanana\u001B[0m \u001B[1;37mBlackberry\u001B[0m \u001B[1;37mBlueberry\u001B[0m \u001B[1;37mBoysenberry\u001B[0m\r\n\u001B[1;37mBreadfruit\u001B[0m \u001B[1;37mCacao\u001B[0m \u001B[1;37mCherry\u001B[0m \u001B[1;37mCloudberry\u001B[0m \u001B[1;37mCoconut\u001B[0m \u001B[1;37mDragonfruit\u001B[0m \u001B[1;37mElderberry\u001B[0m \r\n\u001B[1;37mGrape\u001B[0m \u001B[1;37mGrapefruit\u001B[0m \u001B[1;37mJackfruit\u001B[0m \u001B[1;37mKiwifruit\u001B[0m \u001B[1;37mLemon\u001B[0m \u001B[1;37mLime\u001B[0m \u001B[1;37mMango\u001B[0m \r\n\u001B[1;37mMelon\u001B[0m \u001B[1;37mOrange\u001B[0m \u001B[1;37mBlood orange\u001B[0m \u001B[1;37mClementine\u001B[0m \u001B[1;37mMandarine\u001B[0m \u001B[1;37mTangerine\u001B[0m \u001B[1;37mPapaya\u001B[0m \r\n\u001B[1;37mPassionfruit\u001B[0m \u001B[1;37mPlum\u001B[0m \u001B[1;37mPineapple\u001B[0m \u001B[1;37mPomelo\u001B[0m \u001B[1;37mRaspberry\u001B[0m \u001B[1;37mSalmonberry\u001B[0m \u001B[1;37mStrawberry\u001B[0m \r\n\u001B[1;37mXimenia\u001B[0m \u001B[1;37mYuzu\u001B[0m \r\n"]
|
||||||
|
|
||||||
43
docs/input/assets/casts/columns-rich.cast
Normal file
43
docs/input/assets/casts/columns-rich.cast
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{"version": 2, "width": 84, "height": 24, "title": "columns (rich)", "env": {"TERM": "Spectre.Console"}}
|
||||||
|
[0, "o", "\u001B[?25l"]
|
||||||
|
[0, "o", "\u001B[1;38;5;11mApple\u001B[0m "]
|
||||||
|
[0.219, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m "]
|
||||||
|
[0.422, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m "]
|
||||||
|
[0.625, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m "]
|
||||||
|
[0.828, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m "]
|
||||||
|
[1.032, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m "]
|
||||||
|
[1.235, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m "]
|
||||||
|
[1.438, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m"]
|
||||||
|
[1.641, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m"]
|
||||||
|
[1.844, "o", "\r\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m\r\n\u001B[1;38;5;11mCherry\u001B[0m "]
|
||||||
|
[2.047, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m\r\n\u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m "]
|
||||||
|
[2.266, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m\r\n\u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m "]
|
||||||
|
[2.485, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m\r\n\u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m "]
|
||||||
|
[2.703, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m\r\n\u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m "]
|
||||||
|
[2.907, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m\r\n\u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \u001B[1;38;5;11mGrape\u001B[0m "]
|
||||||
|
[3.11, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m\r\n\u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m"]
|
||||||
|
[3.313, "o", "\r\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m \u001B[1;38;5;11mBreadfruit\u001B[0m\r\n\u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m\r\n\u001B[1;38;5;11mJackfruit\u001B[0m "]
|
||||||
|
[3.532, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m "]
|
||||||
|
[3.735, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m "]
|
||||||
|
[3.953, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m "]
|
||||||
|
[4.157, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m "]
|
||||||
|
[4.36, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m "]
|
||||||
|
[4.578, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m "]
|
||||||
|
[4.782, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m "]
|
||||||
|
[4.985, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m "]
|
||||||
|
[5.188, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m "]
|
||||||
|
[5.407, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m "]
|
||||||
|
[5.61, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m "]
|
||||||
|
[5.813, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m "]
|
||||||
|
[6.016, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m "]
|
||||||
|
[6.219, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m "]
|
||||||
|
[6.438, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m "]
|
||||||
|
[6.657, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m "]
|
||||||
|
[6.86, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m \u001B[1;38;5;11mSalmonberry\u001B[0m "]
|
||||||
|
[7.063, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m \u001B[1;38;5;11mSalmonberry\u001B[0m \u001B[1;38;5;11mStrawberry\u001B[0m "]
|
||||||
|
[7.266, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m \u001B[1;38;5;11mSalmonberry\u001B[0m \u001B[1;38;5;11mStrawberry\u001B[0m \r\n\u001B[1;38;5;11mXimenia\u001B[0m "]
|
||||||
|
[7.485, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m \u001B[1;38;5;11mSalmonberry\u001B[0m \u001B[1;38;5;11mStrawberry\u001B[0m \r\n\u001B[1;38;5;11mXimenia\u001B[0m "]
|
||||||
|
[7.485, "o", "\r\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K\u001B[1A\u001B[2K"]
|
||||||
|
[7.485, "o", "\u001B[?25h"]
|
||||||
|
[7.485, "o", "\u001B[1;38;5;11mApple\u001B[0m \u001B[1;38;5;11mApricot\u001B[0m \u001B[1;38;5;11mAvocado\u001B[0m \u001B[1;38;5;11mBanana\u001B[0m \u001B[1;38;5;11mBlackberry\u001B[0m \u001B[1;38;5;11mBlueberry\u001B[0m \u001B[1;38;5;11mBoysenberry\u001B[0m\r\n\u001B[1;38;5;11mBreadfruit\u001B[0m \u001B[1;38;5;11mCacao\u001B[0m \u001B[1;38;5;11mCherry\u001B[0m \u001B[1;38;5;11mCloudberry\u001B[0m \u001B[1;38;5;11mCoconut\u001B[0m \u001B[1;38;5;11mDragonfruit\u001B[0m \u001B[1;38;5;11mElderberry\u001B[0m \r\n\u001B[1;38;5;11mGrape\u001B[0m \u001B[1;38;5;11mGrapefruit\u001B[0m \u001B[1;38;5;11mJackfruit\u001B[0m \u001B[1;38;5;11mKiwifruit\u001B[0m \u001B[1;38;5;11mLemon\u001B[0m \u001B[1;38;5;11mLime\u001B[0m \u001B[1;38;5;11mMango\u001B[0m \r\n\u001B[1;38;5;11mMelon\u001B[0m \u001B[1;38;5;11mOrange\u001B[0m \u001B[1;38;5;11mBlood orange\u001B[0m \u001B[1;38;5;11mClementine\u001B[0m \u001B[1;38;5;11mMandarine\u001B[0m \u001B[1;38;5;11mTangerine\u001B[0m \u001B[1;38;5;11mPapaya\u001B[0m \r\n\u001B[1;38;5;11mPassionfruit\u001B[0m \u001B[1;38;5;11mPlum\u001B[0m \u001B[1;38;5;11mPineapple\u001B[0m \u001B[1;38;5;11mPomelo\u001B[0m \u001B[1;38;5;11mRaspberry\u001B[0m \u001B[1;38;5;11mSalmonberry\u001B[0m \u001B[1;38;5;11mStrawberry\u001B[0m \r\n\u001B[1;38;5;11mXimenia\u001B[0m \u001B[1;38;5;11mYuzu\u001B[0m \r\n"]
|
||||||
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{"version": 2, "width": 122, "height": 24, "title": "panel (plain)", "env": {"TERM": "Spectre.Console"}}
|
{"version": 2, "width": 84, "height": 24, "title": "panel (plain)", "env": {"TERM": "Spectre.Console"}}
|
||||||
[0, "o", "\u2554\u2550\u2550\u001B[4mPasta Menu\u001B[0m\u2550\u2550\u2557\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u2551 \u001B[31mSpaghetti\u001B[0m \u2551\r\n\u2551 \u001B[31mLinguini\u001B[0m \u2551\r\n\u2551 \u001B[31mFettucine\u001B[0m \u2551\r\n\u2551 \u001B[31mTortellini\u001B[0m \u2551\r\n\u2551 \u001B[31mCapellini\u001B[0m \u2551\r\n\u2551 \u001B[31mLasagna\u001B[0m \u2551\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\r\n"]
|
[0, "o", "\u2554\u2550\u2550\u001B[4mPasta Menu\u001B[0m\u2550\u2550\u2557\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u2551 \u001B[31mSpaghetti\u001B[0m \u2551\r\n\u2551 \u001B[31mLinguini\u001B[0m \u2551\r\n\u2551 \u001B[31mFettucine\u001B[0m \u2551\r\n\u2551 \u001B[31mTortellini\u001B[0m \u2551\r\n\u2551 \u001B[31mCapellini\u001B[0m \u2551\r\n\u2551 \u001B[31mLasagna\u001B[0m \u2551\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\r\n"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{"version": 2, "width": 122, "height": 24, "title": "panel (rich)", "env": {"TERM": "Spectre.Console"}}
|
{"version": 2, "width": 84, "height": 24, "title": "panel (rich)", "env": {"TERM": "Spectre.Console"}}
|
||||||
[0, "o", "\u2554\u2550\u2550\u001B[4mPasta Menu\u001B[0m\u2550\u2550\u2557\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u2551 \u001B[38;5;9mSpaghetti\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mLinguini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mFettucine\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mTortellini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mCapellini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mLasagna\u001B[0m \u2551\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\r\n"]
|
[0, "o", "\u2554\u2550\u2550\u001B[4mPasta Menu\u001B[0m\u2550\u2550\u2557\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u2551 \u001B[38;5;9mSpaghetti\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mLinguini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mFettucine\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mTortellini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mCapellini\u001B[0m \u2551\r\n\u2551 \u001B[38;5;9mLasagna\u001B[0m \u2551\r\n\u2551 \u2551\r\n\u2551 \u2551\r\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\r\n"]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
Title: Spectre.Console 0.47 released!
|
||||||
|
Description: Alacritty terminal support, command line improvements
|
||||||
|
Published: 2023-05-19
|
||||||
|
Category: Release Notes
|
||||||
|
Excluded: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Version 0.47 of Spectre.Console has been released!
|
||||||
|
|
||||||
|
There are a lot of fixes and improvements in this release, the most noteworthy changes being support for the [Alacritty](https://github.com/alacritty/alacritty) terminal and continued improvements to command line parsing.
|
||||||
|
|
||||||
|
Thank you to all contributers.
|
||||||
|
|
||||||
|
## New Contributors
|
||||||
|
* [@wbaldoumas](https://github.com/wbaldoumas) made their first contribution in [#1143](https://github.com/spectreconsole/spectre.console/pull/1143)
|
||||||
|
* [@MartinZikmund](https://github.com/MartinZikmund) made their first contribution in [#1151](https://github.com/spectreconsole/spectre.console/pull/1151)
|
||||||
|
* [@ilyahryapko](https://github.com/ilyahryapko) made their first contribution in [#1131](https://github.com/spectreconsole/spectre.console/pull/1131)
|
||||||
|
* [@meziantou](https://github.com/meziantou) made their first contribution in [#1174](https://github.com/spectreconsole/spectre.console/pull/1174)
|
||||||
|
* [@MaxAtoms](https://github.com/MaxAtoms) made their first contribution in [#1211](https://github.com/spectreconsole/spectre.console/pull/1211)
|
||||||
|
* [@phillip-haydon](https://github.com/phillip-haydon) made their first contribution in [#1218](https://github.com/spectreconsole/spectre.console/pull/1218)
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
* Add Alacritty to the supported terminals in AnsiDetector by [@MaxAtoms](https://github.com/MaxAtoms) in [#1211](https://github.com/spectreconsole/spectre.console/pull/1211)
|
||||||
|
* Add an implicit operator to convert from Color to Style by [@0xced](https://github.com/0xced) in [#1160](https://github.com/spectreconsole/spectre.console/pull/1160)
|
||||||
|
* Allow case-insensitive confirmation prompt by [@MartinZikmund](https://github.com/MartinZikmund) in [#1151](https://github.com/spectreconsole/spectre.console/pull/1151)
|
||||||
|
* Allow configuration of confirmation prompt comparison via `StringComparer` by [@MartinZikmund](https://github.com/MartinZikmund) in [#1161](https://github.com/spectreconsole/spectre.console/pull/1161)
|
||||||
|
* Do not register analyzer if SpectreConsole is not available in the current compilation by [@meziantou](https://github.com/meziantou) in [#1172](https://github.com/spectreconsole/spectre.console/pull/1172)
|
||||||
|
* Ensure correct comparer is used for `TextPrompt` by [@MartinZikmund](https://github.com/MartinZikmund) in [#1152](https://github.com/spectreconsole/spectre.console/pull/1152)
|
||||||
|
* Forward CancellationToken to GetOperation by [@meziantou](https://github.com/meziantou) in [#1173](https://github.com/spectreconsole/spectre.console/pull/1173)
|
||||||
|
* Fix minor typo in Prompt example by [@Frassle](https://github.com/Frassle) in [#1183](https://github.com/spectreconsole/spectre.console/pull/1183)
|
||||||
|
* Fix coconut spelling by [@phillip-haydon](https://github.com/phillip-haydon) in [#1218](https://github.com/spectreconsole/spectre.console/pull/1218)
|
||||||
|
* Improve conversion error messages by [@0xced](https://github.com/0xced) in [#1141](https://github.com/spectreconsole/spectre.console/pull/1141)
|
||||||
|
* Make the code fix more robust and detect more symbols of type IAnsiConsole by [@meziantou](https://github.com/meziantou) in [#1169](https://github.com/spectreconsole/spectre.console/pull/1169)
|
||||||
|
* Minor Refactorings by [@Elisha-Aguilera](https://github.com/Elisha-Aguilera) in [#1081](https://github.com/spectreconsole/spectre.console/pull/1081)
|
||||||
|
* Simplify access to the SemanticModel in analyzers by [@meziantou](https://github.com/meziantou) in [#1167](https://github.com/spectreconsole/spectre.console/pull/1167)
|
||||||
|
* Use SymbolEqualityComparer.Default when possible by [@meziantou](https://github.com/meziantou) in [#1171](https://github.com/spectreconsole/spectre.console/pull/1171)
|
||||||
|
* Use StringComparison.Ordinal instead of culture-sensitive comparisons by [@meziantou](https://github.com/meziantou) in [#1174](https://github.com/spectreconsole/spectre.console/pull/1174)
|
||||||
|
|
||||||
|
## Command line updates
|
||||||
|
* Add possibility to set description and/or data for the default command by [@0xced](https://github.com/0xced) in [#1091](https://github.com/spectreconsole/spectre.console/pull/1091)
|
||||||
|
* Add support for converting command parameters into FileInfo and DirectoryInfo by [@0xced](https://github.com/0xced) in [#1145](https://github.com/spectreconsole/spectre.console/pull/1145)
|
||||||
|
* Add support for arrays in \[DefaultValue\] attributes by [@0xced](https://github.com/0xced) in [#1164](https://github.com/spectreconsole/spectre.console/pull/1164)
|
||||||
|
* Add ability to pass example args using `params` syntax by [@seclerp](https://github.com/seclerp) in [#1166](https://github.com/spectreconsole/spectre.console/pull/1166)
|
||||||
|
* Alias for branches by [@ilyahryapko](https://github.com/ilyahryapko) in [#1131](https://github.com/spectreconsole/spectre.console/pull/1131)
|
||||||
|
* Command line improvements by [@FrankRay78](https://github.com/FrankRay78) in [#1103](https://github.com/spectreconsole/spectre.console/pull/1103)
|
||||||
|
|
||||||
|
## Documentation updates
|
||||||
|
* Alignment => Justification Docs Fixes by [@wbaldoumas](https://github.com/wbaldoumas) in [#1143](https://github.com/spectreconsole/spectre.console/pull/1143)
|
||||||
47
docs/input/cli/command-help.md
Normal file
47
docs/input/cli/command-help.md
Normal file
@@ -0,0 +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.
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ For more complex command hierarchical configurations, they can also be composed
|
|||||||
|
|
||||||
## Customizing Command Configurations
|
## Customizing Command Configurations
|
||||||
|
|
||||||
The `Configure` method is also used to change how help for the commands is generated. This configuration will give our command an additional alias of `file-size` and a description to be used when displaying the help. Additional, an example is specified that will be parsed and displayed for users asking for help. Multiple examples can be provided. Commands can also be marked as hidden. With this option they are still executable, but will not be displayed in help screens.
|
The `Configure` method is also used to change how help for the commands is generated. This configuration will give our command an additional alias of `file-size` and a description to be used when displaying the help. Additionally, an example is specified that will be parsed and displayed for users asking for help. Multiple examples can be provided. Commands can also be marked as hidden. With this option they are still executable, but will not be displayed in help screens.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
var app = new CommandApp();
|
var app = new CommandApp();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Order: 12
|
|||||||
Description: "Handling exceptions in *Spectre.Console.Cli*"
|
Description: "Handling exceptions in *Spectre.Console.Cli*"
|
||||||
---
|
---
|
||||||
|
|
||||||
Exceptions happen.
|
Exceptions happen.
|
||||||
|
|
||||||
`Spectre.Console.Cli` handles exceptions, writes a user friendly message to the console and sets the exitCode
|
`Spectre.Console.Cli` handles exceptions, writes a user friendly message to the console and sets the exitCode
|
||||||
of the application to `-1`.
|
of the application to `-1`.
|
||||||
@@ -49,11 +49,11 @@ namespace MyApp
|
|||||||
|
|
||||||
## Using a custom ExceptionHandler
|
## Using a custom ExceptionHandler
|
||||||
|
|
||||||
Using the `SetErrorHandler()` during configuration it is possible to handle exceptions in a defined way.
|
Using the `SetExceptionHandler()` during configuration it is possible to handle exceptions in a defined way.
|
||||||
This method comes in two flavours: One that uses the default exitCode (or `return` value) of `-1` and one
|
This method comes in two flavours: One that uses the default exitCode (or `return` value) of `-1` and one
|
||||||
where the exitCode needs to be supplied.
|
where the exitCode needs to be supplied.
|
||||||
|
|
||||||
### Using `SetErrorHandler(Func<Exception, int> handler)`
|
### Using `SetExceptionHandler(Func<Exception, int> handler)`
|
||||||
|
|
||||||
Using this method exceptions can be handled in a custom way. The return value of the handler is used as
|
Using this method exceptions can be handled in a custom way. The return value of the handler is used as
|
||||||
the exitCode for the application.
|
the exitCode for the application.
|
||||||
@@ -84,9 +84,9 @@ namespace MyApp
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using `SetErrorHandler(Action<Exception> handler)`
|
### Using `SetExceptionHandler(Action<Exception> handler)`
|
||||||
|
|
||||||
Using this method exceptions can be handled in a custom way, much the same as with the `SetErrorHandler(Func<Exception, int> handler)`.
|
Using this method exceptions can be handled in a custom way, much the same as with the `SetExceptionHandler(Func<Exception, int> handler)`.
|
||||||
Using the `Action` as the handler however, it is not possible (or required) to supply a return value.
|
Using the `Action` as the handler however, it is not possible (or required) to supply a return value.
|
||||||
The exitCode for the application will be `-1`.
|
The exitCode for the application will be `-1`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Title: Specifying Settings
|
Title: Specifying Settings
|
||||||
Order: 5
|
Order: 5
|
||||||
Description: "How to define command line argument settings for your *Spectre.Console.Cli* Commands"
|
Description: "How to define command line argument settings for your *Spectre.Console.Cli* Commands"
|
||||||
Reference:
|
Reference:
|
||||||
- T:Spectre.Console.Cli.CommandSettings
|
- T:Spectre.Console.Cli.CommandSettings
|
||||||
- T:Spectre.Console.Cli.CommandArgumentAttribute
|
- T:Spectre.Console.Cli.CommandArgumentAttribute
|
||||||
- T:Spectre.Console.Cli.CommandOptionAttribute
|
- T:Spectre.Console.Cli.CommandOptionAttribute
|
||||||
@@ -26,7 +26,7 @@ This setting file tells `Spectre.Console.Cli` that our command has two parameter
|
|||||||
|
|
||||||
## CommandArgument
|
## CommandArgument
|
||||||
|
|
||||||
Arguments have a position and a name. The name is not only used for generating help, but its formatting is used to determine whether or not the argument is optional. The name must either be surrounded by square brackets (e.g. `[name]`) or angle brackets (e.g. `<name>`). Angle brackets denote required whereas square brackets denote optional. If neither are specified an exception will be thrown.
|
Arguments have a position and a name. The name is not only used for generating help, but its formatting is used to determine whether or not the argument is optional. Angle brackets denote a required argument (e.g. `<name>`) whereas square brackets denote an optional argument (e.g. `[name]`). If neither are specified an exception will be thrown.
|
||||||
|
|
||||||
The position is used for scenarios where there could be more than one argument.
|
The position is used for scenarios where there could be more than one argument.
|
||||||
|
|
||||||
@@ -86,7 +86,9 @@ public int Count { get; set; }
|
|||||||
|
|
||||||
## Arrays
|
## Arrays
|
||||||
|
|
||||||
`CommandArgument` can be defined as arrays and any additional parameters will be included in the value. For example
|
### Argument Vector
|
||||||
|
|
||||||
|
One (exactly one) `CommandArgument` can be defined as an array, and any additional parameters will be included in the value. For example:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
[CommandArgument(0, "[name]")]
|
[CommandArgument(0, "[name]")]
|
||||||
@@ -95,6 +97,19 @@ public string[] Name { get; set; }
|
|||||||
|
|
||||||
Would allow the user to run `app.exe Dwayne Elizondo "Mountain Dew" Herbert Camacho`. The settings passed to the command would have a 5 element array consisting of Dwayne, Elizondo, Mountain Dew, Herbert and Camacho.
|
Would allow the user to run `app.exe Dwayne Elizondo "Mountain Dew" Herbert Camacho`. The settings passed to the command would have a 5 element array consisting of Dwayne, Elizondo, Mountain Dew, Herbert and Camacho.
|
||||||
|
|
||||||
|
A command can have only one argument vector, and it needs to be the last argument. (I.e. there can be no `CommandArgument` whose position is higher than that of the argument vector.)
|
||||||
|
|
||||||
|
### Option Arrays
|
||||||
|
|
||||||
|
A `CommandOption` can be defined as an array like the following:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[CommandOption("-n|--name <VALUES>")]
|
||||||
|
public string[] Names { get; set; },
|
||||||
|
```
|
||||||
|
|
||||||
|
This would allow the user to run `app.exe --name Dwayne --name Elizondo --name "Mountain Dew" --name Herbert --name Camacho` and would result in a 5 element array consisting of Dwayne, Elizondo, Mountain Dew, Herbert and Camacho.
|
||||||
|
|
||||||
## Constructors
|
## Constructors
|
||||||
|
|
||||||
`Spectre.Console.Cli` supports constructor initialization and init only initialization. For constructor initialization, the parameter name of the constructor must match the name of the property name of the settings class. Order does not matter.
|
`Spectre.Console.Cli` supports constructor initialization and init only initialization. For constructor initialization, the parameter name of the constructor must match the name of the property name of the settings class. Order does not matter.
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ You can set the rule's title alignment.
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var rule = new Rule("[red]Hello[/]");
|
var rule = new Rule("[red]Hello[/]");
|
||||||
rule.Alignment = Justify.Left;
|
rule.Justification = Justify.Left;
|
||||||
AnsiConsole.Write(rule);
|
AnsiConsole.Write(rule);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,11 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"cake.tool": {
|
"cake.tool": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-cake"
|
"dotnet-cake"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gpr": {
|
|
||||||
"version": "0.1.281",
|
|
||||||
"commands": [
|
|
||||||
"gpr"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dotnet-example": {
|
"dotnet-example": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public static partial class Program
|
|||||||
{
|
{
|
||||||
[CommandOption("--count")]
|
[CommandOption("--count")]
|
||||||
[Description("The number of bars to print")]
|
[Description("The number of bars to print")]
|
||||||
[DefaultValue(1)]
|
[DefaultValue(3)]
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<ExampleName>Delegates</ExampleName>
|
<ExampleName>Delegates</ExampleName>
|
||||||
<ExampleDescription>Demonstrates how to specify commands as delegates.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to specify commands as delegates.</ExampleDescription>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
@@ -14,7 +15,13 @@ public static partial class Program
|
|||||||
.WithDescription("Foos the bars");
|
.WithDescription("Foos the bars");
|
||||||
|
|
||||||
config.AddDelegate<BarSettings>("bar", Bar)
|
config.AddDelegate<BarSettings>("bar", Bar)
|
||||||
.WithDescription("Bars the foos"); ;
|
.WithDescription("Bars the foos");
|
||||||
|
|
||||||
|
config.AddAsyncDelegate("fooAsync", FooAsync)
|
||||||
|
.WithDescription("Foos the bars asynchronously");
|
||||||
|
|
||||||
|
config.AddAsyncDelegate<BarSettings>("barAsync", BarAsync)
|
||||||
|
.WithDescription("Bars the foos asynchronously");
|
||||||
});
|
});
|
||||||
|
|
||||||
return app.Run(args);
|
return app.Run(args);
|
||||||
@@ -35,4 +42,20 @@ public static partial class Program
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Task<int> FooAsync(CommandContext context)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine("Foo");
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<int> BarAsync(CommandContext context, BarSettings settings)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < settings.Count; index++)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine("Bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<ExampleName>Demo</ExampleName>
|
<ExampleName>Demo</ExampleName>
|
||||||
<ExampleDescription>Demonstrates the most common use cases of Spectre.Cli.</ExampleDescription>
|
<ExampleDescription>Demonstrates the most common use cases of Spectre.Cli.</ExampleDescription>
|
||||||
|
|||||||
@@ -15,25 +15,25 @@ public static class Program
|
|||||||
{
|
{
|
||||||
config.SetApplicationName("fake-dotnet");
|
config.SetApplicationName("fake-dotnet");
|
||||||
config.ValidateExamples();
|
config.ValidateExamples();
|
||||||
config.AddExample(new[] { "run", "--no-build" });
|
config.AddExample("run", "--no-build");
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
config.AddCommand<RunCommand>("run");
|
config.AddCommand<RunCommand>("run");
|
||||||
|
|
||||||
// Add
|
// Add
|
||||||
config.AddBranch<AddSettings>("add", add =>
|
config.AddBranch<AddSettings>("add", add =>
|
||||||
{
|
{
|
||||||
add.SetDescription("Add a package or reference to a .NET project");
|
add.SetDescription("Add a package or reference to a .NET project");
|
||||||
add.AddCommand<AddPackageCommand>("package");
|
add.AddCommand<AddPackageCommand>("package");
|
||||||
add.AddCommand<AddReferenceCommand>("reference");
|
add.AddCommand<AddReferenceCommand>("reference");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve
|
||||||
|
config.AddCommand<ServeCommand>("serve")
|
||||||
|
.WithExample("serve", "-o", "firefox")
|
||||||
|
.WithExample("serve", "--port", "80", "-o", "firefox");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Serve
|
|
||||||
config.AddCommand<ServeCommand>("serve")
|
|
||||||
.WithExample(new[] { "serve", "-o", "firefox" })
|
|
||||||
.WithExample(new[] { "serve", "--port", "80", "-o", "firefox" });
|
|
||||||
});
|
|
||||||
|
|
||||||
return app.Run(args);
|
return app.Run(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<ExampleName>Dynamic</ExampleName>
|
<ExampleName>Dynamic</ExampleName>
|
||||||
<ExampleDescription>Demonstrates how to define dynamic commands.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to define dynamic commands.</ExampleDescription>
|
||||||
|
|||||||
30
examples/Cli/Help/CustomHelpProvider.cs
Normal file
30
examples/Cli/Help/CustomHelpProvider.cs
Normal file
@@ -0,0 +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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
20
examples/Cli/Help/DefaultCommand.cs
Normal file
20
examples/Cli/Help/DefaultCommand.cs
Normal file
@@ -0,0 +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;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
examples/Cli/Help/Help.csproj
Normal file
18
examples/Cli/Help/Help.csproj
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ExampleName>Help</ExampleName>
|
||||||
|
<ExampleDescription>Demonstrates how to extend the built-in Spectre.Console help provider to render a custom banner at the top of the help information.</ExampleDescription>
|
||||||
|
<ExampleGroup>Cli</ExampleGroup>
|
||||||
|
<ExampleVisible>false</ExampleVisible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
19
examples/Cli/Help/Program.cs
Normal file
19
examples/Cli/Help/Program.cs
Normal file
@@ -0,0 +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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<ExampleName>Injection</ExampleName>
|
<ExampleName>Injection</ExampleName>
|
||||||
<ExampleDescription>Demonstrates how to use dependency injection with Spectre.Cli.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to use dependency injection with Spectre.Cli.</ExampleDescription>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<ExampleName>Logging</ExampleName>
|
<ExampleName>Logging</ExampleName>
|
||||||
<ExampleDescription>Demonstrates how to dynamically configure Serilog for logging using parameters from a command.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to dynamically configure Serilog for logging using parameters from a command.</ExampleDescription>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Screens</ExampleTitle>
|
<ExampleTitle>Screens</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to use alternate screens.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to use alternate screens.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Borders</ExampleTitle>
|
<ExampleTitle>Borders</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates the different kind of borders.</ExampleDescription>
|
<ExampleDescription>Demonstrates the different kind of borders.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -20,21 +20,22 @@ public static class Program
|
|||||||
{
|
{
|
||||||
static IRenderable CreatePanel(string name, BoxBorder border)
|
static IRenderable CreatePanel(string name, BoxBorder border)
|
||||||
{
|
{
|
||||||
return new Panel($"This is a panel with\nthe [yellow]{name}[/] border.")
|
return
|
||||||
.Header($" [blue]{name}[/] ", Justify.Center)
|
new Panel($"This is a panel with\nthe [yellow]{name}[/] border.")
|
||||||
.Border(border)
|
.Header($" [blue]{name}[/] ", Justify.Center)
|
||||||
.BorderStyle(Style.Parse("grey"));
|
.Border(border)
|
||||||
|
.BorderStyle(Style.Parse("grey"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new[]
|
var items = new[]
|
||||||
{
|
{
|
||||||
CreatePanel("Ascii", BoxBorder.Ascii),
|
CreatePanel("Ascii", BoxBorder.Ascii),
|
||||||
CreatePanel("Square", BoxBorder.Square),
|
CreatePanel("Square", BoxBorder.Square),
|
||||||
CreatePanel("Rounded", BoxBorder.Rounded),
|
CreatePanel("Rounded", BoxBorder.Rounded),
|
||||||
CreatePanel("Heavy", BoxBorder.Heavy),
|
CreatePanel("Heavy", BoxBorder.Heavy),
|
||||||
CreatePanel("Double", BoxBorder.Double),
|
CreatePanel("Double", BoxBorder.Double),
|
||||||
CreatePanel("None", BoxBorder.None),
|
CreatePanel("None", BoxBorder.None),
|
||||||
};
|
};
|
||||||
|
|
||||||
AnsiConsole.Write(
|
AnsiConsole.Write(
|
||||||
new Padder(
|
new Padder(
|
||||||
@@ -47,6 +48,7 @@ public static class Program
|
|||||||
static IRenderable CreateTable(string name, TableBorder border)
|
static IRenderable CreateTable(string name, TableBorder border)
|
||||||
{
|
{
|
||||||
var table = new Table().Border(border);
|
var table = new Table().Border(border);
|
||||||
|
table.ShowRowSeparators();
|
||||||
table.AddColumn("[yellow]Header 1[/]", c => c.Footer("[grey]Footer 1[/]"));
|
table.AddColumn("[yellow]Header 1[/]", c => c.Footer("[grey]Footer 1[/]"));
|
||||||
table.AddColumn("[yellow]Header 2[/]", col => col.Footer("[grey]Footer 2[/]").RightAligned());
|
table.AddColumn("[yellow]Header 2[/]", col => col.Footer("[grey]Footer 2[/]").RightAligned());
|
||||||
table.AddRow("Cell", "Cell");
|
table.AddRow("Cell", "Cell");
|
||||||
@@ -54,29 +56,23 @@ public static class Program
|
|||||||
|
|
||||||
return new Panel(table)
|
return new Panel(table)
|
||||||
.Header($" [blue]{name}[/] ", Justify.Center)
|
.Header($" [blue]{name}[/] ", Justify.Center)
|
||||||
|
.PadBottom(1)
|
||||||
.NoBorder();
|
.NoBorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = new[]
|
var items = new[]
|
||||||
{
|
{
|
||||||
CreateTable("Ascii", TableBorder.Ascii),
|
CreateTable("Ascii", TableBorder.Ascii), CreateTable("Ascii2", TableBorder.Ascii2),
|
||||||
CreateTable("Ascii2", TableBorder.Ascii2),
|
CreateTable("AsciiDoubleHead", TableBorder.AsciiDoubleHead),
|
||||||
CreateTable("AsciiDoubleHead", TableBorder.AsciiDoubleHead),
|
CreateTable("Horizontal", TableBorder.Horizontal), CreateTable("Simple", TableBorder.Simple),
|
||||||
CreateTable("Horizontal", TableBorder.Horizontal),
|
CreateTable("SimpleHeavy", TableBorder.SimpleHeavy), CreateTable("Minimal", TableBorder.Minimal),
|
||||||
CreateTable("Simple", TableBorder.Simple),
|
CreateTable("MinimalHeavyHead", TableBorder.MinimalHeavyHead),
|
||||||
CreateTable("SimpleHeavy", TableBorder.SimpleHeavy),
|
CreateTable("MinimalDoubleHead", TableBorder.MinimalDoubleHead),
|
||||||
CreateTable("Minimal", TableBorder.Minimal),
|
CreateTable("Square", TableBorder.Square), CreateTable("Rounded", TableBorder.Rounded),
|
||||||
CreateTable("MinimalHeavyHead", TableBorder.MinimalHeavyHead),
|
CreateTable("Heavy", TableBorder.Heavy), CreateTable("HeavyEdge", TableBorder.HeavyEdge),
|
||||||
CreateTable("MinimalDoubleHead", TableBorder.MinimalDoubleHead),
|
CreateTable("HeavyHead", TableBorder.HeavyHead), CreateTable("Double", TableBorder.Double),
|
||||||
CreateTable("Square", TableBorder.Square),
|
CreateTable("DoubleEdge", TableBorder.DoubleEdge), CreateTable("Markdown", TableBorder.Markdown),
|
||||||
CreateTable("Rounded", TableBorder.Rounded),
|
};
|
||||||
CreateTable("Heavy", TableBorder.Heavy),
|
|
||||||
CreateTable("HeavyEdge", TableBorder.HeavyEdge),
|
|
||||||
CreateTable("HeavyHead", TableBorder.HeavyHead),
|
|
||||||
CreateTable("Double", TableBorder.Double),
|
|
||||||
CreateTable("DoubleEdge", TableBorder.DoubleEdge),
|
|
||||||
CreateTable("Markdown", TableBorder.Markdown),
|
|
||||||
};
|
|
||||||
|
|
||||||
AnsiConsole.Write(new Columns(items).Collapse());
|
AnsiConsole.Write(new Columns(items).Collapse());
|
||||||
}
|
}
|
||||||
@@ -87,4 +83,4 @@ public static class Program
|
|||||||
AnsiConsole.Write(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftJustified());
|
AnsiConsole.Write(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftJustified());
|
||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Calendars</ExampleTitle>
|
<ExampleTitle>Calendars</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render calendars.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render calendars.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Canvas</ExampleTitle>
|
<ExampleTitle>Canvas</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render pixels and images.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render pixels and images.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Charts</ExampleTitle>
|
<ExampleTitle>Charts</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render charts in a console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render charts in a console.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public static class Program
|
|||||||
.FullSize()
|
.FullSize()
|
||||||
.Width(60)
|
.Width(60)
|
||||||
.ShowPercentage()
|
.ShowPercentage()
|
||||||
|
.WithValueColor(Color.Orange1)
|
||||||
.AddItem("SCSS", 37, Color.Red)
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
.AddItem("HTML", 28.3, Color.Blue)
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
.AddItem("C#", 22.6, Color.Green)
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Colors</ExampleTitle>
|
<ExampleTitle>Colors</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Columns</ExampleTitle>
|
<ExampleTitle>Columns</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render data into columns.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render data into columns.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Cursor</ExampleTitle>
|
<ExampleTitle>Cursor</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to move the cursor.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to move the cursor.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
15
examples/Console/Decorations/Decorations.csproj
Normal file
15
examples/Console/Decorations/Decorations.csproj
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ExampleTitle>Decorations</ExampleTitle>
|
||||||
|
<ExampleDescription>Demonstrates how to [italic]use[/] [bold]decorations[/] [dim]in[/] the console.</ExampleDescription>
|
||||||
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
29
examples/Console/Decorations/Program.cs
Normal file
29
examples/Console/Decorations/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace Colors;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static void Main()
|
||||||
|
{
|
||||||
|
AnsiConsole.ResetDecoration();
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
|
if (AnsiConsole.Profile.Capabilities.Ansi)
|
||||||
|
{
|
||||||
|
AnsiConsole.Write(new Rule("[bold green]ANSI Decorations[/]"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AnsiConsole.Write(new Rule("[bold red]Legacy Decorations (unsupported)[/]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var decorations = System.Enum.GetValues(typeof(Decoration));
|
||||||
|
foreach (var decoration in decorations)
|
||||||
|
{
|
||||||
|
var name = System.Enum.GetName(typeof(Decoration),decoration);
|
||||||
|
AnsiConsole.Write(name + ": ");
|
||||||
|
AnsiConsole.Write(new Markup(name+"\n", new Style(decoration: (Decoration)decoration)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Emojis</ExampleTitle>
|
<ExampleTitle>Emojis</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render emojis.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render emojis.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Exceptions</ExampleTitle>
|
<ExampleTitle>Exceptions</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render formatted exceptions.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render formatted exceptions.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Figlet</ExampleTitle>
|
<ExampleTitle>Figlet</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render FIGlet text.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render FIGlet text.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Grids</ExampleTitle>
|
<ExampleTitle>Grids</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render grids in a console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render grids in a console.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Info</ExampleTitle>
|
<ExampleTitle>Info</ExampleTitle>
|
||||||
<ExampleDescription>Displays the capabilities of the current console.</ExampleDescription>
|
<ExampleDescription>Displays the capabilities of the current console.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Json</ExampleTitle>
|
<ExampleTitle>Json</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to print syntax highlighted JSON.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to print syntax highlighted JSON.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj" />
|
<ProjectReference Include="..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Layout</ExampleTitle>
|
<ExampleTitle>Layout</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to use layouts.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to use layouts.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Links</ExampleTitle>
|
<ExampleTitle>Links</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render links in a console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render links in a console.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Live</ExampleTitle>
|
<ExampleTitle>Live</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to do live updates.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to do live updates.</ExampleDescription>
|
||||||
<ExampleGroup>Live</ExampleGroup>
|
<ExampleGroup>Live</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>LiveTable</ExampleTitle>
|
<ExampleTitle>LiveTable</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to do live updates in a table.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to do live updates in a table.</ExampleDescription>
|
||||||
<ExampleGroup>Live</ExampleGroup>
|
<ExampleGroup>Live</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<ExampleTitle>Minimal</ExampleTitle>
|
<ExampleTitle>Minimal</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates a minimal console application.</ExampleDescription>
|
<ExampleDescription>Demonstrates a minimal console application.</ExampleDescription>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Panels</ExampleTitle>
|
<ExampleTitle>Panels</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render items in panels.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render items in panels.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Paths</ExampleTitle>
|
<ExampleTitle>Paths</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render paths.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render paths.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
namespace Progress;
|
namespace Progress;
|
||||||
|
|
||||||
@@ -22,35 +24,36 @@ public static class Program
|
|||||||
new RemainingTimeColumn(), // Remaining time
|
new RemainingTimeColumn(), // Remaining time
|
||||||
new SpinnerColumn(), // Spinner
|
new SpinnerColumn(), // Spinner
|
||||||
})
|
})
|
||||||
|
.UseRenderHook((renderable, tasks) => RenderHook(tasks, renderable))
|
||||||
.Start(ctx =>
|
.Start(ctx =>
|
||||||
{
|
{
|
||||||
var random = new Random(DateTime.Now.Millisecond);
|
var random = new Random(DateTime.Now.Millisecond);
|
||||||
|
|
||||||
// Create some tasks
|
// Create some tasks
|
||||||
var tasks = CreateTasks(ctx, random);
|
var tasks = CreateTasks(ctx, random);
|
||||||
var warpTask = ctx.AddTask("Going to warp", autoStart: false).IsIndeterminate();
|
var warpTask = ctx.AddTask("Going to warp", autoStart: false).IsIndeterminate();
|
||||||
|
|
||||||
// Wait for all tasks (except the indeterminate one) to complete
|
// Wait for all tasks (except the indeterminate one) to complete
|
||||||
while (!ctx.IsFinished)
|
while (!ctx.IsFinished)
|
||||||
{
|
{
|
||||||
// Increment progress
|
// Increment progress
|
||||||
foreach (var (task, increment) in tasks)
|
foreach (var (task, increment) in tasks)
|
||||||
{
|
{
|
||||||
task.Increment(random.NextDouble() * increment);
|
task.Increment(random.NextDouble() * increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write some random things to the terminal
|
// Write some random things to the terminal
|
||||||
if (random.NextDouble() < 0.1)
|
if (random.NextDouble() < 0.1)
|
||||||
{
|
{
|
||||||
WriteLogMessage();
|
WriteLogMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulate some delay
|
// Simulate some delay
|
||||||
Thread.Sleep(100);
|
Thread.Sleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now start the "warp" task
|
// Now start the "warp" task
|
||||||
warpTask.StartTask();
|
warpTask.StartTask();
|
||||||
warpTask.IsIndeterminate(false);
|
warpTask.IsIndeterminate(false);
|
||||||
while (!ctx.IsFinished)
|
while (!ctx.IsFinished)
|
||||||
{
|
{
|
||||||
@@ -65,6 +68,35 @@ public static class Program
|
|||||||
AnsiConsole.MarkupLine("[green]Done![/]");
|
AnsiConsole.MarkupLine("[green]Done![/]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IRenderable RenderHook(IReadOnlyList<ProgressTask> tasks, IRenderable renderable)
|
||||||
|
{
|
||||||
|
var header = new Panel("Going on a :rocket:, we're going to the :crescent_moon:").Expand().RoundedBorder();
|
||||||
|
var footer = new Rows(
|
||||||
|
new Rule(),
|
||||||
|
new Markup(
|
||||||
|
$"[blue]{tasks.Count}[/] total tasks. [green]{tasks.Count(i => i.IsFinished)}[/] complete.")
|
||||||
|
);
|
||||||
|
|
||||||
|
const string ESC = "\u001b";
|
||||||
|
string escapeSequence;
|
||||||
|
if (tasks.All(i => i.IsFinished))
|
||||||
|
{
|
||||||
|
escapeSequence = $"{ESC}]]9;4;0;100{ESC}\\";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var total = tasks.Sum(i => i.MaxValue);
|
||||||
|
var done = tasks.Sum(i => i.Value);
|
||||||
|
var percent = (int)(done / total * 100);
|
||||||
|
escapeSequence = $"{ESC}]]9;4;1;{percent}{ESC}\\";
|
||||||
|
}
|
||||||
|
|
||||||
|
var middleContent = new Grid().AddColumns(new GridColumn(), new GridColumn().Width(20));
|
||||||
|
middleContent.AddRow(renderable, new FigletText(tasks.Count(i => i.IsFinished == false).ToString()));
|
||||||
|
|
||||||
|
return new Rows(header, middleContent, footer, new ControlCode(escapeSequence));
|
||||||
|
}
|
||||||
|
|
||||||
private static List<(ProgressTask Task, int Delay)> CreateTasks(ProgressContext progress, Random random)
|
private static List<(ProgressTask Task, int Delay)> CreateTasks(ProgressContext progress, Random random)
|
||||||
{
|
{
|
||||||
var tasks = new List<(ProgressTask, int)>();
|
var tasks = new List<(ProgressTask, int)>();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Progress</ExampleTitle>
|
<ExampleTitle>Progress</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to show progress bars.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to show progress bars.</ExampleDescription>
|
||||||
<ExampleGroup>Status</ExampleGroup>
|
<ExampleGroup>Status</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<LangVersion>9</LangVersion>
|
<LangVersion>9</LangVersion>
|
||||||
<ExampleTitle>Prompt</ExampleTitle>
|
<ExampleTitle>Prompt</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to get input from a user.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to get input from a user.</ExampleDescription>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Rules</ExampleTitle>
|
<ExampleTitle>Rules</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render horizontal rules (lines).</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render horizontal rules (lines).</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Showcase</ExampleTitle>
|
<ExampleTitle>Showcase</ExampleTitle>
|
||||||
<ExampleDescription>Demonstation of Spectre.Console.</ExampleDescription>
|
<ExampleDescription>Demonstration of Spectre.Console.</ExampleDescription>
|
||||||
<ExampleGroup>Misc</ExampleGroup>
|
<ExampleGroup>Misc</ExampleGroup>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Status</ExampleTitle>
|
<ExampleTitle>Status</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to show status updates.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to show status updates.</ExampleDescription>
|
||||||
<ExampleGroup>Status</ExampleGroup>
|
<ExampleGroup>Status</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Tables</ExampleTitle>
|
<ExampleTitle>Tables</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render tables in a console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render tables in a console.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleTitle>Trees</ExampleTitle>
|
<ExampleTitle>Trees</ExampleTitle>
|
||||||
<ExampleDescription>Demonstrates how to render trees in a console.</ExampleDescription>
|
<ExampleDescription>Demonstrates how to render trees in a console.</ExampleDescription>
|
||||||
<ExampleGroup>Widgets</ExampleGroup>
|
<ExampleGroup>Widgets</ExampleGroup>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ExampleVisible>false</ExampleVisible>
|
<ExampleVisible>false</ExampleVisible>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
12
global.json
12
global.json
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"$schema": "http://json.schemastore.org/global",
|
||||||
"version": "7.0.100"
|
"sdk": {
|
||||||
}
|
"version": "8.0.100",
|
||||||
}
|
"rollForward": "latestFeature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Generator.Commands.Samples
|
|||||||
{
|
{
|
||||||
public abstract void Run(IAnsiConsole console);
|
public abstract void Run(IAnsiConsole console);
|
||||||
public virtual string Name() => PascalToKebab(GetType().Name.Replace("Sample",""));
|
public virtual string Name() => PascalToKebab(GetType().Name.Replace("Sample",""));
|
||||||
public virtual (int Cols, int Rows) ConsoleSize => (120, 24);
|
public virtual (int Cols, int Rows) ConsoleSize => (82, 24);
|
||||||
public virtual IEnumerable<(string Name, Action<Capabilities> CapabilitiesAction)> GetCapabilities()
|
public virtual IEnumerable<(string Name, Action<Capabilities> CapabilitiesAction)> GetCapabilities()
|
||||||
{
|
{
|
||||||
return new (string Name, Action<Capabilities> CapabilitiesAction)[]
|
return new (string Name, Action<Capabilities> CapabilitiesAction)[]
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Generator.Commands.Samples;
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedType.Global
|
||||||
|
public class ColumnsSample : BaseSample
|
||||||
|
{
|
||||||
|
public override void Run(IAnsiConsole console)
|
||||||
|
{
|
||||||
|
var cards = Fruit
|
||||||
|
.LoadFriuts()
|
||||||
|
.Select(GetContent)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Animate
|
||||||
|
console.Live(new Text(""))
|
||||||
|
.AutoClear(true)
|
||||||
|
.Overflow(VerticalOverflow.Ellipsis)
|
||||||
|
.Cropping(VerticalOverflowCropping.Top)
|
||||||
|
.Start(ctx =>
|
||||||
|
{
|
||||||
|
for (var i = 1; i < cards.Count; i++)
|
||||||
|
{
|
||||||
|
var toShow = cards.Take(i);
|
||||||
|
ctx.UpdateTarget(new Columns(toShow));
|
||||||
|
//ctx.Refresh();
|
||||||
|
Thread.Sleep(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render all cards in columns
|
||||||
|
AnsiConsole.Write(new Spectre.Console.Columns(cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetContent(Fruit fruit)
|
||||||
|
{
|
||||||
|
return $"[b][yellow]{fruit.Name}[/][/]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Fruit
|
||||||
|
{
|
||||||
|
public string Name { get; init; }
|
||||||
|
|
||||||
|
public static List<Fruit> LoadFriuts()
|
||||||
|
{
|
||||||
|
return new []
|
||||||
|
{
|
||||||
|
"Apple",
|
||||||
|
"Apricot",
|
||||||
|
"Avocado",
|
||||||
|
"Banana",
|
||||||
|
"Blackberry",
|
||||||
|
"Blueberry",
|
||||||
|
"Boysenberry",
|
||||||
|
"Breadfruit",
|
||||||
|
"Cacao",
|
||||||
|
"Cherry",
|
||||||
|
"Cloudberry",
|
||||||
|
"Coconut",
|
||||||
|
"Dragonfruit",
|
||||||
|
"Elderberry",
|
||||||
|
"Grape",
|
||||||
|
"Grapefruit",
|
||||||
|
"Jackfruit",
|
||||||
|
"Kiwifruit",
|
||||||
|
"Lemon",
|
||||||
|
"Lime",
|
||||||
|
"Mango",
|
||||||
|
"Melon",
|
||||||
|
"Orange",
|
||||||
|
"Blood orange",
|
||||||
|
"Clementine",
|
||||||
|
"Mandarine",
|
||||||
|
"Tangerine",
|
||||||
|
"Papaya",
|
||||||
|
"Passionfruit",
|
||||||
|
"Plum",
|
||||||
|
"Pineapple",
|
||||||
|
"Pomelo",
|
||||||
|
"Raspberry",
|
||||||
|
"Salmonberry",
|
||||||
|
"Strawberry",
|
||||||
|
"Ximenia",
|
||||||
|
"Yuzu",
|
||||||
|
}
|
||||||
|
.Select(x => new Fruit{Name = x})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>default</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Scriban" Version="2.1.3" />
|
<PackageReference Include="Scriban" Version="2.1.3" />
|
||||||
<PackageReference Include="Spectre.IO" Version="0.1.0" />
|
<PackageReference Include="Spectre.IO" Version="0.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{CFE7
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli", "..\..\..\src\Spectre.Console.Cli\Spectre.Console.Cli.csproj", "{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli", "..\..\..\src\Spectre.Console.Cli\Spectre.Console.Cli.csproj", "{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectre.Console.Json", "..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj", "{6C96C268-CEEE-478A-A36F-E1450AC33B73}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -71,6 +73,18 @@ Global
|
|||||||
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x64.Build.0 = Release|Any CPU
|
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x86.ActiveCfg = Release|Any CPU
|
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x86.Build.0 = Release|Any CPU
|
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -79,6 +93,7 @@ Global
|
|||||||
{F75B882A-06DB-426B-9580-A7302D32E684} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
{F75B882A-06DB-426B-9580-A7302D32E684} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
||||||
{112A37CB-1EFE-4A90-BD5B-5437038BE276} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
{112A37CB-1EFE-4A90-BD5B-5437038BE276} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
||||||
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
{18A3F32D-FECD-463B-A194-6EE74EA9E5EC} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
||||||
|
{6C96C268-CEEE-478A-A36F-E1450AC33B73} = {CFE7445D-F971-429D-B6E6-9E68456AE00F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {5F37FDE3-D591-4D43-8DDE-2ED6BAB0A7B4}
|
SolutionGuid = {5F37FDE3-D591-4D43-8DDE-2ED6BAB0A7B4}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
|
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
|||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</param>
|
/// <param name="settings">The settings.</param>
|
||||||
/// <returns>The validation result.</returns>
|
/// <returns>The validation result.</returns>
|
||||||
public virtual ValidationResult Validate([NotNull] CommandContext context, [NotNull] TSettings settings)
|
public virtual ValidationResult Validate(CommandContext context, TSettings settings)
|
||||||
{
|
{
|
||||||
return ValidationResult.Success();
|
return ValidationResult.Success();
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
|||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</param>
|
/// <param name="settings">The settings.</param>
|
||||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||||
public abstract int Execute([NotNull] CommandContext context, [NotNull] TSettings settings);
|
public abstract int Execute(CommandContext context, TSettings settings);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
||||||
|
|||||||
@@ -5,7 +5,65 @@ namespace Spectre.Console.Cli;
|
|||||||
/// and <see cref="IConfigurator{TSettings}"/>.
|
/// and <see cref="IConfigurator{TSettings}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ConfiguratorExtensions
|
public static class ConfiguratorExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
{
|
||||||
|
if (configurator == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
|
}
|
||||||
|
|
||||||
|
configurator.SetHelpProvider<T>();
|
||||||
|
return configurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the culture for the application.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configurator">The configurator.</param>
|
||||||
|
/// <param name="culture">The culture.</param>
|
||||||
|
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Text displayed by <see cref="Help.HelpProvider"/> can be localised, but defaults to English.
|
||||||
|
/// Setting the application culture informs the resource manager which culture to use when fetching strings.
|
||||||
|
/// English will be used when a culture has not been specified
|
||||||
|
/// or a string has not been localised for the specified culture.
|
||||||
|
/// </remarks>
|
||||||
|
public static IConfigurator SetApplicationCulture(this IConfigurator configurator, CultureInfo? culture)
|
||||||
|
{
|
||||||
|
if (configurator == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
|
}
|
||||||
|
|
||||||
|
configurator.Settings.Culture = culture;
|
||||||
|
return configurator;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the name of the application.
|
/// Sets the name of the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -237,6 +295,26 @@ public static class ConfiguratorExtensions
|
|||||||
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a command without settings that executes an async delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configurator">The configurator.</param>
|
||||||
|
/// <param name="name">The name of the command.</param>
|
||||||
|
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||||
|
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||||
|
public static ICommandConfigurator AddAsyncDelegate(
|
||||||
|
this IConfigurator configurator,
|
||||||
|
string name,
|
||||||
|
Func<CommandContext, Task<int>> func)
|
||||||
|
{
|
||||||
|
if (configurator == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a command without settings that executes a delegate.
|
/// Adds a command without settings that executes a delegate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -259,6 +337,28 @@ public static class ConfiguratorExtensions
|
|||||||
return configurator.AddDelegate<TSettings>(name, (c, _) => func(c));
|
return configurator.AddDelegate<TSettings>(name, (c, _) => func(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a command without settings that executes an async delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSettings">The command setting type.</typeparam>
|
||||||
|
/// <param name="configurator">The configurator.</param>
|
||||||
|
/// <param name="name">The name of the command.</param>
|
||||||
|
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||||
|
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||||
|
public static ICommandConfigurator AddAsyncDelegate<TSettings>(
|
||||||
|
this IConfigurator<TSettings> configurator,
|
||||||
|
string name,
|
||||||
|
Func<CommandContext, Task<int>> func)
|
||||||
|
where TSettings : CommandSettings
|
||||||
|
{
|
||||||
|
if (configurator == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return configurator.AddAsyncDelegate<TSettings>(name, (c, _) => func(c));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the ExceptionsHandler.
|
/// Sets the ExceptionsHandler.
|
||||||
/// <para>Setting <see cref="ICommandAppSettings.ExceptionHandler"/> this way will use the
|
/// <para>Setting <see cref="ICommandAppSettings.ExceptionHandler"/> this way will use the
|
||||||
|
|||||||
@@ -1,7 +1,32 @@
|
|||||||
namespace Spectre.Console.Cli;
|
using Spectre.Console.Cli.Resources;
|
||||||
|
|
||||||
internal static class HelpWriter
|
namespace Spectre.Console.Cli.Help;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The help provider for Spectre.Console.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Other IHelpProvider implementations can be injected into the CommandApp, if desired.
|
||||||
|
/// </remarks>
|
||||||
|
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; }
|
||||||
|
|
||||||
private sealed class HelpArgument
|
private sealed class HelpArgument
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
@@ -17,10 +42,10 @@ internal static class HelpWriter
|
|||||||
Description = description;
|
Description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<HelpArgument> Get(CommandInfo? command)
|
public static IReadOnlyList<HelpArgument> Get(ICommandInfo? command)
|
||||||
{
|
{
|
||||||
var arguments = new List<HelpArgument>();
|
var arguments = new List<HelpArgument>();
|
||||||
arguments.AddRange(command?.Parameters?.OfType<CommandArgument>()?.Select(
|
arguments.AddRange(command?.Parameters?.OfType<ICommandArgument>()?.Select(
|
||||||
x => new HelpArgument(x.Value, x.Position, x.Required, x.Description))
|
x => new HelpArgument(x.Value, x.Position, x.Required, x.Description))
|
||||||
?? Array.Empty<HelpArgument>());
|
?? Array.Empty<HelpArgument>());
|
||||||
return arguments;
|
return arguments;
|
||||||
@@ -46,49 +71,77 @@ internal static class HelpWriter
|
|||||||
DefaultValue = defaultValue;
|
DefaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<HelpOption> Get(CommandModel model, CommandInfo? command)
|
public static IReadOnlyList<HelpOption> Get(ICommandInfo? command, HelpProviderResources resources)
|
||||||
{
|
{
|
||||||
var parameters = new List<HelpOption>();
|
var parameters = new List<HelpOption>();
|
||||||
parameters.Add(new HelpOption("h", "help", null, null, "Prints help information", null));
|
parameters.Add(new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null));
|
||||||
|
|
||||||
// At the root and no default command?
|
// Version information applies to the entire application
|
||||||
if (command == null && model?.DefaultCommand == null)
|
// 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
|
||||||
parameters.Add(new HelpOption("v", "version", null, null, "Prints version information", null));
|
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<CommandOption>().Where(o => !o.IsHidden).Select(o =>
|
parameters.AddRange(command?.Parameters.OfType<ICommandOption>().Where(o => !o.IsHidden).Select(o =>
|
||||||
new HelpOption(
|
new HelpOption(
|
||||||
o.ShortNames.FirstOrDefault(), o.LongNames.FirstOrDefault(),
|
o.ShortNames.FirstOrDefault(), o.LongNames.FirstOrDefault(),
|
||||||
o.ValueName, o.ValueIsOptional, o.Description,
|
o.ValueName, o.ValueIsOptional, o.Description,
|
||||||
o.ParameterKind == ParameterKind.Flag && o.DefaultValue?.Value is false ? null : o.DefaultValue?.Value))
|
o.IsFlag && o.DefaultValue?.Value is false ? null : o.DefaultValue?.Value))
|
||||||
?? Array.Empty<HelpOption>());
|
?? Array.Empty<HelpOption>());
|
||||||
return parameters;
|
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;
|
||||||
|
this.TrimTrailingPeriod = settings.TrimTrailingPeriod;
|
||||||
|
|
||||||
public static IEnumerable<IRenderable> Write(CommandModel model, bool writeOptionsDefaultValues)
|
resources = new HelpProviderResources(settings.Culture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
|
||||||
{
|
{
|
||||||
return WriteCommand(model, null, writeOptionsDefaultValues);
|
var result = new List<IRenderable>();
|
||||||
}
|
|
||||||
|
result.AddRange(GetHeader(model, command));
|
||||||
public static IEnumerable<IRenderable> WriteCommand(CommandModel model, CommandInfo? command, bool writeOptionsDefaultValues)
|
result.AddRange(GetDescription(model, command));
|
||||||
{
|
|
||||||
var container = command as ICommandContainer ?? model;
|
|
||||||
var isDefaultCommand = command?.IsDefaultCommand ?? false;
|
|
||||||
|
|
||||||
var result = new List<IRenderable>();
|
|
||||||
result.AddRange(GetDescription(command));
|
|
||||||
result.AddRange(GetUsage(model, command));
|
result.AddRange(GetUsage(model, command));
|
||||||
result.AddRange(GetExamples(model, command));
|
result.AddRange(GetExamples(model, command));
|
||||||
result.AddRange(GetArguments(command));
|
result.AddRange(GetArguments(model, command));
|
||||||
result.AddRange(GetOptions(model, command, writeOptionsDefaultValues));
|
result.AddRange(GetOptions(model, command));
|
||||||
result.AddRange(GetCommands(model, container, isDefaultCommand));
|
result.AddRange(GetCommands(model, command));
|
||||||
|
result.AddRange(GetFooter(model, command));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetDescription(CommandInfo? command)
|
/// <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>
|
||||||
|
public virtual IEnumerable<IRenderable> GetDescription(ICommandModel model, ICommandInfo? command)
|
||||||
{
|
{
|
||||||
if (command?.Description == null)
|
if (command?.Description == null)
|
||||||
{
|
{
|
||||||
@@ -96,23 +149,29 @@ internal static class HelpWriter
|
|||||||
}
|
}
|
||||||
|
|
||||||
var composer = new Composer();
|
var composer = new Composer();
|
||||||
composer.Style("yellow", "DESCRIPTION:").LineBreak();
|
composer.Style("yellow", $"{resources.Description}:").LineBreak();
|
||||||
composer.Text(command.Description).LineBreak();
|
composer.Text(command.Description).LineBreak();
|
||||||
yield return composer.LineBreak();
|
yield return composer.LineBreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetUsage(CommandModel model, CommandInfo? command)
|
/// <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();
|
var composer = new Composer();
|
||||||
composer.Style("yellow", "USAGE:").LineBreak();
|
composer.Style("yellow", $"{resources.Usage}:").LineBreak();
|
||||||
composer.Tab().Text(model.GetApplicationName());
|
composer.Tab().Text(model.ApplicationName);
|
||||||
|
|
||||||
var parameters = new List<string>();
|
var parameters = new List<string>();
|
||||||
|
|
||||||
if (command == null)
|
if (command == null)
|
||||||
{
|
{
|
||||||
parameters.Add("[grey][[OPTIONS]][/]");
|
parameters.Add($"[grey][[{resources.Options}]][/]");
|
||||||
parameters.Add("[aqua]<COMMAND>[/]");
|
parameters.Add($"[aqua]<{resources.Command}>[/]");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -132,18 +191,18 @@ internal static class HelpWriter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.Parameters.OfType<CommandArgument>().Any())
|
if (current.Parameters.OfType<ICommandArgument>().Any())
|
||||||
{
|
{
|
||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
{
|
{
|
||||||
foreach (var argument in current.Parameters.OfType<CommandArgument>()
|
foreach (var argument in current.Parameters.OfType<ICommandArgument>()
|
||||||
.Where(a => a.Required).OrderBy(a => a.Position).ToArray())
|
.Where(a => a.Required).OrderBy(a => a.Position).ToArray())
|
||||||
{
|
{
|
||||||
parameters.Add($"[aqua]<{argument.Value.EscapeMarkup()}>[/]");
|
parameters.Add($"[aqua]<{argument.Value.EscapeMarkup()}>[/]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var optionalArguments = current.Parameters.OfType<CommandArgument>().Where(x => !x.Required).ToArray();
|
var optionalArguments = current.Parameters.OfType<ICommandArgument>().Where(x => !x.Required).ToArray();
|
||||||
if (optionalArguments.Length > 0 || !isCurrent)
|
if (optionalArguments.Length > 0 || !isCurrent)
|
||||||
{
|
{
|
||||||
foreach (var optionalArgument in optionalArguments)
|
foreach (var optionalArgument in optionalArguments)
|
||||||
@@ -155,13 +214,31 @@ internal static class HelpWriter
|
|||||||
|
|
||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
{
|
{
|
||||||
parameters.Add("[grey][[OPTIONS]][/]");
|
parameters.Add($"[grey][[{resources.Options}]][/]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.IsBranch)
|
if (command.IsBranch && command.DefaultCommand == null)
|
||||||
{
|
{
|
||||||
parameters.Add("[aqua]<COMMAND>[/]");
|
// 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
|
||||||
|
// 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}]][/]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,37 +249,48 @@ internal static class HelpWriter
|
|||||||
{
|
{
|
||||||
composer,
|
composer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetExamples(CommandModel model, CommandInfo? 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;
|
var maxExamples = int.MaxValue;
|
||||||
|
|
||||||
var examples = command?.Examples ?? model.Examples ?? new List<string[]>();
|
var examples = command?.Examples?.ToList() ?? model.Examples?.ToList() ?? new List<string[]>();
|
||||||
if (examples.Count == 0)
|
if (examples.Count == 0)
|
||||||
{
|
{
|
||||||
// Since we're not checking direct examples,
|
// Since we're not checking direct examples,
|
||||||
// make sure that we limit the number of examples.
|
// make sure that we limit the number of examples.
|
||||||
maxExamples = 5;
|
maxExamples = MaximumIndirectExamples;
|
||||||
|
|
||||||
// Get the current root command.
|
// Start at the current command (if exists)
|
||||||
var root = command ?? (ICommandContainer)model;
|
// or alternatively commence at the model.
|
||||||
var queue = new Queue<ICommandContainer>(new[] { root });
|
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.
|
// As soon as a node contains commands, bail.
|
||||||
while (queue.Count > 0)
|
while (queue.Count > 0)
|
||||||
{
|
{
|
||||||
var current = queue.Dequeue();
|
var current = queue.Dequeue();
|
||||||
|
|
||||||
foreach (var cmd in current.Commands.Where(x => !x.IsHidden))
|
foreach (var child in current.Commands.Where(x => !x.IsHidden))
|
||||||
{
|
{
|
||||||
if (cmd.Examples.Count > 0)
|
if (child.Examples.Count > 0)
|
||||||
{
|
{
|
||||||
examples.AddRange(cmd.Examples);
|
examples.AddRange(child.Examples);
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.Enqueue(cmd);
|
queue.Enqueue(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examples.Count >= maxExamples)
|
if (examples.Count >= maxExamples)
|
||||||
@@ -212,16 +300,16 @@ internal static class HelpWriter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examples.Count > 0)
|
if (Math.Min(maxExamples, examples.Count) > 0)
|
||||||
{
|
{
|
||||||
var composer = new Composer();
|
var composer = new Composer();
|
||||||
composer.LineBreak();
|
composer.LineBreak();
|
||||||
composer.Style("yellow", "EXAMPLES:").LineBreak();
|
composer.Style("yellow", $"{resources.Examples}:").LineBreak();
|
||||||
|
|
||||||
for (var index = 0; index < Math.Min(maxExamples, examples.Count); index++)
|
for (var index = 0; index < Math.Min(maxExamples, examples.Count); index++)
|
||||||
{
|
{
|
||||||
var args = string.Join(" ", examples[index]);
|
var args = string.Join(" ", examples[index]);
|
||||||
composer.Tab().Text(model.GetApplicationName()).Space().Style("grey", args);
|
composer.Tab().Text(model.ApplicationName).Space().Style("grey", args);
|
||||||
composer.LineBreak();
|
composer.LineBreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,9 +317,15 @@ internal static class HelpWriter
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Array.Empty<IRenderable>();
|
return Array.Empty<IRenderable>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetArguments(CommandInfo? command)
|
/// <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);
|
var arguments = HelpArgument.Get(command);
|
||||||
if (arguments.Count == 0)
|
if (arguments.Count == 0)
|
||||||
@@ -242,7 +336,7 @@ internal static class HelpWriter
|
|||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
new Markup("[yellow]ARGUMENTS:[/]"),
|
new Markup($"[yellow]{resources.Arguments}:[/]"),
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -267,12 +361,18 @@ internal static class HelpWriter
|
|||||||
result.Add(grid);
|
result.Add(grid);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetOptions(CommandModel model, CommandInfo? command, bool writeDefaultValues)
|
/// <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)
|
||||||
{
|
{
|
||||||
// Collect all options into a single structure.
|
// Collect all options into a single structure.
|
||||||
var parameters = HelpOption.Get(model, command);
|
var parameters = HelpOption.Get(command, resources);
|
||||||
if (parameters.Count == 0)
|
if (parameters.Count == 0)
|
||||||
{
|
{
|
||||||
return Array.Empty<IRenderable>();
|
return Array.Empty<IRenderable>();
|
||||||
@@ -281,12 +381,12 @@ internal static class HelpWriter
|
|||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
new Markup("[yellow]OPTIONS:[/]"),
|
new Markup($"[yellow]{resources.Options}:[/]"),
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
};
|
};
|
||||||
|
|
||||||
var helpOptions = parameters.ToArray();
|
var helpOptions = parameters.ToArray();
|
||||||
var defaultValueColumn = writeDefaultValues && helpOptions.Any(e => e.DefaultValue != null);
|
var defaultValueColumn = ShowOptionDefaultValues && helpOptions.Any(e => e.DefaultValue != null);
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true });
|
grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true });
|
||||||
@@ -340,7 +440,7 @@ internal static class HelpWriter
|
|||||||
|
|
||||||
if (defaultValueColumn)
|
if (defaultValueColumn)
|
||||||
{
|
{
|
||||||
grid.AddRow(" ", "[lime]DEFAULT[/]", " ");
|
grid.AddRow(" ", $"[lime]{resources.Default}[/]", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var option in helpOptions)
|
foreach (var option in helpOptions)
|
||||||
@@ -369,14 +469,20 @@ internal static class HelpWriter
|
|||||||
result.Add(grid);
|
result.Add(grid);
|
||||||
|
|
||||||
return result;
|
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>
|
||||||
|
/// <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;
|
||||||
|
|
||||||
private static IEnumerable<IRenderable> GetCommands(
|
var commands = isDefaultCommand ? model.Commands : commandContainer.Commands;
|
||||||
CommandModel model,
|
|
||||||
ICommandContainer command,
|
|
||||||
bool isDefaultCommand)
|
|
||||||
{
|
|
||||||
var commands = isDefaultCommand ? model.Commands : command.Commands;
|
|
||||||
commands = commands.Where(x => !x.IsHidden).ToList();
|
commands = commands.Where(x => !x.IsHidden).ToList();
|
||||||
|
|
||||||
if (commands.Count == 0)
|
if (commands.Count == 0)
|
||||||
@@ -387,7 +493,7 @@ internal static class HelpWriter
|
|||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
new Markup("[yellow]COMMANDS:[/]"),
|
new Markup($"[yellow]{resources.Commands}:[/]"),
|
||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -407,7 +513,7 @@ internal static class HelpWriter
|
|||||||
arguments.Space();
|
arguments.Space();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.TrimTrailingPeriod)
|
if (TrimTrailingPeriod)
|
||||||
{
|
{
|
||||||
grid.AddRow(
|
grid.AddRow(
|
||||||
arguments.ToString().TrimEnd(),
|
arguments.ToString().TrimEnd(),
|
||||||
@@ -424,5 +530,16 @@ internal static class HelpWriter
|
|||||||
result.Add(grid);
|
result.Add(grid);
|
||||||
|
|
||||||
return result;
|
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>
|
||||||
|
public virtual IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo? command)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
131
src/Spectre.Console.Cli/Help/HelpProviderResources.cs
Normal file
131
src/Spectre.Console.Cli/Help/HelpProviderResources.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Cli.Help;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
internal class HelpProviderResources
|
||||||
|
{
|
||||||
|
private readonly ResourceManager resourceManager = new ResourceManager("Spectre.Console.Cli.Resources.HelpProvider", typeof(HelpProvider).Assembly);
|
||||||
|
private readonly CultureInfo? resourceCulture = null;
|
||||||
|
|
||||||
|
public HelpProviderResources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HelpProviderResources(CultureInfo? culture)
|
||||||
|
{
|
||||||
|
resourceCulture = culture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for ARGUMENTS.
|
||||||
|
/// </summary>
|
||||||
|
internal string Arguments
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Arguments", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for COMMAND.
|
||||||
|
/// </summary>
|
||||||
|
internal string Command
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Command", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for COMMANDS.
|
||||||
|
/// </summary>
|
||||||
|
internal string Commands
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Commands", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for DEFAULT.
|
||||||
|
/// </summary>
|
||||||
|
internal string Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Default", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for DESCRIPTION.
|
||||||
|
/// </summary>
|
||||||
|
internal string Description
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Description", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for EXAMPLES.
|
||||||
|
/// </summary>
|
||||||
|
internal string Examples
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Examples", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for OPTIONS.
|
||||||
|
/// </summary>
|
||||||
|
internal string Options
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Options", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for Prints help information.
|
||||||
|
/// </summary>
|
||||||
|
internal string PrintHelpDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("PrintHelpDescription", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for Prints version information.
|
||||||
|
/// </summary>
|
||||||
|
internal string PrintVersionDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("PrintVersionDescription", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the localised string for USAGE.
|
||||||
|
/// </summary>
|
||||||
|
internal string Usage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceManager.GetString("Usage", resourceCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Spectre.Console.Cli/Help/ICommandArgument.cs
Normal file
17
src/Spectre.Console.Cli/Help/ICommandArgument.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Spectre.Console.Cli.Help;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a command argument.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICommandArgument : ICommandParameter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of the argument.
|
||||||
|
/// </summary>
|
||||||
|
string Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position of the argument.
|
||||||
|
/// </summary>
|
||||||
|
int Position { get; }
|
||||||
|
}
|
||||||
25
src/Spectre.Console.Cli/Help/ICommandContainer.cs
Normal file
25
src/Spectre.Console.Cli/Help/ICommandContainer.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace Spectre.Console.Cli.Help;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a command container.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICommandContainer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all the examples for the container.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<string[]> Examples { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all commands in the container.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<ICommandInfo> Commands { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default command for the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns null if a default command has not been set.
|
||||||
|
/// </remarks>
|
||||||
|
ICommandInfo? DefaultCommand { get; }
|
||||||
|
}
|
||||||
42
src/Spectre.Console.Cli/Help/ICommandInfo.cs
Normal file
42
src/Spectre.Console.Cli/Help/ICommandInfo.cs
Normal file
@@ -0,0 +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; }
|
||||||
|
}
|
||||||
23
src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs
Normal file
23
src/Spectre.Console.Cli/Help/ICommandInfoExtensions.cs
Normal file
@@ -0,0 +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();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Spectre.Console.Cli/Help/ICommandModel.cs
Normal file
12
src/Spectre.Console.Cli/Help/ICommandModel.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Spectre.Console.Cli.Help;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a command model.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICommandModel : ICommandContainer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the application.
|
||||||
|
/// </summary>
|
||||||
|
string ApplicationName { get; }
|
||||||
|
}
|
||||||
27
src/Spectre.Console.Cli/Help/ICommandOption.cs
Normal file
27
src/Spectre.Console.Cli/Help/ICommandOption.cs
Normal file
@@ -0,0 +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; }
|
||||||
|
}
|
||||||
32
src/Spectre.Console.Cli/Help/ICommandParameter.cs
Normal file
32
src/Spectre.Console.Cli/Help/ICommandParameter.cs
Normal file
@@ -0,0 +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; }
|
||||||
|
}
|
||||||
20
src/Spectre.Console.Cli/Help/IHelpProvider.cs
Normal file
20
src/Spectre.Console.Cli/Help/IHelpProvider.cs
Normal file
@@ -0,0 +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.
|
||||||
|
/// </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>
|
||||||
|
IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command);
|
||||||
|
}
|
||||||
@@ -5,6 +5,17 @@ namespace Spectre.Console.Cli;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICommandAppSettings
|
public interface ICommandAppSettings
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the culture.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Text displayed by <see cref="Help.HelpProvider"/> can be localised, but defaults to English.
|
||||||
|
/// Setting this property informs the resource manager which culture to use when fetching strings.
|
||||||
|
/// English will be used when a culture has not been specified (ie. this property is null)
|
||||||
|
/// or a string has not been localised for the specified culture.
|
||||||
|
/// </remarks>
|
||||||
|
CultureInfo? Culture { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the application name.
|
/// Gets or sets the application name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -13,12 +24,22 @@ public interface ICommandAppSettings
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the application version (use it to override auto-detected value).
|
/// Gets or sets the application version (use it to override auto-detected value).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string? ApplicationVersion { get; set; }
|
string? ApplicationVersion { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether any default values for command options are shown in the help text.
|
/// Gets or sets a value indicating how many examples from direct children to show in the help text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ShowOptionDefaultValues { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
||||||
@@ -41,11 +62,6 @@ public interface ICommandAppSettings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
CaseSensitivity CaseSensitivity { get; set; }
|
CaseSensitivity CaseSensitivity { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether trailing period of a description is trimmed.
|
|
||||||
/// </summary>
|
|
||||||
bool TrimTrailingPeriod { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether or not parsing is strict.
|
/// Gets or sets a value indicating whether or not parsing is strict.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -4,7 +4,20 @@ namespace Spectre.Console.Cli;
|
|||||||
/// Represents a configurator.
|
/// Represents a configurator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IConfigurator
|
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>
|
/// <summary>
|
||||||
/// Gets the command app settings.
|
/// Gets the command app settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -35,6 +48,16 @@ public interface IConfigurator
|
|||||||
ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||||
where TSettings : CommandSettings;
|
where TSettings : CommandSettings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a command that executes an async delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TSettings">The command setting type.</typeparam>
|
||||||
|
/// <param name="name">The name of the command.</param>
|
||||||
|
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||||
|
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||||
|
ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func)
|
||||||
|
where TSettings : CommandSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a command branch.
|
/// Adds a command branch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -43,5 +66,5 @@ public interface IConfigurator
|
|||||||
/// <param name="action">The command branch configurator.</param>
|
/// <param name="action">The command branch configurator.</param>
|
||||||
/// <returns>A branch configurator that can be used to configure the branch further.</returns>
|
/// <returns>A branch configurator that can be used to configure the branch further.</returns>
|
||||||
IBranchConfigurator AddBranch<TSettings>(string name, Action<IConfigurator<TSettings>> action)
|
IBranchConfigurator AddBranch<TSettings>(string name, Action<IConfigurator<TSettings>> action)
|
||||||
where TSettings : CommandSettings;
|
where TSettings : CommandSettings;
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ public interface IConfigurator<in TSettings>
|
|||||||
/// Adds an example of how to use the branch.
|
/// Adds an example of how to use the branch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The example arguments.</param>
|
/// <param name="args">The example arguments.</param>
|
||||||
void AddExample(string[] args);
|
void AddExample(params string[] args);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a default command.
|
/// Adds a default command.
|
||||||
@@ -57,6 +57,16 @@ public interface IConfigurator<in TSettings>
|
|||||||
ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||||
where TDerivedSettings : TSettings;
|
where TDerivedSettings : TSettings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a command that executes an async delegate.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDerivedSettings">The derived command setting type.</typeparam>
|
||||||
|
/// <param name="name">The name of the command.</param>
|
||||||
|
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||||
|
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||||
|
ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func)
|
||||||
|
where TDerivedSettings : TSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a command branch.
|
/// Adds a command branch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -78,28 +78,16 @@ internal static class CommandValueResolver
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var (converter, stringConstructor) = GetConverter(lookup, binder, resolver, mapped.Parameter);
|
|
||||||
if (converter == null)
|
|
||||||
{
|
|
||||||
throw CommandRuntimeException.NoConverterFound(mapped.Parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
object? value;
|
object? value;
|
||||||
|
var converter = GetConverter(lookup, binder, resolver, mapped.Parameter);
|
||||||
var mappedValue = mapped.Value ?? string.Empty;
|
var mappedValue = mapped.Value ?? string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
try
|
value = converter.ConvertFrom(mappedValue);
|
||||||
{
|
|
||||||
value = converter.ConvertFromInvariantString(mappedValue);
|
|
||||||
}
|
|
||||||
catch (NotSupportedException) when (stringConstructor != null)
|
|
||||||
{
|
|
||||||
value = stringConstructor.Invoke(new object[] { mappedValue });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception exception) when (exception is not CommandRuntimeException)
|
catch (Exception exception) when (exception is not CommandRuntimeException)
|
||||||
{
|
{
|
||||||
throw CommandRuntimeException.ConversionFailed(mapped, converter, exception);
|
throw CommandRuntimeException.ConversionFailed(mapped, converter.TypeConverter, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the value to the parameter.
|
// Assign the value to the parameter.
|
||||||
@@ -130,17 +118,14 @@ internal static class CommandValueResolver
|
|||||||
{
|
{
|
||||||
if (result != null && result.GetType() != parameter.ParameterType)
|
if (result != null && result.GetType() != parameter.ParameterType)
|
||||||
{
|
{
|
||||||
var (converter, _) = GetConverter(lookup, binder, resolver, parameter);
|
var converter = GetConverter(lookup, binder, resolver, parameter);
|
||||||
if (converter != null)
|
result = result is Array array ? ConvertArray(array, converter) : converter.ConvertFrom(result);
|
||||||
{
|
|
||||||
result = result is Array array ? ConvertArray(array, converter) : converter.ConvertFrom(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Array ConvertArray(Array sourceArray, TypeConverter converter)
|
private static Array ConvertArray(Array sourceArray, SmartConverter converter)
|
||||||
{
|
{
|
||||||
Array? targetArray = null;
|
Array? targetArray = null;
|
||||||
for (var i = 0; i < sourceArray.Length; i++)
|
for (var i = 0; i < sourceArray.Length; i++)
|
||||||
@@ -161,14 +146,8 @@ internal static class CommandValueResolver
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "It's OK")]
|
[SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "It's OK")]
|
||||||
private static (TypeConverter? Converter, ConstructorInfo? StringConstructor) GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
|
private static SmartConverter GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
|
||||||
{
|
{
|
||||||
static ConstructorInfo? GetStringConstructor(Type type)
|
|
||||||
{
|
|
||||||
var constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null);
|
|
||||||
return constructor?.GetParameters()[0].ParameterType == typeof(string) ? constructor : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameter.Converter == null)
|
if (parameter.Converter == null)
|
||||||
{
|
{
|
||||||
if (parameter.ParameterType.IsArray)
|
if (parameter.ParameterType.IsArray)
|
||||||
@@ -180,7 +159,7 @@ internal static class CommandValueResolver
|
|||||||
throw new InvalidOperationException("Could not get element type");
|
throw new InvalidOperationException("Could not get element type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (TypeDescriptor.GetConverter(elementType), GetStringConstructor(elementType));
|
return new SmartConverter(TypeDescriptor.GetConverter(elementType), elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameter.IsFlagValue())
|
if (parameter.IsFlagValue())
|
||||||
@@ -200,13 +179,51 @@ internal static class CommandValueResolver
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return a converter for the flag element type.
|
// Return a converter for the flag element type.
|
||||||
return (TypeDescriptor.GetConverter(value.Type), GetStringConstructor(value.Type));
|
return new SmartConverter(TypeDescriptor.GetConverter(value.Type), value.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (TypeDescriptor.GetConverter(parameter.ParameterType), GetStringConstructor(parameter.ParameterType));
|
return new SmartConverter(TypeDescriptor.GetConverter(parameter.ParameterType), parameter.ParameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = Type.GetType(parameter.Converter.ConverterTypeName);
|
var type = Type.GetType(parameter.Converter.ConverterTypeName);
|
||||||
return (resolver.Resolve(type) as TypeConverter, null);
|
if (type == null || resolver.Resolve(type) is not TypeConverter typeConverter)
|
||||||
|
{
|
||||||
|
throw CommandRuntimeException.NoConverterFound(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SmartConverter(typeConverter, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert inputs using the given <see cref="TypeConverter"/> and fallback to finding a constructor taking a single argument of the input type.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ref struct SmartConverter
|
||||||
|
{
|
||||||
|
public SmartConverter(TypeConverter typeConverter, Type type)
|
||||||
|
{
|
||||||
|
TypeConverter = typeConverter;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeConverter TypeConverter { get; }
|
||||||
|
private Type Type { get; }
|
||||||
|
|
||||||
|
public object? ConvertFrom(object input)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return TypeConverter.ConvertFrom(null, CultureInfo.InvariantCulture, input);
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
var constructor = Type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[] { input.GetType() }, null);
|
||||||
|
if (constructor == null)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructor.Invoke(new[] { input });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,92 +1,91 @@
|
|||||||
namespace Spectre.Console.Cli;
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
_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);
|
|
||||||
|
|
||||||
// No default command?
|
|
||||||
if (model.DefaultCommand == null)
|
|
||||||
{
|
|
||||||
// Got at least one argument?
|
|
||||||
var firstArgument = args.FirstOrDefault();
|
|
||||||
if (firstArgument != null)
|
|
||||||
{
|
|
||||||
// Asking for version? Kind of a hack, but it's alright.
|
|
||||||
// We should probably make this a bit better in the future.
|
|
||||||
if (firstArgument.Equals("--version", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
firstArgument.Equals("-v", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Currently the root?
|
|
||||||
if (parsedResult?.Tree == null)
|
|
||||||
{
|
|
||||||
// Display help.
|
|
||||||
configuration.Settings.Console.SafeRender(HelpWriter.Write(model, configuration.Settings.ShowOptionDefaultValues));
|
|
||||||
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(HelpWriter.WriteCommand(model, leaf.Command, configuration.Settings.ShowOptionDefaultValues));
|
|
||||||
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(HelpWriter.WriteCommand(model, leaf.Command, configuration.Settings.ShowOptionDefaultValues));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the arguments with the container.
|
|
||||||
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
|
|
||||||
_registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining);
|
|
||||||
|
|
||||||
// Create the resolver and the context.
|
|
||||||
using (var resolver = new TypeResolverAdapter(_registrar.Build()))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CommandTreeParserResult? ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable<string> args)
|
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 parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments);
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ internal sealed class CommandExecutor
|
|||||||
var lastParsedCommand = lastParsedLeaf?.Command;
|
var lastParsedCommand = lastParsedLeaf?.Command;
|
||||||
if (lastParsedLeaf != null && lastParsedCommand != null &&
|
if (lastParsedLeaf != null && lastParsedCommand != null &&
|
||||||
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
|
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
|
||||||
lastParsedCommand.DefaultCommand != null)
|
lastParsedCommand.DefaultCommand != null)
|
||||||
{
|
{
|
||||||
// Insert this branch's default command into the command line
|
// Insert this branch's default command into the command line
|
||||||
// arguments and try again to see if it will parse.
|
// arguments and try again to see if it will parse.
|
||||||
@@ -113,34 +112,35 @@ internal sealed class CommandExecutor
|
|||||||
|
|
||||||
return parsedResult;
|
return parsedResult;
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CS8603 // Possible null reference return.
|
||||||
private static string ResolveApplicationVersion(IConfiguration configuration)
|
|
||||||
{
|
private static string ResolveApplicationVersion(IConfiguration configuration)
|
||||||
return
|
{
|
||||||
configuration.Settings.ApplicationVersion ?? // potential override
|
return
|
||||||
VersionHelper.GetVersion(Assembly.GetEntryAssembly());
|
configuration.Settings.ApplicationVersion ?? // potential override
|
||||||
}
|
VersionHelper.GetVersion(Assembly.GetEntryAssembly());
|
||||||
|
}
|
||||||
private static Task<int> Execute(
|
|
||||||
CommandTree leaf,
|
private static Task<int> Execute(
|
||||||
CommandTree tree,
|
CommandTree leaf,
|
||||||
CommandContext context,
|
CommandTree tree,
|
||||||
ITypeResolver resolver,
|
CommandContext context,
|
||||||
IConfiguration configuration)
|
ITypeResolver resolver,
|
||||||
{
|
IConfiguration configuration)
|
||||||
// Bind the command tree against the settings.
|
{
|
||||||
var settings = CommandBinder.Bind(tree, leaf.Command.SettingsType, resolver);
|
// Bind the command tree against the settings.
|
||||||
configuration.Settings.Interceptor?.Intercept(context, 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);
|
// Create and validate the command.
|
||||||
var validationResult = command.Validate(context, settings);
|
var command = leaf.CreateCommand(resolver);
|
||||||
if (!validationResult.Successful)
|
var validationResult = command.Validate(context, settings);
|
||||||
{
|
if (!validationResult.Successful)
|
||||||
throw CommandRuntimeException.ValidationFailed(validationResult);
|
{
|
||||||
}
|
throw CommandRuntimeException.ValidationFailed(validationResult);
|
||||||
|
}
|
||||||
// Execute the command.
|
|
||||||
return command.Execute(context, settings);
|
// Execute the command.
|
||||||
}
|
return command.Execute(context, settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ internal sealed class ExplainCommand : Command<ExplainCommand.Settings>
|
|||||||
public bool IncludeHidden { get; }
|
public bool IncludeHidden { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
public override int Execute(CommandContext context, Settings settings)
|
||||||
{
|
{
|
||||||
var tree = new Tree("CLI Configuration");
|
var tree = new Tree("CLI Configuration");
|
||||||
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ internal sealed class VersionCommand : Command<VersionCommand.Settings>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
public override int Execute(CommandContext context, Settings settings)
|
||||||
{
|
{
|
||||||
_writer.MarkupLine(
|
_writer.MarkupLine(
|
||||||
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
public override int Execute(CommandContext context, Settings settings)
|
||||||
{
|
{
|
||||||
_writer.Write(Serialize(_model), Style.Plain);
|
_writer.Write(Serialize(_model), Style.Plain);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ internal sealed class ComponentRegistry : IDisposable
|
|||||||
{
|
{
|
||||||
if (!_registrations.ContainsKey(type))
|
if (!_registrations.ContainsKey(type))
|
||||||
{
|
{
|
||||||
|
// Only add each registration type once.
|
||||||
_registrations.Add(type, new HashSet<ComponentRegistration>());
|
_registrations.Add(type, new HashSet<ComponentRegistration>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
internal sealed class CommandAppSettings : ICommandAppSettings
|
internal sealed class CommandAppSettings : ICommandAppSettings
|
||||||
{
|
{
|
||||||
|
public CultureInfo? Culture { get; set; }
|
||||||
public string? ApplicationName { get; set; }
|
public string? ApplicationName { get; set; }
|
||||||
public string? ApplicationVersion { get; set; }
|
public string? ApplicationVersion { get; set; }
|
||||||
public bool ShowOptionDefaultValues { get; set; }
|
public int MaximumIndirectExamples { get; set; }
|
||||||
|
public bool ShowOptionDefaultValues { get; set; }
|
||||||
public IAnsiConsole? Console { get; set; }
|
public IAnsiConsole? Console { get; set; }
|
||||||
public ICommandInterceptor? Interceptor { get; set; }
|
public ICommandInterceptor? Interceptor { get; set; }
|
||||||
public ITypeRegistrarFrontend Registrar { get; set; }
|
public ITypeRegistrarFrontend Registrar { get; set; }
|
||||||
@@ -18,13 +20,14 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
|||||||
public ParsingMode ParsingMode =>
|
public ParsingMode ParsingMode =>
|
||||||
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
|
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
|
||||||
|
|
||||||
public Func<Exception, int>? ExceptionHandler { get; set; }
|
public Func<Exception, int>? ExceptionHandler { get; set; }
|
||||||
|
|
||||||
public CommandAppSettings(ITypeRegistrar registrar)
|
public CommandAppSettings(ITypeRegistrar registrar)
|
||||||
{
|
{
|
||||||
Registrar = new TypeRegistrar(registrar);
|
Registrar = new TypeRegistrar(registrar);
|
||||||
CaseSensitivity = CaseSensitivity.All;
|
CaseSensitivity = CaseSensitivity.All;
|
||||||
ShowOptionDefaultValues = true;
|
ShowOptionDefaultValues = true;
|
||||||
|
MaximumIndirectExamples = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
||||||
|
|||||||
@@ -19,11 +19,24 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
|||||||
Settings = new CommandAppSettings(registrar);
|
Settings = new CommandAppSettings(registrar);
|
||||||
Examples = new List<string[]>();
|
Examples = new List<string[]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetHelpProvider(IHelpProvider helpProvider)
|
||||||
|
{
|
||||||
|
// 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)
|
public void AddExample(params string[] args)
|
||||||
{
|
{
|
||||||
Examples.Add(args);
|
Examples.Add(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfiguredCommand SetDefaultCommand<TDefaultCommand>()
|
public ConfiguredCommand SetDefaultCommand<TDefaultCommand>()
|
||||||
where TDefaultCommand : class, ICommand
|
where TDefaultCommand : class, ICommand
|
||||||
@@ -42,6 +55,14 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
|||||||
|
|
||||||
public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
|
{
|
||||||
|
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||||
|
name, (context, settings) => Task.FromResult(func(context, (TSettings)settings))));
|
||||||
|
return new CommandConfigurator(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func)
|
||||||
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||||
name, (context, settings) => func(context, (TSettings)settings)));
|
name, (context, settings) => func(context, (TSettings)settings)));
|
||||||
|
|||||||
@@ -1,100 +1,111 @@
|
|||||||
namespace Spectre.Console.Cli;
|
namespace Spectre.Console.Cli;
|
||||||
|
|
||||||
internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConfigurator<TSettings>
|
internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConfigurator<TSettings>
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
private readonly ConfiguredCommand _command;
|
private readonly ConfiguredCommand _command;
|
||||||
private readonly ITypeRegistrar? _registrar;
|
private readonly ITypeRegistrar? _registrar;
|
||||||
|
|
||||||
public Configurator(ConfiguredCommand command, ITypeRegistrar? registrar)
|
public Configurator(ConfiguredCommand command, ITypeRegistrar? registrar)
|
||||||
{
|
{
|
||||||
_command = command;
|
_command = command;
|
||||||
_registrar = registrar;
|
_registrar = registrar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDescription(string description)
|
public void SetDescription(string description)
|
||||||
{
|
{
|
||||||
_command.Description = description;
|
_command.Description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddExample(string[] args)
|
public void AddExample(params string[] args)
|
||||||
{
|
{
|
||||||
_command.Examples.Add(args);
|
_command.Examples.Add(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDefaultCommand<TDefaultCommand>()
|
public void SetDefaultCommand<TDefaultCommand>()
|
||||||
where TDefaultCommand : class, ICommandLimiter<TSettings>
|
where TDefaultCommand : class, ICommandLimiter<TSettings>
|
||||||
{
|
{
|
||||||
var defaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
|
var defaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
|
||||||
CliConstants.DefaultCommandName, isDefaultCommand: true);
|
CliConstants.DefaultCommandName, isDefaultCommand: true);
|
||||||
|
|
||||||
_command.Children.Add(defaultCommand);
|
_command.Children.Add(defaultCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HideBranch()
|
public void HideBranch()
|
||||||
{
|
{
|
||||||
_command.IsHidden = true;
|
_command.IsHidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommandConfigurator AddCommand<TCommand>(string name)
|
public ICommandConfigurator AddCommand<TCommand>(string name)
|
||||||
where TCommand : class, ICommandLimiter<TSettings>
|
where TCommand : class, ICommandLimiter<TSettings>
|
||||||
{
|
{
|
||||||
var command = ConfiguredCommand.FromType<TCommand>(name, isDefaultCommand: false);
|
var command = ConfiguredCommand.FromType<TCommand>(name, isDefaultCommand: false);
|
||||||
var configurator = new CommandConfigurator(command);
|
var configurator = new CommandConfigurator(command);
|
||||||
|
|
||||||
_command.Children.Add(command);
|
_command.Children.Add(command);
|
||||||
return configurator;
|
return configurator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||||
where TDerivedSettings : TSettings
|
where TDerivedSettings : TSettings
|
||||||
{
|
{
|
||||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||||
name, (context, settings) => func(context, (TDerivedSettings)settings));
|
name, (context, settings) => Task.FromResult(func(context, (TDerivedSettings)settings)));
|
||||||
|
|
||||||
_command.Children.Add(command);
|
_command.Children.Add(command);
|
||||||
return new CommandConfigurator(command);
|
return new CommandConfigurator(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBranchConfigurator AddBranch<TDerivedSettings>(string name, Action<IConfigurator<TDerivedSettings>> action)
|
public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func)
|
||||||
where TDerivedSettings : TSettings
|
where TDerivedSettings : TSettings
|
||||||
{
|
{
|
||||||
var command = ConfiguredCommand.FromBranch<TDerivedSettings>(name);
|
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||||
action(new Configurator<TDerivedSettings>(command, _registrar));
|
name, (context, settings) => func(context, (TDerivedSettings)settings));
|
||||||
var added = _command.Children.AddAndReturn(command);
|
|
||||||
return new BranchConfigurator(added);
|
_command.Children.Add(command);
|
||||||
}
|
return new CommandConfigurator(command);
|
||||||
|
}
|
||||||
ICommandConfigurator IUnsafeConfigurator.AddCommand(string name, Type command)
|
|
||||||
{
|
public IBranchConfigurator AddBranch<TDerivedSettings>(string name, Action<IConfigurator<TDerivedSettings>> action)
|
||||||
var method = GetType().GetMethod("AddCommand");
|
where TDerivedSettings : TSettings
|
||||||
if (method == null)
|
{
|
||||||
{
|
var command = ConfiguredCommand.FromBranch<TDerivedSettings>(name);
|
||||||
throw new CommandConfigurationException("Could not find AddCommand by reflection.");
|
action(new Configurator<TDerivedSettings>(command, _registrar));
|
||||||
}
|
var added = _command.Children.AddAndReturn(command);
|
||||||
|
return new BranchConfigurator(added);
|
||||||
method = method.MakeGenericMethod(command);
|
}
|
||||||
|
|
||||||
if (!(method.Invoke(this, new object[] { name }) is ICommandConfigurator result))
|
ICommandConfigurator IUnsafeConfigurator.AddCommand(string name, Type command)
|
||||||
{
|
{
|
||||||
throw new CommandConfigurationException("Invoking AddCommand returned null.");
|
var method = GetType().GetMethod("AddCommand");
|
||||||
}
|
if (method == null)
|
||||||
|
{
|
||||||
return result;
|
throw new CommandConfigurationException("Could not find AddCommand by reflection.");
|
||||||
}
|
}
|
||||||
|
|
||||||
IBranchConfigurator IUnsafeConfigurator.AddBranch(string name, Type settings, Action<IUnsafeBranchConfigurator> action)
|
method = method.MakeGenericMethod(command);
|
||||||
{
|
|
||||||
var command = ConfiguredCommand.FromBranch(settings, name);
|
if (!(method.Invoke(this, new object[] { name }) is ICommandConfigurator result))
|
||||||
|
{
|
||||||
// Create the configurator.
|
throw new CommandConfigurationException("Invoking AddCommand returned null.");
|
||||||
var configuratorType = typeof(Configurator<>).MakeGenericType(settings);
|
}
|
||||||
if (!(Activator.CreateInstance(configuratorType, new object?[] { command, _registrar }) is IUnsafeBranchConfigurator configurator))
|
|
||||||
{
|
return result;
|
||||||
throw new CommandConfigurationException("Could not create configurator by reflection.");
|
}
|
||||||
}
|
|
||||||
|
IBranchConfigurator IUnsafeConfigurator.AddBranch(string name, Type settings, Action<IUnsafeBranchConfigurator> action)
|
||||||
action(configurator);
|
{
|
||||||
var added = _command.Children.AddAndReturn(command);
|
var command = ConfiguredCommand.FromBranch(settings, name);
|
||||||
return new BranchConfigurator(added);
|
|
||||||
|
// Create the configurator.
|
||||||
|
var configuratorType = typeof(Configurator<>).MakeGenericType(settings);
|
||||||
|
if (!(Activator.CreateInstance(configuratorType, new object?[] { command, _registrar }) is IUnsafeBranchConfigurator configurator))
|
||||||
|
{
|
||||||
|
throw new CommandConfigurationException("Could not create configurator by reflection.");
|
||||||
|
}
|
||||||
|
|
||||||
|
action(configurator);
|
||||||
|
var added = _command.Children.AddAndReturn(command);
|
||||||
|
return new BranchConfigurator(added);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ internal sealed class ConfiguredCommand
|
|||||||
public object? Data { get; set; }
|
public object? Data { get; set; }
|
||||||
public Type? CommandType { get; }
|
public Type? CommandType { get; }
|
||||||
public Type SettingsType { get; }
|
public Type SettingsType { get; }
|
||||||
public Func<CommandContext, CommandSettings, int>? Delegate { get; }
|
public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; }
|
||||||
public bool IsDefaultCommand { get; }
|
public bool IsDefaultCommand { get; }
|
||||||
public bool IsHidden { get; set; }
|
public bool IsHidden { get; set; }
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ internal sealed class ConfiguredCommand
|
|||||||
string name,
|
string name,
|
||||||
Type? commandType,
|
Type? commandType,
|
||||||
Type settingsType,
|
Type settingsType,
|
||||||
Func<CommandContext, CommandSettings, int>? @delegate,
|
Func<CommandContext, CommandSettings, Task<int>>? @delegate,
|
||||||
bool isDefaultCommand)
|
bool isDefaultCommand)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -27,10 +27,10 @@ internal sealed class ConfiguredCommand
|
|||||||
CommandType = commandType;
|
CommandType = commandType;
|
||||||
SettingsType = settingsType;
|
SettingsType = settingsType;
|
||||||
Delegate = @delegate;
|
Delegate = @delegate;
|
||||||
IsDefaultCommand = isDefaultCommand;
|
IsDefaultCommand = isDefaultCommand;
|
||||||
|
|
||||||
// Default commands are always created as hidden.
|
// Default commands are always created as hidden.
|
||||||
IsHidden = IsDefaultCommand;
|
IsHidden = IsDefaultCommand;
|
||||||
|
|
||||||
Children = new List<ConfiguredCommand>();
|
Children = new List<ConfiguredCommand>();
|
||||||
Examples = new List<string[]>();
|
Examples = new List<string[]>();
|
||||||
@@ -60,8 +60,8 @@ internal sealed class ConfiguredCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ConfiguredCommand FromDelegate<TSettings>(
|
public static ConfiguredCommand FromDelegate<TSettings>(
|
||||||
string name, Func<CommandContext, CommandSettings, int>? @delegate = null)
|
string name, Func<CommandContext, CommandSettings, Task<int>>? @delegate = null)
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false);
|
return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
internal sealed class DelegateCommand : ICommand
|
internal sealed class DelegateCommand : ICommand
|
||||||
{
|
{
|
||||||
private readonly Func<CommandContext, CommandSettings, int> _func;
|
private readonly Func<CommandContext, CommandSettings, Task<int>> _func;
|
||||||
|
|
||||||
public DelegateCommand(Func<CommandContext, CommandSettings, int> func)
|
public DelegateCommand(Func<CommandContext, CommandSettings, Task<int>> func)
|
||||||
{
|
{
|
||||||
_func = func;
|
_func = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> Execute(CommandContext context, CommandSettings settings)
|
public Task<int> Execute(CommandContext context, CommandSettings settings)
|
||||||
{
|
{
|
||||||
return Task.FromResult(_func(context, settings));
|
return _func(context, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationResult Validate(CommandContext context, CommandSettings settings)
|
public ValidationResult Validate(CommandContext context, CommandSettings settings)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Spectre.Console.Cli;
|
namespace Spectre.Console.Cli;
|
||||||
|
|
||||||
internal sealed class CommandArgument : CommandParameter
|
internal sealed class CommandArgument : CommandParameter, ICommandArgument
|
||||||
{
|
{
|
||||||
public string Value { get; }
|
public string Value { get; }
|
||||||
public int Position { get; set; }
|
public int Position { get; set; }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user