Add Spectre.Cli to Spectre.Console

* Renames Spectre.Cli to Spectre.Console.Cli.
* Now uses Verify with Spectre.Console.Cli tests.
* Removes some duplicate definitions.

Closes #168
This commit is contained in:
Patrik Svensson 2020-12-23 10:41:29 +01:00 committed by Patrik Svensson
parent 0bbf9b81a9
commit 0ae419326d
361 changed files with 13934 additions and 604 deletions

View File

@ -1,5 +1,5 @@
Title: Appendix
Order: 10
Order: 100
---
<h1>Sections</h1>

View File

@ -0,0 +1,12 @@
Title: CLI
Order: 10
---
<h1>Sections</h1>
<ul>
@foreach (IDocument child in OutputPages.GetChildrenOf(Document))
{
<li>@Html.DocumentLink(child)</li>
}
</ul>

View File

@ -0,0 +1,117 @@
Title: Introduction
Order: 1
---
`Spectre.Console.Cli` is a modern library for parsing command line arguments. While it's extremely
opinionated in what it does, it tries to follow established industry conventions, and draws
its inspiration from applications you use everyday.
# How does it work?
The underlying philosophy behind `Spectre.Console.Cli` is to rely on the .NET type system to
declare the commands, but tie everything together via composition.
Imagine the following command structure:
* dotnet *(executable)*
* add `[PROJECT]`
* package `<PACKAGE_NAME>` --version `<VERSION>`
* reference `<PROJECT_REFERENCE>`
For this I would like to implement the commands (the different levels in the tree that
executes something) separately from the settings (the options, flags and arguments),
which I want to be able to inherit from each other.
## Specify the settings
We start by creating some settings that represents the options, flags and arguments
that we want to act upon.
```csharp
public class AddSettings : CommandSettings
{
[CommandArgument(0, "[PROJECT]")]
public string Project { get; set; }
}
public class AddPackageSettings : AddSettings
{
[CommandArgument(0, "<PACKAGE_NAME>")]
public string PackageName { get; set; }
[CommandOption("-v|--version <VERSION>")]
public string Version { get; set; }
}
public class AddReferenceSettings : AddSettings
{
[CommandArgument(0, "<PROJECT_REFERENCE>")]
public string ProjectReference { get; set; }
}
```
## Specify the commands
Now it's time to specify the commands that act on the settings we created
in the previous step.
```csharp
public class AddPackageCommand : Command<AddPackageSettings>
{
public override int Execute(AddPackageSettings settings, ILookup<string, string> remaining)
{
// Omitted
}
}
public class AddReferenceCommand : Command<AddReferenceSettings>
{
public override int Execute(AddReferenceSettings settings, ILookup<string, string> remaining)
{
// Omitted
}
}
```
## Let's tie it together
Now when we have our commands and settings implemented, we can compose a command tree
that tells the parser how to interpret user input.
```csharp
using Spectre.Console.Cli;
namespace MyApp
{
public static class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.AddBranch<AddSettings>("add", add =>
{
add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference");
});
});
return app.Run(args);
}
}
}
```
# So why this way?
Now you might wonder, why do things like this? Well, for starters the different parts
of the application are separated, while still having the option to share things like options,
flags and arguments between them.
This make the resulting code very clean and easy to navigate, not to mention to unit test.
And most importantly at all, the type system guides me to do the right thing. I can't configure
commands in non-compatible ways, and if I want to add a new top-level `add-package` command
(or move the command completely), it's just a single line to change. This makes it easy to
experiment and makes the CLI experience a first class citizen of your application.

View File

@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "1.0.0-rc0001",
"version": "1.0.0-rc0002",
"commands": [
"dotnet-cake"
]
@ -15,7 +15,7 @@
]
},
"dotnet-example": {
"version": "1.1.0",
"version": "1.2.0",
"commands": [
"dotnet-example"
]

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Borders</Title>
<Description>Demonstrates the different kind of borders.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Calendars</Title>
<Description>Demonstrates how to render calendars.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Canvas</Title>
<Description>Demonstrates how to render pixels and images.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj" />
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="cake.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Charts</Title>
<Description>Demonstrates how to render charts in a console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Delegates
{
public static partial class Program
{
public sealed class BarSettings : CommandSettings
{
[CommandOption("--count")]
[Description("The number of bars to print")]
[DefaultValue(1)]
public int Count { get; set; }
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Delegates</ExampleName>
<ExampleDescription>Demonstrates how to specify commands as delegates.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,39 @@
using System;
using Spectre.Console.Cli;
namespace Delegates
{
public static partial class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.AddDelegate("foo", Foo)
.WithDescription("Foos the bars");
config.AddDelegate<BarSettings>("bar", Bar)
.WithDescription("Bars the foos"); ;
});
return app.Run(args);
}
private static int Foo(CommandContext context)
{
Console.WriteLine("Foo");
return 0;
}
private static int Bar(CommandContext context, BarSettings settings)
{
for (var index = 0; index < settings.Count; index++)
{
Console.WriteLine("Bar");
}
return 0;
}
}
}

View File

@ -0,0 +1,47 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Add a NuGet package reference to the project.")]
public sealed class AddPackageCommand : Command<AddPackageCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PACKAGENAME>")]
[Description("The package reference to add.")]
public string PackageName { get; set; }
[CommandOption("-v|--version <VERSION>")]
[Description("The version of the package to add.")]
public string Version { get; set; }
[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("Add the reference only when targeting a specific framework.")]
public string Framework { get; set; }
[CommandOption("--no-restore")]
[Description("Add the reference without performing restore preview and compatibility check.")]
public bool NoRestore { get; set; }
[CommandOption("--source <SOURCE>")]
[Description("The NuGet package source to use during the restore.")]
public string Source { get; set; }
[CommandOption("--package-directory <PACKAGEDIR>")]
[Description("The directory to restore packages to.")]
public string PackageDirectory { get; set; }
[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public bool Interactive { get; set; }
}
public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
}

View File

@ -0,0 +1,30 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
public sealed class AddReferenceCommand : Command<AddReferenceCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PROJECTPATH>")]
[Description("The package reference to add.")]
public string ProjectPath { get; set; }
[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("Add the reference only when targeting a specific framework.")]
public string Framework { get; set; }
[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public bool Interactive { get; set; }
}
public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Demo.Commands
{
public abstract class AddSettings : CommandSettings
{
[CommandArgument(0, "<PROJECT>")]
[Description("The project file to operate on. If a file is not specified, the command will search the current directory for one.")]
public string Project { get; set; }
}
}

View File

@ -0,0 +1,70 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Build and run a .NET project output.")]
public sealed class RunCommand : Command<RunCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-c|--configuration <CONFIGURATION>")]
[Description("The configuration to run for. The default for most projects is '[grey]Debug[/]'.")]
[DefaultValue("Debug")]
public string Configuration { get; set; }
[CommandOption("-f|--framework <FRAMEWORK>")]
[Description("The target framework to run for. The target framework must also be specified in the project file.")]
public string Framework { get; set; }
[CommandOption("-r|--runtime <RUNTIMEIDENTIFIER>")]
[Description("The target runtime to run for.")]
public string RuntimeIdentifier { get; set; }
[CommandOption("-p|--project <PROJECTPATH>")]
[Description("The path to the project file to run (defaults to the current directory if there is only one project).")]
public string ProjectPath { get; set; }
[CommandOption("--launch-profile <LAUNCHPROFILE>")]
[Description("The name of the launch profile (if any) to use when launching the application.")]
public string LaunchProfile { get; set; }
[CommandOption("--no-launch-profile")]
[Description("Do not attempt to use [grey]launchSettings.json[/] to configure the application.")]
public bool NoLaunchProfile { get; set; }
[CommandOption("--no-build")]
[Description("Do not build the project before running. Implies [grey]--no-restore[/].")]
public bool NoBuild { get; set; }
[CommandOption("--interactive")]
[Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")]
public string Interactive { get; set; }
[CommandOption("--no-restore")]
[Description("Do not restore the project before building.")]
public bool NoRestore { get; set; }
[CommandOption("--verbosity <VERBOSITY>")]
[Description("Set the MSBuild verbosity level. Allowed values are q[grey]uiet[/], m[grey]inimal[/], n[grey]ormal[/], d[grey]etailed[/], and diag[grey]nostic[/].")]
[TypeConverter(typeof(VerbosityConverter))]
[DefaultValue(Verbosity.Normal)]
public Verbosity Verbosity { get; set; }
[CommandOption("--no-dependencies")]
[Description("Do not restore project-to-project references and only restore the specified project.")]
public bool NoDependencies { get; set; }
[CommandOption("--force")]
[Description("Force all dependencies to be resolved even if the last restore was successful. This is equivalent to deleting [grey]project.assets.json[/].")]
public bool Force { get; set; }
}
public override int Execute(CommandContext context, Settings settings)
{
SettingsDumper.Dump(settings);
return 0;
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Launches a web server in the current working directory and serves all files in it.")]
public sealed class ServeCommand : Command<ServeCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-p|--port <PORT>")]
[Description("Port to use. Defaults to [grey]8080[/]. Use [grey]0[/] for a dynamic port.")]
public int Port { get; set; }
[CommandOption("-o|--open-browser [BROWSER]")]
[Description("Open a web browser when the server starts. You can also specify which browser to use. If none is specified, the default one will be used.")]
public FlagValue<string> OpenBrowser { get; set; }
}
public override int Execute(CommandContext context, Settings settings)
{
if (settings.OpenBrowser.IsSet)
{
var browser = settings.OpenBrowser.Value;
if (browser != null)
{
Console.WriteLine($"Open in {browser}");
}
else
{
Console.WriteLine($"Open in default browser.");
}
}
SettingsDumper.Dump(settings);
return 0;
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Demo</ExampleName>
<ExampleDescription>Demonstrates the most common use cases of Spectre.Cli.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@
using Demo.Commands;
using Spectre.Console.Cli;
namespace Demo
{
public static class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.SetApplicationName("fake-dotnet");
config.ValidateExamples();
config.AddExample(new[] { "run", "--no-build" });
// Run
config.AddCommand<RunCommand>("run");
// Add
config.AddBranch<AddSettings>("add", add =>
{
add.SetDescription("Add a package or reference to a .NET project");
add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference");
});
// Serve
config.AddCommand<ServeCommand>("serve")
.WithExample(new[] { "serve", "-o", "firefox" })
.WithExample(new[] { "serve", "--port", "80", "-o", "firefox" });
});
return app.Run(args);
}
}
}

View File

@ -0,0 +1,29 @@
using Spectre.Console;
using Spectre.Console.Cli;
namespace Demo.Utilities
{
public static class SettingsDumper
{
public static void Dump(CommandSettings settings)
{
var table = new Table().RoundedBorder();
table.AddColumn("[grey]Name[/]");
table.AddColumn("[grey]Value[/]");
var properties = settings.GetType().GetProperties();
foreach (var property in properties)
{
var value = property.GetValue(settings)
?.ToString()
?.Replace("[", "[[");
table.AddRow(
property.Name,
value ?? "[grey]null[/]");
}
AnsiConsole.Render(table);
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
namespace Demo
{
public enum Verbosity
{
Quiet,
Minimal,
Normal,
Detailed,
Diagnostic
}
public sealed class VerbosityConverter : TypeConverter
{
private readonly Dictionary<string, Verbosity> _lookup;
public VerbosityConverter()
{
_lookup = new Dictionary<string, Verbosity>(StringComparer.OrdinalIgnoreCase)
{
{ "q", Verbosity.Quiet },
{ "quiet", Verbosity.Quiet },
{ "m", Verbosity.Minimal },
{ "minimal", Verbosity.Minimal },
{ "n", Verbosity.Normal },
{ "normal", Verbosity.Normal },
{ "d", Verbosity.Detailed },
{ "detailed", Verbosity.Detailed },
{ "diag", Verbosity.Diagnostic },
{ "diagnostic", Verbosity.Diagnostic }
};
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string stringValue)
{
var result = _lookup.TryGetValue(stringValue, out var verbosity);
if (!result)
{
const string format = "The value '{0}' is not a valid verbosity.";
var message = string.Format(CultureInfo.InvariantCulture, format, value);
throw new InvalidOperationException(message);
}
return verbosity;
}
throw new NotSupportedException("Can't convert value to verbosity.");
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Dynamic</ExampleName>
<ExampleDescription>Demonstrates how to define dynamic commands.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
using System;
using Spectre.Console.Cli;
namespace Dynamic
{
public sealed class MyCommand : Command
{
public override int Execute(CommandContext context)
{
if (!(context.Data is int data))
{
throw new InvalidOperationException("Command has no associated data.");
}
Console.WriteLine("Value = {0}", data);
return 0;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Linq;
using Spectre.Console.Cli;
namespace Dynamic
{
public static class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
foreach(var index in Enumerable.Range(1, 10))
{
config.AddCommand<MyCommand>($"c{index}")
.WithDescription($"Prints the number {index}")
.WithData(index);
}
});
return app.Run(args);
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Injection.Commands
{
public sealed class DefaultCommand : Command<DefaultCommand.Settings>
{
private readonly IGreeter _greeter;
public sealed class Settings : CommandSettings
{
[CommandOption("-n|--name <NAME>")]
[Description("The person or thing to greet.")]
[DefaultValue("World")]
public string Name { get; set; }
}
public DefaultCommand(IGreeter greeter)
{
_greeter = greeter ?? throw new ArgumentNullException(nameof(greeter));
}
public override int Execute(CommandContext context, Settings settings)
{
_greeter.Greet(settings.Name);
return 0;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace Injection
{
public interface IGreeter
{
void Greet(string name);
}
public sealed class HelloWorldGreeter : IGreeter
{
public void Greet(string name)
{
Console.WriteLine($"Hello {name}!");
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Injection
{
public sealed class TypeRegistrar : ITypeRegistrar
{
private readonly IServiceCollection _builder;
public TypeRegistrar(IServiceCollection builder)
{
_builder = builder;
}
public ITypeResolver Build()
{
return new TypeResolver(_builder.BuildServiceProvider());
}
public void Register(Type service, Type implementation)
{
_builder.AddSingleton(service, implementation);
}
public void RegisterInstance(Type service, object implementation)
{
_builder.AddSingleton(service, implementation);
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Injection
{
public sealed class TypeResolver : ITypeResolver
{
private readonly IServiceProvider _provider;
public TypeResolver(IServiceProvider provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public object Resolve(Type type)
{
return _provider.GetRequiredService(type);
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Injection</ExampleName>
<ExampleDescription>Demonstrates how to use dependency injection with Spectre.Cli.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
using Injection.Commands;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Injection
{
public class Program
{
public static int Main(string[] args)
{
// Create a type registrar and register any dependencies.
// A type registrar is an adapter for a DI framework.
var registrations = new ServiceCollection();
registrations.AddSingleton<IGreeter, HelloWorldGreeter>();
var registrar = new TypeRegistrar(registrations);
// Create a new command app with the registrar
// and run it with the provided arguments.
var app = new CommandApp<DefaultCommand>(registrar);
return app.Run(args);
}
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Colors</Title>
<Description>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Borders</ExampleTitle>
<ExampleDescription>Demonstrates the different kind of borders.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Calendars</ExampleTitle>
<ExampleDescription>Demonstrates how to render calendars.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ExampleTitle>Canvas</ExampleTitle>
<ExampleDescription>Demonstrates how to render pixels and images.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="cake.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Charts</ExampleTitle>
<ExampleDescription>Demonstrates how to render charts in a console.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Colors</ExampleTitle>
<ExampleDescription>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Columns</Title>
<Description>Demonstrates how to render data into columns.</Description>
<ExampleTitle>Columns</ExampleTitle>
<ExampleDescription>Demonstrates how to render data into columns.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Cursor</ExampleTitle>
<ExampleDescription>Demonstrates how to move the cursor.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Emojis</ExampleTitle>
<ExampleDescription>Demonstrates how to render emojis.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Exceptions</ExampleTitle>
<ExampleDescription>Demonstrates how to render formatted exceptions.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Figlet</ExampleTitle>
<ExampleDescription>Demonstrates how to render FIGlet text.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Grids</ExampleTitle>
<ExampleDescription>Demonstrates how to render grids in a console.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Info</ExampleTitle>
<ExampleDescription>Displays the capabilities of the current console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Links</ExampleTitle>
<ExampleDescription>Demonstrates how to render links in a console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Panels</ExampleTitle>
<ExampleDescription>Demonstrates how to render items in panels.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Progress</Title>
<Description>Demonstrates how to show progress bars.</Description>
<ExampleTitle>Progress</ExampleTitle>
<ExampleDescription>Demonstrates how to show progress bars.</ExampleDescription>
<ExampleGroup>Status</ExampleGroup>
</PropertyGroup>
<ItemGroup>
@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>9</LangVersion>
<ExampleTitle>Prompt</ExampleTitle>
<ExampleDescription>Demonstrates how to get input from a user.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Rules</ExampleTitle>
<ExampleDescription>Demonstrates how to render horizontal rules (lines).</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Status</Title>
<Description>Demonstrates how to show status updates.</Description>
<ExampleTitle>Status</ExampleTitle>
<ExampleDescription>Demonstrates how to show status updates.</ExampleDescription>
<ExampleGroup>Status</ExampleGroup>
</PropertyGroup>
<ItemGroup>
@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Tables</ExampleTitle>
<ExampleDescription>Demonstrates how to render tables in a console.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Cursor</Title>
<Description>Demonstrates how to move the cursor.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,5 @@
<Project>
<PropertyGroup Label="Settings">
<IsPackable>false</IsPackable>
</PropertyGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Emojis</Title>
<Description>Demonstrates how to render emojis.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Exceptions</Title>
<Description>Demonstrates how to render formatted exceptions.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Figlet</Title>
<Description>Demonstrates how to render FIGlet text.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Grids</Title>
<Description>Demonstrates how to render grids in a console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Info</Title>
<Description>Displays the capabilities of the current console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Links</Title>
<Description>Demonstrates how to render links in a console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Panels</Title>
<Description>Demonstrates how to render items in panels.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>9</LangVersion>
<IsPackable>false</IsPackable>
<Title>Prompt</Title>
<Description>Demonstrates how to get input from a user.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Rules</Title>
<Description>Demonstrates how to render horizontal rules (lines).</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Tables</Title>
<Description>Demonstrates how to render tables in a console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -6,6 +6,7 @@
<DebugType>embedded</DebugType>
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Label="Deterministic Build" Condition="'$(GITHUB_ACTIONS)' == 'true'">
@ -32,13 +33,13 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.4.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113">
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.312">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="3.0.0">

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with ImageSharp superpowers.</Description>
</PropertyGroup>

View File

@ -0,0 +1,14 @@
root = false
[*.cs]
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = none
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.OrderingRules'
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = none
# CA1819: Properties should not return arrays
dotnet_diagnostic.CA1819.severity = none

View File

@ -0,0 +1,92 @@
using System;
using Spectre.Console.Cli;
namespace Spectre.Console.Testing
{
public sealed class CommandAppFixture
{
private Action<CommandApp> _appConfiguration = _ => { };
private Action<IConfigurator> _configuration;
public CommandAppFixture()
{
_configuration = (_) => { };
}
public CommandAppFixture WithDefaultCommand<T>()
where T : class, ICommand
{
_appConfiguration = (app) => app.SetDefaultCommand<T>();
return this;
}
public void Configure(Action<IConfigurator> action)
{
_configuration = action;
}
public (string Message, string Output) RunAndCatch<T>(params string[] args)
where T : Exception
{
CommandContext context = null;
CommandSettings settings = null;
using var console = new FakeConsole();
var app = new CommandApp();
_appConfiguration?.Invoke(app);
app.Configure(_configuration);
app.Configure(c => c.ConfigureConsole(console));
app.Configure(c => c.SetInterceptor(new FakeCommandInterceptor((ctx, s) =>
{
context = ctx;
settings = s;
})));
try
{
app.Run(args);
}
catch (T ex)
{
var output = console.Output
.NormalizeLineEndings()
.TrimLines()
.Trim();
return (ex.Message, output);
}
throw new InvalidOperationException("No exception was thrown");
}
public (int ExitCode, string Output, CommandContext Context, CommandSettings Settings) Run(params string[] args)
{
CommandContext context = null;
CommandSettings settings = null;
using var console = new FakeConsole(width: int.MaxValue);
var app = new CommandApp();
_appConfiguration?.Invoke(app);
app.Configure(_configuration);
app.Configure(c => c.ConfigureConsole(console));
app.Configure(c => c.SetInterceptor(new FakeCommandInterceptor((ctx, s) =>
{
context = ctx;
settings = s;
})));
var result = app.Run(args);
var output = console.Output
.NormalizeLineEndings()
.TrimLines()
.Trim();
return (result, output, context, settings);
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.IO;
using System.Reflection;
namespace Spectre.Console.Tests
{
public static class EmbeddedResourceReader
{
public static Stream LoadResourceStream(string resourceName)
{
if (resourceName is null)
{
throw new ArgumentNullException(nameof(resourceName));
}
var assembly = Assembly.GetCallingAssembly();
resourceName = resourceName.ReplaceExact("/", ".");
return assembly.GetManifestResourceStream(resourceName);
}
public static Stream LoadResourceStream(Assembly assembly, string resourceName)
{
if (assembly is null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (resourceName is null)
{
throw new ArgumentNullException(nameof(resourceName));
}
resourceName = resourceName.ReplaceExact("/", ".");
return assembly.GetManifestResourceStream(resourceName);
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Linq;
using Shouldly;
namespace Spectre.Console.Cli
{
public static class CommandContextExtensions
{
public static void ShouldHaveRemainingArgument(this CommandContext context, string name, string[] values)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (values == null)
{
throw new ArgumentNullException(nameof(values));
}
context.Remaining.Parsed.Contains(name).ShouldBeTrue();
context.Remaining.Parsed[name].Count().ShouldBe(values.Length);
foreach (var value in values)
{
context.Remaining.Parsed[name].ShouldContain(value);
}
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Diagnostics;
using Shouldly;
namespace Spectre.Console
{
public static class ShouldlyExtensions
{
[DebuggerStepThrough]
public static T And<T>(this T item, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
action(item);
return item;
}
[DebuggerStepThrough]
public static void As<T>(this T item, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
action(item);
}
[DebuggerStepThrough]
public static void As<T>(this object item, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
action((T)item);
}
[DebuggerStepThrough]
public static void ShouldBe<T>(this Type item)
{
item.ShouldBe(typeof(T));
}
}
}

View File

@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Spectre.Console
{
public static class StringExtensions
{
private static readonly Regex _lineNumberRegex = new Regex(":\\d+", RegexOptions.Singleline);
private static readonly Regex _filenameRegex = new Regex("\\sin\\s.*cs:nn", RegexOptions.Multiline);
public static string TrimLines(this string value)
{
if (value is null)
{
return string.Empty;
}
var result = new List<string>();
var lines = value.Split(new[] { '\n' });
foreach (var line in lines)
{
var current = line.TrimEnd();
if (string.IsNullOrWhiteSpace(current))
{
result.Add(string.Empty);
}
else
{
result.Add(current);
}
}
return string.Join("\n", result);
}
public static string NormalizeLineEndings(this string value)
{
if (value != null)
{
value = value.Replace("\r\n", "\n");
return value.Replace("\r", string.Empty);
}
return string.Empty;
}
public static string NormalizeStackTrace(this string text)
{
text = _lineNumberRegex.Replace(text, match =>
{
return ":nn";
});
return _filenameRegex.Replace(text, match =>
{
var value = match.Value;
var index = value.LastIndexOfAny(new[] { '\\', '/' });
var filename = value.Substring(index + 1, value.Length - index - 1);
return $" in /xyz/{filename}";
});
}
internal static string ReplaceExact(this string text, string oldValue, string newValue)
{
if (string.IsNullOrWhiteSpace(newValue))
{
return text;
}
#if NET5_0
return text.Replace(oldValue, newValue, StringComparison.Ordinal);
#else
return text.Replace(oldValue, newValue);
#endif
}
}
}

View File

@ -1,6 +1,6 @@
namespace Spectre.Console.Tests
{
internal static class StyleExtensions
public static class StyleExtensions
{
public static Style SetColor(this Style style, Color color, bool foreground)
{

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Xml;
namespace Spectre.Console
{
public static class XmlElementExtensions
{
public static void SetNullableAttribute(this XmlElement element, string name, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetAttribute(name, value ?? "NULL");
}
public static void SetNullableAttribute(this XmlElement element, string name, IEnumerable<string> values)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
if (values?.Any() != true)
{
element.SetAttribute(name, "NULL");
}
element.SetAttribute(name, string.Join(",", values));
}
public static void SetBooleanAttribute(this XmlElement element, string name, bool value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetAttribute(name, value ? "true" : "false");
}
public static void SetEnumAttribute(this XmlElement element, string name, Enum value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
var field = value.GetType().GetField(value.ToString());
var attribute = field.GetCustomAttribute<DescriptionAttribute>(false);
if (attribute == null)
{
throw new InvalidOperationException("Enum is missing description.");
}
element.SetAttribute(name, attribute.Description);
}
}
}

View File

@ -4,9 +4,9 @@ using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Tests
namespace Spectre.Console.Testing
{
public sealed class TestableAnsiConsole : IDisposable, IAnsiConsole
public sealed class FakeAnsiConsole : IDisposable, IAnsiConsole
{
private readonly StringWriter _writer;
private readonly IAnsiConsole _console;
@ -18,12 +18,12 @@ namespace Spectre.Console.Tests
public int Width { get; }
public int Height => _console.Height;
public IAnsiConsoleCursor Cursor => _console.Cursor;
public TestableConsoleInput Input { get; }
public FakeConsoleInput Input { get; }
public RenderPipeline Pipeline => _console.Pipeline;
IAnsiConsoleInput IAnsiConsole.Input => Input;
public TestableAnsiConsole(
public FakeAnsiConsole(
ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes,
InteractionSupport interaction = InteractionSupport.Yes,
int width = 80)
@ -35,11 +35,11 @@ namespace Spectre.Console.Tests
ColorSystem = (ColorSystemSupport)system,
Interactive = interaction,
Out = _writer,
LinkIdentityGenerator = new TestLinkIdentityGenerator(),
LinkIdentityGenerator = new FakeLinkIdentityGenerator(1024),
});
Width = width;
Input = new TestableConsoleInput();
Input = new FakeConsoleInput();
}
public void Dispose()

View File

@ -1,6 +1,6 @@
namespace Spectre.Console.Tests
namespace Spectre.Console.Testing
{
public sealed class DummyCursor : IAnsiConsoleCursor
public sealed class FakeAnsiConsoleCursor : IAnsiConsoleCursor
{
public void Move(CursorDirection direction, int steps)
{

View File

@ -0,0 +1,20 @@
using System;
using Spectre.Console.Cli;
namespace Spectre.Console.Testing
{
public sealed class FakeCommandInterceptor : ICommandInterceptor
{
private readonly Action<CommandContext, CommandSettings> _action;
public FakeCommandInterceptor(Action<CommandContext, CommandSettings> action)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
}
public void Intercept(CommandContext context, CommandSettings settings)
{
_action(context, settings);
}
}
}

View File

@ -5,14 +5,14 @@ using System.Linq;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Tests
namespace Spectre.Console.Testing
{
public sealed class PlainConsole : IAnsiConsole, IDisposable
public sealed class FakeConsole : IAnsiConsole, IDisposable
{
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public IAnsiConsoleCursor Cursor => new DummyCursor();
public TestableConsoleInput Input { get; }
public IAnsiConsoleCursor Cursor => new FakeAnsiConsoleCursor();
public FakeConsoleInput Input { get; }
public int Width { get; }
public int Height { get; }
@ -29,7 +29,7 @@ namespace Spectre.Console.Tests
public string Output => Writer.ToString();
public IReadOnlyList<string> Lines => Output.TrimEnd('\n').Split(new char[] { '\n' });
public PlainConsole(
public FakeConsole(
int width = 80, int height = 9000, Encoding encoding = null,
bool supportsAnsi = true, ColorSystem colorSystem = ColorSystem.Standard,
bool legacyConsole = false, bool interactive = true)
@ -39,7 +39,7 @@ namespace Spectre.Console.Tests
Width = width;
Height = height;
Writer = new StringWriter();
Input = new TestableConsoleInput();
Input = new FakeConsoleInput();
Pipeline = new RenderPipeline();
}

View File

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
namespace Spectre.Console.Tests
namespace Spectre.Console.Testing
{
public sealed class TestableConsoleInput : IAnsiConsoleInput
public sealed class FakeConsoleInput : IAnsiConsoleInput
{
private readonly Queue<ConsoleKeyInfo> _input;
public TestableConsoleInput()
public FakeConsoleInput()
{
_input = new Queue<ConsoleKeyInfo>();
}

Some files were not shown because too many files have changed in this diff Show More