Compare commits

...

39 Commits

Author SHA1 Message Date
Phil Scott
e4dda283bb Adds overloads for MarkUp methods without args
These methods don't require a string.format call so we'll directly call the Render method without a call to string.format.

Added bonus of a a couple fewer allocations too.
2021-03-08 15:04:55 +01:00
Phil Scott
da9c6ee4c2 Add IProgress<double> to ProgressTask.cs
Makes the Report method an explicit implementation to allow for better interoperability with standard .NET progress functionality while keeping backwards compatibility with existing ProgressTask functionality.

Closes #285
2021-03-07 09:24:44 +01:00
Phil Scott
855127f32a Changes progress task IsFinished to account for stopped tasks
Previous behavior was that the only way to get a task to a finished state was to artificially set the Value to MaxValue.

With this change StopTask() will also complete the task with the change that a task cannot be restarted.
2021-03-07 09:24:21 +01:00
stf
fa731070d8 update the docs
To improve discoveratbility of the new option
2021-03-04 07:47:00 -05:00
stf
ef08c5bf2b Improve the unit test around HideCompleted 2021-03-04 07:47:00 -05:00
stf
1c769c6610 Add Progress.HideCompleted 2021-03-04 07:47:00 -05:00
Phil Scott
1cd335e785 Serilog example for logging 2021-03-04 08:39:53 +01:00
Patrik Svensson
29e6e34f83 Support setting the static console 2021-03-01 15:22:35 -05:00
Phil Scott
bff3438a5a using loop instead of linq
In both of these loops context is captured preventing caching of the lambda. this results in a pretty significant amount of allocations especially with progress bars that constantly are remeasuring
2021-03-01 08:22:45 +01:00
Oskar Klintrot
c64884854f Make it possible to set Value directly 2021-02-25 11:56:32 +01:00
Phil Scott
3a42c0a119 Adds DotSettings and tweaks editoconfigs for tests
R# and Rider have quite a bit of noise related to documentation in the testing projects so this disables those warnings.

In the main projects, R# and Rider complain loudly about the namespaces not matching the file structure. The DotSettings file disables that warning.

Once you get rid of that noise there are quite a few opportunities for trimming out redundant code that R# points out especially with the nullable support enabled, plus there are some bugs related to multiple enumerations worth looking into I think.
2021-02-23 22:34:33 +01:00
Thomas Freudenberg
525b414ff8 Make alignment of TaskDescriptionColumn configurable 2021-02-16 23:33:47 +01:00
Thomas Freudenberg
ed0fb29be4 Make default answer for confirmation prompt configurable 2021-02-16 23:32:49 +01:00
Phil Scott
04d0e663d5 Extends maximum size of remaining and elapsed time displayed, plus a failsafe
Elapsed and remaining now support > 9 hours, and if a timespan can't be rendered in that size **:**:** will be displayed
2021-02-16 23:31:55 +01:00
Patrik Svensson
17ee8990f4 Update example image 2021-02-15 13:01:30 +01:00
Bastian Eicher
a1050fc676 Handle output to stderr 2021-02-14 18:08:42 +01:00
Phil Scott
9312663bde Adds text and Progress bar spinner column for tasks yet to be started 2021-02-14 18:03:57 +01:00
Patrik Svensson
102e2dc38d Allow formatting breakdown charts with lambda expr
Relates to #252
2021-02-13 17:09:51 +01:00
Patrik Svensson
28e9c14de4 Register the console lazily in CLI type registrar
This should fix a strange bug we're seeing in Cake on macOS.
2021-02-12 02:04:59 +01:00
Patrik Svensson
fd217ffc83 Update sponsor text 2021-02-11 23:22:43 +01:00
Patrik Svensson
95ec04df40 Add sponsor information 2021-02-11 23:20:11 +01:00
Patrik Svensson
705cf745ea Add formatting support for breakdown chart values
Closes #252
2021-02-05 11:53:55 +01:00
Patrik Svensson
b64e016e8c Add breakdown chart support
This also cleans up the bar chart code slightly and fixes
some minor bugs that were detected in related code.

Closes #244
2021-02-01 01:03:39 +01:00
Patrik Svensson
58400fe74e Fix code generation
Previous changes introduced some bugs to the
code generation scripts and templates, which
now have been fixed.
2021-01-29 21:46:08 +01:00
Patrik Svensson
e20f6284f9 Clean up profile enrichment 2021-01-29 20:16:52 +01:00
David Butler
953008b5e3 Implemented buffer/stream constructors for CanvasImage (#246)
* Implemented buffer/stream constructors for CanvasImage and added section to Canvas example

Signed-off-by: David Butler <mail@davidbutlerdesign.co.uk>
2021-01-27 18:12:22 +01:00
Milosz Krajewski
ad49b6aa67 GH-242: Fix version retrieval for single file applications (#245) 2021-01-26 00:45:38 +01:00
Patrik Svensson
31a5e17a45 Remove InteractivityDetector 2021-01-19 18:12:15 +01:00
Patrik Svensson
f06dc7e7d8 GitHub actions should use default width (for now) 2021-01-19 18:12:15 +01:00
Patrik Svensson
a23bec4082 Add profile support
Closes #231
2021-01-19 17:53:03 +01:00
Patrik Svensson
913a7b1e37 Add support for default choice in selection prompt
Closes #234
2021-01-15 17:05:11 +01:00
Nick
63bae278a9 Add support for selection prompt highlighting 2021-01-15 15:23:09 +01:00
Eslami Sepehr
1a747696a8 Add Persian README 2021-01-15 09:08:37 +01:00
Matt Constable
994540d97f Add cycle detection to tree rendering 2021-01-14 18:37:22 +01:00
Thomas Freudenberg
dee3c01629 mask default value when prompt is a secret 2021-01-14 17:44:18 +01:00
Mattias Karlsson
a3e11b24e5 (GH-226) Switch ParameterValidationAttribute check to IsNullOrWhiteSpace
* fixes #226
2021-01-13 20:31:33 +01:00
Jay Turpin
35568ab823 Updated Commands with with new Execute() method signature 2021-01-13 19:48:15 +01:00
Patrik Svensson
07db28bb6f Add enhancements to progress widget
* Adds TransferSpeedColumn
* Adds DownloadedColumn
* Adds ElapsedTimeColumn
* Minor enhancements to existing columns
2021-01-12 14:10:07 +01:00
ριтєя мαяχ
d87d8e4422 Update exceptions.md 2021-01-10 20:45:44 +01:00
322 changed files with 3757 additions and 1840 deletions

77
README.fa.md Normal file
View File

@@ -0,0 +1,77 @@
# `Spectre.Console`
_[![Spectre.Console NuGet Version](https://img.shields.io/nuget/v/spectre.console.svg?style=flat&label=NuGet%3A%20Spectre.Console)](https://www.nuget.org/packages/spectre.console)_
<div dir="rtl">
یک کتابخانه NET Standard 2.0/.NET 5. که ایجاد Console Applicationهای زیبا و cross platform را آسان‌تر می‌کند.
از کتابخانه عالی [Rich](https://github.com/willmcgugan/rich) برای پایتون، بسیار الهام گرفته شده است.
## فهرست
1. [امکانات](#features)
2. [نصب](#installing)
3. [مستندات](#documentation)
4. [مثال‌ها](#examples)
5. [مجوز](#license)
<h2 id="features">امکانات</h2>
* با در نظر گرفتن تست واحد نوشته شده است.
* جداول، چارچوب‌ها، پنل‌ها و یک زبان نشانه گذاری که از [rich](https://github.com/willmcgugan/rich) الهام گرفته شده است را پشتیبانی می‌کند.
* از رایج ترین پارامترهای SRG در هنگام فرم دهی متن مانند پررنگ، کم نور، اریب، زیرخط، خط زدن و چشمک زدن پشتیبانی می‌کند.
* پشتیبانی از رنگ‌های 28/8/4/3-بیت در ترمینال.
این کتابخانه توانایی ترمینال فعلی را تشخیص داده و در صورت لزوم رنگ‌ها را کاهش می‌دهد.
![Example](docs/input/assets/images/example.png)
<h2 id="installing">نصب</h2>
سریع ترین راه برای شروع `Spectre.Console` نصب از طریق NuGet Package می‌باشد.
<pre dir="ltr">
dotnet add package Spectre.Console
</pre>
<h2 id="documentation">مستندات</h2>
مستندات `Spectre.Console` را در اینجا می‌توایند پیدا کنید:
<div dir="ltr">
https://spectresystems.github.io/spectre.console/
</div>
<h2 id="examples">مثال‌ها</h2>
برای بررسی `Spectre.Console` در عمل، ابزار سراسری
[dotnet-example](https://github.com/patriksvensson/dotnet-example)
را نصب کنید.
<pre dir="ltr">
> dotnet tool restore
</pre>
حالا شما می‌توانید مثال‌های موجود در این مخزن را لیست کنید:
<pre dir="ltr">
> dotnet example
</pre>
و برای اجرای مثال:
<pre dir="ltr">
> dotnet example tables
</pre>
<h2 id="license">مجوز</h2>
<div dir="ltr">
Copyright © Spectre Systems.
</div>
همانطور که Spectre.Console تحت مجوز MIT ارائه شده است؛ برای کسب اطلاعات بیشتر به مجوز مراجعه کنید.
* برای SixLabors.ImageSharp، مشاهده کنید: https://github.com/SixLabors/ImageSharp/blob/master/LICENSE
</div>

View File

@@ -29,7 +29,7 @@ Python用の素晴らしい[Rich ライブラリ](https://github.com/willmcgugan
## 例
![Example](resources/gfx/screenshots/example.png)
![Example](docs/input/assets/images/example.png)
## 使用方法
@@ -111,6 +111,7 @@ Spectre.Consoleでできることを見るために、
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
│ Trees │ examples/Trees/Trees.csproj │ Demonstrates how to render trees in a console. │
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
```

View File

@@ -12,6 +12,7 @@ for Python.
2. [Installing](#installing)
3. [Documentation](#documentation)
4. [Examples](#examples)
5. [Sponsors](#sponsors)
5. [License](#license)
## Features
@@ -26,7 +27,7 @@ for Python.
and downgrade colors as needed.
![Example](resources/gfx/screenshots/example.png)
![Example](docs/input/assets/images/example.png)
## Installing
@@ -63,6 +64,24 @@ And to run an example:
> dotnet example tables
```
## Sponsors
The following people are [sponsoring](https://github.com/sponsors/patriksvensson)
Spectre.Console to show their support and to ensure the longevity of the project.
* [Rodney Littles II](https://github.com/RLittlesII)
* [Martin Björkström](https://github.com/bjorkstromm)
* [Dave Glick](https://github.com/daveaglick)
* [Kim Gunanrsson](https://github.com/kimgunnarsson)
* [Andrew McClenaghan](https://github.com/andymac4182)
* [C. Augusto Proiete](https://github.com/augustoproiete)
* [Viktor Elofsson](https://github.com/vktr)
* [Steven Knox](https://github.com/stevenknox)
* [David Pendray](https://github.com/dpen2000)
I really appreciate it.
**Thank you very much!**
## License
Copyright © Spectre Systems.

View File

@@ -7,7 +7,7 @@ To start contributing to the [Spectre.Console](https://github.com/spectresystems
The documentation site uses [Statiq](https://statiq.dev), a static site generator. To build the documentation site run the following in a command-line terminal.
```
> dotnet run preview --virtual-dir "spectre.console"
> Preview.ps1
```
After the build is complete, you can navigate to [http://localhost:5080/spectre.console](http://localhost:5080/spectre.console).
@@ -29,13 +29,7 @@ Layout and styling can also be found in the [input](./input) directory. Look for
## Custom Build Features
The documentation site has custom enhancements to Statiq located under the [./src](./src) directory. Enhancements to the build process include:
- [Extension Methods](./src/Extensions)
- [Models](./src/Models)
- [Pipelines](./src/Pipelines)
- [Shortcodes](./src/Shortcodes)
- [Utilities](./src/Utilities)
The documentation site has custom enhancements to Statiq located under the [./src](./src) directory.
## License

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

View File

@@ -58,17 +58,19 @@ in the previous step.
```csharp
public class AddPackageCommand : Command<AddPackageSettings>
{
public override int Execute(AddPackageSettings settings, ILookup<string, string> remaining)
public override int Execute(CommandContext context, AddPackageSettings settings)
{
// Omitted
return 0;
}
}
public class AddReferenceCommand : Command<AddReferenceSettings>
{
public override int Execute(AddReferenceSettings settings, ILookup<string, string> remaining)
public override int Execute(CommandContext context, AddReferenceSettings settings)
{
// Omitted
return 0;
}
}
```

View File

@@ -2,7 +2,7 @@ Title: Exceptions
Order: 3
---
Exceptions isn't always readable when viewed in the terminal.
Exceptions aren't always readable when viewed in the terminal.
You can make exception a bit more readable by using the `WriteException` method.
```csharp

View File

@@ -22,5 +22,5 @@ for Python written by Will McGugan.
## Examples
<img src="assets/images/table.gif" style="max-width: 100%; margin-top: 15px; margin-bottom: 25px;" />
<img src="https://github.com/spectresystems/spectre.console/raw/main/resources/gfx/screenshots/example.png" style="max-width: 100%;" />
<img src="./assets/images/example.png" style="max-width: 100%; margin-top: 15px; margin-bottom: 25px;" />
<img src="./assets/images/table.gif" style="max-width: 100%;" />

View File

@@ -63,6 +63,7 @@ await AnsiConsole.Progress()
AnsiConsole.Progress()
.AutoRefresh(false) // Turn off auto refresh
.AutoClear(false) // Do not remove the task list when done
.HideCompleted(false) // Hide tasks as they are completed
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(), // Task description

19
docs/input/sponsors.md Normal file
View File

@@ -0,0 +1,19 @@
Title: Sponsors
Order: 0
---
The following people are [sponsoring](https://github.com/sponsors/patriksvensson)
Spectre.Console to show their support and to ensure the longevity of the project.
* [Rodney Littles II](https://github.com/RLittlesII)
* [Martin Björkström](https://github.com/bjorkstromm)
* [Dave Glick](https://github.com/daveaglick)
* [Kim Gunanrsson](https://github.com/kimgunnarsson)
* [Andrew McClenaghan](https://github.com/andymac4182)
* [C. Augusto Proiete](https://github.com/augustoproiete)
* [Viktor Elofsson](https://github.com/vktr)
* [Steven Knox](https://github.com/stevenknox)
* [David Pendray](https://github.com/dpen2000)
I really appreciate it.
**Thank you very much!**

View File

@@ -27,5 +27,15 @@ namespace Injection
{
_builder.AddSingleton(service, implementation);
}
public void RegisterLazy(Type service, Func<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, (provider) => func());
}
}
}

View File

@@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Logging.Commands
{
public class HelloCommand : Command<HelloCommand.Settings>
{
private ILogger<HelloCommand> _logger;
private IAnsiConsole _console;
public HelloCommand(IAnsiConsole console, ILogger<HelloCommand> logger)
{
_console = console;
_logger = logger;
_logger.LogDebug("{0} initialized", nameof(HelloCommand));
}
public class Settings : LogCommandSettings
{
[CommandArgument(0, "[Name]")]
public string Name { get; set; }
}
public override int Execute(CommandContext context, Settings settings)
{
_logger.LogInformation("Starting my command");
AnsiConsole.MarkupLine($"Hello, [blue]{settings.Name}[/]");
_logger.LogInformation("Completed my command");
return 0;
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using Serilog.Events;
using Spectre.Console.Cli;
namespace Logging.Commands
{
public class LogCommandSettings : CommandSettings
{
[CommandOption("--logFile")]
[Description("Path and file name for logging")]
public string LogFile { get; set; }
[CommandOption("--logLevel")]
[Description("Minimum level for logging")]
[TypeConverter(typeof(VerbosityConverter))]
[DefaultValue(LogEventLevel.Information)]
public LogEventLevel LogLevel { get; set; }
}
public sealed class VerbosityConverter : TypeConverter
{
private readonly Dictionary<string, LogEventLevel> _lookup;
public VerbosityConverter()
{
_lookup = new Dictionary<string, LogEventLevel>(StringComparer.OrdinalIgnoreCase)
{
{"d", LogEventLevel.Debug},
{"v", LogEventLevel.Verbose},
{"i", LogEventLevel.Information},
{"w", LogEventLevel.Warning},
{"e", LogEventLevel.Error},
{"f", LogEventLevel.Fatal}
};
}
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,20 @@
using Logging.Commands;
using Serilog.Core;
using Spectre.Console.Cli;
namespace Logging
{
public class LogInterceptor : ICommandInterceptor
{
public static readonly LoggingLevelSwitch LogLevel = new();
public void Intercept(CommandContext context, CommandSettings settings)
{
if (settings is LogCommandSettings logSettings)
{
LoggingEnricher.Path = logSettings.LogFile ?? "application.log";
LogLevel.MinimumLevel = logSettings.LogLevel;
}
}
}
}

View File

@@ -0,0 +1,38 @@
using Serilog.Core;
using Serilog.Events;
namespace Logging
{
internal class LoggingEnricher : ILogEventEnricher
{
private string _cachedLogFilePath;
private LogEventProperty _cachedLogFilePathProperty;
// this path and level will be set by the LogInterceptor.cs after parsing the settings
public static string Path = string.Empty;
public const string LogFilePathPropertyName = "LogFilePath";
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
// the settings might not have a path or we might not be within a command in which case
// we won't have the setting so a default value for the log file will be required
LogEventProperty logFilePathProperty;
if (_cachedLogFilePathProperty != null && Path.Equals(_cachedLogFilePath))
{
// Path hasn't changed, so let's use the cached property
logFilePathProperty = _cachedLogFilePathProperty;
}
else
{
// We've got a new path for the log. Let's create a new property
// and cache it for future log events to use
_cachedLogFilePath = Path;
_cachedLogFilePathProperty = logFilePathProperty = propertyFactory.CreateProperty(LogFilePathPropertyName, Path);
}
logEvent.AddPropertyIfAbsent(logFilePathProperty);
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Logging
{
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);
}
public void RegisterLazy(Type service, Func<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, _ => func());
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Logging
{
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,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Logging</ExampleName>
<ExampleDescription>Demonstrates how to dynamically configure Serilog for logging using parameters from a command.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,54 @@
using Logging.Commands;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Spectre.Console.Cli;
/*
* Dynamically control serilog configuration via command line parameters
*
* This works around the chicken and egg situation with configuring serilog via the command line.
* The logger needs to be configured prior to executing the parser, but the logger needs the parsed values
* to be configured. By using serilog.sinks.map we can defer configuration. We use a LogLevelSwitch to control the
* logging levels dynamically, and then we use a serilog enricher that has it's state populated via a
* Spectre.Console CommandInterceptor
*/
namespace Logging
{
public class Program
{
static int Main(string[] args)
{
// to retrieve the log file name, we must first parse the command settings
// this will require us to delay setting the file path for the file writer.
// With serilog we can use an enricher and Serilog.Sinks.Map to dynamically
// pull this setting.
var serviceCollection = new ServiceCollection()
.AddLogging(configure =>
configure.AddSerilog(new LoggerConfiguration()
// log level will be dynamically be controlled by our log interceptor upon running
.MinimumLevel.ControlledBy(LogInterceptor.LogLevel)
// the log enricher will add a new property with the log file path from the settings
// that we can use to set the path dynamically
.Enrich.With<LoggingEnricher>()
// serilog.sinks.map will defer the configuration of the sink to be ondemand
// allowing us to look at the properties set by the enricher to set the path appropriately
.WriteTo.Map(LoggingEnricher.LogFilePathPropertyName,
(logFilePath, wt) => wt.File($"{logFilePath}"), 1)
.CreateLogger()
)
);
var registrar = new TypeRegistrar(serviceCollection);
var app = new CommandApp(registrar);
app.Configure(config =>
{
config.SetInterceptor(new LogInterceptor()); // add the interceptor
config.AddCommand<HelloCommand>("hello");
});
return app.Run(args);
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Canvas</ExampleTitle>
<ExampleDescription>Demonstrates how to render pixels and images.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
@@ -14,9 +14,9 @@
</ItemGroup>
<ItemGroup>
<None Update="cake.png">
<EmbeddedResource Include="cake.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1,3 +1,5 @@
using System.Diagnostics;
using System.Reflection;
using SixLabors.ImageSharp.Processing;
using Spectre.Console;
using Spectre.Console.Rendering;
@@ -23,6 +25,16 @@ namespace CanvasExample
image.NoMaxWidth();
image.Mutate(ctx => ctx.Grayscale().Rotate(-45).EntropyCrop());
Render(image, "Image from file (fit, greyscale, rotated)");
// Draw image again, but load from embedded resource rather than file
using (var fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Canvas.cake.png"))
{
Debug.Assert(fileStream != null);
var embeddedImage = new CanvasImage(fileStream);
embeddedImage.BilinearResampler();
embeddedImage.MaxWidth(16);
Render(embeddedImage, "Image from embedded resource (16 wide)");
}
}
private static void Render(IRenderable canvas, string title)

View File

@@ -1,21 +1,42 @@
using Spectre.Console;
using Spectre.Console.Rendering;
namespace InfoExample
namespace Charts
{
public static class Program
{
public static void Main()
{
var chart = new BarChart()
// Render a bar chart
AnsiConsole.WriteLine();
Render("Fruits per month", new BarChart()
.Width(60)
.Label("[green bold underline]Number of fruits[/]")
.CenterLabel()
.AddItem("Apple", 12, Color.Yellow)
.AddItem("Orange", 54, Color.Green)
.AddItem("Banana", 33, Color.Red);
.AddItem("Banana", 33, Color.Red));
// Render a breakdown chart
AnsiConsole.WriteLine();
AnsiConsole.Render(chart);
Render("Languages used", new BreakdownChart()
.FullSize()
.Width(60)
.ShowPercentage()
.AddItem("SCSS", 37, Color.Red)
.AddItem("HTML", 28.3, Color.Blue)
.AddItem("C#", 22.6, Color.Green)
.AddItem("JavaScript", 6, Color.Yellow)
.AddItem("Ruby", 6, Color.LightGreen)
.AddItem("Shell", 0.1, Color.Aqua));
}
private static void Render(string title, IRenderable chart)
{
AnsiConsole.Render(
new Panel(chart)
.Padding(1, 1)
.Header(title));
}
}
}

View File

@@ -6,7 +6,7 @@ namespace ColorExample
{
public static void Main()
{
if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
if (AnsiConsole.Profile.ColorSystem == ColorSystem.NoColors)
{
/////////////////////////////////////////////////////////////////
// No colors
@@ -16,7 +16,7 @@ namespace ColorExample
return;
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.Legacy))
if (AnsiConsole.Profile.Supports(ColorSystem.Legacy))
{
/////////////////////////////////////////////////////////////////
// 3-BIT
@@ -39,7 +39,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.Standard))
if (AnsiConsole.Profile.Supports(ColorSystem.Standard))
{
/////////////////////////////////////////////////////////////////
// 4-BIT
@@ -62,7 +62,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.EightBit))
if (AnsiConsole.Profile.Supports(ColorSystem.EightBit))
{
/////////////////////////////////////////////////////////////////
// 8-BIT
@@ -89,7 +89,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.TrueColor))
if (AnsiConsole.Profile.Supports(ColorSystem.TrueColor))
{
/////////////////////////////////////////////////////////////////
// 24-BIT

View File

@@ -9,12 +9,16 @@ namespace InfoExample
var grid = new Grid()
.AddColumn(new GridColumn().NoWrap().PadRight(4))
.AddColumn()
.AddRow("[b]Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsAnsi)}")
.AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Capabilities.LegacyConsole)}")
.AddRow("[b]Interactive?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsInteraction)}")
.AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Width}")
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Height}");
.AddRow("[b]Enrichers[/]", string.Join(", ", AnsiConsole.Profile.Enrichers))
.AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Ansi)}")
.AddRow("[b]Supports links?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Links)}")
.AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Legacy)}")
.AddRow("[b]Interactive?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Interactive)}")
.AddRow("[b]TTY?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Tty)}")
.AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Profile.Width}")
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Profile.Height}")
.AddRow("[b]Encoding[/]", $"{AnsiConsole.Console.Profile.Encoding.EncodingName}");
AnsiConsole.Render(
new Panel(grid)

View File

@@ -6,7 +6,7 @@ namespace LinkExample
{
public static void Main()
{
if (AnsiConsole.Capabilities.SupportLinks)
if (AnsiConsole.Profile.Capabilities.Links)
{
AnsiConsole.MarkupLine("[link=https://patriksvensson.se]Click to visit my blog[/]!");
}

View File

@@ -7,7 +7,7 @@ namespace Cursor
public static void Main(string[] args)
{
// Check if we can accept key strokes
if (!AnsiConsole.Capabilities.SupportsInteraction)
if (!AnsiConsole.Profile.Capabilities.Interactive)
{
AnsiConsole.MarkupLine("[red]Environment does not support interaction.[/]");
return;

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>9</LangVersion>
<ExampleTitle>Prompt</ExampleTitle>
<ExampleDescription>Demonstrates how to get input from a user.</ExampleDescription>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -19,4 +19,4 @@ if(!$?) {
Pop-Location
# Copy the files to the correct location
Copy-Item (Join-Path "$Output" "Spinner.Generated.cs") -Destination "$Source/Progress/Spinner.Generated.cs"
Copy-Item (Join-Path "$Output" "Spinner.Generated.cs") -Destination "$Source/Widgets/Progress/Spinner.Generated.cs"

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Generated {{ date.now | date.to_string `%F %R` }}
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Generated {{ date.now | date.to_string `%F %R` }}
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -10,7 +9,7 @@
using System.Collections.Generic;
namespace Spectre.Console.Internal
namespace Spectre.Console
{
internal static partial class ColorPalette
{

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Generated {{ date.now | date.to_string `%F %R` }}
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -11,7 +10,7 @@
using System;
using System.Collections.Generic;
namespace Spectre.Console.Internal
namespace Spectre.Console
{
internal static partial class ColorTable
{

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Generated {{ date.now | date.to_string `%F %R` }}
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Generated {{ date.now | date.to_string `%F %R` }}
//
// Partly generated from
// https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
@@ -51,6 +52,24 @@ namespace Spectre.Console
Image = SixLabors.ImageSharp.Image.Load<Rgba32>(filename);
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasImage"/> class.
/// </summary>
/// <param name="data">Buffer containing an image.</param>
public CanvasImage(ReadOnlySpan<byte> data)
{
Image = SixLabors.ImageSharp.Image.Load<Rgba32>(data);
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasImage"/> class.
/// </summary>
/// <param name="data">Stream containing an image.</param>
public CanvasImage(Stream data)
{
Image = SixLabors.ImageSharp.Image.Load<Rgba32>(data);
}
/// <inheritdoc/>
protected override Measurement Measure(RenderContext context, int maxWidth)
{

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks>net5.0;netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with ImageSharp superpowers.</Description>

View File

@@ -7,6 +7,9 @@ dotnet_diagnostic.CS1591.severity = none
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# SA1200: Using directives should be placed correctly
dotnet_diagnostic.SA1200.severity = none
# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.OrderingRules'
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = none

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Testing
@@ -13,10 +12,7 @@ namespace Spectre.Console.Testing
public string Output => _writer.ToString();
public Capabilities Capabilities => _console.Capabilities;
public Encoding Encoding => _console.Encoding;
public int Width { get; }
public int Height => _console.Height;
public Profile Profile => _console.Profile;
public IAnsiConsoleCursor Cursor => _console.Cursor;
public FakeConsoleInput Input { get; }
public RenderPipeline Pipeline => _console.Pipeline;
@@ -24,21 +20,26 @@ namespace Spectre.Console.Testing
IAnsiConsoleInput IAnsiConsole.Input => Input;
public FakeAnsiConsole(
ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes,
InteractionSupport interaction = InteractionSupport.Yes,
ColorSystem system,
AnsiSupport ansi = AnsiSupport.Yes,
int width = 80)
{
_writer = new StringWriter();
_console = AnsiConsole.Create(new AnsiConsoleSettings
var factory = new AnsiConsoleFactory();
_console = factory.Create(new AnsiConsoleSettings
{
Ansi = ansi,
ColorSystem = (ColorSystemSupport)system,
Interactive = interaction,
Out = _writer,
LinkIdentityGenerator = new FakeLinkIdentityGenerator(1024),
Enrichment = new ProfileEnrichment
{
UseDefaultEnrichers = false,
},
});
Width = width;
_console.Profile.Width = width;
Input = new FakeConsoleInput();
}

View File

@@ -9,24 +9,13 @@ namespace Spectre.Console.Testing
{
public sealed class FakeConsole : IAnsiConsole, IDisposable
{
public Capabilities Capabilities { get; }
public Encoding Encoding { get; }
public Profile Profile { get; }
public IAnsiConsoleCursor Cursor => new FakeAnsiConsoleCursor();
public FakeConsoleInput Input { get; }
public int Width { get; }
public int Height { get; }
IAnsiConsoleInput IAnsiConsole.Input => Input;
public RenderPipeline Pipeline { get; }
public Decoration Decoration { get; set; }
public Color Foreground { get; set; }
public Color Background { get; set; }
public string Link { get; set; }
public StringWriter Writer { get; }
public string Output => Writer.ToString();
public FakeConsoleInput Input { get; }
public string Output => Profile.Out.ToString();
public IReadOnlyList<string> Lines => Output.TrimEnd('\n').Split(new char[] { '\n' });
public FakeConsole(
@@ -34,18 +23,22 @@ namespace Spectre.Console.Testing
bool supportsAnsi = true, ColorSystem colorSystem = ColorSystem.Standard,
bool legacyConsole = false, bool interactive = true)
{
Capabilities = new Capabilities(supportsAnsi, colorSystem, legacyConsole, interactive);
Encoding = encoding ?? Encoding.UTF8;
Width = width;
Height = height;
Writer = new StringWriter();
Input = new FakeConsoleInput();
Pipeline = new RenderPipeline();
Profile = new Profile(new StringWriter(), encoding ?? Encoding.UTF8);
Profile.Width = width;
Profile.Height = height;
Profile.ColorSystem = colorSystem;
Profile.Capabilities.Ansi = supportsAnsi;
Profile.Capabilities.Legacy = legacyConsole;
Profile.Capabilities.Interactive = interactive;
Profile.Capabilities.Links = true;
}
public void Dispose()
{
Writer.Dispose();
Profile.Out.Dispose();
}
public void Clear(bool home)
@@ -61,7 +54,7 @@ namespace Spectre.Console.Testing
foreach (var segment in segments)
{
Writer.Write(segment.Text);
Profile.Out.Write(segment.Text);
}
}

View File

@@ -1,17 +0,0 @@
namespace Spectre.Console.Testing
{
public sealed class FakeLinkIdentityGenerator : ILinkIdentityGenerator
{
private readonly int _linkId;
public FakeLinkIdentityGenerator(int linkId)
{
_linkId = linkId;
}
public int GenerateId(string link, string text)
{
return _linkId;
}
}
}

View File

@@ -37,6 +37,19 @@ namespace Spectre.Console.Testing
}
}
public void RegisterLazy(Type service, Func<object> factory)
{
if (factory is null)
{
throw new ArgumentNullException(nameof(factory));
}
if (!Instances.ContainsKey(service))
{
Instances.Add(service, new List<object> { factory() });
}
}
public ITypeResolver Build()
{
return _resolver;

View File

@@ -7,6 +7,12 @@ dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity
# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = none
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# SA1601: Partial elements should be documented
dotnet_diagnostic.SA1601.severity = none
# SA1200: Using directives should be placed correctly
dotnet_diagnostic.SA1200.severity = none

View File

@@ -1,3 +1,5 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests
{
public static class Constants
@@ -5,15 +7,15 @@ namespace Spectre.Console.Tests
public static string[] VersionCommand { get; } =
new[]
{
Spectre.Console.Cli.Internal.Constants.Commands.Branch,
Spectre.Console.Cli.Internal.Constants.Commands.Version,
CliConstants.Commands.Branch,
CliConstants.Commands.Version,
};
public static string[] XmlDocCommand { get; } =
new[]
{
Spectre.Console.Cli.Internal.Constants.Commands.Branch,
Spectre.Console.Cli.Internal.Constants.Commands.XmlDoc,
CliConstants.Commands.Branch,
CliConstants.Commands.XmlDoc,
};
}
}

View File

@@ -1,14 +1,11 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Tests.Data
{
public static class TestExceptions
{
[SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "<Pending>")]
public static bool MethodThatThrows(int? number) => throw new InvalidOperationException("Throwing!");
[SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "<Pending>")]
public static bool GenericMethodThatThrows<T0, T1, TRet>(int? number) => throw new InvalidOperationException("Throwing!");
public static void ThrowWithInnerException()

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data
@@ -6,7 +5,6 @@ namespace Spectre.Console.Tests.Data
public class ArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public string[] Foo { get; set; }
}
}

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data
@@ -6,18 +5,15 @@ namespace Spectre.Console.Tests.Data
public class MultipleArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public string[] Foo { get; set; }
[CommandArgument(0, "<Bars>")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public string[] Bar { get; set; }
}
public class MultipleArgumentVectorSpecifiedFirstSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public string[] Foo { get; set; }
[CommandArgument(1, "<Bar>")]

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data
@@ -6,11 +5,9 @@ namespace Spectre.Console.Tests.Data
public class OptionVectorSettings : CommandSettings
{
[CommandOption("--foo")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public string[] Foo { get; set; }
[CommandOption("--bar")]
[SuppressMessage("Performance", "CA1819:Properties should not return arrays")]
public int[] Bar { get; set; }
}
}

View File

@@ -0,0 +1,4 @@
Number of fruits
Apple 0
Orange █████████████████████████████████████████████████ 54
Banana ████████████████████████████ 33

View File

@@ -0,0 +1,4 @@
████████████████████████████████████████████████████████████
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6
■ Ruby 6 ■ Shell 0.1

View File

@@ -0,0 +1,3 @@
████████████████████████████████████████████████████████████
■ SCSS 37 ■ HTML 28,3 ■ C# 22,6 ■ JavaScript 6
■ Ruby 6 ■ Shell 0,1

View File

@@ -0,0 +1,2 @@
████████████████████████████████████████████████████████████████████████████████
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6 ■ Ruby 6 ■ Shell 0.1

View File

@@ -0,0 +1,4 @@
████████████████████████████████████████████████████████████
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6
■ Ruby 6 ■ Shell 0.1

View File

@@ -0,0 +1,2 @@
████████████████████████████████████████████████████████████
■ SCSS ■ HTML ■ C# ■ JavaScript ■ Ruby ■ Shell

View File

@@ -0,0 +1 @@
████████████████████████████████████████████████████████████

View File

@@ -0,0 +1,3 @@
████████████████████████████████████████████████████████████
■ SCSS 37% ■ HTML 28,3% ■ C# 22,6% ■ JavaScript 6%
■ Ruby 6% ■ Shell 0,1%

View File

@@ -0,0 +1,3 @@
████████████████████████████████████████████████████████████
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6
■ Ruby 6 ■ Shell 0.1

View File

@@ -1,5 +1,5 @@
foo ━━ 0% -:--:-- ⣷
bar ━━ 0% -:--:-- ⣷
baz ━━ 0% -:--:-- ⣷
foo ━━ 0% --:--:-- ⣷
bar ━━ 0% --:--:-- ⣷
baz ━━ 0% --:--:-- ⣷

View File

@@ -0,0 +1 @@
Favorite fruit? (******): ******

View File

@@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="Shouldly" Version="4.0.3" />
<PackageReference Include="Spectre.Verify.Extensions" Version="0.1.0" />
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

View File

@@ -1,18 +1,3 @@
<ProjectConfiguration>
<Settings>
<IgnoredTests>
<NamedTestSelector>
<TestName>Spectre.Console.Tests.Unit.Cli.CommandAppTests+Parsing+UnknownCommand.Should_Return_Correct_Text_With_Suggestion_And_No_Arguments_When_Root_Command_Is_Unknown_And_Distance_Is_Small</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Spectre.Console.Tests.Unit.Cli.CommandAppTests+Parsing+UnknownCommand.Should_Return_Correct_Text_With_Suggestion_When_Command_Followed_By_Argument_Is_Unknown_And_Distance_Is_Small</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Spectre.Console.Tests.Unit.Cli.CommandAppTests+Parsing+UnknownCommand.Should_Return_Correct_Text_With_Suggestion_When_Root_Command_After_Argument_Is_Unknown_And_Distance_Is_Small</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>Spectre.Console.Tests.Unit.Cli.CommandAppTests+Parsing+UnknownCommand.Should_Return_Correct_Text_With_Suggestion_When_Root_Command_Followed_By_Argument_Is_Unknown_And_Distance_Is_Small</TestName>
</NamedTestSelector>
</IgnoredTests>
</Settings>
<Settings />
</ProjectConfiguration>

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Shouldly;
using Spectre.Console.Testing;
using Xunit;
@@ -8,14 +7,11 @@ namespace Spectre.Console.Tests.Unit
{
public partial class AnsiConsoleTests
{
[SuppressMessage("Naming", "CA1724:Type names should not match namespaces")]
public sealed class Markup
{
[Theory]
[InlineData("[yellow]Hello[/]", "Hello")]
[InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")]
[InlineData("[link=https://patriksvensson.se]Click to visit my blog[/]", "]8;id=1024;https://patriksvensson.se\\Click to visit my blog]8;;\\")]
[InlineData("[link]https://patriksvensson.se[/]", "]8;id=1024;https://patriksvensson.se\\https://patriksvensson.se]8;;\\")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{
// Given
@@ -28,6 +24,32 @@ namespace Spectre.Console.Tests.Unit
console.Output.ShouldBe(expected);
}
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Url_And_Text()
{
// Given
var console = new FakeAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When
console.Markup("[link=https://patriksvensson.se]Click to visit my blog[/]");
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\Click to visit my blog]8;;\\\\");
}
[Fact]
public void Should_Output_Expected_Ansi_For_Link_With_Only_Url()
{
// Given
var console = new FakeAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When
console.Markup("[link]https://patriksvensson.se[/]");
// Then
console.Output.ShouldMatch("]8;id=[0-9]*;https:\\/\\/patriksvensson\\.se\\\\https:\\/\\/patriksvensson\\.se]8;;\\\\");
}
[Theory]
[InlineData("[yellow]Hello [[ World[/]", "Hello [ World")]
public void Should_Be_Able_To_Escape_Tags(string markup, string expected)

View File

@@ -28,5 +28,24 @@ namespace Spectre.Console.Tests.Unit
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Zero_Value")]
public async Task Should_Render_Correctly_2()
{
// Given
var console = new FakeConsole(width: 80);
// When
console.Render(new BarChart()
.Width(60)
.Label("Number of fruits")
.AddItem("Apple", 0)
.AddItem("Orange", 54)
.AddItem("Banana", 33));
// Then
await Verifier.Verify(console.Output);
}
}
}

View File

@@ -0,0 +1,150 @@
using System.Threading.Tasks;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
[UsesVerify]
[ExpectationPath("Widgets/BreakdownChart")]
public sealed class BreakdownChartTests
{
[Fact]
[Expectation("Default")]
public async Task Should_Render_Correctly()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart();
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Width")]
public async Task Should_Render_With_Specific_Width()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart().Width(60);
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("TagFormat")]
public async Task Should_Render_Correctly_With_Specific_Value_Formatter()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart()
.Width(60)
.Culture("sv-SE")
.UseValueFormatter((v, c) => string.Format(c, "{0}%", v));
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTags")]
public async Task Should_Render_Correctly_Without_Tags()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart().Width(60).HideTags();
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("HideTagValues")]
public async Task Should_Render_Correctly_Without_Tag_Values()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart().Width(60).HideTagValues();
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Culture")]
public async Task Should_Render_Correctly_With_Specific_Culture()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart().Width(60).Culture("sv-SE");
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("FullSize")]
public async Task Should_Render_FullSize_Mode_Correctly()
{
// Given
var console = new FakeConsole(width: 80);
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
[Fact]
[Expectation("Ansi")]
public async Task Should_Render_Correct_Ansi()
{
// Given
var console = new FakeAnsiConsole(ColorSystem.EightBit, width: 80);
var chart = Fixture.GetChart().Width(60).FullSize();
// When
console.Render(chart);
// Then
await Verifier.Verify(console.Output);
}
public static class Fixture
{
public static BreakdownChart GetChart()
{
return new BreakdownChart()
.AddItem("SCSS", 37, Color.Red)
.AddItem("HTML", 28.3, Color.Blue)
.AddItem("C#", 22.6, Color.Green)
.AddItem("JavaScript", 6, Color.Yellow)
.AddItem("Ruby", 6, Color.LightGreen)
.AddItem("Shell", 0.1, Color.Aqua);
}
}
}
}

View File

@@ -71,5 +71,19 @@ namespace Spectre.Console.Tests.Unit
// Then
console.Output.ShouldBe(output);
}
[Fact]
public void Should_not_fail_with_brackets_on_calls_without_args()
{
// Given
var console = new FakeAnsiConsole(ColorSystem.Standard, AnsiSupport.Yes);
// When
console.MarkupLine("{");
// Then
console.Output.NormalizeLineEndings()
.ShouldBe("{\n");
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Globalization;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class DownloadedColumnTests
{
[Theory]
[InlineData(0, 1, "0/1 byte")]
[InlineData(37, 101, "37/101 bytes")]
[InlineData(101, 101, "101 bytes")]
[InlineData(512, 1024, "0.5/1.0 KB")]
[InlineData(1024, 1024, "1.0 KB")]
[InlineData(1024 * 512, 5 * 1024 * 1024, "0.5/5.0 MB")]
[InlineData(5 * 1024 * 1024, 5 * 1024 * 1024, "5.0 MB")]
public void Should_Return_Correct_Value(double value, double total, string expected)
{
// Given
var fixture = new ProgressColumnFixture<DownloadedColumn>(value, total);
fixture.Column.Culture = CultureInfo.InvariantCulture;
// When
var result = fixture.Render();
// Then
result.ShouldBe(expected);
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Text;
using Spectre.Console.Rendering;
using Spectre.Console.Testing;
namespace Spectre.Console.Tests.Unit
{
public sealed class ProgressColumnFixture<T>
where T : ProgressColumn, new()
{
public T Column { get; }
public ProgressTask Task { get; set; }
public ProgressColumnFixture(double completed, double total)
{
Column = new T();
Task = new ProgressTask(1, "Foo", total);
Task.Increment(completed);
}
public string Render()
{
var console = new FakeConsole();
var context = new RenderContext(Encoding.UTF8, false);
console.Render(Column.Render(context, Task, TimeSpan.Zero));
return console.Output;
}
}
}

View File

@@ -115,5 +115,111 @@ namespace Spectre.Console.Tests.Unit
task.MaxValue.ShouldBe(20);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_Should_Override_Incremented_Value()
{
// Given
var task = default(ProgressTask);
var console = new FakeConsole();
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Increment(50);
task.Value = 20;
});
// Then
task.MaxValue.ShouldBe(100);
task.Value.ShouldBe(20);
}
[Fact]
public void Setting_Value_To_MaxValue_Should_Finish_Task()
{
// Given
var task = default(ProgressTask);
var console = new FakeConsole();
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = task.MaxValue;
});
// Then
task.IsFinished.ShouldBe(true);
}
[Fact]
public void Should_Increment_Manually_Set_Value()
{
// Given
var task = default(ProgressTask);
var console = new FakeConsole();
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false);
// When
progress.Start(ctx =>
{
task = ctx.AddTask("foo");
task.Value = 50;
task.Increment(10);
});
// Then
task.Value.ShouldBe(60);
}
[Fact]
public void Should_Hide_Completed_Tasks()
{
// Given
var taskFinished = default(ProgressTask);
var taskInProgress1 = default(ProgressTask);
var taskInProgress2 = default(ProgressTask);
var console = new FakeAnsiConsole(ColorSystem.TrueColor, width: 10);
var progress = new Progress(console)
.Columns(new[] { new ProgressBarColumn() })
.AutoRefresh(false)
.AutoClear(false)
.HideCompleted(true);
// When
progress.Start(ctx =>
{
taskInProgress1 = ctx.AddTask("foo");
taskFinished = ctx.AddTask("bar");
taskInProgress2 = ctx.AddTask("baz");
taskInProgress2.Increment(20);
taskFinished.Value = taskFinished.MaxValue;
});
// Then
console.Output
.NormalizeLineEndings()
.ShouldBe(
"[?25l" + // Hide cursor
" \n" + // top padding
"━━━━━━━━━━\n" + // taskInProgress1
"━━━━━━━━━━\n" + // taskInProgress2
" \n" + // bottom padding
"[?25h"); // show cursor
}
}
}

View File

@@ -200,5 +200,23 @@ namespace Spectre.Console.Tests.Unit
result.Item1.ShouldBe(2);
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("SecretDefaultValue")]
public Task Should_Chose_Masked_Default_Value_If_Nothing_Is_Entered_And_Prompt_Is_Secret()
{
// Given
var console = new FakeConsole();
console.Input.PushKey(ConsoleKey.Enter);
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.Secret()
.DefaultValue("Banana"));
// Then
return Verifier.Verify(console.Output);
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Spectre.Console.Testing;
using Spectre.Verify.Extensions;
using VerifyXunit;
@@ -56,5 +57,29 @@ namespace Spectre.Console.Tests.Unit
// Then
return Verifier.Verify(console.Output);
}
[Fact]
public void Should_Throw_If_Tree_Contains_Cycles()
{
// Given
var console = new FakeConsole(width: 80);
var child2 = new TreeNode(new Text("child 2"));
var child3 = new TreeNode(new Text("child 3"));
var child1 = new TreeNode(new Text("child 1"));
child1.AddNodes(child2, child3);
var root = new TreeNode(new Text("Branch Node"));
root.AddNodes(child1);
child2.AddNode(root);
var tree = new Tree("root node");
tree.AddNodes(root);
// When
var result = Record.Exception(() => console.Render(tree));
// Then
result.ShouldBeOfType<CircularTreeException>();
}
}
}

View File

@@ -80,6 +80,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{E0E4
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trees", "..\examples\Console\Trees\Trees.csproj", "{CA7AF967-3FA5-4CB1-9564-740CF4527895}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "..\examples\Cli\Logging\Logging.csproj", "{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -426,6 +428,18 @@ Global
{CA7AF967-3FA5-4CB1-9564-740CF4527895}.Release|x64.Build.0 = Release|Any CPU
{CA7AF967-3FA5-4CB1-9564-740CF4527895}.Release|x86.ActiveCfg = Release|Any CPU
{CA7AF967-3FA5-4CB1-9564-740CF4527895}.Release|x86.Build.0 = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|x64.ActiveCfg = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|x64.Build.0 = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|x86.ActiveCfg = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Debug|x86.Build.0 = Debug|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|Any CPU.Build.0 = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|x64.ActiveCfg = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|x64.Build.0 = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|x86.ActiveCfg = Release|Any CPU
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -458,6 +472,7 @@ Global
{E9C02C5A-710C-4A57-A008-E3EAC89305CC} = {42792D7F-0BB6-4EE1-A314-8889305A4C48}
{F83CB4F1-95B8-45A4-A415-6DB5F8CA1E12} = {42792D7F-0BB6-4EE1-A314-8889305A4C48}
{CA7AF967-3FA5-4CB1-9564-740CF4527895} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{33C7075A-DF97-44FC-8AB3-0CCFBA03341A} = {42792D7F-0BB6-4EE1-A314-8889305A4C48}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String></wpf:ResourceDictionary>

View File

@@ -38,10 +38,15 @@ namespace Spectre.Console
/// Displays a prompt with two choices, yes or no.
/// </summary>
/// <param name="prompt">The prompt markup text.</param>
/// <param name="defaultValue">Specifies the default answer.</param>
/// <returns><c>true</c> if the user selected "yes", otherwise <c>false</c>.</returns>
public static bool Confirm(string prompt)
public static bool Confirm(string prompt, bool defaultValue = true)
{
return new ConfirmationPrompt(prompt).Show(Console);
return new ConfirmationPrompt(prompt)
{
DefaultValue = defaultValue,
}
.Show(Console);
}
}
}

View File

@@ -13,7 +13,10 @@ namespace Spectre.Console
/// </summary>
public static void Record()
{
_recorder = new Recorder(_console.Value);
if (_recorder == null)
{
_recorder = new Recorder(Console);
}
}
/// <summary>

View File

@@ -1,5 +1,4 @@
using System;
using Spectre.Console.Internal;
namespace Spectre.Console
{
@@ -8,7 +7,9 @@ namespace Spectre.Console
/// </summary>
public static partial class AnsiConsole
{
private static readonly Lazy<IAnsiConsole> _console = new Lazy<IAnsiConsole>(() =>
private static Recorder? _recorder;
private static Lazy<IAnsiConsole> _console = new Lazy<IAnsiConsole>(
() =>
{
var console = Create(new AnsiConsoleSettings
{
@@ -16,16 +17,33 @@ namespace Spectre.Console
ColorSystem = ColorSystemSupport.Detect,
Out = System.Console.Out,
});
Created = true;
return console;
});
private static Recorder? _recorder;
/// <summary>
/// Gets the underlying <see cref="IAnsiConsole"/>.
/// Gets or sets the underlying <see cref="IAnsiConsole"/>.
/// </summary>
public static IAnsiConsole Console => _recorder ?? _console.Value;
public static IAnsiConsole Console
{
get
{
return _recorder ?? _console.Value;
}
set
{
_console = new Lazy<IAnsiConsole>(() => value);
if (_recorder != null)
{
// Recreate the recorder
_recorder = _recorder.Clone(value);
}
Created = true;
}
}
/// <summary>
/// Gets the <see cref="IAnsiConsoleCursor"/>.
@@ -33,25 +51,9 @@ namespace Spectre.Console
public static IAnsiConsoleCursor Cursor => _recorder?.Cursor ?? _console.Value.Cursor;
/// <summary>
/// Gets the console's capabilities.
/// Gets the console profile.
/// </summary>
public static Capabilities Capabilities => Console.Capabilities;
/// <summary>
/// Gets the buffer width of the console.
/// </summary>
public static int Width
{
get => Console.Width;
}
/// <summary>
/// Gets the buffer height of the console.
/// </summary>
public static int Height
{
get => Console.Height;
}
public static Profile Profile => Console.Profile;
/// <summary>
/// Creates a new <see cref="IAnsiConsole"/> instance
@@ -61,7 +63,8 @@ namespace Spectre.Console
/// <returns>An <see cref="IAnsiConsole"/> instance.</returns>
public static IAnsiConsole Create(AnsiConsoleSettings settings)
{
return BackendBuilder.Build(settings);
var factory = new AnsiConsoleFactory();
return factory.Create(settings);
}
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using Spectre.Console.Enrichment;
namespace Spectre.Console
{
/// <summary>
/// Factory for creating an ANSI console.
/// </summary>
public sealed class AnsiConsoleFactory
{
/// <summary>
/// Creates an ANSI console.
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns>An implementation of <see cref="IAnsiConsole"/>.</returns>
public IAnsiConsole Create(AnsiConsoleSettings settings)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
var buffer = settings.Out ?? System.Console.Out;
// Detect if the terminal support ANSI or not
var (supportsAnsi, legacyConsole) = DetectAnsi(settings, buffer);
// Use the provided encoding or fall back to UTF-8
var encoding = buffer.IsStandardOut() || buffer.IsStandardError() ? System.Console.OutputEncoding : Encoding.UTF8;
// Get the color system
var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect
? ColorSystemDetector.Detect(supportsAnsi)
: (ColorSystem)settings.ColorSystem;
// Get whether or not we consider the terminal interactive
var interactive = settings.Interactive == InteractionSupport.Yes;
if (settings.Interactive == InteractionSupport.Detect)
{
interactive = Environment.UserInteractive;
}
var profile = new Profile(buffer, encoding)
{
ColorSystem = colorSystem,
};
profile.Capabilities.Ansi = supportsAnsi;
profile.Capabilities.Links = supportsAnsi && !legacyConsole;
profile.Capabilities.Legacy = legacyConsole;
profile.Capabilities.Interactive = interactive;
// Enrich the profile
ProfileEnricher.Enrich(
profile,
settings.Enrichment,
settings.EnvironmentVariables);
return new AnsiConsoleFacade(profile);
}
private static (bool Ansi, bool Legacy) DetectAnsi(AnsiConsoleSettings settings, System.IO.TextWriter buffer)
{
var supportsAnsi = settings.Ansi == AnsiSupport.Yes;
var legacyConsole = false;
if (settings.Ansi == AnsiSupport.Detect)
{
(supportsAnsi, legacyConsole) = AnsiDetector.Detect(true);
// Check whether or not this is a legacy console from the existing instance (if any).
// We need to do this because once we upgrade the console to support ENABLE_VIRTUAL_TERMINAL_PROCESSING
// on Windows, there is no way of detecting whether or not we're running on a legacy console or not.
if (AnsiConsole.Created && !legacyConsole && (buffer.IsStandardOut() || buffer.IsStandardError()) && AnsiConsole.Profile.Capabilities.Legacy)
{
legacyConsole = AnsiConsole.Profile.Capabilities.Legacy;
}
}
else
{
if (buffer.IsStandardOut() || buffer.IsStandardError())
{
// Are we running on Windows?
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Not the first console we're creating?
if (AnsiConsole.Created)
{
legacyConsole = AnsiConsole.Profile.Capabilities.Legacy;
}
else
{
// Try detecting whether or not this
(_, legacyConsole) = AnsiDetector.Detect(false);
}
}
}
}
return (supportsAnsi, legacyConsole);
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
namespace Spectre.Console
@@ -19,19 +20,33 @@ namespace Spectre.Console
public ColorSystemSupport ColorSystem { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or
/// not the console is interactive.
/// Gets or sets the out buffer.
/// </summary>
public TextWriter? Out { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not the
/// terminal is interactive or not.
/// </summary>
public InteractionSupport Interactive { get; set; }
/// <summary>
/// Gets or sets the link identity generator.
/// Gets or sets the profile enrichments settings.
/// </summary>
public ILinkIdentityGenerator? LinkIdentityGenerator { get; set; }
public ProfileEnrichment Enrichment { get; set; }
/// <summary>
/// Gets or sets the out buffer.
/// Gets or sets the environment variables.
/// If not value is provided the default environment variables will be used.
/// </summary>
public TextWriter? Out { get; set; }
public Dictionary<string, string>? EnvironmentVariables { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="AnsiConsoleSettings"/> class.
/// </summary>
public AnsiConsoleSettings()
{
Enrichment = new ProfileEnrichment();
}
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace Spectre.Console
{
/// <summary>
@@ -5,14 +7,16 @@ namespace Spectre.Console
/// </summary>
public sealed class Capabilities
{
/// <summary>
/// Gets a value indicating whether or not
/// the console supports Ansi.
/// </summary>
public bool SupportsAnsi { get; }
private readonly Profile _profile;
/// <summary>
/// Gets a value indicating whether or not
/// Gets or sets a value indicating whether or not
/// the console supports Ansi.
/// </summary>
public bool Ansi { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not
/// the console support links.
/// </summary>
/// <remarks>
@@ -20,69 +24,53 @@ namespace Spectre.Console
/// once we have more information about the terminal
/// we're running inside.
/// </remarks>
public bool SupportLinks => SupportsAnsi && !LegacyConsole;
public bool Links { get; set; }
/// <summary>
/// Gets the color system.
/// </summary>
public ColorSystem ColorSystem { get; }
/// <summary>
/// Gets a value indicating whether or not
/// this is a legacy console (cmd.exe).
/// Gets or sets a value indicating whether or not
/// this is a legacy console (cmd.exe) on an OS
/// prior to Windows 10.
/// </summary>
/// <remarks>
/// Only relevant when running on Microsoft Windows.
/// </remarks>
public bool LegacyConsole { get; }
public bool Legacy { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not the console supports interaction.
/// Gets a value indicating whether console output
/// has been redirected.
/// </summary>
public bool SupportsInteraction { get; set; }
public bool Tty
{
get
{
if (_profile.Out.IsStandardOut())
{
return System.Console.IsOutputRedirected;
}
if (_profile.Out.IsStandardError())
{
return System.Console.IsErrorRedirected;
}
// Not stdout, so must be a TTY.
return true;
}
}
/// <summary>
/// Gets or sets a value indicating whether
/// or not the console supports interaction.
/// </summary>
public bool Interactive { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Capabilities"/> class.
/// </summary>
/// <param name="supportsAnsi">Whether or not ANSI escape sequences are supported.</param>
/// <param name="colorSystem">The color system that is supported.</param>
/// <param name="legacyConsole">Whether or not this is a legacy console.</param>
/// <param name="supportsInteraction">Whether or not the console supports interaction.</param>
public Capabilities(bool supportsAnsi, ColorSystem colorSystem, bool legacyConsole, bool supportsInteraction)
internal Capabilities(Profile profile)
{
SupportsAnsi = supportsAnsi;
ColorSystem = colorSystem;
LegacyConsole = legacyConsole;
SupportsInteraction = supportsInteraction;
}
/// <summary>
/// Checks whether the current capabilities supports
/// the specified color system.
/// </summary>
/// <param name="colorSystem">The color system to check.</param>
/// <returns><c>true</c> if the color system is supported, otherwise <c>false</c>.</returns>
public bool Supports(ColorSystem colorSystem)
{
return (int)colorSystem <= (int)ColorSystem;
}
/// <inheritdoc/>
public override string ToString()
{
var supportsAnsi = SupportsAnsi ? "Yes" : "No";
var legacyConsole = LegacyConsole ? "Legacy" : "Modern";
var bits = ColorSystem switch
{
ColorSystem.NoColors => "1 bit",
ColorSystem.Legacy => "3 bits",
ColorSystem.Standard => "4 bits",
ColorSystem.EightBit => "8 bits",
ColorSystem.TrueColor => "24 bits",
_ => "?",
};
return $"ANSI={supportsAnsi}, Colors={ColorSystem}, Kind={legacyConsole} ({bits})";
_profile = profile ?? throw new ArgumentNullException(nameof(profile));
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Spectre.Console.Cli.Internal;
namespace Spectre.Console.Cli
{

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Cli.Internal;
namespace Spectre.Console.Cli
{

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Cli
{
@@ -7,7 +6,6 @@ namespace Spectre.Console.Cli
/// Represents case sensitivity.
/// </summary>
[Flags]
[SuppressMessage("Naming", "CA1714:Flags enums should have plural names")]
public enum CaseSensitivity
{
/// <summary>

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Spectre.Console.Cli.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli
@@ -74,11 +73,11 @@ namespace Spectre.Console.Cli
if (!_executed)
{
// Add built-in (hidden) commands.
_configurator.AddBranch(Constants.Commands.Branch, cli =>
_configurator.AddBranch(CliConstants.Commands.Branch, cli =>
{
cli.HideBranch();
cli.AddCommand<VersionCommand>(Constants.Commands.Version);
cli.AddCommand<XmlDocCommand>(Constants.Commands.XmlDoc);
cli.AddCommand<VersionCommand>(CliConstants.Commands.Version);
cli.AddCommand<XmlDocCommand>(CliConstants.Commands.XmlDoc);
});
_executed = true;

View File

@@ -1,6 +1,5 @@
using System;
using System.Linq;
using Spectre.Console.Cli.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Spectre.Console.Cli.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli

View File

@@ -1,5 +1,4 @@
using System;
using Spectre.Console.Cli.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli
@@ -33,7 +32,7 @@ namespace Spectre.Console.Cli
internal static CommandRuntimeException MissingRequiredArgument(CommandTree node, CommandArgument argument)
{
if (node.Command.Name == Constants.DefaultCommandName)
if (node.Command.Name == CliConstants.DefaultCommandName)
{
return new CommandRuntimeException($"Missing required argument '{argument.Value}'.");
}

View File

@@ -1,5 +1,4 @@
using System.Globalization;
using Spectre.Console.Cli.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli

View File

@@ -25,6 +25,23 @@ namespace Spectre.Console.Cli
return configurator;
}
/// <summary>
/// Overrides the auto-detected version of the application.
/// </summary>
/// <param name="configurator">The configurator.</param>
/// <param name="version">The version of application.</param>
/// <returns>A configurator that can be used to configure the application further.</returns>
public static IConfigurator SetApplicationVersion(this IConfigurator configurator, string version)
{
if (configurator == null)
{
throw new ArgumentNullException(nameof(configurator));
}
configurator.Settings.ApplicationVersion = version;
return configurator;
}
/// <summary>
/// Configures the console.
/// </summary>

View File

@@ -10,6 +10,11 @@ namespace Spectre.Console.Cli
/// </summary>
string? ApplicationName { get; set; }
/// <summary>
/// Gets or sets the application version (use it to override auto-detected value).
/// </summary>
string? ApplicationVersion { get; set; }
/// <summary>
/// Gets or sets the <see cref="IAnsiConsole"/>.
/// </summary>

View File

@@ -21,6 +21,13 @@ namespace Spectre.Console.Cli
/// <param name="implementation">The implementation.</param>
void RegisterInstance(Type service, object implementation);
/// <summary>
/// Registers the specified instance lazily.
/// </summary>
/// <param name="service">The service.</param>
/// <param name="factory">The factory that creates the implementation.</param>
void RegisterLazy(Type service, Func<object> factory);
/// <summary>
/// Builds the type resolver representing the registrations
/// specified in the current instance.

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal static class CommandConstructorBinder
{

View File

@@ -1,6 +1,6 @@
using System;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal static class CommandPropertyBinder
{

View File

@@ -1,6 +1,6 @@
using System;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal sealed class CommandValueBinder
{

View File

@@ -3,7 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal sealed class CommandValueLookup : IEnumerable<(CommandParameter Parameter, object? Value)>
{

View File

@@ -2,7 +2,7 @@ using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal static class CommandValueResolver
{

View File

@@ -1,4 +1,4 @@
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
/// <summary>
/// Representation of a multi map.

View File

@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
internal sealed class MultiMap<TKey, TValue> : IMultiMap, ILookup<TKey, TValue>, IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>

View File

@@ -1,6 +1,6 @@
using System;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal static class CommandBinder
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal sealed class CommandExecutor
{
@@ -24,7 +24,7 @@ namespace Spectre.Console.Cli.Internal
}
_registrar.RegisterInstance(typeof(IConfiguration), configuration);
_registrar.RegisterInstance(typeof(IAnsiConsole), configuration.Settings.Console.GetConsole());
_registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole());
// Create the command model.
var model = CommandModelBuilder.Build(configuration);
@@ -44,7 +44,7 @@ namespace Spectre.Console.Cli.Internal
firstArgument.Equals("-v", StringComparison.OrdinalIgnoreCase))
{
var console = configuration.Settings.Console.GetConsole();
console.WriteLine(VersionHelper.GetVersion(Assembly.GetEntryAssembly()));
console.WriteLine(ResolveApplicationVersion(configuration));
return Task.FromResult(0);
}
}
@@ -83,6 +83,13 @@ namespace Spectre.Console.Cli.Internal
return Execute(leaf, parsedResult.Tree, context, resolver, configuration);
}
private static string ResolveApplicationVersion(IConfiguration configuration)
{
return
configuration.Settings.ApplicationVersion ?? // potential override
VersionHelper.GetVersion(Assembly.GetEntryAssembly());
}
private static Task<int> Execute(
CommandTree leaf,
CommandTree tree,

View File

@@ -1,4 +1,4 @@
namespace Spectre.Console.Cli.Internal
namespace Spectre.Console.Cli
{
internal enum CommandPart
{

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