mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8e92e7f44 | ||
|
|
3718502eee | ||
|
|
39cfc7a62f | ||
|
|
2e90ef28e4 | ||
|
|
2a3763cdc7 | ||
|
|
e0395dfa2b | ||
|
|
ca2e6ce0ad | ||
|
|
fa15389158 | ||
|
|
a5716a35e2 | ||
|
|
644fb76d61 | ||
|
|
e3dfe23b59 | ||
|
|
ad23855b0a | ||
|
|
64f444114a | ||
|
|
b058c54f3c | ||
|
|
8cfe06e77a | ||
|
|
48d49d6e18 | ||
|
|
49e8a980a7 | ||
|
|
d34012cad0 | ||
|
|
c3510f3036 | ||
|
|
786b7670da | ||
|
|
ffd24ec451 | ||
|
|
e081593012 | ||
|
|
bf95564ebb | ||
|
|
3c5b98123b | ||
|
|
7276e11ecc | ||
|
|
d306ad82d1 | ||
|
|
d96817dc9c | ||
|
|
e169df6303 | ||
|
|
57731c0d55 | ||
|
|
170901f584 | ||
|
|
c2b25eea8a |
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: Documentation
|
||||
if: false # Disable for now
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -89,7 +90,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
dotnet tool restore
|
||||
dotnet example --all
|
||||
dotnet example --all --skip live --skip livetable --skip prompt
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'examples/**'
|
||||
- '.github/**'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -14,6 +17,7 @@ jobs:
|
||||
|
||||
build:
|
||||
name: Deploy
|
||||
if: false # Disable for now
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
5
.github/workflows/publish.yaml
vendored
5
.github/workflows/publish.yaml
vendored
@@ -6,8 +6,6 @@ on:
|
||||
- '*'
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'src/**'
|
||||
|
||||
env:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
@@ -21,6 +19,7 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: Documentation
|
||||
if: false # Disable for now
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -46,7 +45,7 @@ jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
needs: [docs]
|
||||
# needs: [docs]
|
||||
if: "!contains(github.event.head_commit.message, 'skip-ci') || startsWith(github.ref, 'refs/tags/')"
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -7,7 +7,7 @@ Severity: Warning
|
||||
|
||||
## Cause
|
||||
|
||||
A violation of this rule occurs when a AnsiConsole prompt is called within the context of an executing renderable e.g. `Progress`, `Status` and `Live`. Concurrent LiveRenderable are not supported and will cause issues when running simultaneously.
|
||||
A violation of this rule occurs when an AnsiConsole prompt is called within the context of an executing renderable e.g. `Progress`, `Status` and `Live`. Concurrent LiveRenderable are not supported and will cause issues when running simultaneously.
|
||||
|
||||
## Reason for rule
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Highlights:
|
||||
- And more...
|
||||
---
|
||||
|
||||
There is different built-in borders you can use for tables and panels.
|
||||
There are different built-in borders you can use for tables and panels.
|
||||
|
||||
## Table borders
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ AnsiConsole.Status()
|
||||
To implement your own spinner, all you have to do is
|
||||
inherit from the `Spinner` base class.
|
||||
|
||||
In the example below, the spinner will alterate between
|
||||
In the example below, the spinner will alternate between
|
||||
the characters `A`, `B` and `C` every 100 ms.
|
||||
|
||||
```csharp
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
Title: Spectre.Console 0.41 released!
|
||||
Description: In this release we (mostly Phil) have been focusing on getting the new fancy Roslyn Analyzers out the door...<br /><br /><br /><br /><br />[More]
|
||||
Published: 20210719
|
||||
Category: Release Notes
|
||||
Excluded: false
|
||||
---
|
||||
|
||||
In this release, we (mostly [Phil](https://twitter.com/philco78)) have been focusing on getting the new fancy Roslyn Analyzers out the door.
|
||||
If you want to try them out, add a reference to [Spectre.Console.Analyzer](https://www.nuget.org/packages/spectre.console.analyzer) in your project, and you should get some best practice tips in your favorite IDE!
|
||||
|
||||
It's summer in the northern hemisphere, so it will probably be a couple of weeks until the next release.
|
||||
|
||||
## Features
|
||||
|
||||
* [#417 - Support cancellation in prompts](https://github.com/spectreconsole/spectre.console/issues/417)
|
||||
* [#324 - Remove AsciiTreeGuide as default tree guide](https://github.com/spectreconsole/spectre.console/issues/324)
|
||||
* [#413 - Support custom characters at the end of a TextPrompt](https://github.com/spectreconsole/spectre.console/issues/413)
|
||||
* [#447 - Alternative to the obsolete 'Select' function for selecting default items in SelectionPrompt](https://github.com/spectreconsole/spectre.console/issues/447)
|
||||
* [#460 - Default values for Ask()](https://github.com/spectreconsole/spectre.console/issues/460)
|
||||
|
||||
## Bugs
|
||||
|
||||
* [#480 - IAnsiConsole.Confirm extension is missing default value parameter](https://github.com/spectreconsole/spectre.console/issues/480)
|
||||
* [#442 - Allow dynamic Figlet hardblank](https://github.com/spectreconsole/spectre.console/pull/442)
|
||||
@@ -20,7 +20,7 @@ app.Configure(config =>
|
||||
|
||||
## Multiple Commands
|
||||
|
||||
In the previous example we have a single command that is configured. For complex command line applications, it is common for them to have multiple commands (or verbs) defined. Examples of applications like this are `git`, `dotnet` and `gh`. For example, git would have an `commit` command and along with other commits like `add` or `rebase`. Each with their own settings and validation. With `Spectre.Console.Cli` we use the `Configure` method to add these commands.
|
||||
In the previous example we have a single command that is configured. For complex command line applications, it is common for them to have multiple commands (or verbs) defined. Examples of applications like this are `git`, `dotnet` and `gh`. For example, git would have a `commit` command and along with other commits like `add` or `rebase`. Each with their own settings and validation. With `Spectre.Console.Cli` we use the `Configure` method to add these commands.
|
||||
|
||||
For example, to add three different commands to the application:
|
||||
|
||||
@@ -34,7 +34,7 @@ app.Configure(config =>
|
||||
});
|
||||
```
|
||||
|
||||
This configuration would allow users to run `app.exe add`, `app.exe commit`, or `app.exe rebase` and have the settings routed the appropriate command.
|
||||
This configuration would allow users to run `app.exe add`, `app.exe commit`, or `app.exe rebase` and have the settings routed to the appropriate command.
|
||||
|
||||
For more complex command hierarchical configurations, they can also be composed via inheritance and branching. See [Composing Commands](./composing).
|
||||
|
||||
@@ -76,7 +76,7 @@ return app.Run(args);
|
||||
|
||||
## Interception
|
||||
|
||||
`CommandApp` also provides a `SetInterceptor` configuration. An interceptor is ran before all commands are executed. This is typically used for configuring logging or other infrastructure concerns.
|
||||
`CommandApp` also provides a `SetInterceptor` configuration. An interceptor is run before all commands are executed. This is typically used for configuring logging or other infrastructure concerns.
|
||||
|
||||
All interceptors must implement `ICommandInterceptor`. Upon execution of a command, an instance of your interceptor will be called with the parsed settings. This provides an opportunity for configuring any infrastructure or modifying the settings.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public class HelloCommand : Command<HelloCommand.Settings>
|
||||
|
||||
## Configuring
|
||||
|
||||
Commands are configured via the [`CommandApp`](commandApp)'s `Configure` method.
|
||||
Commands are configured via the [`CommandApp`](commandapp)'s `Configure` method.
|
||||
|
||||
```csharp
|
||||
var app = new CommandApp();
|
||||
@@ -45,7 +45,7 @@ app.Configure(config =>
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Constructor injection is supported on commands. See the [`CommandApp`](commandApp) documentation for further information on configuring `Spectre.Console` for your container.
|
||||
Constructor injection is supported on commands. See the [`CommandApp`](commandapp) documentation for further information on configuring `Spectre.Console` for your container.
|
||||
|
||||
## Validation
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ Now you might wonder, why do things like this? Well, for starters the different
|
||||
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.
|
||||
This makes 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
|
||||
|
||||
@@ -67,7 +67,7 @@ This command will have three parameters.
|
||||
|
||||
When `args` is passed into the `CommandApp`'s run method, `Spectre.Console.Cli` will parse those arguments and populate an instance of your settings. Upon success, it will then pass those settings into an instance of the specified command's `Execute` method.
|
||||
|
||||
With this in place, we can the following commands will all work
|
||||
With this in place, the following commands will all work
|
||||
|
||||
```text
|
||||
app.exe
|
||||
@@ -78,6 +78,6 @@ app.exe c:\windows --hidden --pattern *.dll
|
||||
|
||||
Much more is possible. You can have multiple commands per application, settings can be customized and extended and the default behavior of the `CommandApp` can be extended and customized.
|
||||
|
||||
* See [CommandApp](./commandApp) for customizing how Spectre.Console.Cli builds the settings.
|
||||
* See [CommandApp](./commandapp) for customizing how Spectre.Console.Cli builds the settings.
|
||||
* See [Create Commands](./commands) for information about different command types and their configurations.
|
||||
* See [Specifying Settings](./settings) for information about defining the settings.
|
||||
|
||||
@@ -117,7 +117,7 @@ Now you might wonder, why do things like this? Well, for starters the different
|
||||
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.
|
||||
This makes 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Title: Migrate from Spectre.Cli
|
||||
Order: 10
|
||||
Description: "Migrating from *Specte.Cli* to *Spectre.Console.Cli*"
|
||||
Description: "Migrating from *Spectre.Cli* to *Spectre.Console.Cli*"
|
||||
---
|
||||
|
||||
The functionality in `Spectre.Cli` has been moved into the `Spectre.Console`
|
||||
@@ -26,7 +26,7 @@ Add the [Spectre.Console](https://www.nuget.org/packages/spectre.console) NuGet
|
||||
## 3. Change using statements
|
||||
|
||||
Change all using statements from `Spectre.Cli`
|
||||
to `Spectre.Console.CLi`.
|
||||
to `Spectre.Console.Cli`.
|
||||
|
||||
```diff
|
||||
- using Spectre.Cli;
|
||||
|
||||
@@ -22,7 +22,7 @@ This setting file tells `Spectre.Console.Cli` that our command has two parameter
|
||||
|
||||
## CommandArgument
|
||||
|
||||
Arguments have a position and a name. The name is not only used for generating help, but it's formatting is used to determine whether or not the argument is optional. The name must either be surrounded by square brackets (e.g. `[name]`) or angle brackets (e.g. `<name>`). Angle brackets denote required whereas square brackets denote optional. If neither are specified an exception will be thrown.
|
||||
Arguments have a position and a name. The name is not only used for generating help, but its formatting is used to determine whether or not the argument is optional. The name must either be surrounded by square brackets (e.g. `[name]`) or angle brackets (e.g. `<name>`). Angle brackets denote required whereas square brackets denote optional. If neither are specified an exception will be thrown.
|
||||
|
||||
The position is used for scenarios where there could be more than one argument.
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ For a list of emoji, see the [Emojis](xref:emojis) appendix section.
|
||||
|
||||
## Colors
|
||||
|
||||
In the examples above, all colors was referenced by their name,
|
||||
In the examples above, all colors were referenced by their name,
|
||||
but you can also use the hex or rgb representation for colors in markdown.
|
||||
|
||||
```csharp
|
||||
|
||||
@@ -36,7 +36,7 @@ AnsiConsole.Render(image);
|
||||
## Manipulating images
|
||||
|
||||
You can take full advantage of [ImageSharp](https://github.com/SixLabors/ImageSharp)
|
||||
and manipulate images directly via it's [Processing API](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Processing.html).
|
||||
and manipulate images directly via its [Processing API](https://docs.sixlabors.com/api/ImageSharp/SixLabors.ImageSharp.Processing.html).
|
||||
|
||||
```csharp
|
||||
// Load an image
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace Docs.Pipelines
|
||||
private IPlaywright _playwright;
|
||||
private IBrowser _browser;
|
||||
private WebApplication _app;
|
||||
private IBrowserContext _context;
|
||||
|
||||
protected override async Task BeforeExecutionAsync(IExecutionContext context)
|
||||
{
|
||||
@@ -74,10 +75,14 @@ namespace Docs.Pipelines
|
||||
|
||||
_playwright = await Playwright.CreateAsync().ConfigureAwait(false);
|
||||
_browser = await _playwright.Chromium.LaunchAsync().ConfigureAwait(false);
|
||||
_context = await _browser.NewContextAsync(new BrowserNewContextOptions {
|
||||
ViewportSize = new ViewportSize { Width = 1200, Height = 618 },
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override async Task FinallyAsync(IExecutionContext context)
|
||||
{
|
||||
await _context.DisposeAsync().ConfigureAwait(false);
|
||||
await _browser.DisposeAsync().ConfigureAwait(false);
|
||||
_playwright.Dispose();
|
||||
await _app.DisposeAsync().ConfigureAwait(false);
|
||||
@@ -87,18 +92,19 @@ namespace Docs.Pipelines
|
||||
protected override async Task<IEnumerable<IDocument>> ExecuteInputAsync(IDocument input, IExecutionContext context)
|
||||
{
|
||||
var url = _app.Urls.FirstOrDefault(u => u.StartsWith("http://"));
|
||||
var page = await _browser.NewPageAsync(new BrowserNewPageOptions
|
||||
{
|
||||
ViewportSize = new ViewportSize { Width = 1200, Height = 618 },
|
||||
}
|
||||
);
|
||||
var page = await _context.NewPageAsync().ConfigureAwait(false);
|
||||
|
||||
var title = input.GetString("Title");
|
||||
var description = input.GetString("Description");
|
||||
var highlights = input.GetList<string>("Highlights") ?? Array.Empty<string>();
|
||||
|
||||
await page.GotoAsync($"{url}/?title={title}&desc={description}&highlights={string.Join("||", highlights)}");
|
||||
var bytes = await page.ScreenshotAsync();
|
||||
|
||||
// This will not just wait for the page to load over the network, but it'll also give
|
||||
// chrome a chance to complete rendering of the fonts while the wait timeout completes.
|
||||
await page.WaitForLoadStateAsync(LoadState.NetworkIdle).ConfigureAwait(false);
|
||||
var bytes = await page.ScreenshotAsync().ConfigureAwait(false);
|
||||
await page.CloseAsync().ConfigureAwait(false);
|
||||
|
||||
var destination = input.Destination.InsertSuffix("-social").ChangeExtension("png");
|
||||
var doc = context.CreateDocument(
|
||||
@@ -107,7 +113,7 @@ namespace Docs.Pipelines
|
||||
new MetadataItems { { "DocId", input.Id }},
|
||||
context.GetContentProvider(bytes));
|
||||
|
||||
return new[] { doc };
|
||||
return new[] { doc };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="preload" as="font" href="/static/CascadiaCodePL.woff2">
|
||||
<link rel="stylesheet" href="static/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"dotnet-example": {
|
||||
"version": "1.3.1",
|
||||
"version": "1.5.0",
|
||||
"commands": [
|
||||
"dotnet-example"
|
||||
]
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Demo.Utilities
|
||||
value ?? "[grey]null[/]");
|
||||
}
|
||||
|
||||
AnsiConsole.Render(table);
|
||||
AnsiConsole.Write(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Spectre.Console.Cli;
|
||||
* 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
|
||||
* logging levels dynamically, and then we use a serilog enricher that has its state populated via a
|
||||
* Spectre.Console CommandInterceptor
|
||||
*/
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Spectre.Console.Examples
|
||||
CreatePanel("None", BoxBorder.None),
|
||||
};
|
||||
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Padder(
|
||||
new Columns(items).PadRight(2),
|
||||
new Padding(2,0,0,0)));
|
||||
@@ -77,13 +77,13 @@ namespace Spectre.Console.Examples
|
||||
CreateTable("Markdown", TableBorder.Markdown),
|
||||
};
|
||||
|
||||
AnsiConsole.Render(new Columns(items).Collapse());
|
||||
AnsiConsole.Write(new Columns(items).Collapse());
|
||||
}
|
||||
|
||||
private static void HorizontalRule(string title)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Spectre.Console.Examples
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Calendar(2020, 10)
|
||||
AnsiConsole.Write(new Calendar(2020, 10)
|
||||
.RoundedBorder()
|
||||
.HighlightStyle(Style.Parse("red"))
|
||||
.HeaderStyle(Style.Parse("yellow"))
|
||||
|
||||
@@ -39,9 +39,9 @@ namespace Spectre.Console.Examples
|
||||
private static void Render(IRenderable canvas, string title)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule($"[yellow]{title}[/]").LeftAligned().RuleStyle("grey"));
|
||||
AnsiConsole.Write(new Rule($"[yellow]{title}[/]").LeftAligned().RuleStyle("grey"));
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(canvas);
|
||||
AnsiConsole.Write(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Spectre.Console.Examples
|
||||
|
||||
private static void Render(string title, IRenderable chart)
|
||||
{
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(chart)
|
||||
.Padding(1, 1)
|
||||
.Header(title));
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
AnsiConsole.ResetColors();
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow bold underline]3-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow bold underline]3-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
for (var i = 0; i < 8; i++)
|
||||
@@ -43,7 +43,7 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
AnsiConsole.ResetColors();
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow bold underline]4-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow bold underline]4-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
for (var i = 0; i < 16; i++)
|
||||
@@ -66,7 +66,7 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
AnsiConsole.ResetColors();
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow bold underline]8-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow bold underline]8-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
for (var i = 0; i < 16; i++)
|
||||
@@ -93,10 +93,10 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
AnsiConsole.ResetColors();
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow bold underline]24-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow bold underline]24-bit Colors[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
AnsiConsole.Render(new ColorBox(width: 80, height: 15));
|
||||
AnsiConsole.Write(new ColorBox(width: 80, height: 15));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Spectre.Console.Examples
|
||||
}
|
||||
|
||||
// Render all cards in columns
|
||||
AnsiConsole.Render(new Columns(cards));
|
||||
AnsiConsole.Write(new Columns(cards));
|
||||
}
|
||||
|
||||
private static string GetCardContent(User user)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Spectre.Console.Examples
|
||||
|
||||
private static void RenderEmoji()
|
||||
{
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel("[yellow]Hello :globe_showing_europe_africa:![/]")
|
||||
.RoundedBorder());
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ namespace Spectre.Console.Examples
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("Default").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("Default").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex);
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("Compact").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("Compact").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks);
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("Compact + Custom colors").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("Compact + Custom colors").LeftAligned());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex, new ExceptionSettings
|
||||
{
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AnsiConsole.Render(new FigletText("Left aligned").LeftAligned().Color(Color.Red));
|
||||
AnsiConsole.Render(new FigletText("Centered").Centered().Color(Color.Green));
|
||||
AnsiConsole.Render(new FigletText("Right aligned").RightAligned().Color(Color.Blue));
|
||||
AnsiConsole.Write(new FigletText("Left aligned").LeftAligned().Color(Color.Red));
|
||||
AnsiConsole.Write(new FigletText("Centered").Centered().Color(Color.Green));
|
||||
AnsiConsole.Write(new FigletText("Right aligned").RightAligned().Color(Color.Blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Spectre.Console.Examples
|
||||
grid.AddRow(" [blue]-c[/], [blue]--configuration[/] <CONFIGURATION>", "The configuration to run for.");
|
||||
grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", "Set the [grey]MSBuild[/] verbosity level.");
|
||||
|
||||
AnsiConsole.Render(grid);
|
||||
AnsiConsole.Write(grid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Spectre.Console.Examples
|
||||
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Profile.Height}")
|
||||
.AddRow("[b]Encoding[/]", $"{AnsiConsole.Console.Profile.Encoding.EncodingName}");
|
||||
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(grid)
|
||||
.Header("Information"));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<ExampleTitle>Live</ExampleTitle>
|
||||
<ExampleDescription>Demonstrates how to do live updates.</ExampleDescription>
|
||||
<ExampleGroup>Misc</ExampleGroup>
|
||||
<ExampleGroup>Live</ExampleGroup>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
15
examples/Console/LiveTable/LiveTable.csproj
Normal file
15
examples/Console/LiveTable/LiveTable.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<ExampleTitle>LiveTable</ExampleTitle>
|
||||
<ExampleDescription>Demonstrates how to do live updates in a table.</ExampleDescription>
|
||||
<ExampleGroup>Live</ExampleGroup>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
86
examples/Console/LiveTable/Program.cs
Normal file
86
examples/Console/LiveTable/Program.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spectre.Console.Examples
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
private const int NumberOfRows = 10;
|
||||
|
||||
private static readonly Random _random = new();
|
||||
private static readonly string[] _exchanges = new string[]
|
||||
{
|
||||
"SGD", "SEK", "PLN",
|
||||
"MYR", "EUR", "USD",
|
||||
"AUD", "JPY", "CNH",
|
||||
"HKD", "CAD", "INR",
|
||||
"DKK", "GBP", "RUB",
|
||||
"NZD", "MXN", "IDR",
|
||||
"TWD", "THB", "VND",
|
||||
};
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var table = new Table().Expand().BorderColor(Color.Grey);
|
||||
table.AddColumn("[yellow]Source currency[/]");
|
||||
table.AddColumn("[yellow]Destination currency[/]");
|
||||
table.AddColumn("[yellow]Exchange rate[/]");
|
||||
|
||||
AnsiConsole.MarkupLine("Press [yellow]CTRL+C[/] to exit");
|
||||
|
||||
await AnsiConsole.Live(table)
|
||||
.AutoClear(false)
|
||||
.Overflow(VerticalOverflow.Ellipsis)
|
||||
.Cropping(VerticalOverflowCropping.Bottom)
|
||||
.StartAsync(async ctx =>
|
||||
{
|
||||
// Add some initial rows
|
||||
foreach (var _ in Enumerable.Range(0, NumberOfRows))
|
||||
{
|
||||
AddExchangeRateRow(table);
|
||||
}
|
||||
|
||||
// Continously update the table
|
||||
while (true)
|
||||
{
|
||||
// More rows than we want?
|
||||
if (table.Rows.Count > NumberOfRows)
|
||||
{
|
||||
// Remove the first one
|
||||
table.Rows.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Add a new row
|
||||
AddExchangeRateRow(table);
|
||||
|
||||
// Refresh and wait for a while
|
||||
ctx.Refresh();
|
||||
await Task.Delay(400);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddExchangeRateRow(Table table)
|
||||
{
|
||||
var (source, destination, rate) = GetExchangeRate();
|
||||
table.AddRow(
|
||||
source, destination,
|
||||
_random.NextDouble() > 0.35D ? $"[green]{rate}[/]" : $"[red]{rate}[/]");
|
||||
}
|
||||
|
||||
private static (string Source, string Destination, double Rate) GetExchangeRate()
|
||||
{
|
||||
var source = _exchanges[_random.Next(0, _exchanges.Length)];
|
||||
var dest = _exchanges[_random.Next(0, _exchanges.Length)];
|
||||
var rate = 200 / ((_random.NextDouble() * 320) + 1);
|
||||
|
||||
while (source == dest)
|
||||
{
|
||||
dest = _exchanges[_random.Next(0, _exchanges.Length)];
|
||||
}
|
||||
|
||||
return (source, dest, rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,20 +8,20 @@ namespace Spectre.Console.Examples
|
||||
"[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" +
|
||||
"So I put a panel in a panel").Centered();
|
||||
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(
|
||||
new Panel(content)
|
||||
.Border(BoxBorder.Rounded)));
|
||||
|
||||
// Left adjusted panel with text
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(new Text("Left adjusted\nLeft").LeftAligned())
|
||||
.Expand()
|
||||
.SquareBorder()
|
||||
.Header("[red]Left[/]"));
|
||||
|
||||
// Centered ASCII panel with text
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(new Text("Centered\nCenter").Centered())
|
||||
.Expand()
|
||||
.AsciiBorder()
|
||||
@@ -29,7 +29,7 @@ namespace Spectre.Console.Examples
|
||||
.HeaderAlignment(Justify.Center));
|
||||
|
||||
// Right adjusted, rounded panel with text
|
||||
AnsiConsole.Render(
|
||||
AnsiConsole.Write(
|
||||
new Panel(new Text("Right adjusted\nRight").RightAligned())
|
||||
.Expand()
|
||||
.RoundedBorder()
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace Spectre.Console.Examples
|
||||
|
||||
// Summary
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Render(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]")
|
||||
AnsiConsole.Write(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]")
|
||||
.RoundedBorder()
|
||||
.BorderColor(Color.Grey)
|
||||
.AddRow("[grey]Name[/]", name)
|
||||
@@ -43,7 +43,7 @@ namespace Spectre.Console.Examples
|
||||
private static string AskName()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftAligned());
|
||||
var name = AnsiConsole.Ask<string>("What's your [green]name[/]?");
|
||||
return name;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace Spectre.Console.Examples
|
||||
private static string AskFruit()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Lists[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Lists[/]").RuleStyle("grey").LeftAligned());
|
||||
|
||||
var favorites = AnsiConsole.Prompt(
|
||||
new MultiSelectionPrompt<string>()
|
||||
@@ -90,7 +90,7 @@ namespace Spectre.Console.Examples
|
||||
private static string AskSport()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftAligned());
|
||||
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("What's your [green]favorite sport[/]?")
|
||||
@@ -104,7 +104,7 @@ namespace Spectre.Console.Examples
|
||||
private static int AskAge()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftAligned());
|
||||
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<int>("How [green]old[/] are you?")
|
||||
@@ -124,7 +124,7 @@ namespace Spectre.Console.Examples
|
||||
private static string AskPassword()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftAligned());
|
||||
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("Enter [green]password[/]?")
|
||||
@@ -135,7 +135,7 @@ namespace Spectre.Console.Examples
|
||||
private static string AskColor()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftAligned());
|
||||
AnsiConsole.Write(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftAligned());
|
||||
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("[grey][[Optional]][/] What is your [green]favorite color[/]?")
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Spectre.Console.Examples
|
||||
|
||||
private static void Render(Rule rule)
|
||||
{
|
||||
AnsiConsole.Render(rule);
|
||||
AnsiConsole.Write(rule);
|
||||
AnsiConsole.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Spectre.Console.Examples
|
||||
|
||||
// Render the table
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(table);
|
||||
AnsiConsole.Write(table);
|
||||
}
|
||||
|
||||
private static IRenderable GetColorTable()
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Spectre.Console.Examples
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
AnsiConsole.Render(CreateTable());
|
||||
AnsiConsole.Write(CreateTable());
|
||||
}
|
||||
|
||||
private static Table CreateTable()
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Spectre.Console.Examples
|
||||
|
||||
// Render the tree
|
||||
var tree = BuildTree();
|
||||
AnsiConsole.Render(tree);
|
||||
AnsiConsole.Write(tree);
|
||||
}
|
||||
|
||||
private static Tree BuildTree()
|
||||
|
||||
@@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tables", "Console\Tables\Ta
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trees", "Console\Trees\Trees.csproj", "{2BD88288-E05D-4978-B045-17937078E63C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveTable", "Console\LiveTable\LiveTable.csproj", "{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -409,6 +411,18 @@ Global
|
||||
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"projects": [ "src", "tests" ],
|
||||
"sdk": {
|
||||
"version": "5.0.301",
|
||||
"rollForward": "latestPatch"
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,12 @@ namespace Spectre.Console.Analyzer
|
||||
return;
|
||||
}
|
||||
|
||||
// if we aren't in a method then it might be too complex for us to handle.
|
||||
if (!invocationOperation.Syntax.Ancestors().OfType<MethodDeclarationSyntax>().Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasFieldAnsiConsole(invocationOperation.Syntax) &&
|
||||
!HasParameterAnsiConsole(invocationOperation.Syntax))
|
||||
{
|
||||
|
||||
@@ -12,7 +12,17 @@ namespace Spectre.Console
|
||||
/// Renders the specified object to the console.
|
||||
/// </summary>
|
||||
/// <param name="renderable">The object to render.</param>
|
||||
[Obsolete("Consider using AnsiConsole.Write instead.")]
|
||||
public static void Render(IRenderable renderable)
|
||||
{
|
||||
Write(renderable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the specified <see cref="IRenderable"/> to the console.
|
||||
/// </summary>
|
||||
/// <param name="renderable">The object to render.</param>
|
||||
public static void Write(IRenderable renderable)
|
||||
{
|
||||
if (renderable is null)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
namespace Spectre.Console.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// An base class attribute used for parameter validation.
|
||||
/// A base class attribute used for parameter validation.
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
|
||||
|
||||
@@ -3,7 +3,7 @@ using System;
|
||||
namespace Spectre.Console.Cli
|
||||
{
|
||||
/// <summary>
|
||||
/// An base class attribute used for parameter completion.
|
||||
/// A base class attribute used for parameter completion.
|
||||
/// </summary>
|
||||
/// <seealso cref="Attribute" />
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Spectre.Console.Cli
|
||||
var validationResult = validator.Validate(context);
|
||||
if (!validationResult.Successful)
|
||||
{
|
||||
// If there is a error message specified in the parameter validator attribute,
|
||||
// If there is an error message specified in the parameter validator attribute,
|
||||
// then use that one, otherwise use the validation result.
|
||||
var result = string.IsNullOrWhiteSpace(validator.ErrorMessage)
|
||||
? validationResult
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
#if NET5_0
|
||||
using System;
|
||||
#endif
|
||||
|
||||
namespace Spectre.Console.Cli
|
||||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
internal static int OrdinalIndexOf(this string text, char token)
|
||||
{
|
||||
#if NET5_0
|
||||
return text.IndexOf(token, StringComparison.Ordinal);
|
||||
#else
|
||||
#if NETSTANDARD2_0
|
||||
return text.IndexOf(token);
|
||||
#else
|
||||
return text.IndexOf(token, System.StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Spectre.Console.Cli
|
||||
|
||||
if (command.CommandType != null)
|
||||
{
|
||||
registrar?.Register(typeof(ICommand), command.CommandType);
|
||||
registrar?.Register(command.CommandType, command.CommandType);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="text">The text to write.</param>
|
||||
/// <param name="style">The text style.</param>
|
||||
public static void Write(this IAnsiConsole console, string text, Style style)
|
||||
/// <param name="style">The text style or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public static void Write(this IAnsiConsole console, string text, Style? style)
|
||||
{
|
||||
if (console is null)
|
||||
{
|
||||
@@ -91,8 +91,8 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="text">The text to write.</param>
|
||||
/// <param name="style">The text style.</param>
|
||||
public static void WriteLine(this IAnsiConsole console, string text, Style style)
|
||||
/// <param name="style">The text style or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public static void WriteLine(this IAnsiConsole console, string text, Style? style)
|
||||
{
|
||||
if (console is null)
|
||||
{
|
||||
|
||||
@@ -238,5 +238,22 @@ namespace Spectre.Console
|
||||
chart.LabelAlignment = Justify.Right;
|
||||
return chart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the max fixed value for the chart.
|
||||
/// </summary>
|
||||
/// <param name="chart">The bar chart.</param>
|
||||
/// <param name="maxValue">Max value for the chart.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static BarChart WithMaxValue(this BarChart chart, double maxValue)
|
||||
{
|
||||
if (chart is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(chart));
|
||||
}
|
||||
|
||||
chart.MaxValue = maxValue;
|
||||
return chart;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Spectre.Console/Extensions/CharExtensions.cs
Normal file
18
src/Spectre.Console/Extensions/CharExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="char"/>.
|
||||
/// </summary>
|
||||
public static class CharExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the cell width of a character.
|
||||
/// </summary>
|
||||
/// <param name="character">The character to get the cell width of.</param>
|
||||
/// <returns>The cell width of the character.</returns>
|
||||
public static int GetCellWidth(this char character)
|
||||
{
|
||||
return Cell.GetCellLength(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,12 @@ namespace Spectre.Console
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
internal static int CellLength(this string text)
|
||||
/// <summary>
|
||||
/// Gets the cell width of the specified text.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to get the cell width of.</param>
|
||||
/// <returns>The cell width of the text.</returns>
|
||||
public static int GetCellWidth(this string text)
|
||||
{
|
||||
return Cell.GetCellLength(text);
|
||||
}
|
||||
@@ -172,19 +177,19 @@ namespace Spectre.Console
|
||||
|
||||
internal static string ReplaceExact(this string text, string oldValue, string? newValue)
|
||||
{
|
||||
#if NET5_0
|
||||
return text.Replace(oldValue, newValue, StringComparison.Ordinal);
|
||||
#else
|
||||
#if NETSTANDARD2_0
|
||||
return text.Replace(oldValue, newValue);
|
||||
#else
|
||||
return text.Replace(oldValue, newValue, StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool ContainsExact(this string text, string value)
|
||||
{
|
||||
#if NET5_0
|
||||
return text.Contains(value, StringComparison.Ordinal);
|
||||
#else
|
||||
#if NETSTANDARD2_0
|
||||
return text.Contains(value);
|
||||
#else
|
||||
return text.Contains(value, StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
@@ -35,6 +36,28 @@ namespace Spectre.Console
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a row to the table.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to add the row to.</param>
|
||||
/// <param name="columns">The row columns to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table AddRow(this Table table, IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
table.Rows.Add(new TableRow(columns));
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a row to the table.
|
||||
/// </summary>
|
||||
@@ -48,7 +71,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
return table.AddRow(columns);
|
||||
return table.AddRow((IEnumerable<IRenderable>)columns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -143,6 +166,130 @@ namespace Spectre.Console
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a row in the table at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to add the row to.</param>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The row columns to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table InsertRow(this Table table, int index, IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
table.Rows.Insert(index, new TableRow(columns));
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a tables cell.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to update.</param>
|
||||
/// <param name="rowIndex">The index of row to update.</param>
|
||||
/// <param name="columnIndex">The index of column to update.</param>
|
||||
/// <param name="cellData">New cell data.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table UpdateCell(this Table table, int rowIndex, int columnIndex, IRenderable cellData)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
if (cellData is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cellData));
|
||||
}
|
||||
|
||||
table.Rows.Update(rowIndex, columnIndex, cellData);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a tables cell.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to update.</param>
|
||||
/// <param name="rowIndex">The index of row to update.</param>
|
||||
/// <param name="columnIndex">The index of column to update.</param>
|
||||
/// <param name="cellData">New cell data.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table UpdateCell(this Table table, int rowIndex, int columnIndex, string cellData)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
if (cellData is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cellData));
|
||||
}
|
||||
|
||||
table.Rows.Update(rowIndex, columnIndex, new Markup(cellData));
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a row in the table at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to add the row to.</param>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The row columns to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table InsertRow(this Table table, int index, params IRenderable[] columns)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
return InsertRow(table, index, (IEnumerable<IRenderable>)columns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a row in the table at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to add the row to.</param>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The row columns to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table InsertRow(this Table table, int index, params string[] columns)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
return InsertRow(table, index, columns.Select(column => new Markup(column)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row from the table with the specified index.
|
||||
/// </summary>
|
||||
/// <param name="table">The table to add the row to.</param>
|
||||
/// <param name="index">The index to remove the row at.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table RemoveRow(this Table table, int index)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(table));
|
||||
}
|
||||
|
||||
table.Rows.RemoveAt(index);
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the table width.
|
||||
/// </summary>
|
||||
|
||||
@@ -32,10 +32,10 @@ namespace Spectre.Console
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLinkHashCode(string link)
|
||||
{
|
||||
#if NET5_0
|
||||
return link.GetHashCode(StringComparison.Ordinal);
|
||||
#else
|
||||
#if NETSTANDARD2_0
|
||||
return link.GetHashCode();
|
||||
#else
|
||||
return link.GetHashCode(StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#if !NET5_0
|
||||
using System.Text.RegularExpressions;
|
||||
#endif
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal static class ColorSystemDetector
|
||||
@@ -61,7 +57,6 @@ namespace Spectre.Console
|
||||
return ColorSystem.EightBit;
|
||||
}
|
||||
|
||||
// See https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/environment-osversion-returns-correct-version
|
||||
private static bool GetWindowsVersionInformation(out int major, out int build)
|
||||
{
|
||||
major = 0;
|
||||
@@ -72,15 +67,15 @@ namespace Spectre.Console
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET5_0
|
||||
// The reason we're not always using this, is because it
|
||||
// will return wrong values on other runtimes than net5.0
|
||||
#if NET5_0_OR_GREATER
|
||||
// The reason we're not always using this, is because it will return wrong values on other runtimes than .NET 5+
|
||||
// See https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/environment-osversion-returns-correct-version
|
||||
var version = Environment.OSVersion.Version;
|
||||
major = version.Major;
|
||||
build = version.Build;
|
||||
return true;
|
||||
#else
|
||||
var regex = new Regex("Microsoft Windows (?'major'[0-9]*).(?'minor'[0-9]*).(?'build'[0-9]*)\\s*$");
|
||||
var regex = new System.Text.RegularExpressions.Regex("Microsoft Windows (?'major'[0-9]*).(?'minor'[0-9]*).(?'build'[0-9]*)\\s*$");
|
||||
var match = regex.Match(RuntimeInformation.OSDescription);
|
||||
if (match.Success && int.TryParse(match.Groups["major"].Value, out major))
|
||||
{
|
||||
|
||||
@@ -6,22 +6,19 @@ namespace Spectre.Console.Internal
|
||||
{
|
||||
internal sealed class DefaultExclusivityMode : IExclusivityMode
|
||||
{
|
||||
private static readonly SemaphoreSlim _semaphore;
|
||||
private readonly SemaphoreSlim _semaphore;
|
||||
|
||||
static DefaultExclusivityMode()
|
||||
public DefaultExclusivityMode()
|
||||
{
|
||||
_semaphore = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
public T Run<T>(Func<T> func)
|
||||
{
|
||||
// Try aquiring the exclusivity semaphore
|
||||
// Try acquiring the exclusivity semaphore
|
||||
if (!_semaphore.Wait(0))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Trying to run one or more interactive functions concurrently. " +
|
||||
"Operations with dynamic displays (e.g. a prompt and a progress display) " +
|
||||
"cannot be running at the same time.");
|
||||
throw CreateExclusivityException();
|
||||
}
|
||||
|
||||
try
|
||||
@@ -36,12 +33,10 @@ namespace Spectre.Console.Internal
|
||||
|
||||
public async Task<T> Run<T>(Func<Task<T>> func)
|
||||
{
|
||||
// Try aquiring the exclusivity semaphore
|
||||
// Try acquiring the exclusivity semaphore
|
||||
if (!await _semaphore.WaitAsync(0).ConfigureAwait(false))
|
||||
{
|
||||
// TODO: Need a better message here
|
||||
throw new InvalidOperationException(
|
||||
"Could not aquire the interactive semaphore");
|
||||
throw CreateExclusivityException();
|
||||
}
|
||||
|
||||
try
|
||||
@@ -53,5 +48,10 @@ namespace Spectre.Console.Internal
|
||||
_semaphore.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception CreateExclusivityException() => new InvalidOperationException(
|
||||
"Trying to run one or more interactive functions concurrently. " +
|
||||
"Operations with dynamic displays (e.g. a prompt and a progress display) " +
|
||||
"cannot be running at the same time.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,13 +134,13 @@ namespace Spectre.Console
|
||||
|
||||
if (Mode == SelectionMode.Leaf)
|
||||
{
|
||||
// Select the node and all it's children
|
||||
// Select the node and all its children
|
||||
foreach (var item in current.Traverse(includeSelf: true))
|
||||
{
|
||||
item.IsSelected = select;
|
||||
}
|
||||
|
||||
// Visit every parent and evaluate if it's selection
|
||||
// Visit every parent and evaluate if its selection
|
||||
// status need to be updated
|
||||
var parent = current.Parent;
|
||||
while (parent != null)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Spectre.Console.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents something renderable that's reconstructed
|
||||
/// when it's state change in any way.
|
||||
/// when its state change in any way.
|
||||
/// </summary>
|
||||
public abstract class JustInTimeRenderable : Renderable
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Wcwidth;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
@@ -329,23 +330,10 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
if (overflow == Overflow.Fold)
|
||||
{
|
||||
var totalLength = segment.Text.CellLength();
|
||||
var lengthLeft = totalLength;
|
||||
while (lengthLeft > 0)
|
||||
var splitted = SplitSegment(segment.Text, maxWidth);
|
||||
foreach (var str in splitted)
|
||||
{
|
||||
var index = totalLength - lengthLeft;
|
||||
|
||||
// How many characters should we take?
|
||||
var take = Math.Min(maxWidth, totalLength - index);
|
||||
if (take <= 0)
|
||||
{
|
||||
// This shouldn't really occur, but I don't like
|
||||
// never ending loops if it does...
|
||||
return new List<Segment>();
|
||||
}
|
||||
|
||||
result.Add(new Segment(segment.Text.Substring(index, take), segment.Style));
|
||||
lengthLeft -= take;
|
||||
result.Add(new Segment(str, segment.Style));
|
||||
}
|
||||
}
|
||||
else if (overflow == Overflow.Crop)
|
||||
@@ -435,7 +423,7 @@ namespace Spectre.Console.Rendering
|
||||
var builder = new StringBuilder();
|
||||
foreach (var character in segment.Text)
|
||||
{
|
||||
var accumulatedCellWidth = builder.ToString().CellLength();
|
||||
var accumulatedCellWidth = builder.ToString().GetCellWidth();
|
||||
if (accumulatedCellWidth >= maxWidth)
|
||||
{
|
||||
break;
|
||||
@@ -461,36 +449,36 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
var result = new List<Segment>();
|
||||
|
||||
var previous = (Segment?)null;
|
||||
var segmentBuilder = (SegmentBuilder?)null;
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
if (previous == null)
|
||||
if (segmentBuilder == null)
|
||||
{
|
||||
previous = segment;
|
||||
segmentBuilder = new SegmentBuilder(segment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both control codes?
|
||||
if (segment.IsControlCode && previous.IsControlCode)
|
||||
if (segment.IsControlCode && segmentBuilder.IsControlCode())
|
||||
{
|
||||
previous = Control(previous.Text + segment.Text);
|
||||
segmentBuilder.Append(segment.Text);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same style?
|
||||
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak && !previous.IsControlCode)
|
||||
if (segmentBuilder.StyleEquals(segment.Style) && !segmentBuilder.IsLineBreak() && !segmentBuilder.IsControlCode())
|
||||
{
|
||||
previous = new Segment(previous.Text + segment.Text, previous.Style);
|
||||
segmentBuilder.Append(segment.Text);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(previous);
|
||||
previous = segment;
|
||||
result.Add(segmentBuilder.Build());
|
||||
segmentBuilder.Reset(segment);
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
if (segmentBuilder != null)
|
||||
{
|
||||
result.Add(previous);
|
||||
result.Add(segmentBuilder.Build());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -567,5 +555,63 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
internal static List<string> SplitSegment(string text, int maxCellLength)
|
||||
{
|
||||
var list = new List<string>();
|
||||
|
||||
var length = 0;
|
||||
var sb = new StringBuilder();
|
||||
foreach (var ch in text)
|
||||
{
|
||||
if (length + UnicodeCalculator.GetWidth(ch) > maxCellLength)
|
||||
{
|
||||
list.Add(sb.ToString());
|
||||
sb.Clear();
|
||||
length = 0;
|
||||
}
|
||||
|
||||
length += UnicodeCalculator.GetWidth(ch);
|
||||
sb.Append(ch);
|
||||
}
|
||||
|
||||
list.Add(sb.ToString());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private class SegmentBuilder
|
||||
{
|
||||
private readonly StringBuilder _textBuilder = new();
|
||||
private Segment _originalSegment;
|
||||
|
||||
public SegmentBuilder(Segment originalSegment)
|
||||
{
|
||||
_originalSegment = originalSegment;
|
||||
Reset(originalSegment);
|
||||
}
|
||||
|
||||
public bool IsControlCode() => _originalSegment.IsControlCode;
|
||||
public bool IsLineBreak() => _originalSegment.IsLineBreak;
|
||||
public bool StyleEquals(Style segmentStyle) => segmentStyle.Equals(_originalSegment.Style);
|
||||
|
||||
public void Append(string text)
|
||||
{
|
||||
_textBuilder.Append(text);
|
||||
}
|
||||
|
||||
public Segment Build()
|
||||
{
|
||||
return new Segment(_textBuilder.ToString(), _originalSegment.Style, _originalSegment.IsLineBreak,
|
||||
_originalSegment.IsControlCode);
|
||||
}
|
||||
|
||||
public void Reset(Segment segment)
|
||||
{
|
||||
_textBuilder.Clear();
|
||||
_textBuilder.Append(segment.Text);
|
||||
_originalSegment = segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Spectre.Console
|
||||
public string? Link { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="Style"/> with the
|
||||
/// Gets a <see cref="Style"/> with the
|
||||
/// default colors and without text decoration.
|
||||
/// </summary>
|
||||
public static Style Plain { get; } = new Style();
|
||||
@@ -165,10 +165,10 @@ namespace Spectre.Console
|
||||
{
|
||||
int? GetLinkHashCode()
|
||||
{
|
||||
#if NET5_0
|
||||
return Link?.GetHashCode(StringComparison.Ordinal);
|
||||
#else
|
||||
#if NETSTANDARD2_0
|
||||
return Link?.GetHashCode();
|
||||
#else
|
||||
return Link?.GetHashCode(StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ namespace Spectre.Console
|
||||
/// <remarks>Defaults to invariant culture.</remarks>
|
||||
public CultureInfo? Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fixed max value for a bar chart.
|
||||
/// </summary>
|
||||
/// <remarks>Defaults to null, which corresponds to largest value in chart.</remarks>
|
||||
public double? MaxValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BarChart"/> class.
|
||||
/// </summary>
|
||||
@@ -62,7 +68,7 @@ namespace Spectre.Console
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||
var maxValue = Data.Max(item => item.Value);
|
||||
var maxValue = Math.Max(MaxValue ?? 0d, Data.Max(item => item.Value));
|
||||
|
||||
var grid = new Grid();
|
||||
grid.Collapse();
|
||||
|
||||
@@ -14,11 +14,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var info = ExceptionParser.Parse(exception.ToString());
|
||||
if (info == null)
|
||||
{
|
||||
return new Text(exception.ToString());
|
||||
}
|
||||
var info = ExceptionParser.Parse(exception);
|
||||
|
||||
return GetException(info, settings);
|
||||
}
|
||||
|
||||
@@ -1,90 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal static class ExceptionParser
|
||||
{
|
||||
private static readonly Regex _messageRegex = new Regex(@"^(?'type'.*):\s(?'message'.*)$");
|
||||
private static readonly Regex _stackFrameRegex = new Regex(@"^\s*\w*\s(?'method'.*)\((?'params'.*)\)");
|
||||
private static readonly Regex _fullStackFrameRegex = new Regex(@"^\s*(?'at'\w*)\s(?'method'.*)\((?'params'.*)\)\s(?'in'\w*)\s(?'path'.*)\:(?'line'\w*)\s(?'linenumber'\d*)$");
|
||||
|
||||
public static ExceptionInfo? Parse(string exception)
|
||||
public static ExceptionInfo Parse(Exception exception)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var lines = exception.SplitLines();
|
||||
return Parse(new Queue<string>(lines));
|
||||
}
|
||||
|
||||
private static ExceptionInfo? Parse(Queue<string> lines)
|
||||
{
|
||||
if (lines.Count == 0)
|
||||
{
|
||||
// Error: No lines to parse
|
||||
return null;
|
||||
}
|
||||
|
||||
var line = lines.Dequeue();
|
||||
line = line.ReplaceExact(" ---> ", string.Empty);
|
||||
|
||||
var match = _messageRegex.Match(line);
|
||||
if (!match.Success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var inner = (ExceptionInfo?)null;
|
||||
|
||||
// Stack frames
|
||||
var frames = new List<StackFrameInfo>();
|
||||
while (lines.Count > 0)
|
||||
{
|
||||
if (lines.Peek().TrimStart().StartsWith("---> ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
inner = Parse(lines);
|
||||
if (inner == null)
|
||||
{
|
||||
// Error: Could not parse inner exception
|
||||
return null;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
line = lines.Dequeue();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
// Empty line
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.TrimStart().StartsWith("--- ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// End of inner exception
|
||||
break;
|
||||
}
|
||||
|
||||
var stackFrame = ParseStackFrame(line);
|
||||
if (stackFrame == null)
|
||||
{
|
||||
// Error: Could not parse stack frame
|
||||
return null;
|
||||
}
|
||||
|
||||
frames.Add(stackFrame);
|
||||
}
|
||||
|
||||
return new ExceptionInfo(
|
||||
match.Groups["type"].Value,
|
||||
match.Groups["message"].Value,
|
||||
frames, inner);
|
||||
var exceptionType = exception.GetType();
|
||||
var frames = exception.StackTrace?.SplitLines().Select(ParseStackFrame).Where(e => e != null).Cast<StackFrameInfo>().ToList() ?? new List<StackFrameInfo>();
|
||||
var inner = exception.InnerException is null ? null : Parse(exception.InnerException);
|
||||
return new ExceptionInfo(exceptionType.FullName ?? exceptionType.Name, exception.Message, frames, inner);
|
||||
}
|
||||
|
||||
private static StackFrameInfo? ParseStackFrame(string frame)
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Spectre.Console
|
||||
}
|
||||
else
|
||||
{
|
||||
// Does it fit on it's own line?
|
||||
// Does it fit on its own line?
|
||||
if (width < maxWidth)
|
||||
{
|
||||
// Flush the line
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Spectre.Console
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the column.
|
||||
/// If <c>null</c>, the column will adapt to it's contents.
|
||||
/// If <c>null</c>, the column will adapt to its contents.
|
||||
/// </summary>
|
||||
public int? Width
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Spectre.Console
|
||||
/// Initializes a new instance of the <see cref="Paragraph"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="style">The style of the text.</param>
|
||||
/// <param name="style">The style of the text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public Paragraph(string text, Style? style = null)
|
||||
: this()
|
||||
{
|
||||
@@ -63,7 +63,7 @@ namespace Spectre.Console
|
||||
/// Appends some text to this paragraph.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to append.</param>
|
||||
/// <param name="style">The style of the appended text.</param>
|
||||
/// <param name="style">The style of the appended text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Paragraph Append(string text, Style? style = null)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace Spectre.Console
|
||||
public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable
|
||||
{
|
||||
private readonly List<TableColumn> _columns;
|
||||
private readonly List<TableRow> _rows;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table columns.
|
||||
@@ -21,7 +20,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// Gets the table rows.
|
||||
/// </summary>
|
||||
public IReadOnlyList<TableRow> Rows => _rows;
|
||||
public TableRowCollection Rows { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TableBorder Border { get; set; } = TableBorder.Square;
|
||||
@@ -81,7 +80,7 @@ namespace Spectre.Console
|
||||
public Table()
|
||||
{
|
||||
_columns = new List<TableColumn>();
|
||||
_rows = new List<TableRow>();
|
||||
Rows = new TableRowCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,7 +95,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(column));
|
||||
}
|
||||
|
||||
if (_rows.Count > 0)
|
||||
if (Rows.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot add new columns to table with existing rows.");
|
||||
}
|
||||
@@ -105,36 +104,6 @@ namespace Spectre.Console
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a row to the table.
|
||||
/// </summary>
|
||||
/// <param name="columns">The row columns to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Table AddRow(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
var rowColumnCount = columns.GetCount();
|
||||
if (rowColumnCount > _columns.Count)
|
||||
{
|
||||
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
||||
}
|
||||
|
||||
_rows.Add(new TableRow(columns));
|
||||
|
||||
// Need to add missing columns?
|
||||
if (rowColumnCount < _columns.Count)
|
||||
{
|
||||
var diff = _columns.Count - rowColumnCount;
|
||||
Enumerable.Range(0, diff).ForEach(_ => _rows.Last().Add(Text.Empty));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||
{
|
||||
@@ -190,7 +159,7 @@ namespace Spectre.Console
|
||||
}
|
||||
|
||||
// Add rows
|
||||
rows.AddRange(_rows);
|
||||
rows.AddRange(Rows);
|
||||
|
||||
// Show footers?
|
||||
if (ShowFooters && _columns.Any(c => c.Footer != null))
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Spectre.Console
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the column.
|
||||
/// If <c>null</c>, the column will adapt to it's contents.
|
||||
/// If <c>null</c>, the column will adapt to its contents.
|
||||
/// </summary>
|
||||
public int? Width { get; set; }
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Spectre.Console
|
||||
{
|
||||
private readonly List<IRenderable> _items;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of columns in the row.
|
||||
/// </summary>
|
||||
public int Count => _items.Count;
|
||||
|
||||
internal bool IsHeader { get; }
|
||||
internal bool IsFooter { get; }
|
||||
|
||||
|
||||
209
src/Spectre.Console/Widgets/Table/TableRowCollection.cs
Normal file
209
src/Spectre.Console/Widgets/Table/TableRowCollection.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection holding table rows.
|
||||
/// </summary>
|
||||
public sealed class TableRowCollection : IReadOnlyList<TableRow>
|
||||
{
|
||||
private readonly Table _table;
|
||||
private readonly IList<TableRow> _list;
|
||||
private readonly object _lock;
|
||||
|
||||
/// <inheritdoc/>
|
||||
TableRow IReadOnlyList<TableRow>.this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _list[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of rows in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal TableRowCollection(Table table)
|
||||
{
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
_list = new List<TableRow>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="columns">The columns that are part of the row to add.</param>
|
||||
/// <returns>The index of the added item.</returns>
|
||||
public int Add(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Add(row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The columns that are part of the row to insert.</param>
|
||||
/// <returns>The index of the inserted item.</returns>
|
||||
public int Insert(int index, IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Insert(index, row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a table cell at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="row">Index of cell row.</param>
|
||||
/// <param name="column">index of cell column.</param>
|
||||
/// <param name="cellData">The new cells details.</param>
|
||||
public void Update(int row, int column, IRenderable cellData)
|
||||
{
|
||||
if (cellData is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cellData));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (row >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
var tableRow = _list.ElementAtOrDefault(row);
|
||||
|
||||
var currentRenderables = tableRow.ToList();
|
||||
|
||||
if (column < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot be negative.");
|
||||
}
|
||||
else if (column >= currentRenderables.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
currentRenderables.RemoveAt(column);
|
||||
|
||||
currentRenderables.Insert(column, cellData);
|
||||
|
||||
var newTableRow = new TableRow(currentRenderables);
|
||||
|
||||
_list.RemoveAt(row);
|
||||
|
||||
_list.Insert(row, newTableRow);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to remove a row at.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (index >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
_list.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all rows.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_list.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<TableRow> GetEnumerator()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var items = new TableRow[_list.Count];
|
||||
_list.CopyTo(items, 0);
|
||||
return new TableRowEnumerator(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
private TableRow CreateRow(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
var row = new TableRow(columns);
|
||||
|
||||
if (row.Count > _table.Columns.Count)
|
||||
{
|
||||
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
||||
}
|
||||
|
||||
// Need to add missing columns
|
||||
if (row.Count < _table.Columns.Count)
|
||||
{
|
||||
var diff = _table.Columns.Count - row.Count;
|
||||
Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty));
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/Spectre.Console/Widgets/Table/TableRowEnumerator.cs
Normal file
36
src/Spectre.Console/Widgets/Table/TableRowEnumerator.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
internal sealed class TableRowEnumerator : IEnumerator<TableRow>
|
||||
{
|
||||
private readonly TableRow[] _items;
|
||||
private int _index;
|
||||
|
||||
public TableRow Current => _items[_index];
|
||||
object? IEnumerator.Current => _items[_index];
|
||||
|
||||
public TableRowEnumerator(TableRow[] items)
|
||||
{
|
||||
_items = items ?? throw new ArgumentNullException(nameof(items));
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _items.Length;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace Spectre.Console
|
||||
/// Initializes a new instance of the <see cref="Text"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="style">The style of the text.</param>
|
||||
/// <param name="style">The style of the text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public Text(string text, Style? style = null)
|
||||
{
|
||||
_paragraph = new Paragraph(text, style);
|
||||
|
||||
@@ -10,6 +10,28 @@ namespace Spectre.Console.Analyzer.Tests.Unit.Analyzers
|
||||
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id,
|
||||
DiagnosticSeverity.Info);
|
||||
|
||||
[Fact]
|
||||
public async void Should_only_warn_within_methods()
|
||||
{
|
||||
const string Source = @"
|
||||
using Spectre.Console;
|
||||
|
||||
internal sealed class Foo
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
|
||||
public Foo(IAnsiConsole console = null)
|
||||
{
|
||||
_console = console ?? AnsiConsole.Create(new AnsiConsoleSettings());
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
await SpectreAnalyzerVerifier<FavorInstanceAnsiConsoleOverStaticAnalyzer>
|
||||
.VerifyAnalyzerAsync(Source)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void Instance_console_has_no_warnings()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │ Column #3 │
|
||||
├───────────┼───────────┼───────────┤
|
||||
│ 1 │ │ │
|
||||
│ 2 │ │ │
|
||||
│ 3 │ 4 │ 5 │
|
||||
└───────────┴───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │ Column #3 │
|
||||
├───────────┼───────────┼───────────┤
|
||||
│ 1 │ │ │
|
||||
│ 2 │ │ │
|
||||
│ 3 │ 4 │ 5 │
|
||||
└───────────┴───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │ Column #3 │
|
||||
├───────────┼───────────┼───────────┤
|
||||
│ 1 │ │ │
|
||||
│ 2 │ │ │
|
||||
│ 3 │ 4 │ 5 │
|
||||
└───────────┴───────────┴───────────┘
|
||||
@@ -0,0 +1,4 @@
|
||||
Number of fruits
|
||||
Apple ███ 12
|
||||
Orange █████████████████████████ 54
|
||||
Banana ██████████████ 33
|
||||
@@ -0,0 +1,8 @@
|
||||
┌───────────────┬───────────────┬──────────────┐
|
||||
│ Foo │ Bar │ Baz │
|
||||
├───────────────┼───────────────┼──────────────┤
|
||||
│ 中文 │ 日本語 │ 한국어 │
|
||||
│ 这是中文测试 │ これは日本語 │ 이것은한국어 │
|
||||
│ 字符串 │ のテスト文字 │ 테스트문자열 │
|
||||
│ │ 列です │ 입니다 │
|
||||
└───────────────┴───────────────┴──────────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┐
|
||||
│ Column #1 │
|
||||
├───────────┤
|
||||
│ 1 │
|
||||
│ 2 │
|
||||
│ 3 │
|
||||
└───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │
|
||||
├───────────┼───────────┤
|
||||
│ 1 │ 1-2 │
|
||||
│ 2 │ 2-2 │
|
||||
│ 3 │ 3-2 │
|
||||
└───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │
|
||||
├───────────┼───────────┤
|
||||
│ 1 │ 1-2 │
|
||||
│ 2 │ 2-2 │
|
||||
│ 3 │ 3-2 │
|
||||
└───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │
|
||||
├───────────┼───────────┤
|
||||
│ 1 │ 1-2 │
|
||||
│ 3 │ 3-2 │
|
||||
│ 2 │ 2-2 │
|
||||
└───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │
|
||||
├───────────┼───────────┤
|
||||
│ 1 │ 1-2 │
|
||||
│ 3 │ 3-2 │
|
||||
│ 2 │ 2-2 │
|
||||
└───────────┴───────────┘
|
||||
@@ -0,0 +1,6 @@
|
||||
┌───────────┬───────────┐
|
||||
│ Column #1 │ Column #2 │
|
||||
├───────────┼───────────┤
|
||||
│ 1 │ 1-2 │
|
||||
│ 3 │ 3-2 │
|
||||
└───────────┴───────────┘
|
||||
@@ -0,0 +1,7 @@
|
||||
┌───────────┐
|
||||
│ Column #1 │
|
||||
├───────────┤
|
||||
│ 1 │
|
||||
│ 3 │
|
||||
│ 2 │
|
||||
└───────────┘
|
||||
@@ -0,0 +1,6 @@
|
||||
┌───────────┐
|
||||
│ Column #1 │
|
||||
├───────────┤
|
||||
│ 1 │
|
||||
│ 3 │
|
||||
└───────────┘
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net5.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('Windows'))">net5.0;net48</TargetFrameworks>
|
||||
<TargetFramework Condition="!$([MSBuild]::IsOSPlatform('Windows'))">net5.0</TargetFramework>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -17,10 +19,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Shouldly" Version="4.0.3" />
|
||||
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
|
||||
@@ -37,6 +41,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Expectations\Widgets\Table\Rows\Extensions\" />
|
||||
<Folder Include="Expectations\Widgets\Tree\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Shouldly;
|
||||
using Spectre.Console.Testing;
|
||||
using Spectre.Console.Tests.Data;
|
||||
|
||||
@@ -382,10 +382,12 @@ namespace Spectre.Console.Tests.Unit.Cli
|
||||
});
|
||||
|
||||
// Then
|
||||
registrar.Registrations.ContainsKey(typeof(ICommand)).ShouldBeTrue();
|
||||
registrar.Registrations[typeof(ICommand)].ShouldContain(typeof(GenericCommand<FooCommandSettings>));
|
||||
registrar.Registrations[typeof(ICommand)].ShouldContain(typeof(DogCommand));
|
||||
registrar.Registrations[typeof(ICommand)].ShouldContain(typeof(HorseCommand));
|
||||
registrar.Registrations.ContainsKey(typeof(GenericCommand<FooCommandSettings>)).ShouldBeTrue();
|
||||
registrar.Registrations.ContainsKey(typeof(DogCommand)).ShouldBeTrue();
|
||||
registrar.Registrations.ContainsKey(typeof(HorseCommand)).ShouldBeTrue();
|
||||
registrar.Registrations[typeof(GenericCommand<FooCommandSettings>)].ShouldContain(typeof(GenericCommand<FooCommandSettings>));
|
||||
registrar.Registrations[typeof(DogCommand)].ShouldContain(typeof(DogCommand));
|
||||
registrar.Registrations[typeof(HorseCommand)].ShouldContain(typeof(HorseCommand));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -406,8 +408,8 @@ namespace Spectre.Console.Tests.Unit.Cli
|
||||
});
|
||||
|
||||
// Then
|
||||
registrar.Registrations.ContainsKey(typeof(ICommand)).ShouldBeTrue();
|
||||
registrar.Registrations[typeof(ICommand)].ShouldContain(typeof(DogCommand));
|
||||
registrar.Registrations.ContainsKey(typeof(DogCommand)).ShouldBeTrue();
|
||||
registrar.Registrations[typeof(DogCommand)].ShouldContain(typeof(DogCommand));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -545,6 +547,45 @@ namespace Spectre.Console.Tests.Unit.Cli
|
||||
result.Context.ShouldHaveRemainingArgument("foo", values: new[] { "bar" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Add_Unknown_Option_To_Remaining_Arguments_In_Strict_Mode()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.UseStrictParsing();
|
||||
config.PropagateExceptions();
|
||||
config.AddBranch<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "4", "dog", "12",
|
||||
"--",
|
||||
"--foo", "bar",
|
||||
"-f", "baz",
|
||||
"qux"
|
||||
});
|
||||
|
||||
// Then
|
||||
result.Context.ShouldNotBeNull();
|
||||
result.Context.Remaining.Parsed.Count.ShouldBe(2);
|
||||
result.Context.ShouldHaveRemainingArgument("foo", values: new[] { "bar" });
|
||||
result.Context.ShouldHaveRemainingArgument("f", values: new[] { "baz" });
|
||||
result.Context.Remaining.Raw.Count.ShouldBe(5);
|
||||
result.Context.Remaining.Raw.ShouldBe(new[]
|
||||
{
|
||||
"--foo", "bar",
|
||||
"-f", "baz",
|
||||
"qux",
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Add_Unknown_Boolean_Option_To_Remaining_Arguments_In_Relaxed_Mode()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Shouldly;
|
||||
using Spectre.Console.Testing;
|
||||
@@ -261,6 +262,9 @@ namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
task = ctx.AddTask("foo");
|
||||
task.Increment(double.Epsilon);
|
||||
// Make sure that at least one millisecond has elapsed between the increments else the RemainingTime is null
|
||||
// when the last timestamp is equal to the first timestamp of the samples.
|
||||
Thread.Sleep(1);
|
||||
task.Increment(double.Epsilon);
|
||||
});
|
||||
|
||||
|
||||
@@ -47,5 +47,26 @@ namespace Spectre.Console.Tests.Unit
|
||||
// Then
|
||||
await Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Fixed_Max_Value")]
|
||||
public async Task Should_Render_Correctly_3()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
|
||||
// When
|
||||
console.Write(new BarChart()
|
||||
.Width(60)
|
||||
.WithMaxValue(100)
|
||||
.Label("Number of fruits")
|
||||
.AddItem("Apple", 12)
|
||||
.AddItem("Orange", 54)
|
||||
.AddItem("Banana", 33));
|
||||
|
||||
// Then
|
||||
await Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spectre.Console.Testing;
|
||||
using Spectre.Verify.Extensions;
|
||||
using VerifyXunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
[UsesVerify]
|
||||
[ExpectationPath("Widgets/Table/Rows/Extensions")]
|
||||
public sealed class TableRowCollectionExtensionsTests
|
||||
{
|
||||
[UsesVerify]
|
||||
public sealed class TheAddRowMethod
|
||||
{
|
||||
[Fact]
|
||||
[Expectation("Add", "Renderables")]
|
||||
public Task Should_Add_Renderables()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddRow(new[] { new Text("1"), new Text("1-2") });
|
||||
table.AddRow(new[] { new Text("2"), new Text("2-2") });
|
||||
table.AddRow(new[] { new Text("3"), new Text("3-2") });
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Add", "Strings")]
|
||||
public Task Should_Add_Strings()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddRow("1", "1-2");
|
||||
table.AddRow("2", "2-2");
|
||||
table.AddRow("3", "3-2");
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
public sealed class TheInsertRowMethod
|
||||
{
|
||||
[Fact]
|
||||
[Expectation("Insert", "Renderables")]
|
||||
public Task Should_Insert_Renderables()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddRow(new[] { new Text("1"), new Text("1-2") });
|
||||
table.AddRow(new[] { new Text("2"), new Text("2-2") });
|
||||
|
||||
// When
|
||||
table.InsertRow(1, new[] { new Text("3"), new Text("3-2") });
|
||||
|
||||
// Then
|
||||
console.Write(table);
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Insert", "Strings")]
|
||||
public Task Should_Insert_Strings()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddRow("1", "1-2");
|
||||
table.AddRow("2", "2-2");
|
||||
|
||||
// When
|
||||
table.InsertRow(1, "3", "3-2");
|
||||
|
||||
|
||||
// Then
|
||||
console.Write(table);
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
public sealed class TheRemoveRowMethod
|
||||
{
|
||||
[Fact]
|
||||
[Expectation("Remove")]
|
||||
public Task Should_Remove_Row()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddRow(new[] { new Text("1"), new Text("1-2") });
|
||||
table.AddRow(new[] { new Text("2"), new Text("2-2") });
|
||||
table.AddRow(new[] { new Text("3"), new Text("3-2") });
|
||||
|
||||
// When
|
||||
table.RemoveRow(1);
|
||||
|
||||
// Then
|
||||
console.Write(table);
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Shouldly;
|
||||
using Spectre.Console.Testing;
|
||||
using Spectre.Verify.Extensions;
|
||||
using VerifyXunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
[ExpectationPath("Widgets/Table/Rows")]
|
||||
public sealed class TableRowCollectionTests
|
||||
{
|
||||
[UsesVerify]
|
||||
public sealed class TheAddMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Throw_If_Columns_Are_Null()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.Rows.Add(null));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<ArgumentNullException>()
|
||||
.ParamName.ShouldBe("columns");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Add_Row_To_Collection()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
|
||||
// When
|
||||
table.Rows.Add(new[] { Text.Empty });
|
||||
|
||||
// Then
|
||||
table.Rows.Count.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Index_Of_Added_Row()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { Text.Empty });
|
||||
|
||||
// When
|
||||
var result = table.Rows.Add(new[] { Text.Empty });
|
||||
|
||||
// Then
|
||||
result.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Add")]
|
||||
public Task Should_Add_Item_At_Correct_Place()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3") });
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
public sealed class TheInsertMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Throw_If_Columns_Are_Null()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.Rows.Insert(0, null));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<ArgumentNullException>()
|
||||
.ParamName.ShouldBe("columns");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Insert_Row()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { Text.Empty });
|
||||
|
||||
// When
|
||||
table.Rows.Insert(0, new[] { Text.Empty });
|
||||
|
||||
// Then
|
||||
table.Rows.Count.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Index_Of_Inserted_Row()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
|
||||
// When
|
||||
var result = table.Rows.Insert(1, new[] { new Text("3") });
|
||||
|
||||
// Then
|
||||
result.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Insert")]
|
||||
public Task Should_Insert_Item_At_Correct_Place()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Insert(1, new[] { new Text("3") });
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
public sealed class TheRemoveMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Is_Negative()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.Rows.RemoveAt(-1));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table row index cannot be negative.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3") });
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.Rows.RemoveAt(3));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Remove")]
|
||||
public Task Should_Remove_Row()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3") });
|
||||
table.Rows.RemoveAt(1);
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TheClearMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Remove_All_Rows()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3") });
|
||||
table.Rows.Clear();
|
||||
|
||||
// When
|
||||
var result = table.Rows.Count;
|
||||
|
||||
// Then
|
||||
result.ShouldBe(0);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
public sealed class TheUpdateMethod
|
||||
{
|
||||
[Fact]
|
||||
public Task Should_Update_Row_With_String()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
|
||||
table.UpdateCell(2, 2, "5");
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task Should_Update_Row_With_Renderable()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
|
||||
table.UpdateCell(2, 2, new Markup("5"));
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Rows()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
table.UpdateCell(2, 2, "5");
|
||||
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.UpdateCell(5, 2, "5"));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Is_Larger_Than_Number_Of_Columns()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
table.UpdateCell(2, 2, "5");
|
||||
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.UpdateCell(2, 5, "5"));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table column index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Row_Is_Negative()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
table.UpdateCell(2, 2, "5");
|
||||
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.UpdateCell(-1, 2, "5"));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table row index cannot be negative.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Index_Column_Is_Negative()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
var table = new Table();
|
||||
table.AddColumn("Column #1");
|
||||
table.AddColumn("Column #2");
|
||||
table.AddColumn("Column #3");
|
||||
table.Rows.Add(new[] { new Text("1") });
|
||||
table.Rows.Add(new[] { new Text("2") });
|
||||
table.Rows.Add(new[] { new Text("3"), new Text("4"), new Text("8") });
|
||||
table.UpdateCell(2, 2, "5");
|
||||
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.UpdateCell(2, -1, "5"));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<IndexOutOfRangeException>()
|
||||
.Message.ShouldBe("Table column index cannot be negative.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,20 +78,6 @@ namespace Spectre.Console.Tests.Unit
|
||||
.ParamName.ShouldBe("columns");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Throw_If_Renderable_Rows_Are_Null()
|
||||
{
|
||||
// Given
|
||||
var table = new Table();
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => table.AddRow(null));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<ArgumentNullException>()
|
||||
.ParamName.ShouldBe("columns");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
|
||||
{
|
||||
@@ -164,6 +150,24 @@ namespace Spectre.Console.Tests.Unit
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Render_EA_Character")]
|
||||
public Task Should_Render_Table_With_EA_Character_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole().Width(48);
|
||||
var table = new Table();
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("中文", "日本語", "한국어");
|
||||
table.AddRow("这是中文测试字符串", "これは日本語のテスト文字列です", "이것은한국어테스트문자열입니다");
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Render_Footers")]
|
||||
public Task Should_Render_Table_With_Footers_Correctly()
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spectre.Console.Tests
|
||||
{
|
||||
|
||||
@@ -13,3 +13,13 @@ namespace Spectre.Console.Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET5_0_OR_GREATER
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||
public sealed class ModuleInitializerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user