mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a01b93aca | ||
|
|
effdecb1d4 | ||
|
|
4cfe55cc27 | ||
|
|
5b33f80213 | ||
|
|
d7bbaf4a85 | ||
|
|
0119364728 | ||
|
|
1d74fb909c | ||
|
|
5d132220ba | ||
|
|
a273f74758 | ||
|
|
717931f11c | ||
|
|
bcfc495843 | ||
|
|
9aa36c4cf0 | ||
|
|
22d4af4482 | ||
|
|
f4d1796e40 | ||
|
|
2dd0eb9f74 | ||
|
|
fa85216554 | ||
|
|
d475e3b30a | ||
|
|
9637066927 | ||
|
|
0b4321115a | ||
|
|
5cd9ece31a | ||
|
|
b0341862cf | ||
|
|
2e7b3d520a | ||
|
|
646f51a628 | ||
|
|
a0bd481255 | ||
|
|
6d197c5140 | ||
|
|
108e56c229 | ||
|
|
66994cd904 | ||
|
|
f9bd936254 | ||
|
|
a068fc68c3 | ||
|
|
aa34c145b9 | ||
|
|
98cf63f485 | ||
|
|
c3286a4842 | ||
|
|
e5bf2bd498 | ||
|
|
5267ebda49 | ||
|
|
f19202b427 | ||
|
|
8e4f33bba4 | ||
|
|
e596e6eb4f | ||
|
|
a80120babc | ||
|
|
214d510d6d | ||
|
|
0986a5f744 | ||
|
|
b72a695c35 |
1
.github/funding.yml
vendored
Normal file
1
.github/funding.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: patriksvensson
|
||||||
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -2,9 +2,7 @@ name: Continuous Integration
|
|||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||||
# Disable sending usage data to Microsoft
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -28,10 +26,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 'Get Git tags'
|
|
||||||
run: git fetch --tags
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
10
.github/workflows/publish.yaml
vendored
10
.github/workflows/publish.yaml
vendored
@@ -8,9 +8,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||||
# Disable sending usage data to Microsoft
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -39,10 +37,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 'Get Git tags'
|
|
||||||
run: git fetch --tags
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
@@ -69,10 +63,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 'Get Git tags'
|
|
||||||
run: git fetch --tags
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
335
README.md
335
README.md
@@ -1,11 +1,22 @@
|
|||||||
# `Spectre.Console`
|
# `Spectre.Console`
|
||||||
|
|
||||||
_[](https://www.nuget.org/packages/spectre.console)_
|
_[](https://www.nuget.org/packages/spectre.console)_
|
||||||
|
|
||||||
A .NET Standard 2.0 library that makes it easier to create beautiful console applications.
|
A .NET Standard 2.0 library that makes it easier to create beautiful console applications.
|
||||||
It is heavily inspired by the excellent [Rich library](https://github.com/willmcgugan/rich)
|
It is heavily inspired by the excellent [Rich library](https://github.com/willmcgugan/rich)
|
||||||
for Python.
|
for Python.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Features](#features)
|
||||||
|
2. [Example](#example)
|
||||||
|
3. [Usage](#usage)
|
||||||
|
3.1. [Using the static API](#using-the-static-api)
|
||||||
|
3.2. [Creating a console](#creating-a-console)
|
||||||
|
4. [Running examples](#running-examples)
|
||||||
|
5. [Available styles](#available-styles)
|
||||||
|
6. [Predefined colors](#predefined-colors)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Written with unit testing in mind.
|
* Written with unit testing in mind.
|
||||||
@@ -40,11 +51,11 @@ like you usually do with the `System.Console` API, but prettier.
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
AnsiConsole.Foreground = Color.CornflowerBlue;
|
AnsiConsole.Foreground = Color.CornflowerBlue;
|
||||||
AnsiConsole.Style = Styles.Underline | Styles.Bold;
|
AnsiConsole.Decoration = Decoration.Underline | Decoration.Bold;
|
||||||
AnsiConsole.WriteLine("Hello World!");
|
AnsiConsole.WriteLine("Hello World!");
|
||||||
|
|
||||||
AnsiConsole.Reset();
|
AnsiConsole.Reset();
|
||||||
AnsiConsole.WriteLine("Good bye!");
|
AnsiConsole.MarkupLine("[bold yellow on red]{0}[/] [underline]world[/]!", "Goodbye");
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to get a reference to the default `IAnsiConsole`,
|
If you want to get a reference to the default `IAnsiConsole`,
|
||||||
@@ -54,7 +65,10 @@ you can access it via `AnsiConsole.Console`.
|
|||||||
|
|
||||||
Sometimes it's useful to explicitly create a console with specific
|
Sometimes it's useful to explicitly create a console with specific
|
||||||
capabilities, such as during unit testing when you want control
|
capabilities, such as during unit testing when you want control
|
||||||
over the environment your code runs in.
|
over the environment your code runs in.
|
||||||
|
|
||||||
|
It's recommended to not use `AnsiConsole` in code that run as
|
||||||
|
part of a unit test.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
IAnsiConsole console = AnsiConsole.Create(
|
IAnsiConsole console = AnsiConsole.Create(
|
||||||
@@ -70,3 +84,316 @@ _NOTE: Even if you can specify a specific color system to use
|
|||||||
when manually creating a console, remember that the user's terminal
|
when manually creating a console, remember that the user's terminal
|
||||||
might not be able to use it, so unless you're creating an IAnsiConsole
|
might not be able to use it, so unless you're creating an IAnsiConsole
|
||||||
for testing, always use `ColorSystemSupport.Detect` and `AnsiSupport.Detect`._
|
for testing, always use `ColorSystemSupport.Detect` and `AnsiSupport.Detect`._
|
||||||
|
|
||||||
|
## Running examples
|
||||||
|
|
||||||
|
To see Spectre.Console in action, install the
|
||||||
|
[dotnet-example](https://github.com/patriksvensson/dotnet-example)
|
||||||
|
global tool.
|
||||||
|
|
||||||
|
```
|
||||||
|
> dotnet tool install -g dotnet-example
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can list available examples in this repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
> dotnet example
|
||||||
|
|
||||||
|
Examples
|
||||||
|
|
||||||
|
Colors Demonstrates how to use colors in the console.
|
||||||
|
Grid Demonstrates how to render grids in a console.
|
||||||
|
Panel Demonstrates how to render items in panels.
|
||||||
|
Table Demonstrates how to render tables in a console.
|
||||||
|
```
|
||||||
|
|
||||||
|
And to run an example:
|
||||||
|
|
||||||
|
```
|
||||||
|
> dotnet example table
|
||||||
|
┌──────────┬──────────┬────────┐
|
||||||
|
│ Foo │ Bar │ Baz │
|
||||||
|
├──────────┼──────────┼────────┤
|
||||||
|
│ Hello │ World! │ │
|
||||||
|
│ Bounjour │ le │ monde! │
|
||||||
|
│ Hej │ Världen! │ │
|
||||||
|
└──────────┴──────────┴────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available styles
|
||||||
|
|
||||||
|
_NOTE: Not all styles are supported in every terminal._
|
||||||
|
|
||||||
|
Name | Description
|
||||||
|
--- | ---
|
||||||
|
`bold` | Bold text
|
||||||
|
`dim` | Dim or faint text
|
||||||
|
`italic` | Italic text
|
||||||
|
`underline` | Underlined text
|
||||||
|
`invert` | Swaps the foreground and background colors
|
||||||
|
`conceal` | Hides the text
|
||||||
|
`slowblink` | Makes text blink slowly
|
||||||
|
`rapidblink` | Makes text blink
|
||||||
|
`strikethrough` | Shows text with a horizontal line through the center
|
||||||
|
|
||||||
|
## Predefined colors
|
||||||
|
|
||||||
|
Number | Name | RGB | Hex | System.ConsoleColor
|
||||||
|
--- | --- | --- | --- | ---
|
||||||
|
`0` | `black` | `0,0,0` | `#000000` | `Black`
|
||||||
|
`1` | `maroon` | `128,0,0` | `#800000` | `DarkRed`
|
||||||
|
`2` | `green` | `0,128,0` | `#008000` | `DarkGreen`
|
||||||
|
`3` | `olive` | `128,128,0` | `#808000` | `DarkYellow`
|
||||||
|
`4` | `navy` | `0,0,128` | `#000080` | `DarkBlue`
|
||||||
|
`5` | `purple` | `128,0,128` | `#800080` | `DarkMagenta`
|
||||||
|
`6` | `teal` | `0,128,128` | `#008080` | `DarkCyan`
|
||||||
|
`7` | `silver` | `192,192,192` | `#c0c0c0` | `Gray`
|
||||||
|
`8` | `grey` | `128,128,128` | `#808080` | `DarkGray`
|
||||||
|
`9` | `red` | `255,0,0` | `#ff0000` | `Red`
|
||||||
|
`10` | `lime` | `0,255,0` | `#00ff00` | `Green`
|
||||||
|
`11` | `yellow` | `255,255,0` | `#ffff00` | `Yellow`
|
||||||
|
`12` | `blue` | `0,0,255` | `#0000ff` | `Blue`
|
||||||
|
`13` | `fuchsia` | `255,0,255` | `#ff00ff` | `Magenta`
|
||||||
|
`14` | `aqua` | `0,255,255` | `#00ffff` | `Cyan`
|
||||||
|
`15` | `white` | `255,255,255` | `#ffffff` | `White`
|
||||||
|
`16` | `grey0` | `0,0,0` | `#000000`
|
||||||
|
`17` | `navyblue` | `0,0,95` | `#00005f`
|
||||||
|
`18` | `darkblue` | `0,0,135` | `#000087`
|
||||||
|
`19` | `blue3` | `0,0,175` | `#0000af`
|
||||||
|
`20` | `blue3_1` | `0,0,215` | `#0000d7`
|
||||||
|
`21` | `blue1` | `0,0,255` | `#0000ff`
|
||||||
|
`22` | `darkgreen` | `0,95,0` | `#005f00`
|
||||||
|
`23` | `deepskyblue4` | `0,95,95` | `#005f5f`
|
||||||
|
`24` | `deepskyblue4_1` | `0,95,135` | `#005f87`
|
||||||
|
`25` | `deepskyblue4_2` | `0,95,175` | `#005faf`
|
||||||
|
`26` | `dodgerblue3` | `0,95,215` | `#005fd7`
|
||||||
|
`27` | `dodgerblue2` | `0,95,255` | `#005fff`
|
||||||
|
`28` | `green4` | `0,135,0` | `#008700`
|
||||||
|
`29` | `springgreen4` | `0,135,95` | `#00875f`
|
||||||
|
`30` | `turquoise4` | `0,135,135` | `#008787`
|
||||||
|
`31` | `deepskyblue3` | `0,135,175` | `#0087af`
|
||||||
|
`32` | `deepskyblue3_1` | `0,135,215` | `#0087d7`
|
||||||
|
`33` | `dodgerblue1` | `0,135,255` | `#0087ff`
|
||||||
|
`34` | `green3` | `0,175,0` | `#00af00`
|
||||||
|
`35` | `springgreen3` | `0,175,95` | `#00af5f`
|
||||||
|
`36` | `darkcyan` | `0,175,135` | `#00af87`
|
||||||
|
`37` | `lightseagreen` | `0,175,175` | `#00afaf`
|
||||||
|
`38` | `deepskyblue2` | `0,175,215` | `#00afd7`
|
||||||
|
`39` | `deepskyblue1` | `0,175,255` | `#00afff`
|
||||||
|
`40` | `green3_1` | `0,215,0` | `#00d700`
|
||||||
|
`41` | `springgreen3_1` | `0,215,95` | `#00d75f`
|
||||||
|
`42` | `springgreen2` | `0,215,135` | `#00d787`
|
||||||
|
`43` | `cyan3` | `0,215,175` | `#00d7af`
|
||||||
|
`44` | `darkturquoise` | `0,215,215` | `#00d7d7`
|
||||||
|
`45` | `turquoise2` | `0,215,255` | `#00d7ff`
|
||||||
|
`46` | `green1` | `0,255,0` | `#00ff00`
|
||||||
|
`47` | `springgreen2_1` | `0,255,95` | `#00ff5f`
|
||||||
|
`48` | `springgreen1` | `0,255,135` | `#00ff87`
|
||||||
|
`49` | `mediumspringgreen` | `0,255,175` | `#00ffaf`
|
||||||
|
`50` | `cyan2` | `0,255,215` | `#00ffd7`
|
||||||
|
`51` | `cyan1` | `0,255,255` | `#00ffff`
|
||||||
|
`52` | `darkred` | `95,0,0` | `#5f0000`
|
||||||
|
`53` | `deeppink4` | `95,0,95` | `#5f005f`
|
||||||
|
`54` | `purple4` | `95,0,135` | `#5f0087`
|
||||||
|
`55` | `purple4_1` | `95,0,175` | `#5f00af`
|
||||||
|
`56` | `purple3` | `95,0,215` | `#5f00d7`
|
||||||
|
`57` | `blueviolet` | `95,0,255` | `#5f00ff`
|
||||||
|
`58` | `orange4` | `95,95,0` | `#5f5f00`
|
||||||
|
`59` | `grey37` | `95,95,95` | `#5f5f5f`
|
||||||
|
`60` | `mediumpurple4` | `95,95,135` | `#5f5f87`
|
||||||
|
`61` | `slateblue3` | `95,95,175` | `#5f5faf`
|
||||||
|
`62` | `slateblue3_1` | `95,95,215` | `#5f5fd7`
|
||||||
|
`63` | `royalblue1` | `95,95,255` | `#5f5fff`
|
||||||
|
`64` | `chartreuse4` | `95,135,0` | `#5f8700`
|
||||||
|
`65` | `darkseagreen4` | `95,135,95` | `#5f875f`
|
||||||
|
`66` | `paleturquoise4` | `95,135,135` | `#5f8787`
|
||||||
|
`67` | `steelblue` | `95,135,175` | `#5f87af`
|
||||||
|
`68` | `steelblue3` | `95,135,215` | `#5f87d7`
|
||||||
|
`69` | `cornflowerblue` | `95,135,255` | `#5f87ff`
|
||||||
|
`70` | `chartreuse3` | `95,175,0` | `#5faf00`
|
||||||
|
`71` | `darkseagreen4_1` | `95,175,95` | `#5faf5f`
|
||||||
|
`72` | `cadetblue` | `95,175,135` | `#5faf87`
|
||||||
|
`73` | `cadetblue_1` | `95,175,175` | `#5fafaf`
|
||||||
|
`74` | `skyblue3` | `95,175,215` | `#5fafd7`
|
||||||
|
`75` | `steelblue1` | `95,175,255` | `#5fafff`
|
||||||
|
`76` | `chartreuse3_1` | `95,215,0` | `#5fd700`
|
||||||
|
`77` | `palegreen3` | `95,215,95` | `#5fd75f`
|
||||||
|
`78` | `seagreen3` | `95,215,135` | `#5fd787`
|
||||||
|
`79` | `aquamarine3` | `95,215,175` | `#5fd7af`
|
||||||
|
`80` | `mediumturquoise` | `95,215,215` | `#5fd7d7`
|
||||||
|
`81` | `steelblue1_1` | `95,215,255` | `#5fd7ff`
|
||||||
|
`82` | `chartreuse2` | `95,255,0` | `#5fff00`
|
||||||
|
`83` | `seagreen2` | `95,255,95` | `#5fff5f`
|
||||||
|
`84` | `seagreen1` | `95,255,135` | `#5fff87`
|
||||||
|
`85` | `seagreen1_1` | `95,255,175` | `#5fffaf`
|
||||||
|
`86` | `aquamarine1` | `95,255,215` | `#5fffd7`
|
||||||
|
`87` | `darkslategray2` | `95,255,255` | `#5fffff`
|
||||||
|
`88` | `darkred_1` | `135,0,0` | `#870000`
|
||||||
|
`89` | `deeppink4_1` | `135,0,95` | `#87005f`
|
||||||
|
`90` | `darkmagenta` | `135,0,135` | `#870087`
|
||||||
|
`91` | `darkmagenta_1` | `135,0,175` | `#8700af`
|
||||||
|
`92` | `darkviolet` | `135,0,215` | `#8700d7`
|
||||||
|
`93` | `purple_1` | `135,0,255` | `#8700ff`
|
||||||
|
`94` | `orange4_1` | `135,95,0` | `#875f00`
|
||||||
|
`95` | `lightpink4` | `135,95,95` | `#875f5f`
|
||||||
|
`96` | `plum4` | `135,95,135` | `#875f87`
|
||||||
|
`97` | `mediumpurple3` | `135,95,175` | `#875faf`
|
||||||
|
`98` | `mediumpurple3_1` | `135,95,215` | `#875fd7`
|
||||||
|
`99` | `slateblue1` | `135,95,255` | `#875fff`
|
||||||
|
`100` | `yellow4` | `135,135,0` | `#878700`
|
||||||
|
`101` | `wheat4` | `135,135,95` | `#87875f`
|
||||||
|
`102` | `grey53` | `135,135,135` | `#878787`
|
||||||
|
`103` | `lightslategrey` | `135,135,175` | `#8787af`
|
||||||
|
`104` | `mediumpurple` | `135,135,215` | `#8787d7`
|
||||||
|
`105` | `lightslateblue` | `135,135,255` | `#8787ff`
|
||||||
|
`106` | `yellow4_1` | `135,175,0` | `#87af00`
|
||||||
|
`107` | `darkolivegreen3` | `135,175,95` | `#87af5f`
|
||||||
|
`108` | `darkseagreen` | `135,175,135` | `#87af87`
|
||||||
|
`109` | `lightskyblue3` | `135,175,175` | `#87afaf`
|
||||||
|
`110` | `lightskyblue3_1` | `135,175,215` | `#87afd7`
|
||||||
|
`111` | `skyblue2` | `135,175,255` | `#87afff`
|
||||||
|
`112` | `chartreuse2_1` | `135,215,0` | `#87d700`
|
||||||
|
`113` | `darkolivegreen3_1` | `135,215,95` | `#87d75f`
|
||||||
|
`114` | `palegreen3_1` | `135,215,135` | `#87d787`
|
||||||
|
`115` | `darkseagreen3` | `135,215,175` | `#87d7af`
|
||||||
|
`116` | `darkslategray3` | `135,215,215` | `#87d7d7`
|
||||||
|
`117` | `skyblue1` | `135,215,255` | `#87d7ff`
|
||||||
|
`118` | `chartreuse1` | `135,255,0` | `#87ff00`
|
||||||
|
`119` | `lightgreen` | `135,255,95` | `#87ff5f`
|
||||||
|
`120` | `lightgreen_1` | `135,255,135` | `#87ff87`
|
||||||
|
`121` | `palegreen1` | `135,255,175` | `#87ffaf`
|
||||||
|
`122` | `aquamarine1_1` | `135,255,215` | `#87ffd7`
|
||||||
|
`123` | `darkslategray1` | `135,255,255` | `#87ffff`
|
||||||
|
`124` | `red3` | `175,0,0` | `#af0000`
|
||||||
|
`125` | `deeppink4_2` | `175,0,95` | `#af005f`
|
||||||
|
`126` | `mediumvioletred` | `175,0,135` | `#af0087`
|
||||||
|
`127` | `magenta3` | `175,0,175` | `#af00af`
|
||||||
|
`128` | `darkviolet_1` | `175,0,215` | `#af00d7`
|
||||||
|
`129` | `purple_2` | `175,0,255` | `#af00ff`
|
||||||
|
`130` | `darkorange3` | `175,95,0` | `#af5f00`
|
||||||
|
`131` | `indianred` | `175,95,95` | `#af5f5f`
|
||||||
|
`132` | `hotpink3` | `175,95,135` | `#af5f87`
|
||||||
|
`133` | `mediumorchid3` | `175,95,175` | `#af5faf`
|
||||||
|
`134` | `mediumorchid` | `175,95,215` | `#af5fd7`
|
||||||
|
`135` | `mediumpurple2` | `175,95,255` | `#af5fff`
|
||||||
|
`136` | `darkgoldenrod` | `175,135,0` | `#af8700`
|
||||||
|
`137` | `lightsalmon3` | `175,135,95` | `#af875f`
|
||||||
|
`138` | `rosybrown` | `175,135,135` | `#af8787`
|
||||||
|
`139` | `grey63` | `175,135,175` | `#af87af`
|
||||||
|
`140` | `mediumpurple2_1` | `175,135,215` | `#af87d7`
|
||||||
|
`141` | `mediumpurple1` | `175,135,255` | `#af87ff`
|
||||||
|
`142` | `gold3` | `175,175,0` | `#afaf00`
|
||||||
|
`143` | `darkkhaki` | `175,175,95` | `#afaf5f`
|
||||||
|
`144` | `navajowhite3` | `175,175,135` | `#afaf87`
|
||||||
|
`145` | `grey69` | `175,175,175` | `#afafaf`
|
||||||
|
`146` | `lightsteelblue3` | `175,175,215` | `#afafd7`
|
||||||
|
`147` | `lightsteelblue` | `175,175,255` | `#afafff`
|
||||||
|
`148` | `yellow3` | `175,215,0` | `#afd700`
|
||||||
|
`149` | `darkolivegreen3_2` | `175,215,95` | `#afd75f`
|
||||||
|
`150` | `darkseagreen3_1` | `175,215,135` | `#afd787`
|
||||||
|
`151` | `darkseagreen2` | `175,215,175` | `#afd7af`
|
||||||
|
`152` | `lightcyan3` | `175,215,215` | `#afd7d7`
|
||||||
|
`153` | `lightskyblue1` | `175,215,255` | `#afd7ff`
|
||||||
|
`154` | `greenyellow` | `175,255,0` | `#afff00`
|
||||||
|
`155` | `darkolivegreen2` | `175,255,95` | `#afff5f`
|
||||||
|
`156` | `palegreen1_1` | `175,255,135` | `#afff87`
|
||||||
|
`157` | `darkseagreen2_1` | `175,255,175` | `#afffaf`
|
||||||
|
`158` | `darkseagreen1` | `175,255,215` | `#afffd7`
|
||||||
|
`159` | `paleturquoise1` | `175,255,255` | `#afffff`
|
||||||
|
`160` | `red3_1` | `215,0,0` | `#d70000`
|
||||||
|
`161` | `deeppink3` | `215,0,95` | `#d7005f`
|
||||||
|
`162` | `deeppink3_1` | `215,0,135` | `#d70087`
|
||||||
|
`163` | `magenta3_1` | `215,0,175` | `#d700af`
|
||||||
|
`164` | `magenta3_2` | `215,0,215` | `#d700d7`
|
||||||
|
`165` | `magenta2` | `215,0,255` | `#d700ff`
|
||||||
|
`166` | `darkorange3_1` | `215,95,0` | `#d75f00`
|
||||||
|
`167` | `indianred_1` | `215,95,95` | `#d75f5f`
|
||||||
|
`168` | `hotpink3_1` | `215,95,135` | `#d75f87`
|
||||||
|
`169` | `hotpink2` | `215,95,175` | `#d75faf`
|
||||||
|
`170` | `orchid` | `215,95,215` | `#d75fd7`
|
||||||
|
`171` | `mediumorchid1` | `215,95,255` | `#d75fff`
|
||||||
|
`172` | `orange3` | `215,135,0` | `#d78700`
|
||||||
|
`173` | `lightsalmon3_1` | `215,135,95` | `#d7875f`
|
||||||
|
`174` | `lightpink3` | `215,135,135` | `#d78787`
|
||||||
|
`175` | `pink3` | `215,135,175` | `#d787af`
|
||||||
|
`176` | `plum3` | `215,135,215` | `#d787d7`
|
||||||
|
`177` | `violet` | `215,135,255` | `#d787ff`
|
||||||
|
`178` | `gold3_1` | `215,175,0` | `#d7af00`
|
||||||
|
`179` | `lightgoldenrod3` | `215,175,95` | `#d7af5f`
|
||||||
|
`180` | `tan` | `215,175,135` | `#d7af87`
|
||||||
|
`181` | `mistyrose3` | `215,175,175` | `#d7afaf`
|
||||||
|
`182` | `thistle3` | `215,175,215` | `#d7afd7`
|
||||||
|
`183` | `plum2` | `215,175,255` | `#d7afff`
|
||||||
|
`184` | `yellow3_1` | `215,215,0` | `#d7d700`
|
||||||
|
`185` | `khaki3` | `215,215,95` | `#d7d75f`
|
||||||
|
`186` | `lightgoldenrod2` | `215,215,135` | `#d7d787`
|
||||||
|
`187` | `lightyellow3` | `215,215,175` | `#d7d7af`
|
||||||
|
`188` | `grey84` | `215,215,215` | `#d7d7d7`
|
||||||
|
`189` | `lightsteelblue1` | `215,215,255` | `#d7d7ff`
|
||||||
|
`190` | `yellow2` | `215,255,0` | `#d7ff00`
|
||||||
|
`191` | `darkolivegreen1` | `215,255,95` | `#d7ff5f`
|
||||||
|
`192` | `darkolivegreen1_1` | `215,255,135` | `#d7ff87`
|
||||||
|
`193` | `darkseagreen1_1` | `215,255,175` | `#d7ffaf`
|
||||||
|
`194` | `honeydew2` | `215,255,215` | `#d7ffd7`
|
||||||
|
`195` | `lightcyan1` | `215,255,255` | `#d7ffff`
|
||||||
|
`196` | `red1` | `255,0,0` | `#ff0000`
|
||||||
|
`197` | `deeppink2` | `255,0,95` | `#ff005f`
|
||||||
|
`198` | `deeppink1` | `255,0,135` | `#ff0087`
|
||||||
|
`199` | `deeppink1_1` | `255,0,175` | `#ff00af`
|
||||||
|
`200` | `magenta2_1` | `255,0,215` | `#ff00d7`
|
||||||
|
`201` | `magenta1` | `255,0,255` | `#ff00ff`
|
||||||
|
`202` | `orangered1` | `255,95,0` | `#ff5f00`
|
||||||
|
`203` | `indianred1` | `255,95,95` | `#ff5f5f`
|
||||||
|
`204` | `indianred1_1` | `255,95,135` | `#ff5f87`
|
||||||
|
`205` | `hotpink` | `255,95,175` | `#ff5faf`
|
||||||
|
`206` | `hotpink_1` | `255,95,215` | `#ff5fd7`
|
||||||
|
`207` | `mediumorchid1_1` | `255,95,255` | `#ff5fff`
|
||||||
|
`208` | `darkorange` | `255,135,0` | `#ff8700`
|
||||||
|
`209` | `salmon1` | `255,135,95` | `#ff875f`
|
||||||
|
`210` | `lightcoral` | `255,135,135` | `#ff8787`
|
||||||
|
`211` | `palevioletred1` | `255,135,175` | `#ff87af`
|
||||||
|
`212` | `orchid2` | `255,135,215` | `#ff87d7`
|
||||||
|
`213` | `orchid1` | `255,135,255` | `#ff87ff`
|
||||||
|
`214` | `orange1` | `255,175,0` | `#ffaf00`
|
||||||
|
`215` | `sandybrown` | `255,175,95` | `#ffaf5f`
|
||||||
|
`216` | `lightsalmon1` | `255,175,135` | `#ffaf87`
|
||||||
|
`217` | `lightpink1` | `255,175,175` | `#ffafaf`
|
||||||
|
`218` | `pink1` | `255,175,215` | `#ffafd7`
|
||||||
|
`219` | `plum1` | `255,175,255` | `#ffafff`
|
||||||
|
`220` | `gold1` | `255,215,0` | `#ffd700`
|
||||||
|
`221` | `lightgoldenrod2_1` | `255,215,95` | `#ffd75f`
|
||||||
|
`222` | `lightgoldenrod2_2` | `255,215,135` | `#ffd787`
|
||||||
|
`223` | `navajowhite1` | `255,215,175` | `#ffd7af`
|
||||||
|
`224` | `mistyrose1` | `255,215,215` | `#ffd7d7`
|
||||||
|
`225` | `thistle1` | `255,215,255` | `#ffd7ff`
|
||||||
|
`226` | `yellow1` | `255,255,0` | `#ffff00`
|
||||||
|
`227` | `lightgoldenrod1` | `255,255,95` | `#ffff5f`
|
||||||
|
`228` | `khaki1` | `255,255,135` | `#ffff87`
|
||||||
|
`229` | `wheat1` | `255,255,175` | `#ffffaf`
|
||||||
|
`230` | `cornsilk1` | `255,255,215` | `#ffffd7`
|
||||||
|
`231` | `grey100` | `255,255,255` | `#ffffff`
|
||||||
|
`232` | `grey3` | `8,8,8` | `#080808`
|
||||||
|
`233` | `grey7` | `18,18,18` | `#121212`
|
||||||
|
`234` | `grey11` | `28,28,28` | `#1c1c1c`
|
||||||
|
`235` | `grey15` | `38,38,38` | `#262626`
|
||||||
|
`236` | `grey19` | `48,48,48` | `#303030`
|
||||||
|
`237` | `grey23` | `58,58,58` | `#3a3a3a`
|
||||||
|
`238` | `grey27` | `68,68,68` | `#444444`
|
||||||
|
`239` | `grey30` | `78,78,78` | `#4e4e4e`
|
||||||
|
`240` | `grey35` | `88,88,88` | `#585858`
|
||||||
|
`241` | `grey39` | `98,98,98` | `#626262`
|
||||||
|
`242` | `grey42` | `108,108,108` | `#6c6c6c`
|
||||||
|
`243` | `grey46` | `118,118,118` | `#767676`
|
||||||
|
`244` | `grey50` | `128,128,128` | `#808080`
|
||||||
|
`245` | `grey54` | `138,138,138` | `#8a8a8a`
|
||||||
|
`246` | `grey58` | `148,148,148` | `#949494`
|
||||||
|
`247` | `grey62` | `158,158,158` | `#9e9e9e`
|
||||||
|
`248` | `grey66` | `168,168,168` | `#a8a8a8`
|
||||||
|
`249` | `grey70` | `178,178,178` | `#b2b2b2`
|
||||||
|
`250` | `grey74` | `188,188,188` | `#bcbcbc`
|
||||||
|
`251` | `grey78` | `198,198,198` | `#c6c6c6`
|
||||||
|
`252` | `grey82` | `208,208,208` | `#d0d0d0`
|
||||||
|
`253` | `grey85` | `218,218,218` | `#dadada`
|
||||||
|
`254` | `grey89` | `228,228,228` | `#e4e4e4`
|
||||||
|
`255` | `grey93` | `238,238,238` | `#eeeeee`
|
||||||
|
|||||||
14
examples/Colors/Colors.csproj
Normal file
14
examples/Colors/Colors.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<Description>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
78
examples/Colors/Program.cs
Normal file
78
examples/Colors/Program.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace ColorExample
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// 4-BIT
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
AnsiConsole.ResetColors();
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
AnsiConsole.MarkupLine("[bold underline]4-bit Colors[/]");
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
AnsiConsole.Background = Color.FromInt32(i);
|
||||||
|
AnsiConsole.Write(string.Format(" {0,-9}", AnsiConsole.Background.ToString()));
|
||||||
|
AnsiConsole.ResetColors();
|
||||||
|
if ((i + 1) % 8 == 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// 8-BIT
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
AnsiConsole.ResetColors();
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
AnsiConsole.MarkupLine("[bold underline]8-bit Colors[/]");
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < 16; j++)
|
||||||
|
{
|
||||||
|
var number = i * 16 + j;
|
||||||
|
AnsiConsole.Background = Color.FromInt32(number);
|
||||||
|
AnsiConsole.Write(string.Format(" {0,-4}", number));
|
||||||
|
AnsiConsole.ResetColors();
|
||||||
|
if ((number + 1) % 16 == 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// 24-BIT
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
AnsiConsole.ResetColors();
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
AnsiConsole.MarkupLine("[bold underline]24-bit Colors[/]");
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
for (var i = 0.0005; i < 1; i += 0.0025)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
|
||||||
|
var color = Utilities.HSL2RGB(i, 0.5, 0.5);
|
||||||
|
AnsiConsole.Background = new Color(color.R, color.G, color.B);
|
||||||
|
AnsiConsole.Write(" ");
|
||||||
|
|
||||||
|
if (index % 50 == 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
examples/Colors/Utilities.cs
Normal file
77
examples/Colors/Utilities.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace ColorExample
|
||||||
|
{
|
||||||
|
public static class Utilities
|
||||||
|
{
|
||||||
|
// Borrowed from https://geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm
|
||||||
|
public static Color HSL2RGB(double h, double sl, double l)
|
||||||
|
{
|
||||||
|
double v;
|
||||||
|
double r, g, b;
|
||||||
|
|
||||||
|
r = l; // default to gray
|
||||||
|
g = l;
|
||||||
|
b = l;
|
||||||
|
v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
|
||||||
|
|
||||||
|
if (v > 0)
|
||||||
|
{
|
||||||
|
double m;
|
||||||
|
double sv;
|
||||||
|
int sextant;
|
||||||
|
double fract, vsf, mid1, mid2;
|
||||||
|
|
||||||
|
m = l + l - v;
|
||||||
|
sv = (v - m) / v;
|
||||||
|
h *= 6.0;
|
||||||
|
|
||||||
|
sextant = (int)h;
|
||||||
|
fract = h - sextant;
|
||||||
|
vsf = v * sv * fract;
|
||||||
|
mid1 = m + vsf;
|
||||||
|
mid2 = v - vsf;
|
||||||
|
|
||||||
|
switch (sextant)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = v;
|
||||||
|
g = mid1;
|
||||||
|
b = m;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = mid2;
|
||||||
|
g = v;
|
||||||
|
b = m;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = m;
|
||||||
|
g = v;
|
||||||
|
b = mid1;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = m;
|
||||||
|
g = mid2;
|
||||||
|
b = v;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
r = mid1;
|
||||||
|
g = m;
|
||||||
|
b = v;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
r = v;
|
||||||
|
g = m;
|
||||||
|
b = mid2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Color(
|
||||||
|
Convert.ToByte(r * 255.0f),
|
||||||
|
Convert.ToByte(g * 255.0f),
|
||||||
|
Convert.ToByte(b * 255.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
examples/Grid/Grid.csproj
Normal file
14
examples/Grid/Grid.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<Description>Demonstrates how to render grids in a console.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
25
examples/Grid/Program.cs
Normal file
25
examples/Grid/Program.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace GridExample
|
||||||
|
{
|
||||||
|
public sealed class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options] [[[[--] <additional arguments>...]][/]");
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn(new GridColumn { NoWrap = true });
|
||||||
|
grid.AddColumn(new GridColumn { NoWrap = true, Width = 2 });
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddRow("Options:", "", "");
|
||||||
|
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "", "Show command line help.");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<Description>Demonstrates how to render items in panels.</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
|
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
49
examples/Panel/Program.cs
Normal file
49
examples/Panel/Program.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace PanelExample
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var content = Text.Markup(
|
||||||
|
"[underline]I[/] heard [underline on blue]you[/] like 📦\n\n\n\n" +
|
||||||
|
"So I put a 📦 in a 📦\n\n" +
|
||||||
|
"😅");
|
||||||
|
|
||||||
|
AnsiConsole.Render(
|
||||||
|
new Panel(
|
||||||
|
new Panel(content)
|
||||||
|
{
|
||||||
|
Alignment = Justify.Center,
|
||||||
|
Border = BorderKind.Rounded
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Left adjusted panel with text
|
||||||
|
AnsiConsole.Render(new Panel(
|
||||||
|
new Text("Left adjusted\nLeft"))
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
Alignment = Justify.Left,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Centered ASCII panel with text
|
||||||
|
AnsiConsole.Render(new Panel(
|
||||||
|
new Text("Centered\nCenter"))
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
Alignment = Justify.Center,
|
||||||
|
Border = BorderKind.Ascii,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Right adjusted, rounded panel with text
|
||||||
|
AnsiConsole.Render(new Panel(
|
||||||
|
new Text("Right adjusted\nRight"))
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
Alignment = Justify.Right,
|
||||||
|
Border = BorderKind.Rounded,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
examples/Table/Program.cs
Normal file
90
examples/Table/Program.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace TableExample
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
// A simple table§
|
||||||
|
RenderSimpleTable();
|
||||||
|
|
||||||
|
// A big table
|
||||||
|
RenderBigTable();
|
||||||
|
|
||||||
|
// A complex table
|
||||||
|
RenderComplexTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderComplexTable()
|
||||||
|
{
|
||||||
|
// Create simple table.
|
||||||
|
var simple = new Table { Border = BorderKind.Rounded };
|
||||||
|
simple.AddColumn(new TableColumn("[u]Foo[/]").Centered());
|
||||||
|
simple.AddColumn(new TableColumn("[u]Bar[/]"));
|
||||||
|
simple.AddColumn(new TableColumn("[u]Baz[/]"));
|
||||||
|
simple.AddRow("Hello", "[red]World![/]", "");
|
||||||
|
simple.AddRow("[blue]Bounjour[/]", "[white]le[/]", "[red]monde![/]");
|
||||||
|
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
|
||||||
|
|
||||||
|
// Create other table.
|
||||||
|
var second = new Table { Border = BorderKind.Square };
|
||||||
|
second.AddColumn(new TableColumn("[u]Foo[/]"));
|
||||||
|
second.AddColumn(new TableColumn("[u]Bar[/]"));
|
||||||
|
second.AddColumn(new TableColumn("[u]Baz[/]"));
|
||||||
|
second.AddRow("Hello", "[red]World![/]", "");
|
||||||
|
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
|
||||||
|
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
|
||||||
|
|
||||||
|
var table = new Table { Border = BorderKind.Rounded };
|
||||||
|
table.AddColumn(new TableColumn(new Panel("[u]Foo[/]")));
|
||||||
|
table.AddColumn(new TableColumn(new Panel("[u]Bar[/]")));
|
||||||
|
table.AddColumn(new TableColumn(new Panel("[u]Baz[/]")));
|
||||||
|
|
||||||
|
// Add some rows
|
||||||
|
table.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/] 🌍"), Text.Empty);
|
||||||
|
table.AddRow(second, new Text("Whaaat"), new Text("Lol"));
|
||||||
|
table.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty);
|
||||||
|
|
||||||
|
AnsiConsole.Render(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderSimpleTable()
|
||||||
|
{
|
||||||
|
// Create the table.
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumn(new TableColumn("[u]Foo[/]"));
|
||||||
|
table.AddColumn(new TableColumn("[u]Bar[/]"));
|
||||||
|
table.AddColumn(new TableColumn("[u]Baz[/]"));
|
||||||
|
|
||||||
|
// Add some rows
|
||||||
|
table.AddRow("Hello", "[red]World![/]", "");
|
||||||
|
table.AddRow("[blue]Bounjour[/]", "[white]le[/]", "[red]monde![/]");
|
||||||
|
table.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
|
||||||
|
|
||||||
|
AnsiConsole.Render(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderBigTable()
|
||||||
|
{
|
||||||
|
// Create the table.
|
||||||
|
var table = new Table { Border = BorderKind.Rounded };
|
||||||
|
table.AddColumn("[red underline]Foo[/]");
|
||||||
|
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
|
||||||
|
|
||||||
|
// Add some rows
|
||||||
|
table.AddRow("[blue][underline]Hell[/]o[/]", "World 🌍");
|
||||||
|
table.AddRow("[yellow]Patrik [green]\"Hello World\"[/] Svensson[/]", "Was [underline]here[/]!");
|
||||||
|
table.AddEmptyRow();
|
||||||
|
table.AddRow(
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
|
||||||
|
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
|
||||||
|
"dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat " +
|
||||||
|
"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "◀ Strange language");
|
||||||
|
table.AddEmptyRow();
|
||||||
|
table.AddRow("Hej 👋", "[green]Världen[/]");
|
||||||
|
|
||||||
|
AnsiConsole.Render(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
examples/Table/Table.csproj
Normal file
14
examples/Table/Table.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<Description>Demonstrates how to render tables in a console.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
BIN
gfx/large-logo.png
Normal file
BIN
gfx/large-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 600 KiB |
BIN
gfx/medium-logo.png
Normal file
BIN
gfx/medium-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
gfx/small-logo.png
Normal file
BIN
gfx/small-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
2
scripts/.gitignore
vendored
Normal file
2
scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Generated
|
||||||
|
Temp
|
||||||
24
scripts/Generate-Colors.ps1
Normal file
24
scripts/Generate-Colors.ps1
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
##########################################################
|
||||||
|
# Script that generates known colors and lookup tables.
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
$Output = Join-Path $PSScriptRoot "Temp"
|
||||||
|
$Source = Join-Path $PSScriptRoot "/../src/Spectre.Console"
|
||||||
|
|
||||||
|
if(!(Test-Path $Output -PathType Container)) {
|
||||||
|
New-Item -ItemType Directory -Path $Output | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate the files
|
||||||
|
Push-Location Generator
|
||||||
|
&dotnet run -- colors "$Output"
|
||||||
|
if(!$?) {
|
||||||
|
Pop-Location
|
||||||
|
Throw "An error occured when generating code."
|
||||||
|
}
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
# Copy the files to the correct location
|
||||||
|
Copy-Item (Join-Path "$Output" "Color.Generated.cs") -Destination "$Source/Color.Generated.cs"
|
||||||
|
Copy-Item (Join-Path "$Output" "ColorPalette.Generated.cs") -Destination "$Source/Internal/Colors/ColorPalette.Generated.cs"
|
||||||
|
Copy-Item (Join-Path "$Output" "ColorTable.Generated.cs") -Destination "$Source/Internal/Colors/ColorTable.Generated.cs"
|
||||||
59
scripts/Generator/Commands/ColorGeneratorCommand.cs
Normal file
59
scripts/Generator/Commands/ColorGeneratorCommand.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Generator.Models;
|
||||||
|
using Scriban;
|
||||||
|
using Spectre.Cli;
|
||||||
|
using Spectre.IO;
|
||||||
|
|
||||||
|
namespace Generator.Commands
|
||||||
|
{
|
||||||
|
public sealed class ColorGeneratorCommand : Command<GeneratorCommandSettings>
|
||||||
|
{
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public ColorGeneratorCommand()
|
||||||
|
{
|
||||||
|
_fileSystem = new FileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Execute(CommandContext context, GeneratorCommandSettings settings)
|
||||||
|
{
|
||||||
|
var templates = new FilePath[]
|
||||||
|
{
|
||||||
|
"Templates/ColorPalette.Generated.template",
|
||||||
|
"Templates/Color.Generated.template",
|
||||||
|
"Templates/ColorTable.Generated.template"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the color model.
|
||||||
|
var model = Color.Parse(File.ReadAllText("Data/colors.json"));
|
||||||
|
|
||||||
|
var output = new DirectoryPath(settings.Output);
|
||||||
|
if (!_fileSystem.Directory.Exists(settings.Output))
|
||||||
|
{
|
||||||
|
_fileSystem.Directory.Create(settings.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var templatePath in templates)
|
||||||
|
{
|
||||||
|
// Parse the Scriban template.
|
||||||
|
var template = Template.Parse(File.ReadAllText(templatePath.FullPath));
|
||||||
|
|
||||||
|
// Render the template with the model.
|
||||||
|
var result = template.Render(new { Colors = model });
|
||||||
|
|
||||||
|
// Write output to file
|
||||||
|
var file = output.CombineWithFilePath(templatePath.GetFilename().ChangeExtension(".cs"));
|
||||||
|
File.WriteAllText(file.FullPath, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GeneratorCommandSettings : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(0, "<OUTPUT>")]
|
||||||
|
public string Output { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
3842
scripts/Generator/Data/colors.json
Normal file
3842
scripts/Generator/Data/colors.json
Normal file
File diff suppressed because it is too large
Load Diff
36
scripts/Generator/Generator.csproj
Normal file
36
scripts/Generator/Generator.csproj
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="out\**" />
|
||||||
|
<EmbeddedResource Remove="out\**" />
|
||||||
|
<None Remove="out\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Data\colors.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Templates\ColorTable.Generated.template">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Templates\Color.Generated.template">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Templates\ColorPalette.Generated.template">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
|
<PackageReference Include="Scriban" Version="2.1.3" />
|
||||||
|
<PackageReference Include="Spectre.Cli" Version="0.36.1-preview.0.6" />
|
||||||
|
<PackageReference Include="Spectre.IO" Version="0.1.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
37
scripts/Generator/Generator.sln
Normal file
37
scripts/Generator/Generator.sln
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30320.27
|
||||||
|
MinimumVisualStudioVersion = 15.0.26124.0
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generator", "Generator.csproj", "{5668D267-53E3-4B99-97AE-59AA597D22ED}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5668D267-53E3-4B99-97AE-59AA597D22ED}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {5F37FDE3-D591-4D43-8DDE-2ED6BAB0A7B4}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
62
scripts/Generator/Models/Color.cs
Normal file
62
scripts/Generator/Models/Color.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Generator.Models
|
||||||
|
{
|
||||||
|
public sealed class Color
|
||||||
|
{
|
||||||
|
public int Number { get; set; }
|
||||||
|
public string Hex { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Rgb Rgb { get; set; }
|
||||||
|
|
||||||
|
public int R => Rgb.R;
|
||||||
|
public int G => Rgb.G;
|
||||||
|
public int B => Rgb.B;
|
||||||
|
|
||||||
|
public static IEnumerable<Color> Parse(string json)
|
||||||
|
{
|
||||||
|
var source = JsonConvert.DeserializeObject<List<Color>>(json);
|
||||||
|
|
||||||
|
var check = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var color in source.OrderBy(c => c.Number))
|
||||||
|
{
|
||||||
|
if (!check.ContainsKey(color.Name))
|
||||||
|
{
|
||||||
|
check.Add(color.Name, color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newName = (string)null;
|
||||||
|
for (int i = 1; i < 100; i++)
|
||||||
|
{
|
||||||
|
if (!check.ContainsKey($"{color.Name}_{i}"))
|
||||||
|
{
|
||||||
|
newName = $"{color.Name}_{i}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newName == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Impossible!");
|
||||||
|
}
|
||||||
|
|
||||||
|
check.Add(newName, color);
|
||||||
|
color.Name = newName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Rgb
|
||||||
|
{
|
||||||
|
public int R { get; set; }
|
||||||
|
public int G { get; set; }
|
||||||
|
public int B { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
scripts/Generator/Models/ColorModel.cs
Normal file
14
scripts/Generator/Models/ColorModel.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Generator.Models
|
||||||
|
{
|
||||||
|
public sealed class ColorModel
|
||||||
|
{
|
||||||
|
public List<Color> Colors { get; set; }
|
||||||
|
|
||||||
|
public ColorModel(IEnumerable<Color> colors)
|
||||||
|
{
|
||||||
|
Colors = new List<Color>(colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
scripts/Generator/Models/Palette.cs
Normal file
6
scripts/Generator/Models/Palette.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Generator.Models
|
||||||
|
{
|
||||||
|
public sealed class Palette
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
19
scripts/Generator/Program.cs
Normal file
19
scripts/Generator/Program.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Generator.Commands;
|
||||||
|
using Spectre.Cli;
|
||||||
|
|
||||||
|
namespace Generator
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
var app = new CommandApp();
|
||||||
|
app.Configure(config =>
|
||||||
|
{
|
||||||
|
config.AddCommand<ColorGeneratorCommand>("colors");
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.Run(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
scripts/Generator/Templates/Color.Generated.template
Normal file
36
scripts/Generator/Templates/Color.Generated.template
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a color.
|
||||||
|
/// </summary>
|
||||||
|
public partial struct Color
|
||||||
|
{
|
||||||
|
internal Color(byte number, byte red, byte green, byte blue, bool isDefault = false)
|
||||||
|
: this(red, green, blue)
|
||||||
|
{
|
||||||
|
Number = number;
|
||||||
|
IsDefault = isDefault;
|
||||||
|
}
|
||||||
|
{{~ for color in colors }}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the color "{{ color.name }}" (RGB {{ color.r }},{{ color.g }},{{ color.b }}).
|
||||||
|
/// </summary>
|
||||||
|
{{- if string.contains color.name "_" }}
|
||||||
|
[SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")]
|
||||||
|
{{- end}}
|
||||||
|
public static Color {{ color.name }} { get; } = new Color({{ color.number }}, {{ color.r }}, {{ color.g }}, {{ color.b }});
|
||||||
|
{{~ end ~}}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
scripts/Generator/Templates/ColorPalette.Generated.template
Normal file
47
scripts/Generator/Templates/ColorPalette.Generated.template
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static partial class ColorPalette
|
||||||
|
{
|
||||||
|
private static List<Color> GenerateLegacyPalette()
|
||||||
|
{
|
||||||
|
return new List<Color>
|
||||||
|
{
|
||||||
|
{{~ for number in 0..7 ~}}
|
||||||
|
Color.{{ colors[number].name }},
|
||||||
|
{{~ end ~}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Color> GenerateStandardPalette(IReadOnlyList<Color> legacy)
|
||||||
|
{
|
||||||
|
return new List<Color>(legacy)
|
||||||
|
{
|
||||||
|
{{~ for number in 8..15 ~}}
|
||||||
|
Color.{{ colors[number].name }},
|
||||||
|
{{~ end ~}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Color> GenerateEightBitPalette(IReadOnlyList<Color> standard)
|
||||||
|
{
|
||||||
|
return new List<Color>(standard)
|
||||||
|
{
|
||||||
|
{{~ for number in 16..255 ~}}
|
||||||
|
Color.{{ colors[number].name }},
|
||||||
|
{{~ end ~}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
scripts/Generator/Templates/ColorTable.Generated.template
Normal file
28
scripts/Generator/Templates/ColorTable.Generated.template
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static partial class ColorTable
|
||||||
|
{
|
||||||
|
private static Dictionary<string, int> GenerateTable()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{{~ for color in colors ~}}
|
||||||
|
{ "{{ string.downcase color.name }}", {{ color.number }} },
|
||||||
|
{{~ end ~}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,3 +75,12 @@ dotnet_diagnostic.CA1032.severity = none
|
|||||||
|
|
||||||
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
|
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
|
||||||
dotnet_diagnostic.CA1826.severity = none
|
dotnet_diagnostic.CA1826.severity = none
|
||||||
|
|
||||||
|
# RCS1079: Throwing of new NotImplementedException.
|
||||||
|
dotnet_diagnostic.RCS1079.severity = warning
|
||||||
|
|
||||||
|
# RCS1057: Add empty line between declarations.
|
||||||
|
dotnet_diagnostic.RCS1057.severity = none
|
||||||
|
|
||||||
|
# IDE0004: Remove Unnecessary Cast
|
||||||
|
dotnet_diagnostic.IDE0004.severity = warning
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
<Authors>Patrik Svensson</Authors>
|
<Authors>Patrik Svensson</Authors>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/spectresystems/spectre.console</RepositoryUrl>
|
<RepositoryUrl>https://github.com/spectresystems/spectre.console</RepositoryUrl>
|
||||||
|
<PackageIcon>small-logo.png</PackageIcon>
|
||||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||||
<PackageProjectUrl>https://github.com/spectresystems/spectre.console</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/spectresystems/spectre.console</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
|
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
root = false
|
|
||||||
[*.cs]
|
|
||||||
|
|
||||||
# Default severity for all analyzer diagnostics
|
|
||||||
dotnet_analyzer_diagnostic.severity = none
|
|
||||||
|
|
||||||
# CS1591: Missing XML comment for publicly visible type or member
|
|
||||||
dotnet_diagnostic.CS1591.severity = none
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Spectre.Console;
|
|
||||||
|
|
||||||
namespace Sample
|
|
||||||
{
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
// Use the static API to write some things to the console.
|
|
||||||
AnsiConsole.Foreground = Color.Chartreuse2;
|
|
||||||
AnsiConsole.Style = Styles.Underline | Styles.Bold;
|
|
||||||
AnsiConsole.WriteLine("Hello World!");
|
|
||||||
AnsiConsole.Reset();
|
|
||||||
AnsiConsole.WriteLine("Capabilities: {0}", AnsiConsole.Capabilities);
|
|
||||||
AnsiConsole.WriteLine($"Width={AnsiConsole.Width}, Height={AnsiConsole.Height}");
|
|
||||||
AnsiConsole.WriteLine("Good bye!");
|
|
||||||
AnsiConsole.WriteLine();
|
|
||||||
|
|
||||||
// We can get the default console via the static API.
|
|
||||||
var console = AnsiConsole.Console;
|
|
||||||
|
|
||||||
// Or you can build it yourself the old fashion way.
|
|
||||||
console = AnsiConsole.Create(
|
|
||||||
new AnsiConsoleSettings()
|
|
||||||
{
|
|
||||||
Ansi = AnsiSupport.Yes,
|
|
||||||
ColorSystem = ColorSystemSupport.Standard,
|
|
||||||
Out = Console.Out,
|
|
||||||
});
|
|
||||||
|
|
||||||
// In this case, we will find the closest colors
|
|
||||||
// and downgrade them to the specified color system.
|
|
||||||
console.Foreground = Color.Chartreuse2;
|
|
||||||
console.Style = Styles.Underline | Styles.Bold;
|
|
||||||
console.WriteLine("Hello World!");
|
|
||||||
console.ResetColors();
|
|
||||||
console.ResetStyle();
|
|
||||||
console.WriteLine("Capabilities: {0}", AnsiConsole.Capabilities);
|
|
||||||
console.WriteLine($"Width={AnsiConsole.Width}, Height={AnsiConsole.Height}");
|
|
||||||
console.WriteLine("Good bye!");
|
|
||||||
console.WriteLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -21,3 +21,6 @@ dotnet_diagnostic.CA1034.severity = none
|
|||||||
|
|
||||||
# CA2000: Dispose objects before losing scope
|
# CA2000: Dispose objects before losing scope
|
||||||
dotnet_diagnostic.CA2000.severity = none
|
dotnet_diagnostic.CA2000.severity = none
|
||||||
|
|
||||||
|
# SA1118: Parameter should not span multiple lines
|
||||||
|
dotnet_diagnostic.SA1118.severity = none
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
using Shouldly;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Tests
|
|
||||||
{
|
|
||||||
public partial class AnsiConsoleTests
|
|
||||||
{
|
|
||||||
[Theory]
|
|
||||||
[InlineData(Styles.Bold, "\u001b[1mHello World[0m")]
|
|
||||||
[InlineData(Styles.Dim, "\u001b[2mHello World[0m")]
|
|
||||||
[InlineData(Styles.Italic, "\u001b[3mHello World[0m")]
|
|
||||||
[InlineData(Styles.Underline, "\u001b[4mHello World[0m")]
|
|
||||||
[InlineData(Styles.Invert, "\u001b[7mHello World[0m")]
|
|
||||||
[InlineData(Styles.Conceal, "\u001b[8mHello World[0m")]
|
|
||||||
[InlineData(Styles.SlowBlink, "\u001b[5mHello World[0m")]
|
|
||||||
[InlineData(Styles.RapidBlink, "\u001b[6mHello World[0m")]
|
|
||||||
[InlineData(Styles.Strikethrough, "\u001b[9mHello World[0m")]
|
|
||||||
public void Should_Write_Style_Correctly(Styles style, string expected)
|
|
||||||
{
|
|
||||||
// Given
|
|
||||||
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
|
|
||||||
fixture.Console.Style = style;
|
|
||||||
|
|
||||||
// When
|
|
||||||
fixture.Console.Write("Hello World");
|
|
||||||
|
|
||||||
// Then
|
|
||||||
fixture.Output.ShouldBe(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(Styles.Bold | Styles.Underline, "\u001b[1;4mHello World[0m")]
|
|
||||||
[InlineData(Styles.Bold | Styles.Underline | Styles.Conceal, "\u001b[1;4;8mHello World[0m")]
|
|
||||||
public void Should_Write_Combined_Styles_Correctly(Styles style, string expected)
|
|
||||||
{
|
|
||||||
// Given
|
|
||||||
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
|
|
||||||
fixture.Console.Style = style;
|
|
||||||
|
|
||||||
// When
|
|
||||||
fixture.Console.Write("Hello World");
|
|
||||||
|
|
||||||
// Then
|
|
||||||
fixture.Output.ShouldBe(expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
src/Spectre.Console.Tests/Extensions/StringExtensions.cs
Normal file
13
src/Spectre.Console.Tests/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests
|
||||||
|
{
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static string NormalizeLineEndings(this string text)
|
||||||
|
{
|
||||||
|
return text?.Replace("\r\n", "\n", StringComparison.OrdinalIgnoreCase)
|
||||||
|
?.Replace("\r", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,16 +11,17 @@ namespace Spectre.Console.Tests
|
|||||||
|
|
||||||
public string Output => _writer.ToString();
|
public string Output => _writer.ToString();
|
||||||
|
|
||||||
public AnsiConsoleFixture(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes)
|
public AnsiConsoleFixture(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
|
||||||
{
|
{
|
||||||
_writer = new StringWriter();
|
_writer = new StringWriter();
|
||||||
|
|
||||||
Console = AnsiConsole.Create(new AnsiConsoleSettings
|
Console = new ConsoleWithWidth(
|
||||||
{
|
AnsiConsole.Create(new AnsiConsoleSettings
|
||||||
Ansi = ansi,
|
{
|
||||||
ColorSystem = (ColorSystemSupport)system,
|
Ansi = ansi,
|
||||||
Out = _writer,
|
ColorSystem = (ColorSystemSupport)system,
|
||||||
});
|
Out = _writer,
|
||||||
|
}), width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
31
src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs
Normal file
31
src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests
|
||||||
|
{
|
||||||
|
public sealed class ConsoleWithWidth : IAnsiConsole
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _console;
|
||||||
|
|
||||||
|
public Capabilities Capabilities => _console.Capabilities;
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height => _console.Height;
|
||||||
|
|
||||||
|
public Encoding Encoding => _console.Encoding;
|
||||||
|
|
||||||
|
public Decoration Decoration { get => _console.Decoration; set => _console.Decoration = value; }
|
||||||
|
public Color Foreground { get => _console.Foreground; set => _console.Foreground = value; }
|
||||||
|
public Color Background { get => _console.Background; set => _console.Background = value; }
|
||||||
|
|
||||||
|
public ConsoleWithWidth(IAnsiConsole console, int width)
|
||||||
|
{
|
||||||
|
_console = console;
|
||||||
|
Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string text)
|
||||||
|
{
|
||||||
|
_console.Write(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/Spectre.Console.Tests/Fixtures/PlainConsole.cs
Normal file
47
src/Spectre.Console.Tests/Fixtures/PlainConsole.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests
|
||||||
|
{
|
||||||
|
public sealed class PlainConsole : IAnsiConsole, IDisposable
|
||||||
|
{
|
||||||
|
public Capabilities Capabilities { get; }
|
||||||
|
public Encoding Encoding { get; }
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
|
||||||
|
public Decoration Decoration { get; set; }
|
||||||
|
public Color Foreground { get; set; }
|
||||||
|
public Color Background { get; set; }
|
||||||
|
|
||||||
|
public StringWriter Writer { get; }
|
||||||
|
public string RawOutput => Writer.ToString();
|
||||||
|
public string Output => Writer.ToString().TrimEnd('\n');
|
||||||
|
public IReadOnlyList<string> Lines => Output.Split(new char[] { '\n' });
|
||||||
|
|
||||||
|
public PlainConsole(
|
||||||
|
int width = 80, int height = 9000, Encoding encoding = null,
|
||||||
|
bool supportsAnsi = true, ColorSystem colorSystem = ColorSystem.Standard,
|
||||||
|
bool legacyConsole = false)
|
||||||
|
{
|
||||||
|
Capabilities = new Capabilities(supportsAnsi, colorSystem, legacyConsole);
|
||||||
|
Encoding = encoding ?? Encoding.UTF8;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Writer = new StringWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string text)
|
||||||
|
{
|
||||||
|
Writer.Write(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||||
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
|
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
|
||||||
<PackageReference Include="xunit" Version="2.4.0" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Spectre.Console.Tests
|
namespace Spectre.Console.Tests.Unit
|
||||||
{
|
{
|
||||||
public partial class AnsiConsoleTests
|
public partial class AnsiConsoleTests
|
||||||
{
|
{
|
||||||
89
src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs
Normal file
89
src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Markup.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public partial class AnsiConsoleTests
|
||||||
|
{
|
||||||
|
[SuppressMessage("Naming", "CA1724:Type names should not match namespaces")]
|
||||||
|
public sealed class Markup
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("[yellow]Hello[/]", "[93mHello[0m")]
|
||||||
|
[InlineData("[yellow]Hello [italic]World[/]![/]", "[93mHello[0m[93m [0m[3;93mWorld[0m[93m![0m")]
|
||||||
|
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Markup(markup);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("[yellow]Hello [[ World[/]", "[93mHello[0m[93m [0m[93m[[0m[93m [0m[93mWorld[0m")]
|
||||||
|
public void Should_Be_Able_To_Escape_Tags(string markup, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Markup(markup);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("[yellow]Hello[", "Encountered malformed markup tag at position 14.")]
|
||||||
|
[InlineData("[yellow]Hello[/", "Encountered malformed markup tag at position 15.")]
|
||||||
|
[InlineData("[yellow]Hello[/foo", "Encountered malformed markup tag at position 15.")]
|
||||||
|
[InlineData("[yellow Hello", "Encountered malformed markup tag at position 13.")]
|
||||||
|
public void Should_Throw_If_Encounters_Malformed_Tag(string markup, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => fixture.Console.Markup(markup));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>()
|
||||||
|
.Message.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Tags_Are_Unbalanced()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => fixture.Console.Markup("[yellow][blue]Hello[/]"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>()
|
||||||
|
.Message.ShouldBe("Unbalanced markup stack. Did you forget to close a tag?");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Encounters_Closing_Tag()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => fixture.Console.Markup("Hello[/]World"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>()
|
||||||
|
.Message.ShouldBe("Encountered closing tag when none was expected near position 5.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs
Normal file
47
src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public partial class AnsiConsoleTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(Decoration.Bold, "\u001b[1mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Dim, "\u001b[2mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Italic, "\u001b[3mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Underline, "\u001b[4mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Invert, "\u001b[7mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Conceal, "\u001b[8mHello World[0m")]
|
||||||
|
[InlineData(Decoration.SlowBlink, "\u001b[5mHello World[0m")]
|
||||||
|
[InlineData(Decoration.RapidBlink, "\u001b[6mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Strikethrough, "\u001b[9mHello World[0m")]
|
||||||
|
public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
|
||||||
|
fixture.Console.Decoration = decoration;
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Write("Hello World");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(Decoration.Bold | Decoration.Underline, "\u001b[1;4mHello World[0m")]
|
||||||
|
[InlineData(Decoration.Bold | Decoration.Underline | Decoration.Conceal, "\u001b[1;4;8mHello World[0m")]
|
||||||
|
public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor);
|
||||||
|
fixture.Console.Decoration = decoration;
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Write("Hello World");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,18 @@ using System.Globalization;
|
|||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Spectre.Console.Tests
|
namespace Spectre.Console.Tests.Unit
|
||||||
{
|
{
|
||||||
public partial class AnsiConsoleTests
|
public partial class AnsiConsoleTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Combine_Style_And_Colors()
|
public void Should_Combine_Decoration_And_Colors()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
||||||
fixture.Console.Foreground = Color.RoyalBlue1;
|
fixture.Console.Foreground = Color.RoyalBlue1;
|
||||||
fixture.Console.Background = Color.NavajoWhite1;
|
fixture.Console.Background = Color.NavajoWhite1;
|
||||||
fixture.Console.Style = Styles.Italic;
|
fixture.Console.Decoration = Decoration.Italic;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
fixture.Console.Write("Hello");
|
fixture.Console.Write("Hello");
|
||||||
@@ -30,7 +30,7 @@ namespace Spectre.Console.Tests
|
|||||||
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
||||||
fixture.Console.Foreground = Color.Default;
|
fixture.Console.Foreground = Color.Default;
|
||||||
fixture.Console.Background = Color.NavajoWhite1;
|
fixture.Console.Background = Color.NavajoWhite1;
|
||||||
fixture.Console.Style = Styles.Italic;
|
fixture.Console.Decoration = Decoration.Italic;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
fixture.Console.Write("Hello");
|
fixture.Console.Write("Hello");
|
||||||
@@ -46,7 +46,7 @@ namespace Spectre.Console.Tests
|
|||||||
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
||||||
fixture.Console.Foreground = Color.RoyalBlue1;
|
fixture.Console.Foreground = Color.RoyalBlue1;
|
||||||
fixture.Console.Background = Color.Default;
|
fixture.Console.Background = Color.Default;
|
||||||
fixture.Console.Style = Styles.Italic;
|
fixture.Console.Decoration = Decoration.Italic;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
fixture.Console.Write("Hello");
|
fixture.Console.Write("Hello");
|
||||||
@@ -56,13 +56,13 @@ namespace Spectre.Console.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Include_Style_If_Set_To_None()
|
public void Should_Not_Include_Decoration_If_Set_To_None()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard);
|
||||||
fixture.Console.Foreground = Color.RoyalBlue1;
|
fixture.Console.Foreground = Color.RoyalBlue1;
|
||||||
fixture.Console.Background = Color.NavajoWhite1;
|
fixture.Console.Background = Color.NavajoWhite1;
|
||||||
fixture.Console.Style = Styles.None;
|
fixture.Console.Decoration = Decoration.None;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
fixture.Console.Write("Hello");
|
fixture.Console.Write("Hello");
|
||||||
@@ -246,6 +246,38 @@ namespace Spectre.Console.Tests
|
|||||||
|
|
||||||
public sealed class WriteLine
|
public sealed class WriteLine
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Reset_Colors_Correctly_After_Line_Break()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Background = ConsoleColor.Red;
|
||||||
|
fixture.Console.WriteLine("Hello");
|
||||||
|
fixture.Console.Background = ConsoleColor.Green;
|
||||||
|
fixture.Console.WriteLine("World");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.NormalizeLineEndings()
|
||||||
|
.ShouldBe("[101mHello[0m\n[102mWorld[0m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Reset_Colors_Correctly_After_Line_Break_In_Text()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new AnsiConsoleFixture(ColorSystem.Standard, AnsiSupport.Yes);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Console.Background = ConsoleColor.Red;
|
||||||
|
fixture.Console.WriteLine("Hello\nWorld");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output.NormalizeLineEndings()
|
||||||
|
.ShouldBe("[101mHello[0m\n[101mWorld[0m\n");
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(AnsiSupport.Yes)]
|
[InlineData(AnsiSupport.Yes)]
|
||||||
[InlineData(AnsiSupport.No)]
|
[InlineData(AnsiSupport.No)]
|
||||||
42
src/Spectre.Console.Tests/Unit/BorderTests.cs
Normal file
42
src/Spectre.Console.Tests/Unit/BorderTests.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Shouldly;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class BorderTests
|
||||||
|
{
|
||||||
|
public sealed class TheGetBorderMethod
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(BorderKind.None, false, typeof(NoBorder))]
|
||||||
|
[InlineData(BorderKind.Ascii, false, typeof(AsciiBorder))]
|
||||||
|
[InlineData(BorderKind.Square, false, typeof(SquareBorder))]
|
||||||
|
[InlineData(BorderKind.Rounded, false, typeof(RoundedBorder))]
|
||||||
|
[InlineData(BorderKind.None, true, typeof(NoBorder))]
|
||||||
|
[InlineData(BorderKind.Ascii, true, typeof(AsciiBorder))]
|
||||||
|
[InlineData(BorderKind.Square, true, typeof(SquareBorder))]
|
||||||
|
[InlineData(BorderKind.Rounded, true, typeof(SquareBorder))]
|
||||||
|
public void Should_Return_Correct_Border_For_Specified_Kind(BorderKind kind, bool safe, Type expected)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Border.GetBorder(kind, safe);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Unknown_Border_Kind_Is_Specified()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Border.GetBorder((BorderKind)int.MaxValue, false));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("Unknown border kind");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
254
src/Spectre.Console.Tests/Unit/ColorTests.cs
Normal file
254
src/Spectre.Console.Tests/Unit/ColorTests.cs
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class ColorTests
|
||||||
|
{
|
||||||
|
public sealed class TheEqualsMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_Color_And_Non_Color_Equal()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = new Color(128, 0, 128);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals("Foo");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_Same_Colors_Equal_By_Component()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = new Color(128, 0, 128);
|
||||||
|
var color2 = new Color(128, 0, 128);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals(color2);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_Same_Known_Colors_Equal()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = Color.Cyan1;
|
||||||
|
var color2 = Color.Cyan1;
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals(color2);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_Known_Color_And_Color_With_Same_Components_Equal()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = Color.Cyan1;
|
||||||
|
var color2 = new Color(0, 255, 255);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals(color2);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Not_Consider_Different_Colors_Equal()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = new Color(128, 0, 128);
|
||||||
|
var color2 = new Color(128, 128, 128);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals(color2);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Shourd_Not_Consider_Black_And_Default_Colors_Equal()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = Color.Default;
|
||||||
|
var color2 = Color.Black;
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = color1.Equals(color2);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheGetHashCodeMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_Same_HashCode_For_Same_Colors()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = new Color(128, 0, 128);
|
||||||
|
var color2 = new Color(128, 0, 128);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var hash1 = color1.GetHashCode();
|
||||||
|
var hash2 = color2.GetHashCode();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
hash1.ShouldBe(hash2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_Different_HashCode_For_Different_Colors()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var color1 = new Color(128, 0, 128);
|
||||||
|
var color2 = new Color(128, 128, 128);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var hash1 = color1.GetHashCode();
|
||||||
|
var hash2 = color2.GetHashCode();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
hash1.ShouldNotBe(hash2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ImplicitConversions
|
||||||
|
{
|
||||||
|
public sealed class Int32ToColor
|
||||||
|
{
|
||||||
|
public static IEnumerable<object[]> Data =>
|
||||||
|
Enumerable.Range(0, 255)
|
||||||
|
.Select(number => new object[] { number });
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Data))]
|
||||||
|
public void Should_Return_Expected_Color(int number)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = (Color)number;
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBe(Color.FromInt32(number));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Integer_Is_Lower_Than_Zero()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => (Color)(-1));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("Color number must be between 0 and 255");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Integer_Is_Higher_Than_255()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => (Color)256);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("Color number must be between 0 and 255");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ConsoleColorToColor
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(ConsoleColor.Black, 0)]
|
||||||
|
[InlineData(ConsoleColor.DarkRed, 1)]
|
||||||
|
[InlineData(ConsoleColor.DarkGreen, 2)]
|
||||||
|
[InlineData(ConsoleColor.DarkYellow, 3)]
|
||||||
|
[InlineData(ConsoleColor.DarkBlue, 4)]
|
||||||
|
[InlineData(ConsoleColor.DarkMagenta, 5)]
|
||||||
|
[InlineData(ConsoleColor.DarkCyan, 6)]
|
||||||
|
[InlineData(ConsoleColor.Gray, 7)]
|
||||||
|
[InlineData(ConsoleColor.DarkGray, 8)]
|
||||||
|
[InlineData(ConsoleColor.Red, 9)]
|
||||||
|
[InlineData(ConsoleColor.Green, 10)]
|
||||||
|
[InlineData(ConsoleColor.Yellow, 11)]
|
||||||
|
[InlineData(ConsoleColor.Blue, 12)]
|
||||||
|
[InlineData(ConsoleColor.Magenta, 13)]
|
||||||
|
[InlineData(ConsoleColor.Cyan, 14)]
|
||||||
|
[InlineData(ConsoleColor.White, 15)]
|
||||||
|
public void Should_Return_Expected_Color(ConsoleColor color, int expected)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = (Color)color;
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBe(Color.FromInt32(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ColorToConsoleColor
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, ConsoleColor.Black)]
|
||||||
|
[InlineData(1, ConsoleColor.DarkRed)]
|
||||||
|
[InlineData(2, ConsoleColor.DarkGreen)]
|
||||||
|
[InlineData(3, ConsoleColor.DarkYellow)]
|
||||||
|
[InlineData(4, ConsoleColor.DarkBlue)]
|
||||||
|
[InlineData(5, ConsoleColor.DarkMagenta)]
|
||||||
|
[InlineData(6, ConsoleColor.DarkCyan)]
|
||||||
|
[InlineData(7, ConsoleColor.Gray)]
|
||||||
|
[InlineData(8, ConsoleColor.DarkGray)]
|
||||||
|
[InlineData(9, ConsoleColor.Red)]
|
||||||
|
[InlineData(10, ConsoleColor.Green)]
|
||||||
|
[InlineData(11, ConsoleColor.Yellow)]
|
||||||
|
[InlineData(12, ConsoleColor.Blue)]
|
||||||
|
[InlineData(13, ConsoleColor.Magenta)]
|
||||||
|
[InlineData(14, ConsoleColor.Cyan)]
|
||||||
|
[InlineData(15, ConsoleColor.White)]
|
||||||
|
public void Should_Return_Expected_ConsoleColor_For_Known_Color(int color, ConsoleColor expected)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = (ConsoleColor)Color.FromInt32(color);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheToStringMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_Color_Name_For_Known_Colors()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var name = Color.Fuchsia.ToString();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
name.ShouldBe("fuchsia");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_Hex_String_For_Unknown_Colors()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var name = new Color(128, 0, 128).ToString();
|
||||||
|
|
||||||
|
// Then
|
||||||
|
name.ShouldBe("#800080 (RGB=128,0,128)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
233
src/Spectre.Console.Tests/Unit/GridTests.cs
Normal file
233
src/Spectre.Console.Tests/Unit/GridTests.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using System;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class GridTests
|
||||||
|
{
|
||||||
|
public sealed class TheAddColumnMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Rows_Are_Not_Empty()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddRow("Hello World!");
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => grid.AddColumn());
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>()
|
||||||
|
.Message.ShouldBe("Cannot add new columns to grid with existing rows.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheAddRowMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Rows_Are_Null()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var grid = new Grid();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => grid.AddRow(null));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<ArgumentNullException>()
|
||||||
|
.ParamName.ShouldBe("columns");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddColumn();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => grid.AddRow("Foo"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("The number of row columns are less than the number of grid columns.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Row_Columns_Are_Greater_Than_Number_Of_Columns()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => grid.AddRow("Foo", "Bar"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("The number of row columns are greater than the number of grid columns.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheAddEmptyRowMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Add_Empty_Row()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumns(2);
|
||||||
|
grid.AddRow("Foo", "Bar");
|
||||||
|
grid.AddEmptyRow();
|
||||||
|
grid.AddRow("Qux", "Corgi");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("Foo Bar ");
|
||||||
|
console.Lines[1].ShouldBe(" ");
|
||||||
|
console.Lines[2].ShouldBe("Qux Corgi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Add_Empty_Row_At_The_End()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumns(2);
|
||||||
|
grid.AddRow("Foo", "Bar");
|
||||||
|
grid.AddEmptyRow();
|
||||||
|
grid.AddRow("Qux", "Corgi");
|
||||||
|
grid.AddEmptyRow();
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(4);
|
||||||
|
console.Lines[0].ShouldBe("Foo Bar ");
|
||||||
|
console.Lines[1].ShouldBe(" ");
|
||||||
|
console.Lines[2].ShouldBe("Qux Corgi");
|
||||||
|
console.Lines[3].ShouldBe(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Grid_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddColumn();
|
||||||
|
grid.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
grid.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(2);
|
||||||
|
console.Lines[0].ShouldBe("Qux Corgi Waldo");
|
||||||
|
console.Lines[1].ShouldBe("Grault Garply Fred ");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Grid_Column_Alignment_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn(new GridColumn { Alignment = Justify.Right });
|
||||||
|
grid.AddColumn(new GridColumn { Alignment = Justify.Center });
|
||||||
|
grid.AddColumn(new GridColumn { Alignment = Justify.Left });
|
||||||
|
grid.AddRow("Foo", "Bar", "Baz");
|
||||||
|
grid.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
grid.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe(" Foo Bar Baz ");
|
||||||
|
console.Lines[1].ShouldBe(" Qux Corgi Waldo");
|
||||||
|
console.Lines[2].ShouldBe("Grault Garply Fred ");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Use_Default_Padding()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumns(3);
|
||||||
|
grid.AddRow("Foo", "Bar", "Baz");
|
||||||
|
grid.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
grid.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("Foo Bar Baz ");
|
||||||
|
console.Lines[1].ShouldBe("Qux Corgi Waldo");
|
||||||
|
console.Lines[2].ShouldBe("Grault Garply Fred ");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Explicit_Grid_Column_Padding_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn(new GridColumn { Padding = new Padding(3, 0) });
|
||||||
|
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0) });
|
||||||
|
grid.AddColumn(new GridColumn { Padding = new Padding(0, 3) });
|
||||||
|
grid.AddRow("Foo", "Bar", "Baz");
|
||||||
|
grid.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
grid.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe(" Foo Bar Baz ");
|
||||||
|
console.Lines[1].ShouldBe(" Qux Corgi Waldo ");
|
||||||
|
console.Lines[2].ShouldBe(" GraultGarplyFred ");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Grid()
|
||||||
|
{
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.AddColumn(new GridColumn { NoWrap = true });
|
||||||
|
grid.AddColumn(new GridColumn { Padding = new Padding(2, 0) });
|
||||||
|
grid.AddRow("[bold]Options[/]", string.Empty);
|
||||||
|
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "Show command line help.");
|
||||||
|
grid.AddRow(" [blue]-c[/], [blue]--configuration[/]", "The configuration to run for.\nThe default for most projects is [green]Debug[/].");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(grid);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(4);
|
||||||
|
console.Lines[0].ShouldBe("Options ");
|
||||||
|
console.Lines[1].ShouldBe(" -h, --help Show command line help. ");
|
||||||
|
console.Lines[2].ShouldBe(" -c, --configuration The configuration to run for. ");
|
||||||
|
console.Lines[3].ShouldBe(" The default for most projects is Debug.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
178
src/Spectre.Console.Tests/Unit/PanelTests.cs
Normal file
178
src/Spectre.Console.Tests/Unit/PanelTests.cs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class PanelTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Text("Hello World")));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌─────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└─────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Padding()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Text("Hello World"))
|
||||||
|
{
|
||||||
|
Padding = new Padding(3, 5),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└───────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Unicode_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Text(" \n💩\n ")));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(5);
|
||||||
|
console.Lines[0].ShouldBe("┌────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ │");
|
||||||
|
console.Lines[2].ShouldBe("│ 💩 │");
|
||||||
|
console.Lines[3].ShouldBe("│ │");
|
||||||
|
console.Lines[4].ShouldBe("└────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Multiple_Lines()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Text("Hello World\nFoo Bar")));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(4);
|
||||||
|
console.Lines[0].ShouldBe("┌─────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("│ Foo Bar │");
|
||||||
|
console.Lines[3].ShouldBe("└─────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Preserve_Explicit_Line_Ending()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var text = new Panel(
|
||||||
|
Text.Markup("I heard [underline on blue]you[/] like 📦\n\n\n\nSo I put a 📦 in a 📦"));
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(text);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(7);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ I heard you like 📦 │");
|
||||||
|
console.Lines[2].ShouldBe("│ │");
|
||||||
|
console.Lines[3].ShouldBe("│ │");
|
||||||
|
console.Lines[4].ShouldBe("│ │");
|
||||||
|
console.Lines[5].ShouldBe("│ So I put a 📦 in a 📦 │");
|
||||||
|
console.Lines[6].ShouldBe("└───────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Expand_Panel_If_Enabled()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Text("Hello World"))
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].Length.ShouldBe(80);
|
||||||
|
console.Lines[0].ShouldBe("┌──────────────────────────────────────────────────────────────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Justify_Child_To_Right()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 25);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(
|
||||||
|
new Panel(new Text("Hello World").RightAligned())
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└───────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Justify_Child_To_Center()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 25);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(
|
||||||
|
new Panel(new Text("Hello World").Centered())
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└───────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_Inside_Panel_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel(new Panel(new Text("Hello World"))));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(5);
|
||||||
|
console.Lines[0].ShouldBe("┌─────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ ┌─────────────┐ │");
|
||||||
|
console.Lines[2].ShouldBe("│ │ Hello World │ │");
|
||||||
|
console.Lines[3].ShouldBe("│ └─────────────┘ │");
|
||||||
|
console.Lines[4].ShouldBe("└─────────────────┘");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
src/Spectre.Console.Tests/Unit/SegmentTests.cs
Normal file
92
src/Spectre.Console.Tests/Unit/SegmentTests.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using Shouldly;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class SegmentTests
|
||||||
|
{
|
||||||
|
public sealed class TheSplitMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Split_Segment_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var style = new Style(Color.Red, Color.Green, Decoration.Bold);
|
||||||
|
var segment = new Segment("Foo Bar", style);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var (first, second) = segment.Split(3);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
first.Text.ShouldBe("Foo");
|
||||||
|
first.Style.ShouldBe(style);
|
||||||
|
second.Text.ShouldBe(" Bar");
|
||||||
|
second.Style.ShouldBe(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheSplitLinesMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Split_Segment()
|
||||||
|
{
|
||||||
|
var lines = Segment.SplitLines(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new Segment("Foo"),
|
||||||
|
new Segment("Bar"),
|
||||||
|
new Segment("\n"),
|
||||||
|
new Segment("Baz"),
|
||||||
|
new Segment("Qux"),
|
||||||
|
new Segment("\n"),
|
||||||
|
new Segment("Corgi"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
lines.Count.ShouldBe(3);
|
||||||
|
|
||||||
|
lines[0].Count.ShouldBe(2);
|
||||||
|
lines[0][0].Text.ShouldBe("Foo");
|
||||||
|
lines[0][1].Text.ShouldBe("Bar");
|
||||||
|
|
||||||
|
lines[1].Count.ShouldBe(2);
|
||||||
|
lines[1][0].Text.ShouldBe("Baz");
|
||||||
|
lines[1][1].Text.ShouldBe("Qux");
|
||||||
|
|
||||||
|
lines[2].Count.ShouldBe(1);
|
||||||
|
lines[2][0].Text.ShouldBe("Corgi");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Split_Segments_With_Linebreak_In_Text()
|
||||||
|
{
|
||||||
|
var lines = Segment.SplitLines(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new Segment("Foo\n"),
|
||||||
|
new Segment("Bar\n"),
|
||||||
|
new Segment("Baz"),
|
||||||
|
new Segment("Qux\n"),
|
||||||
|
new Segment("Corgi"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
lines.Count.ShouldBe(4);
|
||||||
|
|
||||||
|
lines[0].Count.ShouldBe(1);
|
||||||
|
lines[0][0].Text.ShouldBe("Foo");
|
||||||
|
|
||||||
|
lines[1].Count.ShouldBe(1);
|
||||||
|
lines[1][0].Text.ShouldBe("Bar");
|
||||||
|
|
||||||
|
lines[2].Count.ShouldBe(2);
|
||||||
|
lines[2][0].Text.ShouldBe("Baz");
|
||||||
|
lines[2][1].Text.ShouldBe("Qux");
|
||||||
|
|
||||||
|
lines[3].Count.ShouldBe(1);
|
||||||
|
lines[3][0].Text.ShouldBe("Corgi");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
216
src/Spectre.Console.Tests/Unit/StyleTests.cs
Normal file
216
src/Spectre.Console.Tests/Unit/StyleTests.cs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
using System;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class StyleTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Combine_Two_Styles_As_Expected()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic);
|
||||||
|
var other = new Style(Color.Green, Color.Silver, Decoration.Underline);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = first.Combine(other);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.Foreground.ShouldBe(Color.Green);
|
||||||
|
result.Background.ShouldBe(Color.Silver);
|
||||||
|
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Italic | Decoration.Underline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheParseMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Default_Keyword_Should_Return_Default_Style()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse("default");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Foreground.ShouldBe(Color.Default);
|
||||||
|
result.Background.ShouldBe(Color.Default);
|
||||||
|
result.Decoration.ShouldBe(Decoration.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("bold", Decoration.Bold)]
|
||||||
|
[InlineData("b", Decoration.Bold)]
|
||||||
|
[InlineData("dim", Decoration.Dim)]
|
||||||
|
[InlineData("i", Decoration.Italic)]
|
||||||
|
[InlineData("italic", Decoration.Italic)]
|
||||||
|
[InlineData("underline", Decoration.Underline)]
|
||||||
|
[InlineData("u", Decoration.Underline)]
|
||||||
|
[InlineData("invert", Decoration.Invert)]
|
||||||
|
[InlineData("conceal", Decoration.Conceal)]
|
||||||
|
[InlineData("slowblink", Decoration.SlowBlink)]
|
||||||
|
[InlineData("rapidblink", Decoration.RapidBlink)]
|
||||||
|
[InlineData("strikethrough", Decoration.Strikethrough)]
|
||||||
|
[InlineData("s", Decoration.Strikethrough)]
|
||||||
|
public void Should_Parse_Decoration(string text, Decoration decoration)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse(text);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Decoration.ShouldBe(decoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Parse_Text_And_Decoration()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse("bold underline blue on green");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline);
|
||||||
|
result.Foreground.ShouldBe(Color.Blue);
|
||||||
|
result.Background.ShouldBe(Color.Green);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Parse_Background_If_Foreground_Is_Set_To_Default()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse("default on green");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Decoration.ShouldBe(Decoration.None);
|
||||||
|
result.Foreground.ShouldBe(Color.Default);
|
||||||
|
result.Background.ShouldBe(Color.Green);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Foreground_Is_Set_Twice()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse("green yellow"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("A foreground color has already been set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Background_Is_Set_Twice()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse("green on blue yellow"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("A background color has already been set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Color_Name_Could_Not_Be_Found()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse("bold lol"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("Could not find color or style 'lol'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Background_Color_Name_Could_Not_Be_Found()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse("blue on lol"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("Could not find color 'lol'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("#FF0000 on #0000FF")]
|
||||||
|
[InlineData("#F00 on #00F")]
|
||||||
|
public void Should_Parse_Hex_Colors_Correctly(string style)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse(style);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.Foreground.ShouldBe(Color.Red);
|
||||||
|
result.Background.ShouldBe(Color.Blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("#", "Invalid hex color '#'.")]
|
||||||
|
[InlineData("#FF00FF00FF", "Invalid hex color '#FF00FF00FF'.")]
|
||||||
|
[InlineData("#FOO", "Invalid hex color '#FOO'. Could not find any recognizable digits.")]
|
||||||
|
public void Should_Return_Error_If_Hex_Color_Is_Invalid(string style, string expected)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse(style));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Message.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("rgb(255,0,0) on rgb(0,0,255)")]
|
||||||
|
public void Should_Parse_Rgb_Colors_Correctly(string style)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.Parse(style);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.Foreground.ShouldBe(Color.Red);
|
||||||
|
result.Background.ShouldBe(Color.Blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("rgb()", "Invalid RGB color 'rgb()'.")]
|
||||||
|
[InlineData("rgb(", "Invalid RGB color 'rgb('.")]
|
||||||
|
[InlineData("rgb(255)", "Invalid RGB color 'rgb(255)'.")]
|
||||||
|
[InlineData("rgb(255,255)", "Invalid RGB color 'rgb(255,255)'.")]
|
||||||
|
[InlineData("rgb(255,255,255", "Invalid RGB color 'rgb(255,255,255'.")]
|
||||||
|
[InlineData("rgb(A,B,C)", "Invalid RGB color 'rgb(A,B,C)'. Input string was not in a correct format.")]
|
||||||
|
public void Should_Return_Error_If_Rgb_Color_Is_Invalid(string style, string expected)
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Record.Exception(() => Style.Parse(style));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldNotBeNull();
|
||||||
|
result.Message.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheTryParseMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_True_If_Parsing_Succeeded()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.TryParse("bold", out var style);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeTrue();
|
||||||
|
style.ShouldNotBeNull();
|
||||||
|
style.Decoration.ShouldBe(Decoration.Bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Return_False_If_Parsing_Failed()
|
||||||
|
{
|
||||||
|
// Given, When
|
||||||
|
var result = Style.TryParse("lol", out _);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
388
src/Spectre.Console.Tests/Unit/TableTests.cs
Normal file
388
src/Spectre.Console.Tests/Unit/TableTests.cs
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
using System;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class TableTests
|
||||||
|
{
|
||||||
|
public sealed class TheAddColumnMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Column_Is_Null()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var table = new Table();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => table.AddColumn((string)null));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<ArgumentNullException>()
|
||||||
|
.ParamName.ShouldBe("column");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Rows_Are_Not_Empty()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var grid = new Table();
|
||||||
|
grid.AddColumn("Foo");
|
||||||
|
grid.AddRow("Hello World");
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => grid.AddColumn("Bar"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>()
|
||||||
|
.Message.ShouldBe("Cannot add new columns to table with existing rows.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheAddColumnsMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Columns_Are_Null()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var table = new Table();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => table.AddColumns((string[])null));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<ArgumentNullException>()
|
||||||
|
.ParamName.ShouldBe("columns");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheAddRowMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_String_Rows_Are_Null()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var table = new Table();
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => table.AddRow((string[])null));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<ArgumentNullException>()
|
||||||
|
.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_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumn("Hello");
|
||||||
|
table.AddColumn("World");
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => table.AddRow("Foo"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("The number of row columns are less than the number of table columns.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Throw_If_Row_Columns_Are_Greater_Than_Number_Of_Columns()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumn("Hello");
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = Record.Exception(() => table.AddRow("Foo", "Bar"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result.ShouldBeOfType<InvalidOperationException>();
|
||||||
|
result.Message.ShouldBe("The number of row columns are greater than the number of table columns.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TheAddEmptyRowMethod
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddEmptyRow();
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(7);
|
||||||
|
console.Lines[0].ShouldBe("┌────────┬────────┬───────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼───────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ │ │ │");
|
||||||
|
console.Lines[5].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[6].ShouldBe("└────────┴────────┴───────┘");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(6);
|
||||||
|
console.Lines[0].ShouldBe("┌────────┬────────┬───────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼───────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[5].ShouldBe("└────────┴────────┴───────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_Nested_In_Panels_Correctly()
|
||||||
|
{
|
||||||
|
// A simple table
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table() { Border = BorderKind.Rounded };
|
||||||
|
table.AddColumn("Foo");
|
||||||
|
table.AddColumn("Bar");
|
||||||
|
table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right });
|
||||||
|
table.AddRow("Qux\nQuuuuuux", "[blue]Corgi[/]", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// Render a table in some panels.
|
||||||
|
console.Render(new Panel(new Panel(table)
|
||||||
|
{
|
||||||
|
Border = BorderKind.Ascii,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(11);
|
||||||
|
console.Lines[00].ShouldBe("┌───────────────────────────────────┐");
|
||||||
|
console.Lines[01].ShouldBe("│ +-------------------------------+ │");
|
||||||
|
console.Lines[02].ShouldBe("│ | ╭──────────┬────────┬───────╮ | │");
|
||||||
|
console.Lines[03].ShouldBe("│ | │ Foo │ Bar │ Baz │ | │");
|
||||||
|
console.Lines[04].ShouldBe("│ | ├──────────┼────────┼───────┤ | │");
|
||||||
|
console.Lines[05].ShouldBe("│ | │ Qux │ Corgi │ Waldo │ | │");
|
||||||
|
console.Lines[06].ShouldBe("│ | │ Quuuuuux │ │ │ | │");
|
||||||
|
console.Lines[07].ShouldBe("│ | │ Grault │ Garply │ Fred │ | │");
|
||||||
|
console.Lines[08].ShouldBe("│ | ╰──────────┴────────┴───────╯ | │");
|
||||||
|
console.Lines[09].ShouldBe("│ +-------------------------------+ │");
|
||||||
|
console.Lines[10].ShouldBe("└───────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_Column_Justification_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumn(new TableColumn("Foo") { Alignment = Justify.Left });
|
||||||
|
table.AddColumn(new TableColumn("Bar") { Alignment = Justify.Right });
|
||||||
|
table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Center });
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Lorem ipsum dolor sit amet");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(6);
|
||||||
|
console.Lines[0].ShouldBe("┌────────┬────────┬────────────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼────────────────────────────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Grault │ Garply │ Lorem ipsum dolor sit amet │");
|
||||||
|
console.Lines[5].ShouldBe("└────────┴────────┴────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Expand_Table_To_Available_Space_If_Specified()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table() { Expand = true };
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(6);
|
||||||
|
console.Lines[0].Length.ShouldBe(80);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────────────┬───────────────────────────┬──────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├───────────────────────────┼───────────────────────────┼──────────────────────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[5].ShouldBe("└───────────────────────────┴───────────────────────────┴──────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_Ascii_Border_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table { Border = BorderKind.Ascii };
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(6);
|
||||||
|
console.Lines[0].ShouldBe("+-------------------------+");
|
||||||
|
console.Lines[1].ShouldBe("| Foo | Bar | Baz |");
|
||||||
|
console.Lines[2].ShouldBe("|--------+--------+-------|");
|
||||||
|
console.Lines[3].ShouldBe("| Qux | Corgi | Waldo |");
|
||||||
|
console.Lines[4].ShouldBe("| Grault | Garply | Fred |");
|
||||||
|
console.Lines[5].ShouldBe("+-------------------------+");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_Rounded_Border_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table { Border = BorderKind.Rounded };
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(6);
|
||||||
|
console.Lines[0].ShouldBe("╭────────┬────────┬───────╮");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼───────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[5].ShouldBe("╰────────┴────────┴───────╯");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_No_Border_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table { Border = BorderKind.None };
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("Foo Bar Baz ");
|
||||||
|
console.Lines[1].ShouldBe("Qux Corgi Waldo");
|
||||||
|
console.Lines[2].ShouldBe("Grault Garply Fred ");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_Multiple_Rows_In_Cell_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumns("Foo", "Bar", "Baz");
|
||||||
|
table.AddRow("Qux\nQuuux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(7);
|
||||||
|
console.Lines[0].ShouldBe("┌────────┬────────┬───────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼───────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Quuux │ │ │");
|
||||||
|
console.Lines[5].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[6].ShouldBe("└────────┴────────┴───────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_With_Cell_Padding_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumns("Foo", "Bar");
|
||||||
|
table.AddColumn(new TableColumn("Baz") { Padding = new Padding(3, 2) });
|
||||||
|
table.AddRow("Qux\nQuuux", "Corgi", "Waldo");
|
||||||
|
table.AddRow("Grault", "Garply", "Fred");
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(7);
|
||||||
|
console.Lines[0].ShouldBe("┌────────┬────────┬──────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("├────────┼────────┼──────────┤");
|
||||||
|
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||||
|
console.Lines[4].ShouldBe("│ Quuux │ │ │");
|
||||||
|
console.Lines[5].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||||
|
console.Lines[6].ShouldBe("└────────┴────────┴──────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Table_Without_Footer_If_No_Rows_Are_Added()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
var table = new Table();
|
||||||
|
table.AddColumns("Foo", "Bar");
|
||||||
|
table.AddColumn(new TableColumn("Baz") { Padding = new Padding(3, 2) });
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(table);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌─────┬─────┬────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||||
|
console.Lines[2].ShouldBe("└─────┴─────┴────────┘");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/Spectre.Console.Tests/Unit/TextTests.cs
Normal file
81
src/Spectre.Console.Tests/Unit/TextTests.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Shouldly;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class TextTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_The_Longest_Word_As_Minimum_Width()
|
||||||
|
{
|
||||||
|
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
|
||||||
|
|
||||||
|
var result = ((IRenderable)text).Measure(new RenderContext(Encoding.Unicode, false), 80);
|
||||||
|
|
||||||
|
result.Min.ShouldBe(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Consider_The_Longest_Line_As_Maximum_Width()
|
||||||
|
{
|
||||||
|
var text = new Text("Foo Bar Baz\nQux\nLol mobile");
|
||||||
|
|
||||||
|
var result = ((IRenderable)text).Measure(new RenderContext(Encoding.Unicode, false), 80);
|
||||||
|
|
||||||
|
result.Max.ShouldBe(11);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Unstyled_Text_As_Expected()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new PlainConsole(width: 80);
|
||||||
|
var text = new Text("Hello World");
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Render(text);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output
|
||||||
|
.NormalizeLineEndings()
|
||||||
|
.ShouldBe("Hello World");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Hello\n\nWorld\n\n")]
|
||||||
|
[InlineData("Hello\r\n\r\nWorld\r\n\r\n")]
|
||||||
|
public void Should_Write_Line_Breaks(string input)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new PlainConsole(width: 5);
|
||||||
|
var text = new Text(input);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Render(text);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(5, "Hello World", "Hello\nWorld")]
|
||||||
|
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
|
||||||
|
public void Should_Split_Unstyled_Text_To_New_Lines_If_Width_Exceeds_Console_Width(
|
||||||
|
int width, string input, string expected)
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new PlainConsole(width);
|
||||||
|
var text = new Text(input);
|
||||||
|
|
||||||
|
// When
|
||||||
|
fixture.Render(text);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
fixture.Output
|
||||||
|
.NormalizeLineEndings()
|
||||||
|
.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console", "Spectre.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Tests", "Spectre.Console.Tests\Spectre.Console.Tests.csproj", "{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Tests", "Spectre.Console.Tests\Spectre.Console.Tests.csproj", "{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{272E6092-BD31-4EB6-A9FF-F4179F91958F}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20595AD4-8D75-4AF8-B6BC-9C38C160423F}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20595AD4-8D75-4AF8-B6BC-9C38C160423F}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
@@ -17,6 +15,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
stylecop.json = stylecop.json
|
stylecop.json = stylecop.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{F0575243-121F-4DEE-9F6B-246E26DC0844}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Table", "..\examples\Table\Table.csproj", "{94ECCBA8-7EBF-4B53-8379-52EB2327417E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Panel", "..\examples\Panel\Panel.csproj", "{BFF37228-B376-4ADD-9657-4E501F929713}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grid", "..\examples\Grid\Grid.csproj", "{C7FF6FDB-FB59-4517-8669-521C96AB7323}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "..\examples\Colors\Colors.csproj", "{1F51C55C-BA4C-4856-9001-0F7924FFB179}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -51,22 +59,64 @@ Global
|
|||||||
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x64.Build.0 = Release|Any CPU
|
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.ActiveCfg = Release|Any CPU
|
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.Build.0 = Release|Any CPU
|
{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x64.Build.0 = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x86.Build.0 = Debug|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|Any CPU.Build.0 = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x64.ActiveCfg = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x64.Build.0 = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x86.ActiveCfg = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x86.Build.0 = Release|Any CPU
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{94ECCBA8-7EBF-4B53-8379-52EB2327417E} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
{BFF37228-B376-4ADD-9657-4E501F929713} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
|||||||
52
src/Spectre.Console/AnsiConsole.Markup.cs
Normal file
52
src/Spectre.Console/AnsiConsole.Markup.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A console capable of writing ANSI escape sequences.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class AnsiConsole
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void Markup(string format, params object[] args)
|
||||||
|
{
|
||||||
|
Console.Markup(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void Markup(IFormatProvider provider, string format, params object[] args)
|
||||||
|
{
|
||||||
|
Console.Markup(provider, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void MarkupLine(string format, params object[] args)
|
||||||
|
{
|
||||||
|
Console.MarkupLine(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void MarkupLine(IFormatProvider provider, string format, params object[] args)
|
||||||
|
{
|
||||||
|
Console.MarkupLine(provider, format, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/Spectre.Console/AnsiConsole.Rendering.cs
Normal file
19
src/Spectre.Console/AnsiConsole.Rendering.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A console capable of writing ANSI escape sequences.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class AnsiConsole
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the specified object to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="renderable">The object to render.</param>
|
||||||
|
public static void Render(IRenderable renderable)
|
||||||
|
{
|
||||||
|
Console.Render(renderable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,12 +10,14 @@ namespace Spectre.Console
|
|||||||
{
|
{
|
||||||
private static readonly Lazy<IAnsiConsole> _console = new Lazy<IAnsiConsole>(() =>
|
private static readonly Lazy<IAnsiConsole> _console = new Lazy<IAnsiConsole>(() =>
|
||||||
{
|
{
|
||||||
return Create(new AnsiConsoleSettings
|
var console = Create(new AnsiConsoleSettings
|
||||||
{
|
{
|
||||||
Ansi = AnsiSupport.Detect,
|
Ansi = AnsiSupport.Detect,
|
||||||
ColorSystem = ColorSystemSupport.Detect,
|
ColorSystem = ColorSystemSupport.Detect,
|
||||||
Out = System.Console.Out,
|
Out = System.Console.Out,
|
||||||
});
|
});
|
||||||
|
Created = true;
|
||||||
|
return console;
|
||||||
});
|
});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -26,7 +28,9 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the console's capabilities.
|
/// Gets the console's capabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static AnsiConsoleCapabilities Capabilities => Console.Capabilities;
|
public static Capabilities Capabilities => Console.Capabilities;
|
||||||
|
|
||||||
|
internal static bool Created { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the buffer width of the console.
|
/// Gets the buffer width of the console.
|
||||||
@@ -63,12 +67,12 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the style.
|
/// Gets or sets the text decoration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Styles Style
|
public static Decoration Decoration
|
||||||
{
|
{
|
||||||
get => Console.Style;
|
get => Console.Decoration;
|
||||||
set => Console.Style = value;
|
set => Console.Decoration = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -83,7 +87,7 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets colors and styles to the default ones.
|
/// Resets colors and text decorations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
@@ -91,15 +95,15 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the current style back to the default one.
|
/// Resets the current applied text decorations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void ResetStyle()
|
public static void ResetDecoration()
|
||||||
{
|
{
|
||||||
Console.ResetStyle();
|
Console.ResetDecoration();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the foreground and background colors to the default ones.
|
/// Resets the current applied foreground and background colors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void ResetColors()
|
public static void ResetColors()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
namespace Spectre.Console
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents console capabilities.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class AnsiConsoleCapabilities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether or not
|
|
||||||
/// the console supports Ansi.
|
|
||||||
/// </summary>
|
|
||||||
public bool SupportsAnsi { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the color system.
|
|
||||||
/// </summary>
|
|
||||||
public ColorSystem ColorSystem { get; }
|
|
||||||
|
|
||||||
internal AnsiConsoleCapabilities(bool supportsAnsi, ColorSystem colorSystem)
|
|
||||||
{
|
|
||||||
SupportsAnsi = supportsAnsi;
|
|
||||||
ColorSystem = colorSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var supportsAnsi = SupportsAnsi ? "Yes" : "No";
|
|
||||||
var bits = ColorSystem switch
|
|
||||||
{
|
|
||||||
ColorSystem.NoColors => "1 bit",
|
|
||||||
ColorSystem.Legacy => "3 bits",
|
|
||||||
ColorSystem.Standard => "4 bits",
|
|
||||||
ColorSystem.EightBit => "8 bits",
|
|
||||||
ColorSystem.TrueColor => "24 bits",
|
|
||||||
_ => "?"
|
|
||||||
};
|
|
||||||
|
|
||||||
return $"ANSI={supportsAnsi}, Colors={ColorSystem} ({bits})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,7 @@ using System.IO;
|
|||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Settings used by <see cref="ConsoleBuilder"/>
|
/// Settings used when building a <see cref="IAnsiConsole"/>.
|
||||||
/// when building a <see cref="IAnsiConsole"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AnsiConsoleSettings
|
public sealed class AnsiConsoleSettings
|
||||||
{
|
{
|
||||||
@@ -22,6 +21,6 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the out buffer.
|
/// Gets or sets the out buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TextWriter Out { get; set; }
|
public TextWriter? Out { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/Spectre.Console/Capabilities.cs
Normal file
59
src/Spectre.Console/Capabilities.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents console capabilities.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Capabilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether or not
|
||||||
|
/// the console supports Ansi.
|
||||||
|
/// </summary>
|
||||||
|
public bool SupportsAnsi { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the color system.
|
||||||
|
/// </summary>
|
||||||
|
public ColorSystem ColorSystem { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether or not
|
||||||
|
/// this is a legacy console (cmd.exe).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Only relevant when running on Microsoft Windows.
|
||||||
|
/// </remarks>
|
||||||
|
public bool LegacyConsole { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Capabilities"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="supportsAnsi">Whether or not ANSI escape sequences are supported.</param>
|
||||||
|
/// <param name="colorSystem">The color system that is supported.</param>
|
||||||
|
/// <param name="legacyConsole">Whether or not this is a legacy console.</param>
|
||||||
|
public Capabilities(bool supportsAnsi, ColorSystem colorSystem, bool legacyConsole)
|
||||||
|
{
|
||||||
|
SupportsAnsi = supportsAnsi;
|
||||||
|
ColorSystem = colorSystem;
|
||||||
|
LegacyConsole = legacyConsole;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var supportsAnsi = SupportsAnsi ? "Yes" : "No";
|
||||||
|
var legacyConsole = LegacyConsole ? "Legacy" : "Modern";
|
||||||
|
var bits = ColorSystem switch
|
||||||
|
{
|
||||||
|
ColorSystem.NoColors => "1 bit",
|
||||||
|
ColorSystem.Legacy => "3 bits",
|
||||||
|
ColorSystem.Standard => "4 bits",
|
||||||
|
ColorSystem.EightBit => "8 bits",
|
||||||
|
ColorSystem.TrueColor => "24 bits",
|
||||||
|
_ => "?",
|
||||||
|
};
|
||||||
|
|
||||||
|
return $"ANSI={supportsAnsi}, Colors={ColorSystem}, Kind={legacyConsole} ({bits})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Globalization;
|
||||||
using Spectre.Console.Internal;
|
using Spectre.Console.Internal;
|
||||||
|
|
||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
@@ -17,7 +17,7 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
static Color()
|
static Color()
|
||||||
{
|
{
|
||||||
Default = new Color(0, "default", 0, 0, 0, true);
|
Default = new Color(0, 0, 0, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,11 +35,6 @@ namespace Spectre.Console
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public byte B { get; }
|
public byte B { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name of the color, if any.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of the color, if any.
|
/// Gets the number of the color, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,7 +57,6 @@ namespace Spectre.Console
|
|||||||
G = green;
|
G = green;
|
||||||
B = blue;
|
B = blue;
|
||||||
IsDefault = false;
|
IsDefault = false;
|
||||||
Name = null;
|
|
||||||
Number = null;
|
Number = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +74,7 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
return obj is Color color && Equals(color);
|
return obj is Color color && Equals(color);
|
||||||
}
|
}
|
||||||
@@ -88,7 +82,8 @@ namespace Spectre.Console
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Equals(Color other)
|
public bool Equals(Color other)
|
||||||
{
|
{
|
||||||
return Number == other.Number || (R == other.R && G == other.G && B == other.B);
|
return (IsDefault && other.IsDefault) ||
|
||||||
|
(IsDefault == other.IsDefault && R == other.R && G == other.G && B == other.B);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -113,6 +108,15 @@ namespace Spectre.Console
|
|||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convers a <see cref="int"/> to a <see cref="Color"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">The color number to convert.</param>
|
||||||
|
public static implicit operator Color(int number)
|
||||||
|
{
|
||||||
|
return FromInt32(number);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
|
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -123,18 +127,12 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convers a color number into a <see cref="Color"/>.
|
/// Convers a <see cref="Color"/> to a <see cref="ConsoleColor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="number">The color number.</param>
|
/// <param name="color">The console color to convert.</param>
|
||||||
/// <returns>The color representing the specified color number.</returns>
|
public static implicit operator ConsoleColor(Color color)
|
||||||
public static Color FromColorNumber(int number)
|
|
||||||
{
|
{
|
||||||
if (number < 0 || number > 255)
|
return ToConsoleColor(color);
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Color number must be between 0 and 255");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ColorPalette.EightBit.First(x => x.Number == number);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -179,6 +177,16 @@ namespace Spectre.Console
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convers a color number into a <see cref="Color"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">The color number.</param>
|
||||||
|
/// <returns>The color representing the specified color number.</returns>
|
||||||
|
public static Color FromInt32(int number)
|
||||||
|
{
|
||||||
|
return ColorTable.GetColor(number);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
|
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -207,5 +215,20 @@ namespace Spectre.Console
|
|||||||
_ => Default,
|
_ => Default,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Number != null)
|
||||||
|
{
|
||||||
|
var name = ColorTable.GetName(Number.Value);
|
||||||
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2} (RGB={0},{1},{2})", R, G, B);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/Spectre.Console/ConsoleExtensions.Markup.cs
Normal file
59
src/Spectre.Console/ConsoleExtensions.Markup.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Spectre.Console.Internal;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for <see cref="IAnsiConsole"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class ConsoleExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="console">The console to write to.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void Markup(this IAnsiConsole console, string format, params object[] args)
|
||||||
|
{
|
||||||
|
Markup(console, CultureInfo.CurrentCulture, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="console">The console to write to.</param>
|
||||||
|
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void Markup(this IAnsiConsole console, IFormatProvider provider, string format, params object[] args)
|
||||||
|
{
|
||||||
|
console.Render(MarkupParser.Parse(string.Format(provider, format, args)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="console">The console to write to.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void MarkupLine(this IAnsiConsole console, string format, params object[] args)
|
||||||
|
{
|
||||||
|
MarkupLine(console, CultureInfo.CurrentCulture, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="console">The console to write to.</param>
|
||||||
|
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||||
|
/// <param name="format">A composite format string.</param>
|
||||||
|
/// <param name="args">An array of objects to write.</param>
|
||||||
|
public static void MarkupLine(this IAnsiConsole console, IFormatProvider provider, string format, params object[] args)
|
||||||
|
{
|
||||||
|
Markup(console, provider, format, args);
|
||||||
|
console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Spectre.Console/ConsoleExtensions.Rendering.cs
Normal file
54
src/Spectre.Console/ConsoleExtensions.Rendering.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using Spectre.Console.Internal;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for <see cref="IAnsiConsole"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class ConsoleExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the specified object to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="console">The console to render to.</param>
|
||||||
|
/// <param name="renderable">The object to render.</param>
|
||||||
|
public static void Render(this IAnsiConsole console, IRenderable renderable)
|
||||||
|
{
|
||||||
|
if (console is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(console));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderable is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(renderable));
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new RenderContext(console.Encoding, console.Capabilities.LegacyConsole);
|
||||||
|
|
||||||
|
using (console.PushStyle(Style.Plain))
|
||||||
|
{
|
||||||
|
var current = Style.Plain;
|
||||||
|
foreach (var segment in renderable.Render(options, console.Width))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(segment.Text))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!segment.Style.Equals(current))
|
||||||
|
{
|
||||||
|
console.Foreground = segment.Style.Foreground;
|
||||||
|
console.Background = segment.Style.Background;
|
||||||
|
console.Decoration = segment.Style.Decoration;
|
||||||
|
current = segment.Style;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.Write(segment.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace Spectre.Console
|
|||||||
public static partial class ConsoleExtensions
|
public static partial class ConsoleExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets both colors and style for the console.
|
/// Resets colors and text decorations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="console">The console to reset.</param>
|
/// <param name="console">The console to reset.</param>
|
||||||
public static void Reset(this IAnsiConsole console)
|
public static void Reset(this IAnsiConsole console)
|
||||||
@@ -19,25 +19,25 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.ResetColors();
|
console.ResetColors();
|
||||||
console.ResetStyle();
|
console.ResetDecoration();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the current style back to the default one.
|
/// Resets the current applied text decorations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="console">The console to reset the style for.</param>
|
/// <param name="console">The console to reset the text decorations for.</param>
|
||||||
public static void ResetStyle(this IAnsiConsole console)
|
public static void ResetDecoration(this IAnsiConsole console)
|
||||||
{
|
{
|
||||||
if (console is null)
|
if (console is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(console));
|
throw new ArgumentNullException(nameof(console));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.Style = Styles.None;
|
console.Decoration = Decoration.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the foreground and background colors to the default ones.
|
/// Resets the current applied foreground and background colors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="console">The console to reset colors for.</param>
|
/// <param name="console">The console to reset colors for.</param>
|
||||||
public static void ResetColors(this IAnsiConsole console)
|
public static void ResetColors(this IAnsiConsole console)
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a style.
|
/// Represents text decoration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Support for different styles is up to the terminal.
|
/// Support for text decorations is up to the terminal.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum Styles
|
[SuppressMessage("Naming", "CA1714:Flags enums should have plural names")]
|
||||||
|
public enum Decoration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No style.
|
/// No text decoration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -8,22 +10,27 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the console's capabilities.
|
/// Gets the console's capabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AnsiConsoleCapabilities Capabilities { get; }
|
Capabilities Capabilities { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the buffer width of the console.
|
/// Gets the buffer width of the console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Width { get; }
|
int Width { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the buffer height of the console.
|
/// Gets the buffer height of the console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Height { get; }
|
int Height { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the current style.
|
/// Gets the console output encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Styles Style { get; set; }
|
Encoding Encoding { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current text decoration.
|
||||||
|
/// </summary>
|
||||||
|
Decoration Decoration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the current foreground.
|
/// Gets or sets the current foreground.
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace Spectre.Console.Internal
|
|||||||
public static string GetAnsi(
|
public static string GetAnsi(
|
||||||
ColorSystem system,
|
ColorSystem system,
|
||||||
string text,
|
string text,
|
||||||
Styles style,
|
Decoration decoration,
|
||||||
Color foreground,
|
Color foreground,
|
||||||
Color background)
|
Color background)
|
||||||
{
|
{
|
||||||
var codes = AnsiStyleBuilder.GetAnsiCodes(style);
|
var codes = AnsiDecorationBuilder.GetAnsiCodes(decoration);
|
||||||
|
|
||||||
// Got foreground?
|
// Got foreground?
|
||||||
if (foreground != Color.Default)
|
if (foreground != Color.Default)
|
||||||
|
|||||||
@@ -2,52 +2,52 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Spectre.Console.Internal
|
namespace Spectre.Console.Internal
|
||||||
{
|
{
|
||||||
internal static class AnsiStyleBuilder
|
internal static class AnsiDecorationBuilder
|
||||||
{
|
{
|
||||||
// TODO: Rewrite this to not yield
|
// TODO: Rewrite this to not yield
|
||||||
public static IEnumerable<byte> GetAnsiCodes(Styles style)
|
public static IEnumerable<byte> GetAnsiCodes(Decoration decoration)
|
||||||
{
|
{
|
||||||
if ((style & Styles.Bold) != 0)
|
if ((decoration & Decoration.Bold) != 0)
|
||||||
{
|
{
|
||||||
yield return 1;
|
yield return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Dim) != 0)
|
if ((decoration & Decoration.Dim) != 0)
|
||||||
{
|
{
|
||||||
yield return 2;
|
yield return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Italic) != 0)
|
if ((decoration & Decoration.Italic) != 0)
|
||||||
{
|
{
|
||||||
yield return 3;
|
yield return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Underline) != 0)
|
if ((decoration & Decoration.Underline) != 0)
|
||||||
{
|
{
|
||||||
yield return 4;
|
yield return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.SlowBlink) != 0)
|
if ((decoration & Decoration.SlowBlink) != 0)
|
||||||
{
|
{
|
||||||
yield return 5;
|
yield return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.RapidBlink) != 0)
|
if ((decoration & Decoration.RapidBlink) != 0)
|
||||||
{
|
{
|
||||||
yield return 6;
|
yield return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Invert) != 0)
|
if ((decoration & Decoration.Invert) != 0)
|
||||||
{
|
{
|
||||||
yield return 7;
|
yield return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Conceal) != 0)
|
if ((decoration & Decoration.Conceal) != 0)
|
||||||
{
|
{
|
||||||
yield return 8;
|
yield return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((style & Styles.Strikethrough) != 0)
|
if ((decoration & Decoration.Strikethrough) != 0)
|
||||||
{
|
{
|
||||||
yield return 9;
|
yield return 9;
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Spectre.Console.Internal
|
|||||||
{
|
{
|
||||||
internal static class AnsiDetector
|
internal static class AnsiDetector
|
||||||
{
|
{
|
||||||
private static readonly Regex[] Regexes = new[]
|
private static readonly Regex[] _regexes = new[]
|
||||||
{
|
{
|
||||||
new Regex("^xterm"), // xterm, PuTTY, Mintty
|
new Regex("^xterm"), // xterm, PuTTY, Mintty
|
||||||
new Regex("^rxvt"), // RXVT
|
new Regex("^rxvt"), // RXVT
|
||||||
@@ -32,12 +32,12 @@ namespace Spectre.Console.Internal
|
|||||||
new Regex("bvterm"), // Bitvise SSH Client
|
new Regex("bvterm"), // Bitvise SSH Client
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool SupportsAnsi(bool upgrade)
|
public static (bool SupportsAnsi, bool LegacyConsole) Detect(bool upgrade)
|
||||||
{
|
{
|
||||||
// Github action doesn't setup a correct PTY but supports ANSI.
|
// Github action doesn't setup a correct PTY but supports ANSI.
|
||||||
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTION")))
|
if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTION")))
|
||||||
{
|
{
|
||||||
return true;
|
return (true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running on Windows?
|
// Running on Windows?
|
||||||
@@ -47,23 +47,24 @@ namespace Spectre.Console.Internal
|
|||||||
var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
|
var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
|
||||||
if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return true;
|
return (true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Windows.SupportsAnsi(upgrade);
|
var supportsAnsi = Windows.SupportsAnsi(upgrade, out var legacyConsole);
|
||||||
|
return (supportsAnsi, legacyConsole);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the terminal is of type ANSI/VT100/xterm compatible.
|
// Check if the terminal is of type ANSI/VT100/xterm compatible.
|
||||||
var term = Environment.GetEnvironmentVariable("TERM");
|
var term = Environment.GetEnvironmentVariable("TERM");
|
||||||
if (!string.IsNullOrWhiteSpace(term))
|
if (!string.IsNullOrWhiteSpace(term))
|
||||||
{
|
{
|
||||||
if (Regexes.Any(regex => regex.IsMatch(term)))
|
if (_regexes.Any(regex => regex.IsMatch(term)))
|
||||||
{
|
{
|
||||||
return true;
|
return (true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return (false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Design", "CA1060:Move pinvokes to native methods class")]
|
[SuppressMessage("Design", "CA1060:Move pinvokes to native methods class")]
|
||||||
@@ -71,8 +72,10 @@ namespace Spectre.Console.Internal
|
|||||||
{
|
{
|
||||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
||||||
private const int STD_OUTPUT_HANDLE = -11;
|
private const int STD_OUTPUT_HANDLE = -11;
|
||||||
|
|
||||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
||||||
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
|
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
|
||||||
|
|
||||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")]
|
||||||
private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
|
private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
|
||||||
|
|
||||||
@@ -89,12 +92,14 @@ namespace Spectre.Console.Internal
|
|||||||
public static extern uint GetLastError();
|
public static extern uint GetLastError();
|
||||||
|
|
||||||
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
||||||
public static bool SupportsAnsi(bool upgrade)
|
public static bool SupportsAnsi(bool upgrade, out bool isLegacy)
|
||||||
{
|
{
|
||||||
|
isLegacy = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var @out = GetStdHandle(STD_OUTPUT_HANDLE);
|
var @out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (!GetConsoleMode(@out, out uint mode))
|
if (!GetConsoleMode(@out, out var mode))
|
||||||
{
|
{
|
||||||
// Could not get console mode.
|
// Could not get console mode.
|
||||||
return false;
|
return false;
|
||||||
@@ -102,6 +107,8 @@ namespace Spectre.Console.Internal
|
|||||||
|
|
||||||
if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
|
if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
|
||||||
{
|
{
|
||||||
|
isLegacy = true;
|
||||||
|
|
||||||
if (!upgrade)
|
if (!upgrade)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
namespace Spectre.Console.Internal
|
||||||
{
|
{
|
||||||
@@ -8,8 +9,9 @@ namespace Spectre.Console.Internal
|
|||||||
private readonly TextWriter _out;
|
private readonly TextWriter _out;
|
||||||
private readonly ColorSystem _system;
|
private readonly ColorSystem _system;
|
||||||
|
|
||||||
public AnsiConsoleCapabilities Capabilities { get; }
|
public Capabilities Capabilities { get; }
|
||||||
public Styles Style { get; set; }
|
public Encoding Encoding { get; }
|
||||||
|
public Decoration Decoration { get; set; }
|
||||||
public Color Foreground { get; set; }
|
public Color Foreground { get; set; }
|
||||||
public Color Background { get; set; }
|
public Color Background { get; set; }
|
||||||
|
|
||||||
@@ -39,29 +41,16 @@ namespace Spectre.Console.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnsiConsoleRenderer(TextWriter @out, ColorSystem system)
|
public AnsiConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole)
|
||||||
{
|
{
|
||||||
_out = @out ?? throw new ArgumentNullException(nameof(@out));
|
_out = @out ?? throw new ArgumentNullException(nameof(@out));
|
||||||
_system = system;
|
_system = system;
|
||||||
|
|
||||||
Capabilities = new AnsiConsoleCapabilities(true, system);
|
Capabilities = new Capabilities(true, system, legacyConsole);
|
||||||
|
Encoding = @out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8;
|
||||||
Foreground = Color.Default;
|
Foreground = Color.Default;
|
||||||
Background = Color.Default;
|
Background = Color.Default;
|
||||||
Style = Styles.None;
|
Decoration = Decoration.None;
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset(bool colors, bool styles)
|
|
||||||
{
|
|
||||||
if (colors)
|
|
||||||
{
|
|
||||||
Foreground = Color.Default;
|
|
||||||
Background = Color.Default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styles)
|
|
||||||
{
|
|
||||||
Style = Styles.None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(string text)
|
public void Write(string text)
|
||||||
@@ -71,12 +60,19 @@ namespace Spectre.Console.Internal
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_out.Write(AnsiBuilder.GetAnsi(
|
var parts = text.NormalizeLineEndings().Split(new[] { '\n' });
|
||||||
_system,
|
foreach (var (_, _, last, part) in parts.Enumerate())
|
||||||
text,
|
{
|
||||||
Style,
|
if (!string.IsNullOrEmpty(part))
|
||||||
Foreground,
|
{
|
||||||
Background));
|
_out.Write(AnsiBuilder.GetAnsi(_system, part, Decoration, Foreground, Background));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
{
|
||||||
|
_out.Write(Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
294
src/Spectre.Console/Internal/Colors/ColorPalette.Generated.cs
Normal file
294
src/Spectre.Console/Internal/Colors/ColorPalette.Generated.cs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated 2020-08-03 15:17
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static partial class ColorPalette
|
||||||
|
{
|
||||||
|
private static List<Color> GenerateLegacyPalette()
|
||||||
|
{
|
||||||
|
return new List<Color>
|
||||||
|
{
|
||||||
|
Color.Black,
|
||||||
|
Color.Maroon,
|
||||||
|
Color.Green,
|
||||||
|
Color.Olive,
|
||||||
|
Color.Navy,
|
||||||
|
Color.Purple,
|
||||||
|
Color.Teal,
|
||||||
|
Color.Silver,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Color> GenerateStandardPalette(IReadOnlyList<Color> legacy)
|
||||||
|
{
|
||||||
|
return new List<Color>(legacy)
|
||||||
|
{
|
||||||
|
Color.Grey,
|
||||||
|
Color.Red,
|
||||||
|
Color.Lime,
|
||||||
|
Color.Yellow,
|
||||||
|
Color.Blue,
|
||||||
|
Color.Fuchsia,
|
||||||
|
Color.Aqua,
|
||||||
|
Color.White,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Color> GenerateEightBitPalette(IReadOnlyList<Color> standard)
|
||||||
|
{
|
||||||
|
return new List<Color>(standard)
|
||||||
|
{
|
||||||
|
Color.Grey0,
|
||||||
|
Color.NavyBlue,
|
||||||
|
Color.DarkBlue,
|
||||||
|
Color.Blue3,
|
||||||
|
Color.Blue3_1,
|
||||||
|
Color.Blue1,
|
||||||
|
Color.DarkGreen,
|
||||||
|
Color.DeepSkyBlue4,
|
||||||
|
Color.DeepSkyBlue4_1,
|
||||||
|
Color.DeepSkyBlue4_2,
|
||||||
|
Color.DodgerBlue3,
|
||||||
|
Color.DodgerBlue2,
|
||||||
|
Color.Green4,
|
||||||
|
Color.SpringGreen4,
|
||||||
|
Color.Turquoise4,
|
||||||
|
Color.DeepSkyBlue3,
|
||||||
|
Color.DeepSkyBlue3_1,
|
||||||
|
Color.DodgerBlue1,
|
||||||
|
Color.Green3,
|
||||||
|
Color.SpringGreen3,
|
||||||
|
Color.DarkCyan,
|
||||||
|
Color.LightSeaGreen,
|
||||||
|
Color.DeepSkyBlue2,
|
||||||
|
Color.DeepSkyBlue1,
|
||||||
|
Color.Green3_1,
|
||||||
|
Color.SpringGreen3_1,
|
||||||
|
Color.SpringGreen2,
|
||||||
|
Color.Cyan3,
|
||||||
|
Color.DarkTurquoise,
|
||||||
|
Color.Turquoise2,
|
||||||
|
Color.Green1,
|
||||||
|
Color.SpringGreen2_1,
|
||||||
|
Color.SpringGreen1,
|
||||||
|
Color.MediumSpringGreen,
|
||||||
|
Color.Cyan2,
|
||||||
|
Color.Cyan1,
|
||||||
|
Color.DarkRed,
|
||||||
|
Color.DeepPink4,
|
||||||
|
Color.Purple4,
|
||||||
|
Color.Purple4_1,
|
||||||
|
Color.Purple3,
|
||||||
|
Color.BlueViolet,
|
||||||
|
Color.Orange4,
|
||||||
|
Color.Grey37,
|
||||||
|
Color.MediumPurple4,
|
||||||
|
Color.SlateBlue3,
|
||||||
|
Color.SlateBlue3_1,
|
||||||
|
Color.RoyalBlue1,
|
||||||
|
Color.Chartreuse4,
|
||||||
|
Color.DarkSeaGreen4,
|
||||||
|
Color.PaleTurquoise4,
|
||||||
|
Color.SteelBlue,
|
||||||
|
Color.SteelBlue3,
|
||||||
|
Color.CornflowerBlue,
|
||||||
|
Color.Chartreuse3,
|
||||||
|
Color.DarkSeaGreen4_1,
|
||||||
|
Color.CadetBlue,
|
||||||
|
Color.CadetBlue_1,
|
||||||
|
Color.SkyBlue3,
|
||||||
|
Color.SteelBlue1,
|
||||||
|
Color.Chartreuse3_1,
|
||||||
|
Color.PaleGreen3,
|
||||||
|
Color.SeaGreen3,
|
||||||
|
Color.Aquamarine3,
|
||||||
|
Color.MediumTurquoise,
|
||||||
|
Color.SteelBlue1_1,
|
||||||
|
Color.Chartreuse2,
|
||||||
|
Color.SeaGreen2,
|
||||||
|
Color.SeaGreen1,
|
||||||
|
Color.SeaGreen1_1,
|
||||||
|
Color.Aquamarine1,
|
||||||
|
Color.DarkSlateGray2,
|
||||||
|
Color.DarkRed_1,
|
||||||
|
Color.DeepPink4_1,
|
||||||
|
Color.DarkMagenta,
|
||||||
|
Color.DarkMagenta_1,
|
||||||
|
Color.DarkViolet,
|
||||||
|
Color.Purple_1,
|
||||||
|
Color.Orange4_1,
|
||||||
|
Color.LightPink4,
|
||||||
|
Color.Plum4,
|
||||||
|
Color.MediumPurple3,
|
||||||
|
Color.MediumPurple3_1,
|
||||||
|
Color.SlateBlue1,
|
||||||
|
Color.Yellow4,
|
||||||
|
Color.Wheat4,
|
||||||
|
Color.Grey53,
|
||||||
|
Color.LightSlateGrey,
|
||||||
|
Color.MediumPurple,
|
||||||
|
Color.LightSlateBlue,
|
||||||
|
Color.Yellow4_1,
|
||||||
|
Color.DarkOliveGreen3,
|
||||||
|
Color.DarkSeaGreen,
|
||||||
|
Color.LightSkyBlue3,
|
||||||
|
Color.LightSkyBlue3_1,
|
||||||
|
Color.SkyBlue2,
|
||||||
|
Color.Chartreuse2_1,
|
||||||
|
Color.DarkOliveGreen3_1,
|
||||||
|
Color.PaleGreen3_1,
|
||||||
|
Color.DarkSeaGreen3,
|
||||||
|
Color.DarkSlateGray3,
|
||||||
|
Color.SkyBlue1,
|
||||||
|
Color.Chartreuse1,
|
||||||
|
Color.LightGreen,
|
||||||
|
Color.LightGreen_1,
|
||||||
|
Color.PaleGreen1,
|
||||||
|
Color.Aquamarine1_1,
|
||||||
|
Color.DarkSlateGray1,
|
||||||
|
Color.Red3,
|
||||||
|
Color.DeepPink4_2,
|
||||||
|
Color.MediumVioletRed,
|
||||||
|
Color.Magenta3,
|
||||||
|
Color.DarkViolet_1,
|
||||||
|
Color.Purple_2,
|
||||||
|
Color.DarkOrange3,
|
||||||
|
Color.IndianRed,
|
||||||
|
Color.HotPink3,
|
||||||
|
Color.MediumOrchid3,
|
||||||
|
Color.MediumOrchid,
|
||||||
|
Color.MediumPurple2,
|
||||||
|
Color.DarkGoldenrod,
|
||||||
|
Color.LightSalmon3,
|
||||||
|
Color.RosyBrown,
|
||||||
|
Color.Grey63,
|
||||||
|
Color.MediumPurple2_1,
|
||||||
|
Color.MediumPurple1,
|
||||||
|
Color.Gold3,
|
||||||
|
Color.DarkKhaki,
|
||||||
|
Color.NavajoWhite3,
|
||||||
|
Color.Grey69,
|
||||||
|
Color.LightSteelBlue3,
|
||||||
|
Color.LightSteelBlue,
|
||||||
|
Color.Yellow3,
|
||||||
|
Color.DarkOliveGreen3_2,
|
||||||
|
Color.DarkSeaGreen3_1,
|
||||||
|
Color.DarkSeaGreen2,
|
||||||
|
Color.LightCyan3,
|
||||||
|
Color.LightSkyBlue1,
|
||||||
|
Color.GreenYellow,
|
||||||
|
Color.DarkOliveGreen2,
|
||||||
|
Color.PaleGreen1_1,
|
||||||
|
Color.DarkSeaGreen2_1,
|
||||||
|
Color.DarkSeaGreen1,
|
||||||
|
Color.PaleTurquoise1,
|
||||||
|
Color.Red3_1,
|
||||||
|
Color.DeepPink3,
|
||||||
|
Color.DeepPink3_1,
|
||||||
|
Color.Magenta3_1,
|
||||||
|
Color.Magenta3_2,
|
||||||
|
Color.Magenta2,
|
||||||
|
Color.DarkOrange3_1,
|
||||||
|
Color.IndianRed_1,
|
||||||
|
Color.HotPink3_1,
|
||||||
|
Color.HotPink2,
|
||||||
|
Color.Orchid,
|
||||||
|
Color.MediumOrchid1,
|
||||||
|
Color.Orange3,
|
||||||
|
Color.LightSalmon3_1,
|
||||||
|
Color.LightPink3,
|
||||||
|
Color.Pink3,
|
||||||
|
Color.Plum3,
|
||||||
|
Color.Violet,
|
||||||
|
Color.Gold3_1,
|
||||||
|
Color.LightGoldenrod3,
|
||||||
|
Color.Tan,
|
||||||
|
Color.MistyRose3,
|
||||||
|
Color.Thistle3,
|
||||||
|
Color.Plum2,
|
||||||
|
Color.Yellow3_1,
|
||||||
|
Color.Khaki3,
|
||||||
|
Color.LightGoldenrod2,
|
||||||
|
Color.LightYellow3,
|
||||||
|
Color.Grey84,
|
||||||
|
Color.LightSteelBlue1,
|
||||||
|
Color.Yellow2,
|
||||||
|
Color.DarkOliveGreen1,
|
||||||
|
Color.DarkOliveGreen1_1,
|
||||||
|
Color.DarkSeaGreen1_1,
|
||||||
|
Color.Honeydew2,
|
||||||
|
Color.LightCyan1,
|
||||||
|
Color.Red1,
|
||||||
|
Color.DeepPink2,
|
||||||
|
Color.DeepPink1,
|
||||||
|
Color.DeepPink1_1,
|
||||||
|
Color.Magenta2_1,
|
||||||
|
Color.Magenta1,
|
||||||
|
Color.OrangeRed1,
|
||||||
|
Color.IndianRed1,
|
||||||
|
Color.IndianRed1_1,
|
||||||
|
Color.HotPink,
|
||||||
|
Color.HotPink_1,
|
||||||
|
Color.MediumOrchid1_1,
|
||||||
|
Color.DarkOrange,
|
||||||
|
Color.Salmon1,
|
||||||
|
Color.LightCoral,
|
||||||
|
Color.PaleVioletRed1,
|
||||||
|
Color.Orchid2,
|
||||||
|
Color.Orchid1,
|
||||||
|
Color.Orange1,
|
||||||
|
Color.SandyBrown,
|
||||||
|
Color.LightSalmon1,
|
||||||
|
Color.LightPink1,
|
||||||
|
Color.Pink1,
|
||||||
|
Color.Plum1,
|
||||||
|
Color.Gold1,
|
||||||
|
Color.LightGoldenrod2_1,
|
||||||
|
Color.LightGoldenrod2_2,
|
||||||
|
Color.NavajoWhite1,
|
||||||
|
Color.MistyRose1,
|
||||||
|
Color.Thistle1,
|
||||||
|
Color.Yellow1,
|
||||||
|
Color.LightGoldenrod1,
|
||||||
|
Color.Khaki1,
|
||||||
|
Color.Wheat1,
|
||||||
|
Color.Cornsilk1,
|
||||||
|
Color.Grey100,
|
||||||
|
Color.Grey3,
|
||||||
|
Color.Grey7,
|
||||||
|
Color.Grey11,
|
||||||
|
Color.Grey15,
|
||||||
|
Color.Grey19,
|
||||||
|
Color.Grey23,
|
||||||
|
Color.Grey27,
|
||||||
|
Color.Grey30,
|
||||||
|
Color.Grey35,
|
||||||
|
Color.Grey39,
|
||||||
|
Color.Grey42,
|
||||||
|
Color.Grey46,
|
||||||
|
Color.Grey50,
|
||||||
|
Color.Grey54,
|
||||||
|
Color.Grey58,
|
||||||
|
Color.Grey62,
|
||||||
|
Color.Grey66,
|
||||||
|
Color.Grey70,
|
||||||
|
Color.Grey74,
|
||||||
|
Color.Grey78,
|
||||||
|
Color.Grey82,
|
||||||
|
Color.Grey85,
|
||||||
|
Color.Grey89,
|
||||||
|
Color.Grey93,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Spectre.Console.Internal
|
namespace Spectre.Console.Internal
|
||||||
{
|
{
|
||||||
internal static class ColorPalette
|
internal static partial class ColorPalette
|
||||||
{
|
{
|
||||||
public static IReadOnlyList<Color> Legacy { get; }
|
public static IReadOnlyList<Color> Legacy { get; }
|
||||||
public static IReadOnlyList<Color> Standard { get; }
|
public static IReadOnlyList<Color> Standard { get; }
|
||||||
@@ -12,92 +12,15 @@ namespace Spectre.Console.Internal
|
|||||||
|
|
||||||
static ColorPalette()
|
static ColorPalette()
|
||||||
{
|
{
|
||||||
Legacy = new List<Color>
|
Legacy = GenerateLegacyPalette();
|
||||||
{
|
Standard = GenerateStandardPalette(Legacy);
|
||||||
Color.Black, Color.Maroon, Color.Green, Color.Olive,
|
EightBit = GenerateEightBitPalette(Standard);
|
||||||
Color.Navy, Color.Purple, Color.Teal, Color.Silver,
|
|
||||||
};
|
|
||||||
|
|
||||||
Standard = new List<Color>(Legacy)
|
|
||||||
{
|
|
||||||
Color.Grey, Color.Red, Color.Lime, Color.Yellow,
|
|
||||||
Color.Blue, Color.Fuchsia, Color.Aqua, Color.White,
|
|
||||||
};
|
|
||||||
|
|
||||||
EightBit = new List<Color>(Standard)
|
|
||||||
{
|
|
||||||
Color.Grey0, Color.NavyBlue, Color.DarkBlue, Color.Blue3,
|
|
||||||
Color.Blue3_1, Color.Blue1, Color.DarkGreen, Color.DeepSkyBlue4,
|
|
||||||
Color.DeepSkyBlue4_1, Color.DeepSkyBlue4_2, Color.DodgerBlue3, Color.DodgerBlue2,
|
|
||||||
Color.Green4, Color.SpringGreen4, Color.Turquoise4, Color.DeepSkyBlue3,
|
|
||||||
Color.DeepSkyBlue3_1, Color.DodgerBlue1, Color.Green3, Color.SpringGreen3,
|
|
||||||
Color.DarkCyan, Color.LightSeaGreen, Color.DeepSkyBlue2, Color.DeepSkyBlue1,
|
|
||||||
Color.Green3_1, Color.SpringGreen3_1, Color.SpringGreen2, Color.Cyan3,
|
|
||||||
Color.DarkTurquoise, Color.Turquoise2, Color.Green1, Color.SpringGreen2_1,
|
|
||||||
Color.SpringGreen1, Color.MediumSpringGreen, Color.Cyan2, Color.Cyan1,
|
|
||||||
Color.DarkRed, Color.DeepPink4, Color.Purple4, Color.Purple4_1,
|
|
||||||
Color.Purple3, Color.BlueViolet, Color.Orange4, Color.Grey37,
|
|
||||||
Color.MediumPurple4, Color.SlateBlue3, Color.SlateBlue3_1, Color.RoyalBlue1,
|
|
||||||
Color.Chartreuse4, Color.DarkSeaGreen4, Color.PaleTurquoise4, Color.SteelBlue,
|
|
||||||
Color.SteelBlue3, Color.CornflowerBlue, Color.Chartreuse3, Color.DarkSeaGreen4_1,
|
|
||||||
Color.CadetBlue, Color.CadetBlue_1, Color.SkyBlue3, Color.SteelBlue1,
|
|
||||||
Color.Chartreuse3_1, Color.PaleGreen3, Color.SeaGreen3, Color.Aquamarine3,
|
|
||||||
Color.MediumTurquoise, Color.SteelBlue1_1, Color.Chartreuse2, Color.SeaGreen2,
|
|
||||||
Color.SeaGreen1, Color.SeaGreen1_1, Color.Aquamarine1, Color.DarkSlateGray2,
|
|
||||||
Color.DarkRed_1, Color.DeepPink4_1, Color.DarkMagenta, Color.DarkMagenta_1,
|
|
||||||
Color.DarkViolet, Color.Purple_1, Color.Orange4_1, Color.LightPink4,
|
|
||||||
Color.Plum4, Color.MediumPurple3, Color.MediumPurple3_1, Color.SlateBlue1,
|
|
||||||
Color.Yellow4, Color.Wheat4, Color.Grey53, Color.LightSlateGrey,
|
|
||||||
Color.MediumPurple, Color.LightSlateBlue, Color.Yellow4_1, Color.DarkOliveGreen3,
|
|
||||||
Color.DarkSeaGreen, Color.LightSkyBlue3, Color.LightSkyBlue3_1, Color.SkyBlue2,
|
|
||||||
Color.Chartreuse2_1, Color.DarkOliveGreen3_1, Color.PaleGreen3_1, Color.DarkSeaGreen3,
|
|
||||||
Color.DarkSlateGray3, Color.SkyBlue1, Color.Chartreuse1, Color.LightGreen,
|
|
||||||
Color.LightGreen_1, Color.PaleGreen1, Color.Aquamarine1_1, Color.DarkSlateGray1,
|
|
||||||
Color.Red3, Color.DeepPink4_2, Color.MediumVioletRed, Color.Magenta3,
|
|
||||||
Color.DarkViolet_1, Color.Purple_2, Color.DarkOrange3, Color.IndianRed,
|
|
||||||
Color.HotPink3, Color.MediumOrchid3, Color.MediumOrchid, Color.MediumPurple2,
|
|
||||||
Color.DarkGoldenrod, Color.LightSalmon3, Color.RosyBrown, Color.Grey63,
|
|
||||||
Color.MediumPurple2_1, Color.MediumPurple1, Color.Gold3, Color.DarkKhaki,
|
|
||||||
Color.NavajoWhite3, Color.Grey69, Color.LightSteelBlue3, Color.LightSteelBlue,
|
|
||||||
Color.Yellow3, Color.DarkOliveGreen3_2, Color.DarkSeaGreen3_1, Color.DarkSeaGreen2,
|
|
||||||
Color.LightCyan3, Color.LightSkyBlue1, Color.GreenYellow, Color.DarkOliveGreen2,
|
|
||||||
Color.PaleGreen1_1, Color.DarkSeaGreen2_1, Color.DarkSeaGreen1, Color.PaleTurquoise1,
|
|
||||||
Color.Red3_1, Color.DeepPink3, Color.DeepPink3_1, Color.Magenta3_1,
|
|
||||||
Color.Magenta3_2, Color.Magenta2, Color.DarkOrange3_1, Color.IndianRed_1,
|
|
||||||
Color.HotPink3_1, Color.HotPink2, Color.Orchid, Color.MediumOrchid1,
|
|
||||||
Color.Orange3, Color.LightSalmon3_1, Color.LightPink3, Color.Pink3,
|
|
||||||
Color.Plum3, Color.Violet, Color.Gold3_1, Color.LightGoldenrod3,
|
|
||||||
Color.Tan, Color.MistyRose3, Color.Thistle3, Color.Plum2,
|
|
||||||
Color.Yellow3_1, Color.Khaki3, Color.LightGoldenrod2, Color.LightYellow3,
|
|
||||||
Color.Grey84, Color.LightSteelBlue1, Color.Yellow2, Color.DarkOliveGreen1,
|
|
||||||
Color.DarkOliveGreen1_1, Color.DarkSeaGreen1_1, Color.Honeydew2, Color.LightCyan1,
|
|
||||||
Color.Red1, Color.DeepPink2, Color.DeepPink1, Color.DeepPink1_1,
|
|
||||||
Color.Magenta2_1, Color.Magenta1, Color.OrangeRed1, Color.IndianRed1,
|
|
||||||
Color.IndianRed1_1, Color.HotPink, Color.HotPink_1, Color.MediumOrchid1_1,
|
|
||||||
Color.DarkOrange, Color.Salmon1, Color.LightCoral, Color.PaleVioletRed1,
|
|
||||||
Color.Orchid2, Color.Orchid1, Color.Orange1, Color.SandyBrown,
|
|
||||||
Color.LightSalmon1, Color.LightPink1, Color.Pink1, Color.Plum1,
|
|
||||||
Color.Gold1, Color.LightGoldenrod2_1, Color.LightGoldenrod2_2, Color.NavajoWhite1,
|
|
||||||
Color.MistyRose1, Color.Thistle1, Color.Yellow1, Color.LightGoldenrod1,
|
|
||||||
Color.Khaki1, Color.Wheat1, Color.Cornsilk1, Color.Grey100,
|
|
||||||
Color.Grey3, Color.Grey7, Color.Grey11, Color.Grey15,
|
|
||||||
Color.Grey19, Color.Grey23, Color.Grey27, Color.Grey30,
|
|
||||||
Color.Grey35, Color.Grey39, Color.Grey42, Color.Grey46,
|
|
||||||
Color.Grey50, Color.Grey54, Color.Grey58, Color.Grey62,
|
|
||||||
Color.Grey66, Color.Grey70, Color.Grey74, Color.Grey78,
|
|
||||||
Color.Grey82, Color.Grey85, Color.Grey89, Color.Grey93,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Color ExactOrClosest(ColorSystem system, Color color)
|
internal static Color ExactOrClosest(ColorSystem system, Color color)
|
||||||
{
|
{
|
||||||
var exact = Exact(system, color);
|
var exact = Exact(system, color);
|
||||||
if (exact != null)
|
return exact ?? Closest(system, color);
|
||||||
{
|
|
||||||
return exact.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Closest(system, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Color? Exact(ColorSystem system, Color color)
|
private static Color? Exact(ColorSystem system, Color color)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Spectre.Console.Internal
|
|||||||
{
|
{
|
||||||
if (supportsAnsi)
|
if (supportsAnsi)
|
||||||
{
|
{
|
||||||
var regex = new Regex("^Microsoft Windows (?'major'[0-9]*).(?'minor'[0-9]*).(?'build'[0-9]*)$");
|
var regex = new Regex("^Microsoft Windows (?'major'[0-9]*).(?'minor'[0-9]*).(?'build'[0-9]*)\\s*$");
|
||||||
var match = regex.Match(RuntimeInformation.OSDescription);
|
var match = regex.Match(RuntimeInformation.OSDescription);
|
||||||
if (match.Success && int.TryParse(match.Groups["major"].Value, out var major))
|
if (match.Success && int.TryParse(match.Groups["major"].Value, out var major))
|
||||||
{
|
{
|
||||||
|
|||||||
281
src/Spectre.Console/Internal/Colors/ColorTable.Generated.cs
Normal file
281
src/Spectre.Console/Internal/Colors/ColorTable.Generated.cs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated 2020-08-03 15:17
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static partial class ColorTable
|
||||||
|
{
|
||||||
|
private static Dictionary<string, int> GenerateTable()
|
||||||
|
{
|
||||||
|
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "black", 0 },
|
||||||
|
{ "maroon", 1 },
|
||||||
|
{ "green", 2 },
|
||||||
|
{ "olive", 3 },
|
||||||
|
{ "navy", 4 },
|
||||||
|
{ "purple", 5 },
|
||||||
|
{ "teal", 6 },
|
||||||
|
{ "silver", 7 },
|
||||||
|
{ "grey", 8 },
|
||||||
|
{ "red", 9 },
|
||||||
|
{ "lime", 10 },
|
||||||
|
{ "yellow", 11 },
|
||||||
|
{ "blue", 12 },
|
||||||
|
{ "fuchsia", 13 },
|
||||||
|
{ "aqua", 14 },
|
||||||
|
{ "white", 15 },
|
||||||
|
{ "grey0", 16 },
|
||||||
|
{ "navyblue", 17 },
|
||||||
|
{ "darkblue", 18 },
|
||||||
|
{ "blue3", 19 },
|
||||||
|
{ "blue3_1", 20 },
|
||||||
|
{ "blue1", 21 },
|
||||||
|
{ "darkgreen", 22 },
|
||||||
|
{ "deepskyblue4", 23 },
|
||||||
|
{ "deepskyblue4_1", 24 },
|
||||||
|
{ "deepskyblue4_2", 25 },
|
||||||
|
{ "dodgerblue3", 26 },
|
||||||
|
{ "dodgerblue2", 27 },
|
||||||
|
{ "green4", 28 },
|
||||||
|
{ "springgreen4", 29 },
|
||||||
|
{ "turquoise4", 30 },
|
||||||
|
{ "deepskyblue3", 31 },
|
||||||
|
{ "deepskyblue3_1", 32 },
|
||||||
|
{ "dodgerblue1", 33 },
|
||||||
|
{ "green3", 34 },
|
||||||
|
{ "springgreen3", 35 },
|
||||||
|
{ "darkcyan", 36 },
|
||||||
|
{ "lightseagreen", 37 },
|
||||||
|
{ "deepskyblue2", 38 },
|
||||||
|
{ "deepskyblue1", 39 },
|
||||||
|
{ "green3_1", 40 },
|
||||||
|
{ "springgreen3_1", 41 },
|
||||||
|
{ "springgreen2", 42 },
|
||||||
|
{ "cyan3", 43 },
|
||||||
|
{ "darkturquoise", 44 },
|
||||||
|
{ "turquoise2", 45 },
|
||||||
|
{ "green1", 46 },
|
||||||
|
{ "springgreen2_1", 47 },
|
||||||
|
{ "springgreen1", 48 },
|
||||||
|
{ "mediumspringgreen", 49 },
|
||||||
|
{ "cyan2", 50 },
|
||||||
|
{ "cyan1", 51 },
|
||||||
|
{ "darkred", 52 },
|
||||||
|
{ "deeppink4", 53 },
|
||||||
|
{ "purple4", 54 },
|
||||||
|
{ "purple4_1", 55 },
|
||||||
|
{ "purple3", 56 },
|
||||||
|
{ "blueviolet", 57 },
|
||||||
|
{ "orange4", 58 },
|
||||||
|
{ "grey37", 59 },
|
||||||
|
{ "mediumpurple4", 60 },
|
||||||
|
{ "slateblue3", 61 },
|
||||||
|
{ "slateblue3_1", 62 },
|
||||||
|
{ "royalblue1", 63 },
|
||||||
|
{ "chartreuse4", 64 },
|
||||||
|
{ "darkseagreen4", 65 },
|
||||||
|
{ "paleturquoise4", 66 },
|
||||||
|
{ "steelblue", 67 },
|
||||||
|
{ "steelblue3", 68 },
|
||||||
|
{ "cornflowerblue", 69 },
|
||||||
|
{ "chartreuse3", 70 },
|
||||||
|
{ "darkseagreen4_1", 71 },
|
||||||
|
{ "cadetblue", 72 },
|
||||||
|
{ "cadetblue_1", 73 },
|
||||||
|
{ "skyblue3", 74 },
|
||||||
|
{ "steelblue1", 75 },
|
||||||
|
{ "chartreuse3_1", 76 },
|
||||||
|
{ "palegreen3", 77 },
|
||||||
|
{ "seagreen3", 78 },
|
||||||
|
{ "aquamarine3", 79 },
|
||||||
|
{ "mediumturquoise", 80 },
|
||||||
|
{ "steelblue1_1", 81 },
|
||||||
|
{ "chartreuse2", 82 },
|
||||||
|
{ "seagreen2", 83 },
|
||||||
|
{ "seagreen1", 84 },
|
||||||
|
{ "seagreen1_1", 85 },
|
||||||
|
{ "aquamarine1", 86 },
|
||||||
|
{ "darkslategray2", 87 },
|
||||||
|
{ "darkred_1", 88 },
|
||||||
|
{ "deeppink4_1", 89 },
|
||||||
|
{ "darkmagenta", 90 },
|
||||||
|
{ "darkmagenta_1", 91 },
|
||||||
|
{ "darkviolet", 92 },
|
||||||
|
{ "purple_1", 93 },
|
||||||
|
{ "orange4_1", 94 },
|
||||||
|
{ "lightpink4", 95 },
|
||||||
|
{ "plum4", 96 },
|
||||||
|
{ "mediumpurple3", 97 },
|
||||||
|
{ "mediumpurple3_1", 98 },
|
||||||
|
{ "slateblue1", 99 },
|
||||||
|
{ "yellow4", 100 },
|
||||||
|
{ "wheat4", 101 },
|
||||||
|
{ "grey53", 102 },
|
||||||
|
{ "lightslategrey", 103 },
|
||||||
|
{ "mediumpurple", 104 },
|
||||||
|
{ "lightslateblue", 105 },
|
||||||
|
{ "yellow4_1", 106 },
|
||||||
|
{ "darkolivegreen3", 107 },
|
||||||
|
{ "darkseagreen", 108 },
|
||||||
|
{ "lightskyblue3", 109 },
|
||||||
|
{ "lightskyblue3_1", 110 },
|
||||||
|
{ "skyblue2", 111 },
|
||||||
|
{ "chartreuse2_1", 112 },
|
||||||
|
{ "darkolivegreen3_1", 113 },
|
||||||
|
{ "palegreen3_1", 114 },
|
||||||
|
{ "darkseagreen3", 115 },
|
||||||
|
{ "darkslategray3", 116 },
|
||||||
|
{ "skyblue1", 117 },
|
||||||
|
{ "chartreuse1", 118 },
|
||||||
|
{ "lightgreen", 119 },
|
||||||
|
{ "lightgreen_1", 120 },
|
||||||
|
{ "palegreen1", 121 },
|
||||||
|
{ "aquamarine1_1", 122 },
|
||||||
|
{ "darkslategray1", 123 },
|
||||||
|
{ "red3", 124 },
|
||||||
|
{ "deeppink4_2", 125 },
|
||||||
|
{ "mediumvioletred", 126 },
|
||||||
|
{ "magenta3", 127 },
|
||||||
|
{ "darkviolet_1", 128 },
|
||||||
|
{ "purple_2", 129 },
|
||||||
|
{ "darkorange3", 130 },
|
||||||
|
{ "indianred", 131 },
|
||||||
|
{ "hotpink3", 132 },
|
||||||
|
{ "mediumorchid3", 133 },
|
||||||
|
{ "mediumorchid", 134 },
|
||||||
|
{ "mediumpurple2", 135 },
|
||||||
|
{ "darkgoldenrod", 136 },
|
||||||
|
{ "lightsalmon3", 137 },
|
||||||
|
{ "rosybrown", 138 },
|
||||||
|
{ "grey63", 139 },
|
||||||
|
{ "mediumpurple2_1", 140 },
|
||||||
|
{ "mediumpurple1", 141 },
|
||||||
|
{ "gold3", 142 },
|
||||||
|
{ "darkkhaki", 143 },
|
||||||
|
{ "navajowhite3", 144 },
|
||||||
|
{ "grey69", 145 },
|
||||||
|
{ "lightsteelblue3", 146 },
|
||||||
|
{ "lightsteelblue", 147 },
|
||||||
|
{ "yellow3", 148 },
|
||||||
|
{ "darkolivegreen3_2", 149 },
|
||||||
|
{ "darkseagreen3_1", 150 },
|
||||||
|
{ "darkseagreen2", 151 },
|
||||||
|
{ "lightcyan3", 152 },
|
||||||
|
{ "lightskyblue1", 153 },
|
||||||
|
{ "greenyellow", 154 },
|
||||||
|
{ "darkolivegreen2", 155 },
|
||||||
|
{ "palegreen1_1", 156 },
|
||||||
|
{ "darkseagreen2_1", 157 },
|
||||||
|
{ "darkseagreen1", 158 },
|
||||||
|
{ "paleturquoise1", 159 },
|
||||||
|
{ "red3_1", 160 },
|
||||||
|
{ "deeppink3", 161 },
|
||||||
|
{ "deeppink3_1", 162 },
|
||||||
|
{ "magenta3_1", 163 },
|
||||||
|
{ "magenta3_2", 164 },
|
||||||
|
{ "magenta2", 165 },
|
||||||
|
{ "darkorange3_1", 166 },
|
||||||
|
{ "indianred_1", 167 },
|
||||||
|
{ "hotpink3_1", 168 },
|
||||||
|
{ "hotpink2", 169 },
|
||||||
|
{ "orchid", 170 },
|
||||||
|
{ "mediumorchid1", 171 },
|
||||||
|
{ "orange3", 172 },
|
||||||
|
{ "lightsalmon3_1", 173 },
|
||||||
|
{ "lightpink3", 174 },
|
||||||
|
{ "pink3", 175 },
|
||||||
|
{ "plum3", 176 },
|
||||||
|
{ "violet", 177 },
|
||||||
|
{ "gold3_1", 178 },
|
||||||
|
{ "lightgoldenrod3", 179 },
|
||||||
|
{ "tan", 180 },
|
||||||
|
{ "mistyrose3", 181 },
|
||||||
|
{ "thistle3", 182 },
|
||||||
|
{ "plum2", 183 },
|
||||||
|
{ "yellow3_1", 184 },
|
||||||
|
{ "khaki3", 185 },
|
||||||
|
{ "lightgoldenrod2", 186 },
|
||||||
|
{ "lightyellow3", 187 },
|
||||||
|
{ "grey84", 188 },
|
||||||
|
{ "lightsteelblue1", 189 },
|
||||||
|
{ "yellow2", 190 },
|
||||||
|
{ "darkolivegreen1", 191 },
|
||||||
|
{ "darkolivegreen1_1", 192 },
|
||||||
|
{ "darkseagreen1_1", 193 },
|
||||||
|
{ "honeydew2", 194 },
|
||||||
|
{ "lightcyan1", 195 },
|
||||||
|
{ "red1", 196 },
|
||||||
|
{ "deeppink2", 197 },
|
||||||
|
{ "deeppink1", 198 },
|
||||||
|
{ "deeppink1_1", 199 },
|
||||||
|
{ "magenta2_1", 200 },
|
||||||
|
{ "magenta1", 201 },
|
||||||
|
{ "orangered1", 202 },
|
||||||
|
{ "indianred1", 203 },
|
||||||
|
{ "indianred1_1", 204 },
|
||||||
|
{ "hotpink", 205 },
|
||||||
|
{ "hotpink_1", 206 },
|
||||||
|
{ "mediumorchid1_1", 207 },
|
||||||
|
{ "darkorange", 208 },
|
||||||
|
{ "salmon1", 209 },
|
||||||
|
{ "lightcoral", 210 },
|
||||||
|
{ "palevioletred1", 211 },
|
||||||
|
{ "orchid2", 212 },
|
||||||
|
{ "orchid1", 213 },
|
||||||
|
{ "orange1", 214 },
|
||||||
|
{ "sandybrown", 215 },
|
||||||
|
{ "lightsalmon1", 216 },
|
||||||
|
{ "lightpink1", 217 },
|
||||||
|
{ "pink1", 218 },
|
||||||
|
{ "plum1", 219 },
|
||||||
|
{ "gold1", 220 },
|
||||||
|
{ "lightgoldenrod2_1", 221 },
|
||||||
|
{ "lightgoldenrod2_2", 222 },
|
||||||
|
{ "navajowhite1", 223 },
|
||||||
|
{ "mistyrose1", 224 },
|
||||||
|
{ "thistle1", 225 },
|
||||||
|
{ "yellow1", 226 },
|
||||||
|
{ "lightgoldenrod1", 227 },
|
||||||
|
{ "khaki1", 228 },
|
||||||
|
{ "wheat1", 229 },
|
||||||
|
{ "cornsilk1", 230 },
|
||||||
|
{ "grey100", 231 },
|
||||||
|
{ "grey3", 232 },
|
||||||
|
{ "grey7", 233 },
|
||||||
|
{ "grey11", 234 },
|
||||||
|
{ "grey15", 235 },
|
||||||
|
{ "grey19", 236 },
|
||||||
|
{ "grey23", 237 },
|
||||||
|
{ "grey27", 238 },
|
||||||
|
{ "grey30", 239 },
|
||||||
|
{ "grey35", 240 },
|
||||||
|
{ "grey39", 241 },
|
||||||
|
{ "grey42", 242 },
|
||||||
|
{ "grey46", 243 },
|
||||||
|
{ "grey50", 244 },
|
||||||
|
{ "grey54", 245 },
|
||||||
|
{ "grey58", 246 },
|
||||||
|
{ "grey62", 247 },
|
||||||
|
{ "grey66", 248 },
|
||||||
|
{ "grey70", 249 },
|
||||||
|
{ "grey74", 250 },
|
||||||
|
{ "grey78", 251 },
|
||||||
|
{ "grey82", 252 },
|
||||||
|
{ "grey85", 253 },
|
||||||
|
{ "grey89", 254 },
|
||||||
|
{ "grey93", 255 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Spectre.Console/Internal/Colors/ColorTable.cs
Normal file
54
src/Spectre.Console/Internal/Colors/ColorTable.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static partial class ColorTable
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, string> _nameLookup;
|
||||||
|
private static readonly Dictionary<string, int> _numberLookup;
|
||||||
|
|
||||||
|
[SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline")]
|
||||||
|
static ColorTable()
|
||||||
|
{
|
||||||
|
_numberLookup = GenerateTable();
|
||||||
|
_nameLookup = new Dictionary<int, string>();
|
||||||
|
foreach (var pair in _numberLookup)
|
||||||
|
{
|
||||||
|
_nameLookup.Add(pair.Value, pair.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Color GetColor(int number)
|
||||||
|
{
|
||||||
|
if (number < 0 || number > 255)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Color number must be between 0 and 255");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColorPalette.EightBit[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Color? GetColor(string name)
|
||||||
|
{
|
||||||
|
if (!_numberLookup.TryGetValue(name, out var number))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number > ColorPalette.EightBit.Count - 1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColorPalette.EightBit[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetName(int number)
|
||||||
|
{
|
||||||
|
_nameLookup.TryGetValue(number, out var name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
internal sealed class Composer : IRenderable
|
|
||||||
{
|
|
||||||
private readonly BlockElement _root;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => _root.Length;
|
|
||||||
|
|
||||||
public Composer()
|
|
||||||
{
|
|
||||||
_root = new BlockElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Composer New()
|
|
||||||
{
|
|
||||||
return new Composer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Composer Text(string text)
|
|
||||||
{
|
|
||||||
_root.Append(new TextElement(text));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Composer Foreground(Color color, Action<Composer> action)
|
|
||||||
{
|
|
||||||
if (action is null)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = new Composer();
|
|
||||||
action(content);
|
|
||||||
_root.Append(new ForegroundElement(color, content));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Composer Background(Color color, Action<Composer> action)
|
|
||||||
{
|
|
||||||
if (action is null)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = new Composer();
|
|
||||||
action(content);
|
|
||||||
_root.Append(new BackgroundElement(color, content));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Composer Style(Styles style, Action<Composer> action)
|
|
||||||
{
|
|
||||||
if (action is null)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = new Composer();
|
|
||||||
action(content);
|
|
||||||
_root.Append(new StyleElement(style, content));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
_root.Render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class BackgroundElement : IRenderable
|
|
||||||
{
|
|
||||||
private readonly Color _color;
|
|
||||||
private readonly IRenderable _element;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => _element.Length;
|
|
||||||
|
|
||||||
public BackgroundElement(Color color, IRenderable element)
|
|
||||||
{
|
|
||||||
_color = color;
|
|
||||||
_element = element ?? throw new ArgumentNullException(nameof(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
if (renderer is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (renderer.PushColor(_color, foreground: false))
|
|
||||||
{
|
|
||||||
_element.Render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class BlockElement : IRenderable
|
|
||||||
{
|
|
||||||
private readonly List<IRenderable> _elements;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length { get; private set; }
|
|
||||||
|
|
||||||
public IReadOnlyList<IRenderable> Elements => _elements;
|
|
||||||
|
|
||||||
public BlockElement()
|
|
||||||
{
|
|
||||||
_elements = new List<IRenderable>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockElement Append(IRenderable element)
|
|
||||||
{
|
|
||||||
if (element != null)
|
|
||||||
{
|
|
||||||
_elements.Add(element);
|
|
||||||
Length += element.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
foreach (var element in _elements)
|
|
||||||
{
|
|
||||||
element.Render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class ForegroundElement : IRenderable
|
|
||||||
{
|
|
||||||
private readonly Color _color;
|
|
||||||
private readonly IRenderable _element;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => _element.Length;
|
|
||||||
|
|
||||||
public ForegroundElement(Color color, IRenderable element)
|
|
||||||
{
|
|
||||||
_color = color;
|
|
||||||
_element = element ?? throw new ArgumentNullException(nameof(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
if (renderer is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (renderer.PushColor(_color, foreground: true))
|
|
||||||
{
|
|
||||||
_element.Render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class LineBreakElement : IRenderable
|
|
||||||
{
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => 0;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
renderer.Write(Environment.NewLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class StyleElement : IRenderable
|
|
||||||
{
|
|
||||||
private readonly Styles _style;
|
|
||||||
private readonly IRenderable _element;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => _element.Length;
|
|
||||||
|
|
||||||
public StyleElement(Styles style, IRenderable element)
|
|
||||||
{
|
|
||||||
_style = style;
|
|
||||||
_element = element ?? throw new ArgumentNullException(nameof(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
if (renderer is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (renderer.PushStyle(_style))
|
|
||||||
{
|
|
||||||
_element.Render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")]
|
|
||||||
internal sealed class TextElement : IRenderable
|
|
||||||
{
|
|
||||||
private readonly string _text;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int Length => _text.Length;
|
|
||||||
|
|
||||||
public TextElement(string text)
|
|
||||||
{
|
|
||||||
_text = text ?? throw new System.ArgumentNullException(nameof(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Render(IAnsiConsole renderer)
|
|
||||||
{
|
|
||||||
renderer.Write(_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Spectre.Console.Internal
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents something that can be rendered to a console.
|
|
||||||
/// </summary>
|
|
||||||
internal interface IRenderable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the length of the element.
|
|
||||||
/// </summary>
|
|
||||||
int Length { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Renders the element using the specified renderer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="console">The renderer to use.</param>
|
|
||||||
void Render(IAnsiConsole console);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Spectre.Console.Internal;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Spectre.Console
|
namespace Spectre.Console.Internal
|
||||||
{
|
{
|
||||||
internal static class ConsoleBuilder
|
internal static class ConsoleBuilder
|
||||||
{
|
{
|
||||||
@@ -14,9 +14,41 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
var buffer = settings.Out ?? System.Console.Out;
|
var buffer = settings.Out ?? System.Console.Out;
|
||||||
|
|
||||||
var supportsAnsi = settings.Ansi == AnsiSupport.Detect
|
var supportsAnsi = settings.Ansi == AnsiSupport.Yes;
|
||||||
? AnsiDetector.SupportsAnsi(true)
|
var legacyConsole = false;
|
||||||
: settings.Ansi == AnsiSupport.Yes;
|
|
||||||
|
if (settings.Ansi == AnsiSupport.Detect)
|
||||||
|
{
|
||||||
|
(supportsAnsi, legacyConsole) = AnsiDetector.Detect(true);
|
||||||
|
|
||||||
|
// Check whether or not this is a legacy console from the existing instance (if any).
|
||||||
|
// We need to do this because once we upgrade the console to support ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
// on Windows, there is no way of detecting whether or not we're running on a legacy console or not.
|
||||||
|
if (AnsiConsole.Created && !legacyConsole && buffer.IsStandardOut() && AnsiConsole.Capabilities.LegacyConsole)
|
||||||
|
{
|
||||||
|
legacyConsole = AnsiConsole.Capabilities.LegacyConsole;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (buffer.IsStandardOut())
|
||||||
|
{
|
||||||
|
// Are we running on Windows?
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
// Not the first console we're creating?
|
||||||
|
if (AnsiConsole.Created)
|
||||||
|
{
|
||||||
|
legacyConsole = AnsiConsole.Capabilities.LegacyConsole;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try detecting whether or not this
|
||||||
|
(_, legacyConsole) = AnsiDetector.Detect(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect
|
var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect
|
||||||
? ColorSystemDetector.Detect(supportsAnsi)
|
? ColorSystemDetector.Detect(supportsAnsi)
|
||||||
@@ -24,13 +56,13 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
if (supportsAnsi)
|
if (supportsAnsi)
|
||||||
{
|
{
|
||||||
return new AnsiConsoleRenderer(buffer, colorSystem)
|
return new AnsiConsoleRenderer(buffer, colorSystem, legacyConsole)
|
||||||
{
|
{
|
||||||
Style = Styles.None,
|
Decoration = Decoration.None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FallbackConsoleRenderer(buffer, colorSystem);
|
return new FallbackConsoleRenderer(buffer, colorSystem, legacyConsole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/Spectre.Console/Internal/DecorationTable.cs
Normal file
39
src/Spectre.Console/Internal/DecorationTable.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class DecorationTable
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, Decoration?> _lookup;
|
||||||
|
|
||||||
|
[SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline")]
|
||||||
|
static DecorationTable()
|
||||||
|
{
|
||||||
|
_lookup = new Dictionary<string, Decoration?>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "none", Decoration.None },
|
||||||
|
{ "bold", Decoration.Bold },
|
||||||
|
{ "b", Decoration.Bold },
|
||||||
|
{ "dim", Decoration.Dim },
|
||||||
|
{ "italic", Decoration.Italic },
|
||||||
|
{ "i", Decoration.Italic },
|
||||||
|
{ "underline", Decoration.Underline },
|
||||||
|
{ "u", Decoration.Underline },
|
||||||
|
{ "invert", Decoration.Invert },
|
||||||
|
{ "conceal", Decoration.Conceal },
|
||||||
|
{ "slowblink", Decoration.SlowBlink },
|
||||||
|
{ "rapidblink", Decoration.RapidBlink },
|
||||||
|
{ "strikethrough", Decoration.Strikethrough },
|
||||||
|
{ "s", Decoration.Strikethrough },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Decoration? GetDecoration(string name)
|
||||||
|
{
|
||||||
|
_lookup.TryGetValue(name, out var result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,25 @@ namespace Spectre.Console.Internal
|
|||||||
{
|
{
|
||||||
internal static class ConsoleExtensions
|
internal static class ConsoleExtensions
|
||||||
{
|
{
|
||||||
|
public static IDisposable PushStyle(this IAnsiConsole console, Style style)
|
||||||
|
{
|
||||||
|
if (console is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(console));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = new Style(console.Foreground, console.Background, console.Decoration);
|
||||||
|
console.SetColor(style.Foreground, true);
|
||||||
|
console.SetColor(style.Background, false);
|
||||||
|
console.Decoration = style.Decoration;
|
||||||
|
return new StyleScope(console, current);
|
||||||
|
}
|
||||||
|
|
||||||
public static IDisposable PushColor(this IAnsiConsole console, Color color, bool foreground)
|
public static IDisposable PushColor(this IAnsiConsole console, Color color, bool foreground)
|
||||||
{
|
{
|
||||||
if (console is null)
|
if (console is null)
|
||||||
@@ -12,21 +31,21 @@ namespace Spectre.Console.Internal
|
|||||||
throw new ArgumentNullException(nameof(console));
|
throw new ArgumentNullException(nameof(console));
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = console.Foreground;
|
var current = foreground ? console.Foreground : console.Background;
|
||||||
console.SetColor(color, foreground);
|
console.SetColor(color, foreground);
|
||||||
return new ColorScope(console, current, foreground);
|
return new ColorScope(console, current, foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDisposable PushStyle(this IAnsiConsole console, Styles style)
|
public static IDisposable PushDecoration(this IAnsiConsole console, Decoration decoration)
|
||||||
{
|
{
|
||||||
if (console is null)
|
if (console is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(console));
|
throw new ArgumentNullException(nameof(console));
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = console.Style;
|
var current = console.Decoration;
|
||||||
console.Style = style;
|
console.Decoration = decoration;
|
||||||
return new StyleScope(console, current);
|
return new DecorationScope(console, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetColor(this IAnsiConsole console, Color color, bool foreground)
|
public static void SetColor(this IAnsiConsole console, Color color, bool foreground)
|
||||||
@@ -47,6 +66,33 @@ namespace Spectre.Console.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal sealed class StyleScope : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _console;
|
||||||
|
private readonly Style _style;
|
||||||
|
|
||||||
|
public StyleScope(IAnsiConsole console, Style style)
|
||||||
|
{
|
||||||
|
_console = console ?? throw new ArgumentNullException(nameof(console));
|
||||||
|
_style = style ?? throw new ArgumentNullException(nameof(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
|
||||||
|
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
|
||||||
|
~StyleScope()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Style scope was not disposed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
_console.SetColor(_style.Foreground, true);
|
||||||
|
_console.SetColor(_style.Background, false);
|
||||||
|
_console.Decoration = _style.Decoration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal sealed class ColorScope : IDisposable
|
internal sealed class ColorScope : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IAnsiConsole _console;
|
private readonly IAnsiConsole _console;
|
||||||
@@ -74,28 +120,28 @@ namespace Spectre.Console.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class StyleScope : IDisposable
|
internal sealed class DecorationScope : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IAnsiConsole _console;
|
private readonly IAnsiConsole _console;
|
||||||
private readonly Styles _style;
|
private readonly Decoration _decoration;
|
||||||
|
|
||||||
public StyleScope(IAnsiConsole console, Styles color)
|
public DecorationScope(IAnsiConsole console, Decoration decoration)
|
||||||
{
|
{
|
||||||
_console = console ?? throw new ArgumentNullException(nameof(console));
|
_console = console ?? throw new ArgumentNullException(nameof(console));
|
||||||
_style = color;
|
_decoration = decoration;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
|
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")]
|
||||||
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
|
[SuppressMessage("Performance", "CA1821:Remove empty Finalizers")]
|
||||||
~StyleScope()
|
~DecorationScope()
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Style scope was not disposed.");
|
throw new InvalidOperationException("Decoration scope was not disposed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
_console.Style = _style;
|
_console.Decoration = _decoration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
|
||||||
|
{
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
action(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AnyTrue(this IEnumerable<bool> source)
|
||||||
|
{
|
||||||
|
return source.Any(b => b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<(int Index, bool First, bool Last, T Item)> Enumerate<T>(this IEnumerable<T> source)
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerate(source.GetEnumerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<(int Index, bool First, bool Last, T Item)> Enumerate<T>(this IEnumerator<T> source)
|
||||||
|
{
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
var last = !source.MoveNext();
|
||||||
|
T current;
|
||||||
|
|
||||||
|
for (var index = 0; !last; index++)
|
||||||
|
{
|
||||||
|
current = source.Current;
|
||||||
|
last = !source.MoveNext();
|
||||||
|
yield return (index, first, last, current);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> SelectIndex<T, TResult>(this IEnumerable<T> source, Func<T, int, TResult> func)
|
||||||
|
{
|
||||||
|
return source.Select((value, index) => func(value, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(
|
||||||
|
this IEnumerable<TFirst> source, IEnumerable<TSecond> first)
|
||||||
|
{
|
||||||
|
return source.Zip(first, (first, second) => (first, second));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(
|
||||||
|
this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
|
||||||
|
{
|
||||||
|
return first.Zip(second, (a, b) => (a, b))
|
||||||
|
.Zip(third, (a, b) => (a.a, a.b, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/Spectre.Console/Internal/Extensions/StringExtensions.cs
Normal file
82
src/Spectre.Console/Internal/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class StringExtensions
|
||||||
|
{
|
||||||
|
// Cache whether or not internally normalized line endings
|
||||||
|
// already are normalized. No reason to do yet another replace if it is.
|
||||||
|
private static readonly bool _alreadyNormalized
|
||||||
|
= Environment.NewLine.Equals("\n", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public static int CellLength(this string text, Encoding encoding)
|
||||||
|
{
|
||||||
|
return Cell.GetCellLength(encoding, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string NormalizeLineEndings(this string text, bool native = false)
|
||||||
|
{
|
||||||
|
text ??= string.Empty;
|
||||||
|
|
||||||
|
var normalized = text?.Replace("\r\n", "\n")?.Replace("\r", string.Empty) ?? string.Empty;
|
||||||
|
if (native && !_alreadyNormalized)
|
||||||
|
{
|
||||||
|
normalized = normalized.Replace("\n", Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] SplitLines(this string text)
|
||||||
|
{
|
||||||
|
var result = text?.NormalizeLineEndings()?.Split(new[] { '\n' }, StringSplitOptions.None);
|
||||||
|
return result ?? Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] SplitWords(this string word, StringSplitOptions options = StringSplitOptions.None)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
static string Read(StringBuffer reader, Func<char, bool> criteria)
|
||||||
|
{
|
||||||
|
var buffer = new StringBuilder();
|
||||||
|
while (!reader.Eof)
|
||||||
|
{
|
||||||
|
var current = reader.Peek();
|
||||||
|
if (!criteria(current))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Append(reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var reader = new StringBuffer(word))
|
||||||
|
{
|
||||||
|
while (!reader.Eof)
|
||||||
|
{
|
||||||
|
var current = reader.Peek();
|
||||||
|
if (char.IsWhiteSpace(current))
|
||||||
|
{
|
||||||
|
var x = Read(reader, c => char.IsWhiteSpace(c));
|
||||||
|
if (options != StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
{
|
||||||
|
result.Add(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add(Read(reader, c => !char.IsWhiteSpace(c)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/Spectre.Console/Internal/Extensions/StyleExtensions.cs
Normal file
29
src/Spectre.Console/Internal/Extensions/StyleExtensions.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class StyleExtensions
|
||||||
|
{
|
||||||
|
public static Style Combine(this Style style, IEnumerable<Style> source)
|
||||||
|
{
|
||||||
|
if (style is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = style;
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
current = current.Combine(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Spectre.Console.Internal
|
namespace Spectre.Console.Internal
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,9 @@ namespace Spectre.Console.Internal
|
|||||||
private ConsoleColor _foreground;
|
private ConsoleColor _foreground;
|
||||||
private ConsoleColor _background;
|
private ConsoleColor _background;
|
||||||
|
|
||||||
public AnsiConsoleCapabilities Capabilities { get; }
|
public Capabilities Capabilities { get; }
|
||||||
|
|
||||||
|
public Encoding Encoding { get; }
|
||||||
|
|
||||||
public int Width
|
public int Width
|
||||||
{
|
{
|
||||||
@@ -41,7 +44,7 @@ namespace Spectre.Console.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Styles Style { get; set; }
|
public Decoration Decoration { get; set; }
|
||||||
|
|
||||||
public Color Foreground
|
public Color Foreground
|
||||||
{
|
{
|
||||||
@@ -82,28 +85,32 @@ namespace Spectre.Console.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FallbackConsoleRenderer(TextWriter @out, ColorSystem system)
|
public FallbackConsoleRenderer(TextWriter @out, ColorSystem system, bool legacyConsole)
|
||||||
{
|
{
|
||||||
_out = @out;
|
_out = @out;
|
||||||
_system = system;
|
_system = system;
|
||||||
|
|
||||||
Capabilities = new AnsiConsoleCapabilities(false, _system);
|
|
||||||
|
|
||||||
if (_out.IsStandardOut())
|
if (_out.IsStandardOut())
|
||||||
{
|
{
|
||||||
_defaultForeground = System.Console.ForegroundColor;
|
_defaultForeground = System.Console.ForegroundColor;
|
||||||
_defaultBackground = System.Console.BackgroundColor;
|
_defaultBackground = System.Console.BackgroundColor;
|
||||||
|
|
||||||
|
Encoding = System.Console.OutputEncoding;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_defaultForeground = ConsoleColor.Gray;
|
_defaultForeground = ConsoleColor.Gray;
|
||||||
_defaultBackground = ConsoleColor.Black;
|
_defaultBackground = ConsoleColor.Black;
|
||||||
|
|
||||||
|
Encoding = Encoding.UTF8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Capabilities = new Capabilities(false, _system, legacyConsole);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(string text)
|
public void Write(string text)
|
||||||
{
|
{
|
||||||
_out.Write(text);
|
_out.Write(text.NormalizeLineEndings(native: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
155
src/Spectre.Console/Internal/Text/Cell.cs
Normal file
155
src/Spectre.Console/Internal/Text/Cell.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// Taken and modified from NStack project by Miguel de Icaza, licensed under BSD-3
|
||||||
|
// https://github.com/migueldeicaza/NStack/blob/3fc024fb2c2e99927d3e12991570fb54db8ce01e/NStack/unicode/Rune.ColumnWidth.cs
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class Cell
|
||||||
|
{
|
||||||
|
[SuppressMessage("Design", "RCS1169:Make field read-only.")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500:Braces for multi-line statements should not share line")]
|
||||||
|
private static readonly uint[,] _combining = new uint[,]
|
||||||
|
{
|
||||||
|
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
|
||||||
|
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||||
|
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
|
||||||
|
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
|
||||||
|
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||||
|
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||||
|
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
|
||||||
|
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||||
|
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||||
|
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||||
|
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||||
|
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||||
|
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||||
|
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||||
|
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||||
|
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
|
||||||
|
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||||
|
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||||
|
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
|
||||||
|
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||||
|
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
|
||||||
|
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||||
|
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||||
|
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||||
|
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||||
|
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||||
|
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
|
||||||
|
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||||
|
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
|
||||||
|
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
|
||||||
|
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||||
|
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||||
|
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||||
|
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||||
|
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||||
|
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||||
|
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||||
|
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
|
||||||
|
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
|
||||||
|
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
|
||||||
|
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||||
|
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
|
||||||
|
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
|
||||||
|
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
|
||||||
|
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
|
||||||
|
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
|
||||||
|
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
|
||||||
|
{ 0xE0100, 0xE01EF },
|
||||||
|
};
|
||||||
|
|
||||||
|
public static int GetCellLength(Encoding encoding, string text)
|
||||||
|
{
|
||||||
|
return text.Sum(c => GetCellLength(encoding, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetCellLength(Encoding encoding, char rune)
|
||||||
|
{
|
||||||
|
if (rune == '\r' || rune == '\n')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it represented by a single byte?
|
||||||
|
// In that case we don't have to calculate the
|
||||||
|
// actual cell width.
|
||||||
|
if (encoding.GetByteCount(new[] { rune }) == 1)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var irune = (uint)rune;
|
||||||
|
if (irune < 32)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irune < 127)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irune >= 0x7f && irune <= 0xa0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search in table of non-spacing characters
|
||||||
|
if (BinarySearch(irune, _combining, _combining.GetLength(0) - 1) != 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we arrive here, ucs is not a combining or C0/C1 control character
|
||||||
|
return 1 +
|
||||||
|
((irune >= 0x1100 &&
|
||||||
|
(irune <= 0x115f || /* Hangul Jamo init. consonants */
|
||||||
|
irune == 0x2329 || irune == 0x232a ||
|
||||||
|
(irune >= 0x2e80 && irune <= 0xa4cf &&
|
||||||
|
irune != 0x303f) || /* CJK ... Yi */
|
||||||
|
(irune >= 0xac00 && irune <= 0xd7a3) || /* Hangul Syllables */
|
||||||
|
(irune >= 0xf900 && irune <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||||
|
(irune >= 0xfe10 && irune <= 0xfe19) || /* Vertical forms */
|
||||||
|
(irune >= 0xfe30 && irune <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||||
|
(irune >= 0xff00 && irune <= 0xff60) || /* Fullwidth Forms */
|
||||||
|
(irune >= 0xffe0 && irune <= 0xffe6) ||
|
||||||
|
(irune >= 0x20000 && irune <= 0x2fffd) ||
|
||||||
|
(irune >= 0x30000 && irune <= 0x3fffd))) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int BinarySearch(uint rune, uint[,] table, int max)
|
||||||
|
{
|
||||||
|
var min = 0;
|
||||||
|
int mid;
|
||||||
|
|
||||||
|
if (rune < table[0, 0] || rune > table[max, 1])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (max >= min)
|
||||||
|
{
|
||||||
|
mid = (min + max) / 2;
|
||||||
|
if (rune > table[mid, 1])
|
||||||
|
{
|
||||||
|
min = mid + 1;
|
||||||
|
}
|
||||||
|
else if (rune < table[mid, 0])
|
||||||
|
{
|
||||||
|
max = mid - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs
Normal file
60
src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class MarkupParser
|
||||||
|
{
|
||||||
|
public static Text Parse(string text, Style? style = null)
|
||||||
|
{
|
||||||
|
style ??= Style.Plain;
|
||||||
|
|
||||||
|
var result = new Text();
|
||||||
|
using var tokenizer = new MarkupTokenizer(text);
|
||||||
|
|
||||||
|
var stack = new Stack<Style>();
|
||||||
|
|
||||||
|
while (tokenizer.MoveNext())
|
||||||
|
{
|
||||||
|
var token = tokenizer.Current;
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.Kind == MarkupTokenKind.Open)
|
||||||
|
{
|
||||||
|
var parsedStyle = StyleParser.Parse(token.Value);
|
||||||
|
stack.Push(parsedStyle);
|
||||||
|
}
|
||||||
|
else if (token.Kind == MarkupTokenKind.Close)
|
||||||
|
{
|
||||||
|
if (stack.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Encountered closing tag when none was expected near position {token.Position}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.Pop();
|
||||||
|
}
|
||||||
|
else if (token.Kind == MarkupTokenKind.Text)
|
||||||
|
{
|
||||||
|
// Get the effecive style.
|
||||||
|
var effectiveStyle = style.Combine(stack.Reverse());
|
||||||
|
result.Append(token.Value, effectiveStyle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Encountered unkown markup token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.Count > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unbalanced markup stack. Did you forget to close a tag?");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Spectre.Console/Internal/Text/Markup/MarkupToken.cs
Normal file
18
src/Spectre.Console/Internal/Text/Markup/MarkupToken.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal sealed class MarkupToken
|
||||||
|
{
|
||||||
|
public MarkupTokenKind Kind { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
public int Position { get; set; }
|
||||||
|
|
||||||
|
public MarkupToken(MarkupTokenKind kind, string value, int position)
|
||||||
|
{
|
||||||
|
Kind = kind;
|
||||||
|
Value = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
Position = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal enum MarkupTokenKind
|
||||||
|
{
|
||||||
|
Text = 0,
|
||||||
|
Open,
|
||||||
|
Close,
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs
Normal file
110
src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal sealed class MarkupTokenizer : IDisposable
|
||||||
|
{
|
||||||
|
private readonly StringBuffer _reader;
|
||||||
|
|
||||||
|
public MarkupToken? Current { get; private set; }
|
||||||
|
|
||||||
|
public MarkupTokenizer(string text)
|
||||||
|
{
|
||||||
|
_reader = new StringBuffer(text ?? throw new ArgumentNullException(nameof(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_reader.Eof)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = _reader.Peek();
|
||||||
|
if (current == '[')
|
||||||
|
{
|
||||||
|
var position = _reader.Position;
|
||||||
|
|
||||||
|
_reader.Read();
|
||||||
|
|
||||||
|
if (_reader.Eof)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
current = _reader.Peek();
|
||||||
|
if (current == '[')
|
||||||
|
{
|
||||||
|
_reader.Read();
|
||||||
|
Current = new MarkupToken(MarkupTokenKind.Text, "[", position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current == '/')
|
||||||
|
{
|
||||||
|
_reader.Read();
|
||||||
|
|
||||||
|
if (_reader.Eof)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
current = _reader.Peek();
|
||||||
|
if (current != ']')
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_reader.Read();
|
||||||
|
Current = new MarkupToken(MarkupTokenKind.Close, string.Empty, position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
while (!_reader.Eof)
|
||||||
|
{
|
||||||
|
current = _reader.Peek();
|
||||||
|
if (current == ']')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(_reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reader.Eof)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Encountered malformed markup tag at position {_reader.Position}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_reader.Read();
|
||||||
|
Current = new MarkupToken(MarkupTokenKind.Open, builder.ToString(), position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var position = _reader.Position;
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
while (!_reader.Eof)
|
||||||
|
{
|
||||||
|
current = _reader.Peek();
|
||||||
|
if (current == '[')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(_reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
Current = new MarkupToken(MarkupTokenKind.Text, builder.ToString(), position);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/Spectre.Console/Internal/Text/StringBuffer.cs
Normal file
52
src/Spectre.Console/Internal/Text/StringBuffer.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal sealed class StringBuffer : IDisposable
|
||||||
|
{
|
||||||
|
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "False positive")]
|
||||||
|
private readonly StringReader _reader;
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
public int Position { get; private set; }
|
||||||
|
public bool Eof => Position >= _length;
|
||||||
|
|
||||||
|
public StringBuffer(string text)
|
||||||
|
{
|
||||||
|
text ??= string.Empty;
|
||||||
|
|
||||||
|
_reader = new StringReader(text);
|
||||||
|
_length = text.Length;
|
||||||
|
|
||||||
|
Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public char Peek()
|
||||||
|
{
|
||||||
|
if (Eof)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Tried to peek past the end of the text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char)_reader.Peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public char Read()
|
||||||
|
{
|
||||||
|
if (Eof)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Tried to read past the end of the text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Position++;
|
||||||
|
return (char)_reader.Read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
199
src/Spectre.Console/Internal/Text/StyleParser.cs
Normal file
199
src/Spectre.Console/Internal/Text/StyleParser.cs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class StyleParser
|
||||||
|
{
|
||||||
|
public static Style Parse(string text)
|
||||||
|
{
|
||||||
|
var style = Parse(text, out var error);
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style == null)
|
||||||
|
{
|
||||||
|
// This should not happen, but we need to please the compiler
|
||||||
|
// which cannot know that style isn't null here.
|
||||||
|
throw new InvalidOperationException("Could not parse style.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParse(string text, out Style? style)
|
||||||
|
{
|
||||||
|
style = Parse(text, out var error);
|
||||||
|
return error == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Style? Parse(string text, out string? error)
|
||||||
|
{
|
||||||
|
var effectiveDecoration = (Decoration?)null;
|
||||||
|
var effectiveForeground = (Color?)null;
|
||||||
|
var effectiveBackground = (Color?)null;
|
||||||
|
|
||||||
|
var parts = text.Split(new[] { ' ' });
|
||||||
|
var foreground = true;
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
if (part.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.Equals("on", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
foreground = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoration = DecorationTable.GetDecoration(part);
|
||||||
|
if (decoration != null)
|
||||||
|
{
|
||||||
|
if (effectiveDecoration == null)
|
||||||
|
{
|
||||||
|
effectiveDecoration = Decoration.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveDecoration |= decoration.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var color = ColorTable.GetColor(part);
|
||||||
|
if (color == null)
|
||||||
|
{
|
||||||
|
if (part.StartsWith("#", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
color = ParseHexColor(part, out error);
|
||||||
|
if (!string.IsNullOrWhiteSpace(error))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (part.StartsWith("rgb", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
color = ParseRgbColor(part, out error);
|
||||||
|
if (!string.IsNullOrWhiteSpace(error))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = !foreground
|
||||||
|
? $"Could not find color '{part}'."
|
||||||
|
: $"Could not find color or style '{part}'.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foreground)
|
||||||
|
{
|
||||||
|
if (effectiveForeground != null)
|
||||||
|
{
|
||||||
|
error = "A foreground color has already been set.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveForeground = color;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (effectiveBackground != null)
|
||||||
|
{
|
||||||
|
error = "A background color has already been set.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveBackground = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = null;
|
||||||
|
return new Style(effectiveForeground, effectiveBackground, effectiveDecoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
||||||
|
private static Color? ParseHexColor(string hex, out string? error)
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
hex ??= string.Empty;
|
||||||
|
hex = hex.Replace("#", string.Empty).Trim();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(hex))
|
||||||
|
{
|
||||||
|
if (hex.Length == 6)
|
||||||
|
{
|
||||||
|
return new Color(
|
||||||
|
(byte)Convert.ToUInt32(hex.Substring(0, 2), 16),
|
||||||
|
(byte)Convert.ToUInt32(hex.Substring(2, 2), 16),
|
||||||
|
(byte)Convert.ToUInt32(hex.Substring(4, 2), 16));
|
||||||
|
}
|
||||||
|
else if (hex.Length == 3)
|
||||||
|
{
|
||||||
|
return new Color(
|
||||||
|
(byte)Convert.ToUInt32(new string(hex[0], 2), 16),
|
||||||
|
(byte)Convert.ToUInt32(new string(hex[1], 2), 16),
|
||||||
|
(byte)Convert.ToUInt32(new string(hex[2], 2), 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = $"Invalid hex color '#{hex}'. {ex.Message}";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = $"Invalid hex color '#{hex}'.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
||||||
|
private static Color? ParseRgbColor(string rgb, out string? error)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
var normalized = rgb ?? string.Empty;
|
||||||
|
if (normalized.Length >= 3)
|
||||||
|
{
|
||||||
|
// Trim parenthesises
|
||||||
|
normalized = normalized.Substring(3).Trim();
|
||||||
|
|
||||||
|
if (normalized.StartsWith("(", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
normalized.EndsWith(")", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
normalized = normalized.Trim('(').Trim(')');
|
||||||
|
|
||||||
|
var parts = normalized.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 3)
|
||||||
|
{
|
||||||
|
return new Color(
|
||||||
|
(byte)Convert.ToInt32(parts[0], CultureInfo.InvariantCulture),
|
||||||
|
(byte)Convert.ToInt32(parts[1], CultureInfo.InvariantCulture),
|
||||||
|
(byte)Convert.ToInt32(parts[2], CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = $"Invalid RGB color '{rgb}'. {ex.Message}";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = $"Invalid RGB color '{rgb}'.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/Spectre.Console/Internal/Utilities/Ratio.cs
Normal file
75
src/Spectre.Console/Internal/Utilities/Ratio.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Ported from Rich by Will McGugan, licensed under MIT.
|
||||||
|
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/_ratio.py
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Internal
|
||||||
|
{
|
||||||
|
internal static class Ratio
|
||||||
|
{
|
||||||
|
public static List<int> Reduce(int total, List<int> ratios, List<int> maximums, List<int> values)
|
||||||
|
{
|
||||||
|
ratios = ratios.Zip(maximums, (a, b) => (ratio: a, max: b)).Select(a => a.max > 0 ? a.ratio : 0).ToList();
|
||||||
|
var totalRatio = ratios.Sum();
|
||||||
|
if (totalRatio <= 0)
|
||||||
|
{
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalRemaining = total;
|
||||||
|
var result = new List<int>();
|
||||||
|
|
||||||
|
foreach (var (ratio, maximum, value) in ratios.Zip(maximums, values))
|
||||||
|
{
|
||||||
|
if (ratio != 0 && totalRatio > 0)
|
||||||
|
{
|
||||||
|
var distributed = (int)Math.Min(maximum, Math.Round((double)(ratio * totalRemaining / totalRatio)));
|
||||||
|
result.Add(value - distributed);
|
||||||
|
totalRemaining -= distributed;
|
||||||
|
totalRatio -= ratio;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<int> Distribute(int total, List<int> ratios, List<int>? minimums = null)
|
||||||
|
{
|
||||||
|
if (minimums != null)
|
||||||
|
{
|
||||||
|
ratios = ratios.Zip(minimums, (a, b) => (ratio: a, min: b)).Select(a => a.min > 0 ? a.ratio : 0).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalRatio = ratios.Sum();
|
||||||
|
Debug.Assert(totalRatio > 0, "Sum or ratios must be > 0");
|
||||||
|
|
||||||
|
var totalRemaining = total;
|
||||||
|
var distributedTotal = new List<int>();
|
||||||
|
|
||||||
|
if (minimums == null)
|
||||||
|
{
|
||||||
|
minimums = ratios.Select(_ => 0).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (ratio, minimum) in ratios.Zip(minimums, (a, b) => (a, b)))
|
||||||
|
{
|
||||||
|
var distributed = (totalRatio > 0)
|
||||||
|
? Math.Max(minimum, (int)Math.Ceiling(ratio * totalRemaining / (double)totalRatio))
|
||||||
|
: totalRemaining;
|
||||||
|
|
||||||
|
distributedTotal.Add(distributed);
|
||||||
|
totalRatio -= ratio;
|
||||||
|
totalRemaining -= distributed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return distributedTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/Spectre.Console/Rendering/AlignableExtensions.cs
Normal file
61
src/Spectre.Console/Rendering/AlignableExtensions.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for <see cref="IAlignable"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class AlignableExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the alignment for an <see cref="IAlignable"/> object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The alignable type.</typeparam>
|
||||||
|
/// <param name="alignable">The alignable object.</param>
|
||||||
|
/// <param name="alignment">The alignment.</param>
|
||||||
|
/// <returns>The same alignable object.</returns>
|
||||||
|
public static T WithAlignment<T>(this T alignable, Justify alignment)
|
||||||
|
where T : IAlignable
|
||||||
|
{
|
||||||
|
alignable.Alignment = alignment;
|
||||||
|
return alignable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the <see cref="IAlignable"/> object to be left aligned.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The alignable type.</typeparam>
|
||||||
|
/// <param name="alignable">The alignable object.</param>
|
||||||
|
/// <returns>The same alignable object.</returns>
|
||||||
|
public static T LeftAligned<T>(this T alignable)
|
||||||
|
where T : IAlignable
|
||||||
|
{
|
||||||
|
alignable.Alignment = Justify.Left;
|
||||||
|
return alignable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the <see cref="IAlignable"/> object to be centered.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The alignable type.</typeparam>
|
||||||
|
/// <param name="alignable">The alignable object.</param>
|
||||||
|
/// <returns>The same alignable object.</returns>
|
||||||
|
public static T Centered<T>(this T alignable)
|
||||||
|
where T : IAlignable
|
||||||
|
{
|
||||||
|
alignable.Alignment = Justify.Center;
|
||||||
|
return alignable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the <see cref="IAlignable"/> object to be right aligned.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The alignable type.</typeparam>
|
||||||
|
/// <param name="alignable">The alignable object.</param>
|
||||||
|
/// <returns>The same alignable object.</returns>
|
||||||
|
public static T RightAligned<T>(this T alignable)
|
||||||
|
where T : IAlignable
|
||||||
|
{
|
||||||
|
alignable.Alignment = Justify.Right;
|
||||||
|
return alignable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/Spectre.Console/Rendering/Border.cs
Normal file
108
src/Spectre.Console/Rendering/Border.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Rendering
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a border used by tables.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Border
|
||||||
|
{
|
||||||
|
private readonly Dictionary<BorderPart, string> _lookup;
|
||||||
|
|
||||||
|
private static readonly Dictionary<BorderKind, Border> _borders = new Dictionary<BorderKind, Border>
|
||||||
|
{
|
||||||
|
{ BorderKind.None, new NoBorder() },
|
||||||
|
{ BorderKind.Ascii, new AsciiBorder() },
|
||||||
|
{ BorderKind.Square, new SquareBorder() },
|
||||||
|
{ BorderKind.Rounded, new RoundedBorder() },
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<BorderKind, BorderKind> _safeLookup = new Dictionary<BorderKind, BorderKind>
|
||||||
|
{
|
||||||
|
{ BorderKind.Rounded, BorderKind.Square },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Border"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected Border()
|
||||||
|
{
|
||||||
|
_lookup = Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Border"/> represented by the specified <see cref="BorderKind"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The kind of border to get.</param>
|
||||||
|
/// <param name="safe">Whether or not to get a "safe" border that can be rendered in a legacy console.</param>
|
||||||
|
/// <returns>A <see cref="Border"/> instance representing the specified <see cref="BorderKind"/>.</returns>
|
||||||
|
public static Border GetBorder(BorderKind kind, bool safe)
|
||||||
|
{
|
||||||
|
if (safe && _safeLookup.TryGetValue(kind, out var safeKind))
|
||||||
|
{
|
||||||
|
kind = safeKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_borders.TryGetValue(kind, out var border))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unknown border kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
return border;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<BorderPart, string> Initialize()
|
||||||
|
{
|
||||||
|
var lookup = new Dictionary<BorderPart, string>();
|
||||||
|
foreach (BorderPart? part in Enum.GetValues(typeof(BorderPart)))
|
||||||
|
{
|
||||||
|
if (part == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = GetBoxPart(part.Value);
|
||||||
|
if (text.Length > 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A box part cannot contain more than one character.");
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup.Add(part.Value, GetBoxPart(part.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string representation of a specific border part.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="part">The part to get a string representation for.</param>
|
||||||
|
/// <param name="count">The number of repetitions.</param>
|
||||||
|
/// <returns>A string representation of the specified border part.</returns>
|
||||||
|
public string GetPart(BorderPart part, int count)
|
||||||
|
{
|
||||||
|
// TODO: This need some optimization...
|
||||||
|
return string.Join(string.Empty, Enumerable.Repeat(GetBoxPart(part)[0], count));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string representation of a specific border part.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="part">The part to get a string representation for.</param>
|
||||||
|
/// <returns>A string representation of the specified border part.</returns>
|
||||||
|
public string GetPart(BorderPart part)
|
||||||
|
{
|
||||||
|
return _lookup[part].ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the character representing the specified border part.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="part">The part to get the character representation for.</param>
|
||||||
|
/// <returns>A character representation of the specified border part.</returns>
|
||||||
|
protected abstract string GetBoxPart(BorderPart part);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user