Compare commits

...

41 Commits

Author SHA1 Message Date
Patrik Svensson
1601ef24b3 Update border documentation 2020-09-09 14:27:40 +02:00
Patrik Svensson
b46d0fa4f6 Group IAnsiConsole extensions in csproj 2020-09-09 08:47:55 +02:00
Patrik Svensson
4f06687104 Restructure solution a bit 2020-09-09 08:43:48 +02:00
Patrik Svensson
003e15686f Add documentation for tables
Also moves "Colors" and "Styles" sections to appendix.
2020-09-08 18:21:52 +02:00
Patrik Svensson
3e9796849b Add more borders for grids, tables, and panels
* Ascii2
* AsciiDoubleHead
* Double
* DoubleEdge
* Heavy
* HeavyEdge
* HeavyHead
* Horizontal
* Minimal
* MinimalDoubleHead
* MinimalHeavyHead
* Simple
* SimpleHeavy
2020-09-08 00:16:20 +02:00
Patrik Svensson
3cc19520b0 Add implicit conversion op from string to Style
Closes #71
2020-09-06 23:03:51 +02:00
Patrik Svensson
87bde3e5a2 Remove BorderKind in favour of Border 2020-09-06 12:29:48 +02:00
Patrik Svensson
e6e9a4d950 Add description for 'Info' example 2020-09-05 08:52:06 +02:00
Patrik Svensson
29ce14e1e8 Rename 'Diagnostic' example to 'Info' 2020-09-05 08:50:00 +02:00
Patrik Svensson
ceb3572d6a Make table and grid extension methods fluent 2020-09-05 08:49:36 +02:00
Patrik Svensson
13c56eca01 Allow mutation of segments 2020-09-05 08:39:48 +02:00
Patrik Svensson
a477884d36 Move Panel extensions to Spectre.Console namespace 2020-09-05 08:20:13 +02:00
Patrik Svensson
ae6d2c63a3 Add column support
Adds support for rendering arbitrary data into columns.

Closes #67
2020-09-05 07:45:38 +02:00
Patrik Svensson
e946289bd9 Make styles composable
Also adds some new extension methods and make some APIs a bit more consistent.

Closes #64
2020-09-03 21:26:20 +02:00
Patrik Svensson
bcaaa6b2d3 Update table of contents (skip-ci) 2020-09-03 20:04:06 +02:00
Patrik Svensson
d3588a4b06 Remove styles and colors (skip-ci) 2020-09-03 20:03:26 +02:00
Patrik Svensson
7471e9d38c Add panel header support
Closes #63
2020-09-03 19:02:29 +02:00
Patrik Svensson
9f8ca6d648 Add text overflow support
Closes #61
2020-09-03 14:57:57 +02:00
Patrik Svensson
88edfe68ec Add border for color representations 2020-09-02 09:55:44 +02:00
Patrik Svensson
5d32764a64 Fix typo: SRG -> SGR 2020-09-02 09:02:59 +02:00
Patrik Svensson
4f6c9c62c7 Set fetch depth to 0 2020-09-01 22:05:46 +02:00
Patrik Svensson
f2677213a4 Separate build and run when publishing docs 2020-09-01 22:00:44 +02:00
Patrik Svensson
bdaf00a556 Remove empty documentation pages 2020-09-01 21:51:25 +02:00
Patrik Svensson
7de4b6c7b9 Build docs as part of CI 2020-09-01 21:50:36 +02:00
Patrik Svensson
0bc801e3eb Build docs when source change 2020-09-01 21:40:49 +02:00
Patrik Svensson
88a82cdad0 Build docs in release mode 2020-09-01 21:39:40 +02:00
Patrik Svensson
0cecb555d5 Show documentation version in footer 2020-09-01 21:33:15 +02:00
Patrik Svensson
52e3ee17b0 Fix logo link 2020-09-01 21:28:52 +02:00
Dave Glick
caf7661e66 Moves all the pages up a level and hardcodes the section name 2020-09-01 21:23:01 +02:00
Dave Glick
65f0a085cc Updated to the latest Statiq Web with breakage fixes 2020-09-01 21:03:47 +02:00
Patrik Svensson
a123806cd8 Add docs for escaping [ and ] in markup 2020-09-01 16:52:40 +02:00
Patrik Svensson
173645cdd2 Added documentation for markup text 2020-08-31 14:05:28 +02:00
Patrik Svensson
7fd2efaeb5 Merge segments before rendering
This will reduce the number of segments to render
and produce cleaner ANSI escape code sequences.

Closes #46
2020-08-30 12:40:34 +02:00
Patrik Svensson
47fd646d21 Enable publish for all tags (skip-ci) 2020-08-30 10:22:53 +02:00
Patrik Svensson
a06859dcb5 Only publish on tags (skip-ci) 2020-08-30 10:12:53 +02:00
Patrik Svensson
7d3a67e6ae Always publish tags (skip-ci) 2020-08-30 10:04:29 +02:00
Patrik Svensson
f4497b1278 Docs: Added color number (skip-ci) 2020-08-29 11:25:19 +02:00
Patrik Svensson
a16daade6c Fallback to default buffer size
Also fixes a bug when using Spectre.Console in GitHub Actions.
2020-08-28 11:56:54 +02:00
Khalid Abuhakmeh
ab73d16583 Fix Unescaped Bracket
Was throwing error because of unescaped end bracket
at the end of options.
2020-08-27 20:53:10 +02:00
Patrik Svensson
f2566f2ca4 Remove commented out code (skip-ci) 2020-08-27 20:13:35 +02:00
Patrik Svensson
358b0225b4 Add documentation header (skip-ci) 2020-08-27 20:12:13 +02:00
119 changed files with 3949 additions and 1060 deletions

View File

@@ -6,6 +6,35 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs: jobs:
###################################################
# DOCS
###################################################
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.301' # SDK Version to use.
- name: Build
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd docs
dotnet run --configuration Release
###################################################
# BUILD
###################################################
build: build:
name: Build name: Build
if: "!contains(github.event.head_commit.message, 'skip-ci')" if: "!contains(github.event.head_commit.message, 'skip-ci')"
@@ -31,6 +60,16 @@ jobs:
with: with:
dotnet-version: 3.1.301 dotnet-version: 3.1.301
- name: Integration Tests
shell: bash
run: |
dotnet tool restore
dotnet example info
dotnet example table
dotnet example grid
dotnet example panel
dotnet example colors
- name: Build - name: Build
shell: bash shell: bash
run: | run: |

View File

@@ -4,14 +4,22 @@ on:
push: push:
paths: paths:
- 'docs/**' - 'docs/**'
- 'src/**'
jobs: jobs:
###################################################
# DOCS
###################################################
build: build:
name: Deploy name: Deploy
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@master uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
@@ -24,4 +32,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
cd docs cd docs
dotnet run -- deploy dotnet run --configuration Release -- deploy

View File

@@ -6,6 +6,8 @@ on:
- '*' - '*'
branches: branches:
- main - main
paths:
- 'src/**'
env: env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -13,13 +15,38 @@ env:
jobs: jobs:
###################################################
# DOCS
###################################################
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.301' # SDK Version to use.
- name: Build
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd docs
dotnet run --configuration Release
################################################### ###################################################
# BUILD # BUILD
################################################### ###################################################
build: build:
name: Build name: Build
if: "!contains(github.event.head_commit.message, 'skip-ci')" needs: [docs]
if: "!contains(github.event.head_commit.message, 'skip-ci') || startsWith(github.ref, 'refs/tags/')"
strategy: strategy:
matrix: matrix:
kind: ['linux', 'windows', 'macOS'] kind: ['linux', 'windows', 'macOS']
@@ -55,7 +82,7 @@ jobs:
publish: publish:
name: Publish name: Publish
needs: [build] needs: [build]
if: "!contains(github.event.head_commit.message, 'skip-ci')" if: "!contains(github.event.head_commit.message, 'skip-ci') || startsWith(github.ref, 'refs/tags/')"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout

281
README.md
View File

@@ -14,8 +14,6 @@ for Python.
3.1. [Using the static API](#using-the-static-api) 3.1. [Using the static API](#using-the-static-api)
3.2. [Creating a console](#creating-a-console) 3.2. [Creating a console](#creating-a-console)
4. [Running examples](#running-examples) 4. [Running examples](#running-examples)
5. [Available styles](#available-styles)
6. [Predefined colors](#predefined-colors)
## Features ## Features
@@ -122,281 +120,4 @@ And to run an example:
│ Bonjour │ le │ monde! │ │ Bonjour │ le │ monde! │
│ Hej │ Världen! │ │ │ 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`

View File

@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
<DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes>
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -22,8 +23,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Statiq.Web" Version="1.0.0-alpha.9" /> <PackageReference Include="Statiq.Web" Version="1.0.0-beta.5" />
<PackageReference Include="NJsonSchema" Version="10.1.12" /> <PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
</ItemGroup> </ItemGroup>
<Target Name="Versioning" BeforeTargets="MinVer">
<PropertyGroup Label="Build">
<MinVerDefaultPreReleasePhase>preview</MinVerDefaultPreReleasePhase>
<MinVerVerbosity>normal</MinVerVerbosity>
</PropertyGroup>
</Target>
</Project> </Project>

1
docs/Preview.ps1 Normal file
View File

@@ -0,0 +1 @@
dotnet run -- preview --virtual-dir "spectre.console"

8
docs/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Documentation
Preview the documentation locally by running the following
from your favourite shell:
```
> dotnet run -- preview --virtual-dir "spectre.console"
```

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@@ -26,13 +26,34 @@
</head> </head>
<body class="d-flex flex-column"> <body class="d-flex flex-column">
<div class="flex-grow-1 d-flex flex-column"> <div class="flex-grow-1 d-flex flex-column">
<nav id="topnav" class="navbar navbar-expand-lg navbar-light">
<div class="container py-3">
<a class="navbar-brand" href="/spectre.console"><img id="logo" src="/spectre.console/assets/logo.svg" alt="Spectre.Console"> Spectre.Console</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link font-weight-light font-size-lg text-red" href="https://github.com/sponsors/patriksvensson"><i class="far fa-heart"></i> Sponsor</a>
</li>
<li class="nav-item">
<a class="nav-link font-weight-light font-size-lg" href="https://github.com/spectresystems/spectre.console"><i class="fab fa-github"></i> GitHub</a>
</li>
</ul>
</div>
</div>
</nav>
@if (IsSectionDefined(Constants.Sections.Splash)) @if (IsSectionDefined(Constants.Sections.Splash))
{ {
@RenderSection(Constants.Sections.Splash, false) @RenderSection(Constants.Sections.Splash, false)
} }
@{ @{
string section = Document.Destination.Segments.Length > 1 ? Document.Destination.Segments[0].ToString() : null; string section = "docs";
} }
<div class="flex-grow-1 d-flex bg-body flex-column @(section != null ? "section-" + section : null)"> <div class="flex-grow-1 d-flex bg-body flex-column @(section != null ? "section-" + section : null)">
@@ -41,7 +62,7 @@
<div id="titlebar" class="py-4"> <div id="titlebar" class="py-4">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@{ @{
string titleBarClasses = Document.GetBool(Constants.NoSidebar) ? string.Empty : "offset-md-3 offset-lg-2"; string titleBarClasses = Document.GetBool(Constants.NoSidebar) ? string.Empty : "offset-md-3 offset-lg-2";
} }
<div class="@titleBarClasses px-3 px-md-0"> <div class="@titleBarClasses px-3 px-md-0">
@@ -76,7 +97,7 @@
</div> </div>
</div> </div>
} }
<div class="flex-grow-1 d-flex flex-column bg-body"> <div class="flex-grow-1 d-flex flex-column bg-body">
@if (Document.GetBool(Constants.NoContainer)) @if (Document.GetBool(Constants.NoContainer))
{ {
@@ -105,21 +126,22 @@
} }
else else
{ {
IDocument root = Outputs[nameof(Content)].First(x => x.Destination == section + "/index.html"); IDocument root = OutputPages["index.html"].First();
<div class="sidebar-nav-item @(Document.IdEquals(root) ? "active" : null)"> <div class="sidebar-nav-item @(Document.IdEquals(root) ? "active" : null)">
@Html.DocumentLink(root) @Html.DocumentLink(root)
</div> </div>
@foreach (IDocument document in root.GetChildren().OnlyVisible()) @foreach (IDocument document in OutputPages.GetChildrenOf(root).OnlyVisible())
{ {
<div class="sidebar-nav-item @(Document.IdEquals(document) ? "active" : null) @(document.HasChildren() ? "has-children" : null)"> DocumentList<IDocument> documentChildren = OutputPages.GetChildrenOf(document);
<div class="sidebar-nav-item @(Document.IdEquals(document) ? "active" : null) @(documentChildren.Any() ? "has-children" : null)">
@Html.DocumentLink(document) @Html.DocumentLink(document)
</div> </div>
@if (document.HasVisibleChildren()) @if (documentChildren.OnlyVisible().Any())
{ {
<div class="sidebar-nav-children @(Document.IdEquals(document) || document.GetChildren().Any(x => Document.IdEquals(x)) ? "active" : null)"> <div class="sidebar-nav-children @(Document.IdEquals(document) || documentChildren.Any(x => Document.IdEquals(x)) ? "active" : null)">
@foreach (IDocument child in document.GetChildren().OnlyVisible()) @foreach (IDocument child in documentChildren.OnlyVisible())
{ {
<div class="sidebar-nav-child @(Document.IdEquals(child) ? "active" : null)"> <div class="sidebar-nav-child @(Document.IdEquals(child) ? "active" : null)">
@Html.DocumentLink(child) @Html.DocumentLink(child)
@@ -161,7 +183,10 @@
</div> </div>
<div id="footer" class="p-3 text-white font-size-sm"> <div id="footer" class="p-3 text-white font-size-sm">
<div class="container"> <div class="container">
<div>© @DateTime.Today.Year Spectre Systems AB</div> <div>
<span>© @DateTime.Today.Year Spectre Systems AB</span>
<span class="float-right" style="color: #888888;">@VersionUtilities.GetVersion()</span>
</div>
</div> </div>
</div> </div>
<script> <script>
@@ -174,7 +199,7 @@
}, },
startOnLoad: false, startOnLoad: false,
cloneCssStyles: false cloneCssStyles: false
}); });
mermaid.init(undefined, ".mermaid"); mermaid.init(undefined, ".mermaid");
// Remove the max-width setting that Mermaid sets // Remove the max-width setting that Mermaid sets
@@ -193,13 +218,13 @@
center: true, center: true,
maxZoom: 20, maxZoom: 20,
zoomScaleSensitivity: 0.6 zoomScaleSensitivity: 0.6
}); });
// Do the reset once right away to fit the diagram // Do the reset once right away to fit the diagram
panZoom.resize(); panZoom.resize();
panZoom.fit(); panZoom.fit();
panZoom.center(); panZoom.center();
$(window).resize(function(){ $(window).resize(function(){
panZoom.resize(); panZoom.resize();
panZoom.fit(); panZoom.fit();

View File

@@ -3,5 +3,6 @@
@using Statiq.Web @using Statiq.Web
@using Statiq.Web.Pipelines @using Statiq.Web.Pipelines
@using Docs @using Docs
@using Docs.Utilities;
@inherits StatiqRazorPage<IDocument> @inherits StatiqRazorPage<IDocument>

View File

@@ -0,0 +1,19 @@
Title: Borders
Order: 2
---
There is different built-in borders you can use for tables and panels.
# Built-in borders
<img src="../assets/images/borders.png" style="max-width: 100%;">
# Usage
To create a table and set it's border to `SimpleHeavy` as seen in the
image above:
```csharp
var table = new Table();
table.Border = Border.SimpleHeavy;
```

View File

@@ -1,5 +1,5 @@
Title: Colors Title: Colors
Order: 2 Order: 0
--- ---
The following is a list of the standard 8-bit colors supported in terminals. The following is a list of the standard 8-bit colors supported in terminals.

View File

@@ -0,0 +1,3 @@
Title: Appendix
Order: 10
---

View File

@@ -0,0 +1,44 @@
Title: Styles
Order: 1
---
Note that what styles that can be used is defined by the system or your terminal software, and may not appear as they should.
<table class="table">
<tr>
<td><code>bold</code></td>
<td>Bold text</td>
</tr>
<tr>
<td><code>dim</code></td>
<td>Dim or faint text</td>
</tr>
<tr>
<td><code>italic</code></td>
<td>Italic text</td>
</tr>
<tr>
<td><code>underline</code></td>
<td>Underlined text</td>
</tr>
<tr>
<td><code>invert</code></td>
<td>Swaps the foreground and background colors</td>
</tr>
<tr>
<td><code>conceal</code></td>
<td>Hides the text</td>
</tr>
<tr>
<td><code>slowblink</code></td>
<td>Makes text blink slowly</td>
</tr>
<tr>
<td><code>rapidblink</code></td>
<td>Makes text blink</td>
</tr>
<tr>
<td><code>strikethrough</code></td>
<td>Shows text with a horizontal line through the center</td>
</tr>
</table>

View File

@@ -18,6 +18,18 @@ $thebackground: $gray-200;
display: inline-block; display: inline-block;
width: 60px; width: 60px;
height: 15px; height: 15px;
border: 2px solid #000000;
}
#topnav {
padding: 0px;
margin: 0px;
}
#topnav .container {
padding-top: 0px !important;
padding-bottom: 0px !important;
margin-bottom: 0px;
} }
@media screen { @media screen {

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -1,6 +0,0 @@
Title: Console API
Order: 2
Hidden: True
---
__To be written__

View File

@@ -1,6 +0,0 @@
Title: Grids
Order: 4
Hidden: True
---
__To be written__

View File

@@ -1,6 +0,0 @@
Title: Panels
Order: 5
Hidden: True
---
__To be written__

View File

@@ -1,6 +0,0 @@
Title: Tables
Order: 3
Hidden: True
---
__To be written__

View File

@@ -1,84 +0,0 @@
Title: Start
NoContainer: true
---
@section Splash {
<div id="hero" class="jumbotron jumbotron-fluid mb-0">
<div class="container">
<div class="display-4 text-white">Spectre.Console</div>
<p class="lead text-white">
A .NET Standard library that makes it easier to create beautiful console applications.
<br /><br />
<a class="btn btn-primary" href="/spectre.console/docs" role="button">Take me to the documentation</a>
</p>
</div>
</div>
}
@* <div class="container py-3">
<div class="row">
<div class="col-sm py-3">
<div class="row">
<div class="col-md-2">
<i class="fad fa-heartbeat fa-2x icon-md-100 icon-theme"></i>
</div>
<div class="col-md-10">
<h3 class="font-weight-bold font-size-base text-dark mb-2">A beautiful console</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aenean elementum est lorem, at auctor nibh rhoncus eget.
Ut lorem leo, aliquet sit amet tortor vitae, aliquet eleifend dui.
Etiam lectus diam, maximus a pretium a, lacinia in augue.
</p>
</div>
</div>
</div>
<div class="col-sm py-3">
<div class="row">
<div class="col-md-2">
<i class="fad fa-code fa-2x icon-md-100 icon-theme"></i>
</div>
<div class="col-md-10">
<h3 class="font-weight-bold font-size-base text-dark mb-2">Rich formatting language</h3>
<p>
Donec at rutrum turpis. Etiam tempor sed ex quis pharetra.
Suspendisse ullamcorper at magna ac efficitur. Nulla ornare,
lectus feugiat ornare ultricies, nunc lorem maximus ipsum,
nec accumsan eros tellus quis ante
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm py-3">
<div class="row">
<div class="col-md-2">
<i class="fad fa-cogs fa-2x icon-md-100 icon-theme"></i>
</div>
<div class="col-md-10">
<h3 class="font-weight-bold font-size-base text-dark mb-2">Configurable</h3>
<p>
Suspendisse vel condimentum tortor. Fusce tempus ligula ut eros finibus,
nec fermentum ipsum hendrerit. Proin eu diam in tortor vestibulum bibendum.
Integer tincidunt felis eu urna pulvinar vehicula.
</p>
</div>
</div>
</div>
<div class="col-sm py-3">
<div class="row">
<div class="col-md-2">
<i class="fad fa-search fa-2x icon-md-100 icon-theme"></i>
</div>
<div class="col-md-10">
<h3 class="font-weight-bold font-size-base text-dark mb-2">Find problems quickly</h3>
<p>
Aliquam nec nisl ullamcorper, aliquam mauris sit amet, sagittis orci.
Aenean sapien dui, eleifend eget sapien nec, tristique accumsan tortor.
Quisque ex quam, rhoncus sagittis consequat id, euismod sit amet dolor.
</p>
</div>
</div>
</div>
</div>
</div> *@

View File

@@ -13,7 +13,7 @@ for Python written by Will McGugan.
* Supports tables, grids, panels, and a [Rich](https://github.com/willmcgugan/rich) * Supports tables, grids, panels, and a [Rich](https://github.com/willmcgugan/rich)
inspired markup language. inspired markup language.
* Supports the most common * Supports the most common
[SRG parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) [SGR parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters)
when it comes to text styling such as bold, dim, italic, underline, strikethrough, when it comes to text styling such as bold, dim, italic, underline, strikethrough,
and blinking text. and blinking text.
* Supports `3`/`4`/`8`/`24`-bit colors in the terminal. * Supports `3`/`4`/`8`/`24`-bit colors in the terminal.

63
docs/input/markup.md Normal file
View File

@@ -0,0 +1,63 @@
Title: Markup
Order: 2
---
In `Spectre.Console` there's a class called `Markup` that
allows you to output rich text to the console.
# Syntax
Console markup uses a syntax inspired by bbcode. If you write the style (see Styles)
in square brackets, e.g. `[bold red]`, that style will apply until it is closed with a `[/]`.
```csharp
AnsiConsole.Render(new Markup("[bold yellow]Hello[/] [red]World![/]"));
```
The `Markup` class implements `IRenderable` which means that you
can use this in tables, grids, and panels. Most classes that support
rendering of `IRenderable` also have overloads for rendering rich text.
```csharp
var table = new Table();
table.AddColumn(new TableColumn(new Markup("[yellow]Foo[/]")));
table.AddColumn(new TableColumn("[blue]Bar[/]"));
```
# Convenience methods
There is also convenience methods on `AnsiConsole` that can be used
to write markup text to the console without instantiating a new `Markup`
instance.
```csharp
AnsiConsole.Markup("[underline green]Hello[/] ");
AnsiConsole.MarkupLine("[bold]World[/]");
```
# Escaping format characters
To output a `[` you use `[[`, and to output a `]` you use `]]`.
```csharp
AnsiConsole.Markup("[[Hello]] "); // [Hello]
AnsiConsole.Markup("[red][[World]][/]"); // [World]
```
# Setting background color
You can set the background color in markup by prefixing the color with
`on`.
```
[bold yellow on blue]Hello[/]
[default on blue]World[/]
```
# Colors
For a list of colors, see the [Colors](xref:colors) appendix section.
# Styles
For a list of styles, see the [Styles](xref:styles) appendix section.

120
docs/input/tables.md Normal file
View File

@@ -0,0 +1,120 @@
Title: Tables
Order: 3
---
Tables are a perfect way of displaying tabular data in a terminal.
`Spectre.Console` is super smart about rendering tables and will adjust
all columns to fit whatever is inside them. Anything that implements
`IRenderable` can be used as a column header or column cell, even another table!
# Usage
<!------------------------->
<!--- USAGE --->
<!------------------------->
To render a table, create a `Table` instance, add the number of
columns that you need, and then add the rows. Finish by passing the
table to a console's `Render` method.
```csharp
// Create a table
var table = new Table();
// Add some columns
table.AddColumn("Foo");
table.AddColumn(new TableColumn("Bar").Centered());
// Add some rows
table.AddRow("Baz", "[green]Qux[/]");
table.AddRow(new Markup("[blue]Corgi[/]"), new Panel("Waldo"));
// Render the table to the console
AnsiConsole.Render(table);
```
This will render the following output:
![Table](assets/images/table.png)
# Table appearance
<!------------------------->
<!--- TABLE APPEARANCE --->
<!------------------------->
## Borders
For a list of borders, see the [Borders](xref:borders) appendix section.
```csharp
// Sets the border
table.SetBorder(Border.None);
table.SetBorder(Border.Ascii);
table.SetBorder(Border.Square);
table.SetBorder(Border.Rounded);
```
## Expand / Collapse
```csharp
// Table will take up as much space as it can
// with respect to other things.
table.Expand();
// Table will take up minimal width
table.Collapse();
```
## Hide headers
```csharp
// Hides all column headers
table.HideHeaders();
```
## Set table width
```csharp
// Sets the table width to 50 cells
table.SetWidth(50);
```
# Column appearance
<!------------------------->
<!--- COLUMN APPEARANCE --->
<!------------------------->
## Alignment
```csharp
// Set the alignment explicitly
column.SetAlignment(Justify.Right);
```
## Padding
```csharp
// Set left and right padding
column.SetPadding(left: 3, right: 5);
// Set padding individually.
column.PadLeft(3);
column.PadRight(5);
```
## Disable column wrapping
```csharp
// Disable column wrapping
column.NoWrap();
```
## Set column width
```csharp
// Set the column width (no fluent extension method for this yet)
column.Width = 15;
```

View File

@@ -11,15 +11,6 @@ namespace Docs
return document?.GetString(Constants.Description, string.Empty) ?? string.Empty; return document?.GetString(Constants.Description, string.Empty) ?? string.Empty;
} }
public static bool HasVisibleChildren(this IDocument document)
{
if (document != null)
{
return document.HasChildren() && document.GetChildren().Any(x => x.IsVisible());
}
return false;
}
public static bool IsVisible(this IDocument document) public static bool IsVisible(this IDocument document)
{ {
return !document.GetBool(Constants.Hidden, false); return !document.GetBool(Constants.Hidden, false);

View File

@@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Docs.Models; using Docs.Models;
using NJsonSchema;
using Statiq.Common; using Statiq.Common;
using Statiq.Core; using Statiq.Core;

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NJsonSchema;
using Statiq.Common; using Statiq.Common;
using System.Xml.Linq; using System.Xml.Linq;
using Docs.Pipelines; using Docs.Pipelines;
@@ -26,6 +25,7 @@ namespace Docs.Shortcodes
var table = new XElement("table", new XAttribute("class", "table")); var table = new XElement("table", new XAttribute("class", "table"));
var header = new XElement("tr", new XAttribute("class", "color-row")); var header = new XElement("tr", new XAttribute("class", "color-row"));
header.Add(new XElement("th", "")); header.Add(new XElement("th", ""));
header.Add(new XElement("th", "#"));
header.Add(new XElement("th", "Name")); header.Add(new XElement("th", "Name"));
header.Add(new XElement("th", "RGB")); header.Add(new XElement("th", "RGB"));
header.Add(new XElement("th", "Hex")); header.Add(new XElement("th", "Hex"));
@@ -38,7 +38,8 @@ namespace Docs.Shortcodes
new XElement("span", new XElement("span",
new XAttribute("class", "color-representation"), new XAttribute("class", "color-representation"),
new XAttribute("style", $"background-color:{color.Hex};"))); new XAttribute("style", $"background-color:{color.Hex};")));
var name = new XElement("td", new XElement("code", color.Name.ToLower())); var name = new XElement("td", new XElement("code", color.Number.ToString()));
var number = new XElement("td", new XElement("code", color.Name.ToLower()));
var rgb = new XElement("td", new XElement("code", $"{color.R},{color.G},{color.B}")); var rgb = new XElement("td", new XElement("code", $"{color.R},{color.G},{color.B}"));
var hex = new XElement("td", new XElement("code", color.Hex)); var hex = new XElement("td", new XElement("code", color.Hex));
var clr = new XElement("td", new XElement("code", color.ClrName)); var clr = new XElement("td", new XElement("code", color.ClrName));
@@ -47,6 +48,7 @@ namespace Docs.Shortcodes
var row = new XElement("tr"); var row = new XElement("tr");
row.Add(rep); row.Add(rep);
row.Add(name); row.Add(name);
row.Add(number);
row.Add(rgb); row.Add(rgb);
row.Add(hex); row.Add(hex);
row.Add(clr); row.Add(clr);

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
namespace Docs.Utilities
{
public static class VersionUtilities
{
public static string GetVersion()
{
return GetVersion(typeof(VersionUtilities).Assembly);
}
private static string GetVersion(Assembly assembly)
{
if (assembly == null)
{
return "?";
}
try
{
var info = FileVersionInfo.GetVersionInfo(assembly.Location);
return info.ProductVersion ?? "?";
}
catch
{
return "?";
}
}
}
}

View File

@@ -13,6 +13,12 @@
"commands": [ "commands": [
"gpr" "gpr"
] ]
},
"dotnet-example": {
"version": "0.8.0",
"commands": [
"dotnet-example"
]
} }
} }
} }

View File

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

View File

@@ -0,0 +1,48 @@
using System;
using Spectre.Console;
using Spectre.Console.Rendering;
namespace Borders
{
public static class Program
{
public static void Main()
{
var items = new[]
{
Create("Ascii", Border.Ascii),
Create("Ascii2", Border.Ascii2),
Create("AsciiDoubleHead", Border.AsciiDoubleHead),
Create("Horizontal", Border.Horizontal),
Create("Simple", Border.Simple),
Create("SimpleHeavy", Border.SimpleHeavy),
Create("Minimal", Border.Minimal),
Create("MinimalHeavyHead", Border.MinimalHeavyHead),
Create("MinimalDoubleHead", Border.MinimalDoubleHead),
Create("Square", Border.Square),
Create("Rounded", Border.Rounded),
Create("Heavy", Border.Heavy),
Create("HeavyEdge", Border.HeavyEdge),
Create("HeavyHead", Border.HeavyHead),
Create("Double", Border.Double),
Create("DoubleEdge", Border.DoubleEdge),
};
AnsiConsole.WriteLine();
AnsiConsole.Render(new Columns(items).Collapse());
}
private static IRenderable Create(string name, Border border)
{
var table = new Table().SetBorder(border);
table.AddColumns("[yellow]Header 1[/]", "[yellow]Header 2[/]");
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return new Panel(table)
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.NoBorder();
}
}
}

View File

@@ -2,75 +2,117 @@ using Spectre.Console;
namespace ColorExample namespace ColorExample
{ {
class Program public static class Program
{ {
static void Main(string[] args) public static void Main()
{ {
///////////////////////////////////////////////////////////////// if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
// 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())); // No colors
AnsiConsole.ResetColors(); /////////////////////////////////////////////////////////////////
if ((i + 1) % 8 == 0)
{ AnsiConsole.WriteLine("No colors are supported.");
AnsiConsole.WriteLine(); return;
}
} }
///////////////////////////////////////////////////////////////// if (AnsiConsole.Capabilities.Supports(ColorSystem.Legacy))
// 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++) /////////////////////////////////////////////////////////////////
// 3-BIT
/////////////////////////////////////////////////////////////////
AnsiConsole.ResetColors();
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[bold underline]3-bit Colors[/]");
AnsiConsole.WriteLine();
for (var i = 0; i < 8; i++)
{ {
var number = i * 16 + j; AnsiConsole.Background = Color.FromInt32(i);
AnsiConsole.Background = Color.FromInt32(number); AnsiConsole.Write(string.Format(" {0,-9}", AnsiConsole.Background.ToString()));
AnsiConsole.Write(string.Format(" {0,-4}", number));
AnsiConsole.ResetColors(); AnsiConsole.ResetColors();
if ((number + 1) % 16 == 0) if ((i + 1) % 8 == 0)
{ {
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
} }
} }
} }
///////////////////////////////////////////////////////////////// if (AnsiConsole.Capabilities.Supports(ColorSystem.Standard))
// 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++; /////////////////////////////////////////////////////////////////
// 4-BIT
/////////////////////////////////////////////////////////////////
var color = Utilities.HSL2RGB(i, 0.5, 0.5); AnsiConsole.ResetColors();
AnsiConsole.Background = new Color(color.R, color.G, color.B); AnsiConsole.WriteLine();
AnsiConsole.Write(" "); AnsiConsole.MarkupLine("[bold underline]4-bit Colors[/]");
AnsiConsole.WriteLine();
if (index % 50 == 0) for (var i = 0; i < 16; i++)
{ {
AnsiConsole.WriteLine(); AnsiConsole.Background = Color.FromInt32(i);
AnsiConsole.Write(string.Format(" {0,-9}", AnsiConsole.Background.ToString()));
AnsiConsole.ResetColors();
if ((i + 1) % 8 == 0)
{
AnsiConsole.WriteLine();
}
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.EightBit))
{
/////////////////////////////////////////////////////////////////
// 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();
}
}
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.TrueColor))
{
/////////////////////////////////////////////////////////////////
// 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();
}
} }
} }
} }

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Description>Demonstrates how to render data into columns.</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Spectre.Console;
namespace ColumnsExample
{
public static class Program
{
public static async Task Main()
{
// Download some random users
using var client = new HttpClient();
dynamic users = JObject.Parse(
await client.GetStringAsync("https://randomuser.me/api/?results=15"));
// Create a card for each user
var cards = new List<Panel>();
foreach(var user in users.results)
{
cards.Add(new Panel(GetCard(user))
.SetHeader($"{user.location.country}")
.RoundedBorder().Expand());
}
// Render all cards in columns
AnsiConsole.Render(new Columns(cards));
}
private static string GetCard(dynamic user)
{
var name = $"{user.name.first} {user.name.last}";
var country = $"{user.location.city}";
return $"[b]{name}[/]\n[yellow]{country}[/]";
}
}
}

View File

@@ -1,25 +1,25 @@
using Spectre.Console; using Spectre.Console;
namespace GridExample namespace GridExample
{ {
public sealed class Program public static class Program
{ {
static void Main(string[] args) public static void Main()
{ {
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options] [[[[--] <additional arguments>...]][/]"); AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options]] [[[[--]] <additional arguments>...]]]][/]");
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
var grid = new Grid(); var grid = new Grid();
grid.AddColumn(new GridColumn { NoWrap = true }); grid.AddColumn(new GridColumn { NoWrap = true });
grid.AddColumn(new GridColumn { NoWrap = true, Width = 2 }); grid.AddColumn(new GridColumn { NoWrap = true, Width = 2 });
grid.AddColumn(); grid.AddColumn();
grid.AddRow("Options:", "", ""); grid.AddRow("Options:", "", "");
grid.AddRow(" [blue]-h[/], [blue]--help[/]", "", "Show command line help."); 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]-c[/], [blue]--configuration[/] <CONFIGURATION>", "", "The configuration to run for.");
grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", "", "Set the [grey]MSBuild[/] verbosity level."); grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", "", "Set the [grey]MSBuild[/] verbosity level.");
AnsiConsole.Render(grid); AnsiConsole.Render(grid);
} }
} }
} }

14
examples/Info/Info.csproj Normal file
View File

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

23
examples/Info/Program.cs Normal file
View File

@@ -0,0 +1,23 @@
using Spectre.Console;
namespace Info
{
public static class Program
{
public static void Main()
{
var grid = new Grid()
.AddColumn(new GridColumn().NoWrap().PadRight(4))
.AddColumn()
.AddRow("[b]Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{AnsiConsole.Capabilities.SupportsAnsi}")
.AddRow("[b]Legacy console?[/]", $"{AnsiConsole.Capabilities.LegacyConsole}")
.AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Width}")
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Height}");
AnsiConsole.Render(
new Panel(grid)
.SetHeader("Information"));
}
}
}

View File

@@ -2,44 +2,39 @@ using Spectre.Console;
namespace PanelExample namespace PanelExample
{ {
class Program public static class Program
{ {
static void Main(string[] args) public static void Main()
{ {
var content = new Markup( var content = new Markup(
"[underline]I[/] heard [underline on blue]you[/] like 📦\n\n\n\n" + "[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" +
"So I put a 📦 in a 📦\n\n" + "So I put a panel in a panel").Centered();
"😅").Centered();
AnsiConsole.Render( AnsiConsole.Render(
new Panel( new Panel(
new Panel(content) new Panel(content)
{ .SetBorder(Border.Rounded)));
Border = BorderKind.Rounded
}));
// Left adjusted panel with text // Left adjusted panel with text
AnsiConsole.Render(new Panel( AnsiConsole.Render(
new Text("Left adjusted\nLeft").LeftAligned()) new Panel(new Text("Left adjusted\nLeft").LeftAligned())
{ .Expand()
Expand = true, .SquareBorder()
}); .SetHeader("Left", Style.WithForeground(Color.Red)));
// Centered ASCII panel with text // Centered ASCII panel with text
AnsiConsole.Render(new Panel( AnsiConsole.Render(
new Text("Centered\nCenter").Centered()) new Panel(new Text("Centered\nCenter").Centered())
{ .Expand()
Expand = true, .AsciiBorder()
Border = BorderKind.Ascii, .SetHeader("Center", Style.WithForeground(Color.Green), Justify.Center));
});
// Right adjusted, rounded panel with text // Right adjusted, rounded panel with text
AnsiConsole.Render(new Panel( AnsiConsole.Render(
new Text("Right adjusted\nRight").RightAligned()) new Panel(new Text("Right adjusted\nRight").RightAligned())
{ .Expand()
Expand = true, .RoundedBorder()
Border = BorderKind.Rounded, .SetHeader("Right", Style.WithForeground(Color.Blue), Justify.Right));
});
} }
} }
} }

View File

@@ -1,11 +1,10 @@
using Spectre.Console; using Spectre.Console;
using Spectre.Console.Rendering;
namespace TableExample namespace TableExample
{ {
public static class Program public static class Program
{ {
public static void Main(string[] args) public static void Main()
{ {
// A simple table // A simple table
RenderSimpleTable(); RenderSimpleTable();
@@ -36,21 +35,21 @@ namespace TableExample
private static void RenderBigTable() private static void RenderBigTable()
{ {
// Create the table. // Create the table.
var table = new Table().SetBorder(BorderKind.Rounded); var table = new Table().SetBorder(Border.Rounded);
table.AddColumn("[red underline]Foo[/]"); table.AddColumn("[red underline]Foo[/]");
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true }); table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
// Add some rows // Add some rows
table.AddRow("[blue][underline]Hell[/]o[/]", "World 🌍"); table.AddRow("[blue][underline]Hell[/]o[/]", "World");
table.AddRow("[yellow]Patrik [green]\"Hello World\"[/] Svensson[/]", "Was [underline]here[/]!"); table.AddRow("[yellow]Patrik [green]\"Hello World\"[/] Svensson[/]", "Was [underline]here[/]!");
table.AddEmptyRow(); table.AddEmptyRow();
table.AddRow( table.AddRow(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + "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 " + "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 " + "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"); "non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "<- Strange language");
table.AddEmptyRow(); table.AddEmptyRow();
table.AddRow("Hej 👋", "[green]Världen[/]"); table.AddRow("Hej", "[green]Världen[/]");
AnsiConsole.Render(table); AnsiConsole.Render(table);
} }
@@ -58,7 +57,7 @@ namespace TableExample
private static void RenderNestedTable() private static void RenderNestedTable()
{ {
// Create simple table. // Create simple table.
var simple = new Table().SetBorder(BorderKind.Rounded).SetBorderColor(Color.Red); var simple = new Table().SetBorder(Border.Rounded).SetBorderColor(Color.Red);
simple.AddColumn(new TableColumn("[u]Foo[/]").Centered()); simple.AddColumn(new TableColumn("[u]Foo[/]").Centered());
simple.AddColumn(new TableColumn("[u]Bar[/]")); simple.AddColumn(new TableColumn("[u]Bar[/]"));
simple.AddColumn(new TableColumn("[u]Baz[/]")); simple.AddColumn(new TableColumn("[u]Baz[/]"));
@@ -67,7 +66,7 @@ namespace TableExample
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", ""); simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
// Create other table. // Create other table.
var second = new Table().SetBorder(BorderKind.Square).SetBorderColor(Color.Green); var second = new Table().SetBorder(Border.Square).SetBorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]")); second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]")); second.AddColumn(new TableColumn("[u]Bar[/]"));
second.AddColumn(new TableColumn("[u]Baz[/]")); second.AddColumn(new TableColumn("[u]Baz[/]"));
@@ -75,13 +74,13 @@ namespace TableExample
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz")); second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", ""); second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
var table = new Table().SetBorder(BorderKind.Rounded); var table = new Table().SetBorder(Border.Rounded);
table.AddColumn(new TableColumn(new Panel("[u]Foo[/]").SetBorderColor(Color.Red))); table.AddColumn(new TableColumn(new Panel("[u]Foo[/]").SetBorderColor(Color.Red)));
table.AddColumn(new TableColumn(new Panel("[u]Bar[/]").SetBorderColor(Color.Green))); table.AddColumn(new TableColumn(new Panel("[u]Bar[/]").SetBorderColor(Color.Green)));
table.AddColumn(new TableColumn(new Panel("[u]Baz[/]").SetBorderColor(Color.Blue))); table.AddColumn(new TableColumn(new Panel("[u]Baz[/]").SetBorderColor(Color.Blue)));
// Add some rows // Add some rows
table.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/] 🌍"), Text.Empty); table.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/]"), Text.Empty);
table.AddRow(second, new Text("Whaaat"), new Text("Lol")); table.AddRow(second, new Text("Whaaat"), new Text("Lol"));
table.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty); table.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty);

View File

@@ -82,5 +82,8 @@ dotnet_diagnostic.RCS1079.severity = warning
# RCS1057: Add empty line between declarations. # RCS1057: Add empty line between declarations.
dotnet_diagnostic.RCS1057.severity = none dotnet_diagnostic.RCS1057.severity = none
# RCS1057: Validate arguments correctly
dotnet_diagnostic.RCS1227.severity = none
# IDE0004: Remove Unnecessary Cast # IDE0004: Remove Unnecessary Cast
dotnet_diagnostic.IDE0004.severity = warning dotnet_diagnostic.IDE0004.severity = warning

View File

@@ -42,7 +42,7 @@
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113"> <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="2.3.0"> <PackageReference Include="Roslynator.Analyzers" Version="3.0.0">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>

View File

@@ -12,7 +12,7 @@ namespace Spectre.Console.Tests.Unit
{ {
[Theory] [Theory]
[InlineData("[yellow]Hello[/]", "Hello")] [InlineData("[yellow]Hello[/]", "Hello")]
[InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")] [InlineData("[yellow]Hello [italic]World[/]![/]", "Hello World!")]
public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected) public void Should_Output_Expected_Ansi_For_Markup(string markup, string expected)
{ {
// Given // Given
@@ -26,7 +26,7 @@ namespace Spectre.Console.Tests.Unit
} }
[Theory] [Theory]
[InlineData("[yellow]Hello [[ World[/]", "Hello [ World")] [InlineData("[yellow]Hello [[ World[/]", "Hello [ World")]
public void Should_Be_Able_To_Escape_Tags(string markup, string expected) public void Should_Be_Able_To_Escape_Tags(string markup, string expected)
{ {
// Given // Given

View File

@@ -1,4 +1,3 @@
using System;
using Shouldly; using Shouldly;
using Spectre.Console.Rendering; using Spectre.Console.Rendering;
using Xunit; using Xunit;
@@ -7,35 +6,794 @@ namespace Spectre.Console.Tests.Unit
{ {
public sealed class BorderTests public sealed class BorderTests
{ {
public sealed class TheGetBorderMethod public sealed class NoBorder
{ {
[Theory] [Fact]
[InlineData(BorderKind.None, false, typeof(NoBorder))] public void Should_Return_Correct_Visibility()
[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 // Given, When
var result = Border.GetBorder(kind, safe); var visibility = Border.None.Visible;
// Then // Then
result.ShouldBeOfType(expected); visibility.ShouldBeFalse();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.None.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.None);
}
} }
[Fact] [Fact]
public void Should_Throw_If_Unknown_Border_Kind_Is_Specified() public void Should_Render_As_Expected()
{ {
// Given, When // Given
var result = Record.Exception(() => Border.GetBorder((BorderKind)int.MaxValue, false)); var console = new PlainConsole();
var table = Fixture.GetTable().NoBorder();
// When
console.Render(table);
// Then // Then
result.ShouldBeOfType<InvalidOperationException>(); console.Lines.Count.ShouldBe(3);
result.Message.ShouldBe("Unknown border kind"); console.Lines[0].ShouldBe("Header 1 Header 2");
console.Lines[1].ShouldBe("Cell Cell ");
console.Lines[2].ShouldBe("Cell Cell ");
}
}
public sealed class AsciiBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().AsciiBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("+---------------------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|----------+----------|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+---------------------+");
}
}
public sealed class Ascii2Border
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii2.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii2.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii2);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().Ascii2Border();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("+----------+----------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|----------+----------|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+----------+----------+");
}
}
public sealed class AsciiDoubleHeadBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.AsciiDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.AsciiDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.AsciiDoubleHead);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().AsciiDoubleHeadBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("+----------+----------+");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("|==========+==========|");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe("+----------+----------+");
}
}
public sealed class SquareBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Square.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Square.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().SquareBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("┌──────────┬──────────┐");
console.Lines[1].ShouldBe("│ Header 1 │ Header 2 │");
console.Lines[2].ShouldBe("├──────────┼──────────┤");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("└──────────┴──────────┘");
}
}
public sealed class RoundedBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Rounded.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Rounded.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().RoundedBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("╭──────────┬──────────╮");
console.Lines[1].ShouldBe("│ Header 1 │ Header 2 │");
console.Lines[2].ShouldBe("├──────────┼──────────┤");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("╰──────────┴──────────╯");
}
}
public sealed class MinimalBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Minimal.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Minimal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().MinimalBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ──────────┼────────── ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
}
}
public sealed class MinimalHeavyHeadBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalHeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalHeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().MinimalHeavyHeadBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ━━━━━━━━━━┿━━━━━━━━━━ ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
}
}
public sealed class MinimalDoubleHeadBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.MinimalDoubleHead);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().MinimalDoubleHeadBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 │ Header 2 ");
console.Lines[2].ShouldBe(" ══════════╪══════════ ");
console.Lines[3].ShouldBe(" Cell │ Cell ");
console.Lines[4].ShouldBe(" Cell │ Cell ");
console.Lines[5].ShouldBe(" ");
}
}
public sealed class SimpleBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Simple.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Simple.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().SimpleBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("───────────────────────");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe(" ");
}
}
public sealed class HorizontalBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Horizontal.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Horizontal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Horizontal);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().HorizontalBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("───────────────────────");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("───────────────────────");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe("───────────────────────");
}
}
public sealed class SimpleHeavyBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.SimpleHeavy.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.SimpleHeavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().SimpleHeavyBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe(" Header 1 Header 2 ");
console.Lines[2].ShouldBe("━━━━━━━━━━━━━━━━━━━━━━━");
console.Lines[3].ShouldBe(" Cell Cell ");
console.Lines[4].ShouldBe(" Cell Cell ");
console.Lines[5].ShouldBe(" ");
}
}
public sealed class HeavyBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Heavy.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Heavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().HeavyBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┳━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 ┃ Header 2 ┃");
console.Lines[2].ShouldBe("┣━━━━━━━━━━╋━━━━━━━━━━┫");
console.Lines[3].ShouldBe("┃ Cell ┃ Cell ┃");
console.Lines[4].ShouldBe("┃ Cell ┃ Cell ┃");
console.Lines[5].ShouldBe("┗━━━━━━━━━━┻━━━━━━━━━━┛");
}
}
public sealed class HeavyEdgeBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyEdge.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().HeavyEdgeBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┯━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 │ Header 2 ┃");
console.Lines[2].ShouldBe("┠──────────┼──────────┨");
console.Lines[3].ShouldBe("┃ Cell │ Cell ┃");
console.Lines[4].ShouldBe("┃ Cell │ Cell ┃");
console.Lines[5].ShouldBe("┗━━━━━━━━━━┷━━━━━━━━━━┛");
}
}
public sealed class HeavyHeadBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().HeavyHeadBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("┏━━━━━━━━━━┳━━━━━━━━━━┓");
console.Lines[1].ShouldBe("┃ Header 1 ┃ Header 2 ┃");
console.Lines[2].ShouldBe("┡━━━━━━━━━━╇━━━━━━━━━━┩");
console.Lines[3].ShouldBe("│ Cell │ Cell │");
console.Lines[4].ShouldBe("│ Cell │ Cell │");
console.Lines[5].ShouldBe("└──────────┴──────────┘");
}
}
public sealed class DoubleBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Double.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Double.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Double);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().DoubleBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("╔══════════╦══════════╗");
console.Lines[1].ShouldBe("║ Header 1 ║ Header 2 ║");
console.Lines[2].ShouldBe("╠══════════╬══════════╣");
console.Lines[3].ShouldBe("║ Cell ║ Cell ║");
console.Lines[4].ShouldBe("║ Cell ║ Cell ║");
console.Lines[5].ShouldBe("╚══════════╩══════════╝");
}
}
public sealed class DoubleEdgeBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.DoubleEdge.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.DoubleEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.DoubleEdge);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().DoubleEdgeBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe("╔══════════╤══════════╗");
console.Lines[1].ShouldBe("║ Header 1 │ Header 2 ║");
console.Lines[2].ShouldBe("╟──────────┼──────────╢");
console.Lines[3].ShouldBe("║ Cell │ Cell ║");
console.Lines[4].ShouldBe("║ Cell │ Cell ║");
console.Lines[5].ShouldBe("╚══════════╧══════════╝");
}
}
private static class Fixture
{
public static Table GetTable()
{
var table = new Table();
table.AddColumns("Header 1", "Header 2");
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return table;
} }
} }
} }

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class ColumnsTests
{
private sealed class User
{
public string Name { get; set; }
public string Country { get; set; }
}
[Fact]
public void Should_Render_Columns_Correctly()
{
// Given
var console = new PlainConsole(width: 61);
var users = new[]
{
new User { Name = "Savannah Thompson", Country = "Australia" },
new User { Name = "Sophie Ramos", Country = "United States" },
new User { Name = "Katrin Goldberg", Country = "Germany" },
};
var cards = new List<Panel>();
foreach (var user in users)
{
cards.Add(
new Panel($"[b]{user.Name}[/]\n[yellow]{user.Country}[/]")
.RoundedBorder().Expand());
}
// When
console.Render(new Columns(cards));
// Then
console.Lines.Count.ShouldBe(4);
console.Lines[0].ShouldBe("╭────────────────────╮ ╭────────────────╮ ╭─────────────────╮");
console.Lines[1].ShouldBe("│ Savannah Thompson │ │ Sophie Ramos │ │ Katrin Goldberg │");
console.Lines[2].ShouldBe("│ Australia │ │ United States │ │ Germany │");
console.Lines[3].ShouldBe("╰────────────────────╯ ╰────────────────╯ ╰─────────────────╯");
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Spectre.Console.Tests.Unit
} }
[Fact] [Fact]
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns() public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
{ {
// Given // Given
var grid = new Grid(); var grid = new Grid();
@@ -50,11 +50,10 @@ namespace Spectre.Console.Tests.Unit
grid.AddColumn(); grid.AddColumn();
// When // When
var result = Record.Exception(() => grid.AddRow("Foo")); grid.AddRow("Foo");
// Then // Then
result.ShouldBeOfType<InvalidOperationException>(); grid.RowCount.ShouldBe(1);
result.Message.ShouldBe("The number of row columns are less than the number of grid columns.");
} }
[Fact] [Fact]

View File

@@ -40,6 +40,108 @@ namespace Spectre.Console.Tests.Unit
console.Lines[2].ShouldBe("└───────────────────┘"); console.Lines[2].ShouldBe("└───────────────────┘");
} }
[Fact]
public void Should_Render_Panel_With_Header()
{
// Given
var console = new PlainConsole(width: 80);
// When
console.Render(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
Padding = new Padding(2, 2),
});
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌─Greeting─────────────────────────────────────────────────────────────────────┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
}
[Fact]
public void Should_Render_Panel_With_Left_Aligned_Header()
{
// Given
var console = new PlainConsole(width: 80);
// When
console.Render(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").LeftAligned(),
Expand = true,
});
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌─Greeting─────────────────────────────────────────────────────────────────────┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
}
[Fact]
public void Should_Render_Panel_With_Centered_Header()
{
// Given
var console = new PlainConsole(width: 80);
// When
console.Render(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").Centered(),
Expand = true,
});
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌───────────────────────────────────Greeting───────────────────────────────────┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
}
[Fact]
public void Should_Render_Panel_With_Right_Aligned_Header()
{
// Given
var console = new PlainConsole(width: 80);
// When
console.Render(new Panel("Hello World")
{
Header = new PanelHeader("Greeting").RightAligned(),
Expand = true,
});
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌─────────────────────────────────────────────────────────────────────Greeting─┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
}
[Fact]
public void Should_Collapse_Header_If_It_Will_Not_Fit()
{
// Given
var console = new PlainConsole(width: 10);
// When
console.Render(new Panel("Hello World")
{
Header = new PanelHeader("Greeting"),
Expand = true,
});
// Then
console.Lines.Count.ShouldBe(4);
console.Lines[0].ShouldBe("┌─Greet…─┐");
console.Lines[1].ShouldBe("│ Hello │");
console.Lines[2].ShouldBe("│ World │");
console.Lines[3].ShouldBe("└────────┘");
}
[Fact] [Fact]
public void Should_Render_Panel_With_Unicode_Correctly() public void Should_Render_Panel_With_Unicode_Correctly()
{ {

View File

@@ -87,7 +87,7 @@ namespace Spectre.Console.Tests.Unit
} }
[Fact] [Fact]
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns() public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
{ {
// Given // Given
var table = new Table(); var table = new Table();
@@ -95,11 +95,10 @@ namespace Spectre.Console.Tests.Unit
table.AddColumn("World"); table.AddColumn("World");
// When // When
var result = Record.Exception(() => table.AddRow("Foo")); table.AddRow("Foo");
// Then // Then
result.ShouldBeOfType<InvalidOperationException>(); table.RowCount.ShouldBe(1);
result.Message.ShouldBe("The number of row columns are less than the number of table columns.");
} }
[Fact] [Fact]
@@ -174,7 +173,7 @@ namespace Spectre.Console.Tests.Unit
{ {
// A simple table // A simple table
var console = new PlainConsole(width: 80); var console = new PlainConsole(width: 80);
var table = new Table() { Border = BorderKind.Rounded }; var table = new Table() { Border = Border.Rounded };
table.AddColumn("Foo"); table.AddColumn("Foo");
table.AddColumn("Bar"); table.AddColumn("Bar");
table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right }); table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right });
@@ -184,7 +183,7 @@ namespace Spectre.Console.Tests.Unit
// Render a table in some panels. // Render a table in some panels.
console.Render(new Panel(new Panel(table) console.Render(new Panel(new Panel(table)
{ {
Border = BorderKind.Ascii, Border = Border.Ascii,
})); }));
// Then // Then
@@ -256,7 +255,7 @@ namespace Spectre.Console.Tests.Unit
{ {
// Given // Given
var console = new PlainConsole(width: 80); var console = new PlainConsole(width: 80);
var table = new Table { Border = BorderKind.Ascii }; var table = new Table { Border = Border.Ascii };
table.AddColumns("Foo", "Bar", "Baz"); table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo"); table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred"); table.AddRow("Grault", "Garply", "Fred");
@@ -279,7 +278,7 @@ namespace Spectre.Console.Tests.Unit
{ {
// Given // Given
var console = new PlainConsole(width: 80); var console = new PlainConsole(width: 80);
var table = new Table { Border = BorderKind.Rounded }; var table = new Table { Border = Border.Rounded };
table.AddColumns("Foo", "Bar", "Baz"); table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo"); table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred"); table.AddRow("Grault", "Garply", "Fred");
@@ -302,7 +301,7 @@ namespace Spectre.Console.Tests.Unit
{ {
// Given // Given
var console = new PlainConsole(width: 80); var console = new PlainConsole(width: 80);
var table = new Table { Border = BorderKind.None }; var table = new Table { Border = Border.None };
table.AddColumns("Foo", "Bar", "Baz"); table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo"); table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred"); table.AddRow("Grault", "Garply", "Fred");

View File

@@ -65,6 +65,21 @@ namespace Spectre.Console.Tests.Unit
fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n"); fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n");
} }
[Fact]
public void Should_Render_Panel_2()
{
// Given
var console = new PlainConsole(width: 80);
// When
console.Render(new Markup("[b]Hello World[/]\n[yellow]Hello World[/]"));
// Then
console.Lines.Count.ShouldBe(2);
console.Lines[0].ShouldBe("Hello World");
console.Lines[1].ShouldBe("Hello World");
}
[Theory] [Theory]
[InlineData(5, "Hello World", "Hello\nWorld")] [InlineData(5, "Hello World", "Hello\nWorld")]
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")] [InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
@@ -83,5 +98,25 @@ namespace Spectre.Console.Tests.Unit
.NormalizeLineEndings() .NormalizeLineEndings()
.ShouldBe(expected); .ShouldBe(expected);
} }
[Theory]
[InlineData(Overflow.Fold, "foo \npneumonoultram\nicroscopicsili\ncovolcanoconio\nsis bar qux")]
[InlineData(Overflow.Crop, "foo \npneumonoultram\nbar qux")]
[InlineData(Overflow.Ellipsis, "foo \npneumonoultra…\nbar qux")]
public void Should_Overflow_Text_Correctly(Overflow overflow, string expected)
{
// Given
var fixture = new PlainConsole(14);
var text = new Text("foo pneumonoultramicroscopicsilicovolcanoconiosis bar qux")
.SetOverflow(overflow);
// When
fixture.Render(text);
// Then
fixture.Output
.NormalizeLineEndings()
.ShouldBe(expected);
}
} }
} }

View File

@@ -25,6 +25,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grid", "..\examples\Grid\Gr
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "..\examples\Colors\Colors.csproj", "{1F51C55C-BA4C-4856-9001-0F7924FFB179}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "..\examples\Colors\Colors.csproj", "{1F51C55C-BA4C-4856-9001-0F7924FFB179}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Columns", "..\examples\Columns\Columns.csproj", "{33357599-C79D-4299-888F-634E2C3EACEF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Info", "..\examples\Info\Info.csproj", "{225CE0D4-06AB-411A-8D29-707504FE53B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Borders", "..\examples\Borders\Borders.csproj", "{094245E6-4C94-485D-B5AC-3153E878B112}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -107,6 +113,42 @@ Global
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
{1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x86.Build.0 = Release|Any CPU {1F51C55C-BA4C-4856-9001-0F7924FFB179}.Release|x86.Build.0 = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x64.ActiveCfg = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x64.Build.0 = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x86.ActiveCfg = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x86.Build.0 = Debug|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|Any CPU.Build.0 = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x64.ActiveCfg = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x64.Build.0 = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x86.ActiveCfg = Release|Any CPU
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x86.Build.0 = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|x64.ActiveCfg = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|x64.Build.0 = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|x86.ActiveCfg = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Debug|x86.Build.0 = Debug|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|Any CPU.Build.0 = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|x64.ActiveCfg = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|x64.Build.0 = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|x86.ActiveCfg = Release|Any CPU
{225CE0D4-06AB-411A-8D29-707504FE53B3}.Release|x86.Build.0 = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|Any CPU.Build.0 = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|x64.ActiveCfg = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|x64.Build.0 = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|x86.ActiveCfg = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Debug|x86.Build.0 = Debug|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|Any CPU.ActiveCfg = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|Any CPU.Build.0 = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x64.ActiveCfg = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x64.Build.0 = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x86.ActiveCfg = Release|Any CPU
{094245E6-4C94-485D-B5AC-3153E878B112}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -116,6 +158,9 @@ Global
{BFF37228-B376-4ADD-9657-4E501F929713} = {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} {C7FF6FDB-FB59-4517-8669-521C96AB7323} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{1F51C55C-BA4C-4856-9001-0F7924FFB179} = {F0575243-121F-4DEE-9F6B-246E26DC0844} {1F51C55C-BA4C-4856-9001-0F7924FFB179} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{33357599-C79D-4299-888F-634E2C3EACEF} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{225CE0D4-06AB-411A-8D29-707504FE53B3} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{094245E6-4C94-485D-B5AC-3153E878B112} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C} SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@@ -0,0 +1,97 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
{
/// <summary>
/// Gets an invisible border.
/// </summary>
public static Border None { get; } = new NoBorder();
/// <summary>
/// Gets an ASCII border.
/// </summary>
public static Border Ascii { get; } = new AsciiBorder();
/// <summary>
/// Gets another ASCII border.
/// </summary>
public static Border Ascii2 { get; } = new Ascii2Border();
/// <summary>
/// Gets an ASCII border with a double header border.
/// </summary>
public static Border AsciiDoubleHead { get; } = new AsciiDoubleHeadBorder();
/// <summary>
/// Gets a square border.
/// </summary>
public static Border Square { get; } = new SquareBorder();
/// <summary>
/// Gets a rounded border.
/// </summary>
public static Border Rounded { get; } = new RoundedBorder();
/// <summary>
/// Gets a minimal border.
/// </summary>
public static Border Minimal { get; } = new MinimalBorder();
/// <summary>
/// Gets a minimal border with a heavy head.
/// </summary>
public static Border MinimalHeavyHead { get; } = new MinimalHeavyHeadBorder();
/// <summary>
/// Gets a minimal border with a double header border.
/// </summary>
public static Border MinimalDoubleHead { get; } = new MinimalDoubleHeadBorder();
/// <summary>
/// Gets a simple border.
/// </summary>
public static Border Simple { get; } = new SimpleBorder();
/// <summary>
/// Gets a simple border with heavy lines.
/// </summary>
public static Border SimpleHeavy { get; } = new SimpleHeavyBorder();
/// <summary>
/// Gets a horizontal border.
/// </summary>
public static Border Horizontal { get; } = new HorizontalBorder();
/// <summary>
/// Gets a heavy border.
/// </summary>
public static Border Heavy { get; } = new HeavyBorder();
/// <summary>
/// Gets a border with a heavy edge.
/// </summary>
public static Border HeavyEdge { get; } = new HeavyEdgeBorder();
/// <summary>
/// Gets a border with a heavy header.
/// </summary>
public static Border HeavyHead { get; } = new HeavyHeadBorder();
/// <summary>
/// Gets a double border.
/// </summary>
[SuppressMessage("Naming", "CA1720:Identifier contains type name")]
public static Border Double { get; } = new DoubleBorder();
/// <summary>
/// Gets a border with a double edge.
/// </summary>
public static Border DoubleEdge { get; } = new DoubleEdgeBorder();
}
}

View File

@@ -2,28 +2,26 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Spectre.Console.Rendering;
namespace Spectre.Console.Rendering namespace Spectre.Console
{ {
/// <summary> /// <summary>
/// Represents a border used by tables. /// Represents a border.
/// </summary> /// </summary>
public abstract class Border public abstract partial class Border
{ {
private readonly Dictionary<BorderPart, string> _lookup; private readonly Dictionary<BorderPart, string> _lookup;
private static readonly Dictionary<BorderKind, Border> _borders = new Dictionary<BorderKind, Border> /// <summary>
{ /// Gets a value indicating whether or not the border is visible.
{ BorderKind.None, new NoBorder() }, /// </summary>
{ BorderKind.Ascii, new AsciiBorder() }, public virtual bool Visible { get; } = true;
{ BorderKind.Square, new SquareBorder() },
{ BorderKind.Rounded, new RoundedBorder() },
};
private static readonly Dictionary<BorderKind, BorderKind> _safeLookup = new Dictionary<BorderKind, BorderKind> /// <summary>
{ /// Gets the safe border for this border or <c>null</c> if none exist.
{ BorderKind.Rounded, BorderKind.Square }, /// </summary>
}; public virtual Border? SafeBorder { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Border"/> class. /// Initializes a new instance of the <see cref="Border"/> class.
@@ -33,27 +31,6 @@ namespace Spectre.Console.Rendering
_lookup = Initialize(); _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() private Dictionary<BorderPart, string> Initialize()
{ {
var lookup = new Dictionary<BorderPart, string>(); var lookup = new Dictionary<BorderPart, string>();

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents another old school ASCII border.
/// </summary>
public sealed class Ascii2Border : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "+",
BorderPart.HeaderTop => "-",
BorderPart.HeaderTopSeparator => "+",
BorderPart.HeaderTopRight => "+",
BorderPart.HeaderLeft => "|",
BorderPart.HeaderSeparator => "|",
BorderPart.HeaderRight => "|",
BorderPart.HeaderBottomLeft => "|",
BorderPart.HeaderBottom => "-",
BorderPart.HeaderBottomSeparator => "+",
BorderPart.HeaderBottomRight => "|",
BorderPart.CellLeft => "|",
BorderPart.CellSeparator => "|",
BorderPart.CellRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "+",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border with a double header border.
/// </summary>
public sealed class AsciiDoubleHeadBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "+",
BorderPart.HeaderTop => "-",
BorderPart.HeaderTopSeparator => "+",
BorderPart.HeaderTopRight => "+",
BorderPart.HeaderLeft => "|",
BorderPart.HeaderSeparator => "|",
BorderPart.HeaderRight => "|",
BorderPart.HeaderBottomLeft => "|",
BorderPart.HeaderBottom => "=",
BorderPart.HeaderBottomSeparator => "+",
BorderPart.HeaderBottomRight => "|",
BorderPart.CellLeft => "|",
BorderPart.CellSeparator => "|",
BorderPart.CellRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "+",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a double border.
/// </summary>
public sealed class DoubleBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "╔",
BorderPart.HeaderTop => "═",
BorderPart.HeaderTopSeparator => "╦",
BorderPart.HeaderTopRight => "╗",
BorderPart.HeaderLeft => "║",
BorderPart.HeaderSeparator => "║",
BorderPart.HeaderRight => "║",
BorderPart.HeaderBottomLeft => "╠",
BorderPart.HeaderBottom => "═",
BorderPart.HeaderBottomSeparator => "╬",
BorderPart.HeaderBottomRight => "╣",
BorderPart.CellLeft => "║",
BorderPart.CellSeparator => "║",
BorderPart.CellRight => "║",
BorderPart.FooterBottomLeft => "╚",
BorderPart.FooterBottom => "═",
BorderPart.FooterBottomSeparator => "╩",
BorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a double edge.
/// </summary>
public sealed class DoubleEdgeBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "╔",
BorderPart.HeaderTop => "═",
BorderPart.HeaderTopSeparator => "╤",
BorderPart.HeaderTopRight => "╗",
BorderPart.HeaderLeft => "║",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => "║",
BorderPart.HeaderBottomLeft => "╟",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "┼",
BorderPart.HeaderBottomRight => "╢",
BorderPart.CellLeft => "║",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => "║",
BorderPart.FooterBottomLeft => "╚",
BorderPart.FooterBottom => "═",
BorderPart.FooterBottomSeparator => "╧",
BorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a heavy border.
/// </summary>
public sealed class HeavyBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "┏",
BorderPart.HeaderTop => "━",
BorderPart.HeaderTopSeparator => "┳",
BorderPart.HeaderTopRight => "┓",
BorderPart.HeaderLeft => "┃",
BorderPart.HeaderSeparator => "┃",
BorderPart.HeaderRight => "┃",
BorderPart.HeaderBottomLeft => "┣",
BorderPart.HeaderBottom => "━",
BorderPart.HeaderBottomSeparator => "╋",
BorderPart.HeaderBottomRight => "┫",
BorderPart.CellLeft => "┃",
BorderPart.CellSeparator => "┃",
BorderPart.CellRight => "┃",
BorderPart.FooterBottomLeft => "┗",
BorderPart.FooterBottom => "━",
BorderPart.FooterBottomSeparator => "┻",
BorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy edge.
/// </summary>
public sealed class HeavyEdgeBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "┏",
BorderPart.HeaderTop => "━",
BorderPart.HeaderTopSeparator => "┯",
BorderPart.HeaderTopRight => "┓",
BorderPart.HeaderLeft => "┃",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => "┃",
BorderPart.HeaderBottomLeft => "┠",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "┼",
BorderPart.HeaderBottomRight => "┨",
BorderPart.CellLeft => "┃",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => "┃",
BorderPart.FooterBottomLeft => "┗",
BorderPart.FooterBottom => "━",
BorderPart.FooterBottomSeparator => "┷",
BorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy header.
/// </summary>
public sealed class HeavyHeadBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "┏",
BorderPart.HeaderTop => "━",
BorderPart.HeaderTopSeparator => "┳",
BorderPart.HeaderTopRight => "┓",
BorderPart.HeaderLeft => "┃",
BorderPart.HeaderSeparator => "┃",
BorderPart.HeaderRight => "┃",
BorderPart.HeaderBottomLeft => "┡",
BorderPart.HeaderBottom => "━",
BorderPart.HeaderBottomSeparator => "╇",
BorderPart.HeaderBottomRight => "┩",
BorderPart.CellLeft => "│",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => "│",
BorderPart.FooterBottomLeft => "└",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "┴",
BorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a horizontal border.
/// </summary>
public sealed class HorizontalBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => "─",
BorderPart.HeaderTop => "─",
BorderPart.HeaderTopSeparator => "─",
BorderPart.HeaderTopRight => "─",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => " ",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => "─",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "─",
BorderPart.HeaderBottomRight => "─",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => " ",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => "─",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "─",
BorderPart.FooterBottomRight => "─",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border.
/// </summary>
public sealed class MinimalBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => " ",
BorderPart.HeaderTop => " ",
BorderPart.HeaderTopSeparator => " ",
BorderPart.HeaderTopRight => " ",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => " ",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "┼",
BorderPart.HeaderBottomRight => " ",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a double header border.
/// </summary>
public sealed class MinimalDoubleHeadBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => " ",
BorderPart.HeaderTop => " ",
BorderPart.HeaderTopSeparator => " ",
BorderPart.HeaderTopRight => " ",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => " ",
BorderPart.HeaderBottom => "═",
BorderPart.HeaderBottomSeparator => "╪",
BorderPart.HeaderBottomRight => " ",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a heavy header.
/// </summary>
public sealed class MinimalHeavyHeadBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Minimal;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => " ",
BorderPart.HeaderTop => " ",
BorderPart.HeaderTopSeparator => " ",
BorderPart.HeaderTopRight => " ",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => " ",
BorderPart.HeaderBottom => "━",
BorderPart.HeaderBottomSeparator => "┿",
BorderPart.HeaderBottomRight => " ",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => "│",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -5,6 +5,9 @@ namespace Spectre.Console.Rendering
/// </summary> /// </summary>
public sealed class NoBorder : Border public sealed class NoBorder : Border
{ {
/// <inheritdoc/>
public override bool Visible => false;
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetBoxPart(BorderPart part) protected override string GetBoxPart(BorderPart part)
{ {

View File

@@ -7,6 +7,9 @@ namespace Spectre.Console.Rendering
/// </summary> /// </summary>
public sealed class RoundedBorder : Border public sealed class RoundedBorder : Border
{ {
/// <inheritdoc/>
public override Border? SafeBorder { get; } = Border.Square;
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetBoxPart(BorderPart part) protected override string GetBoxPart(BorderPart part)
{ {

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border.
/// </summary>
public sealed class SimpleBorder : Border
{
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => " ",
BorderPart.HeaderTop => " ",
BorderPart.HeaderTopSeparator => " ",
BorderPart.HeaderTopRight => " ",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => " ",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => "─",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "─",
BorderPart.HeaderBottomRight => "─",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => " ",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border with heavy lines.
/// </summary>
public sealed class SimpleHeavyBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Simple;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => " ",
BorderPart.HeaderTop => " ",
BorderPart.HeaderTopSeparator => " ",
BorderPart.HeaderTopRight => " ",
BorderPart.HeaderLeft => " ",
BorderPart.HeaderSeparator => " ",
BorderPart.HeaderRight => " ",
BorderPart.HeaderBottomLeft => "━",
BorderPart.HeaderBottom => "━",
BorderPart.HeaderBottomSeparator => "━",
BorderPart.HeaderBottomRight => "━",
BorderPart.CellLeft => " ",
BorderPart.CellSeparator => " ",
BorderPart.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@@ -38,6 +38,17 @@ namespace Spectre.Console
LegacyConsole = legacyConsole; LegacyConsole = legacyConsole;
} }
/// <summary>
/// Checks whether the current capabilities supports
/// the specified color system.
/// </summary>
/// <param name="colorSystem">The color system to check.</param>
/// <returns><c>true</c> if the color system is supported, otherwise <c>false</c>.</returns>
public bool Supports(ColorSystem colorSystem)
{
return (int)colorSystem <= (int)ColorSystem;
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ {

View File

@@ -7,7 +7,7 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>. /// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary> /// </summary>
public static partial class ConsoleExtensions public static partial class AnsiConsoleExtensions
{ {
/// <summary> /// <summary>
/// Writes the specified markup to the console. /// Writes the specified markup to the console.

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using Spectre.Console.Internal; using Spectre.Console.Internal;
using Spectre.Console.Rendering; using Spectre.Console.Rendering;
@@ -7,7 +8,7 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>. /// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary> /// </summary>
public static partial class ConsoleExtensions public static partial class AnsiConsoleExtensions
{ {
/// <summary> /// <summary>
/// Renders the specified object to the console. /// Renders the specified object to the console.
@@ -30,8 +31,11 @@ namespace Spectre.Console
using (console.PushStyle(Style.Plain)) using (console.PushStyle(Style.Plain))
{ {
var segments = renderable.Render(options, console.Width).Where(x => !(x.Text.Length == 0 && !x.IsLineBreak)).ToArray();
segments = Segment.Merge(segments).ToArray();
var current = Style.Plain; var current = Style.Plain;
foreach (var segment in renderable.Render(options, console.Width)) foreach (var segment in segments)
{ {
if (string.IsNullOrEmpty(segment.Text)) if (string.IsNullOrEmpty(segment.Text))
{ {

View File

@@ -6,7 +6,7 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>. /// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary> /// </summary>
public static partial class ConsoleExtensions public static partial class AnsiConsoleExtensions
{ {
/// <summary> /// <summary>
/// Writes the specified string value to the console. /// Writes the specified string value to the console.

View File

@@ -6,7 +6,7 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>. /// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary> /// </summary>
public static partial class ConsoleExtensions public static partial class AnsiConsoleExtensions
{ {
/// <summary> /// <summary>
/// Writes an empty line to the console. /// Writes an empty line to the console.

View File

@@ -5,7 +5,7 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>. /// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary> /// </summary>
public static partial class ConsoleExtensions public static partial class AnsiConsoleExtensions
{ {
/// <summary> /// <summary>
/// Resets colors and text decorations. /// Resets colors and text decorations.

View File

@@ -0,0 +1,31 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Contains extension methods for <see cref="Border"/>.
/// </summary>
public static class BorderExtensions
{
/// <summary>
/// Gets the safe border for a border.
/// </summary>
/// <param name="border">The border to get the safe border for.</param>
/// <param name="safe">Whether or not to return the safe border.</param>
/// <returns>The safe border if one exist, otherwise the original border.</returns>
public static Border GetSafeBorder(this Border border, bool safe)
{
if (border is null)
{
throw new ArgumentNullException(nameof(border));
}
if (safe && border.SafeBorder != null)
{
border = border.SafeBorder;
}
return border;
}
}
}

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Spectre.Console.Rendering namespace Spectre.Console
{ {
/// <summary> /// <summary>
/// Contains extension methods for <see cref="IExpandable"/>. /// Contains extension methods for <see cref="IExpandable"/>.

View File

@@ -0,0 +1,101 @@
using System;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Grid"/>.
/// </summary>
public static class GridExtensions
{
/// <summary>
/// Adds a column to the grid.
/// </summary>
/// <param name="grid">The grid to add the column to.</param>
/// <param name="count">The number of columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Grid AddColumns(this Grid grid, int count)
{
if (grid is null)
{
throw new ArgumentNullException(nameof(grid));
}
for (var index = 0; index < count; index++)
{
grid.AddColumn(new GridColumn());
}
return grid;
}
/// <summary>
/// Adds a column to the grid.
/// </summary>
/// <param name="grid">The grid to add the column to.</param>
/// <param name="columns">The columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Grid AddColumns(this Grid grid, params GridColumn[] columns)
{
if (grid is null)
{
throw new ArgumentNullException(nameof(grid));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
foreach (var column in columns)
{
grid.AddColumn(column);
}
return grid;
}
/// <summary>
/// Adds an empty row to the grid.
/// </summary>
/// <param name="grid">The grid to add the row to.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Grid AddEmptyRow(this Grid grid)
{
if (grid is null)
{
throw new ArgumentNullException(nameof(grid));
}
var columns = new IRenderable[grid.ColumnCount];
Enumerable.Range(0, grid.ColumnCount).ForEach(index => columns[index] = Text.Empty);
grid.AddRow(columns);
return grid;
}
/// <summary>
/// Adds a new row to the grid.
/// </summary>
/// <param name="grid">The grid to add the row to.</param>
/// <param name="columns">The columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Grid AddRow(this Grid grid, params string[] columns)
{
if (grid is null)
{
throw new ArgumentNullException(nameof(grid));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
grid.AddRow(columns.Select(column => new Markup(column)).ToArray());
return grid;
}
}
}

View File

@@ -0,0 +1,289 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasBorder"/>.
/// </summary>
public static class HasBorderExtensions
{
/// <summary>
/// Do not display a border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.None);
}
/// <summary>
/// Display a square border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SquareBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Square);
}
/// <summary>
/// Display an ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Ascii);
}
/// <summary>
/// Display another ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Ascii2Border<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Ascii2);
}
/// <summary>
/// Display an ASCII border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiDoubleHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.AsciiDoubleHead);
}
/// <summary>
/// Display a rounded border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T RoundedBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Rounded);
}
/// <summary>
/// Display a minimal border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Minimal);
}
/// <summary>
/// Display a minimal border with a heavy head.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalHeavyHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.MinimalHeavyHead);
}
/// <summary>
/// Display a minimal border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalDoubleHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.MinimalDoubleHead);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Simple);
}
/// <summary>
/// Display a simple border with heavy lines.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleHeavyBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.SimpleHeavy);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HorizontalBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Horizontal);
}
/// <summary>
/// Display a heavy border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Heavy);
}
/// <summary>
/// Display a border with a heavy edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyEdgeBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.HeavyEdge);
}
/// <summary>
/// Display a border with a heavy header.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.HeavyHead);
}
/// <summary>
/// Display a double border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Double);
}
/// <summary>
/// Display a border with a double edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleEdgeBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.DoubleEdge);
}
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, Border border)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
/// <summary>
/// Disables the safe border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoSafeBorder<T>(this T obj)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.UseSafeBorder = false;
return obj;
}
/// <summary>
/// Sets the border style.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border color for.</param>
/// <param name="style">The border style to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorderStyle<T>(this T obj, Style style)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.BorderStyle = style;
return obj;
}
/// <summary>
/// Sets the border color.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border color for.</param>
/// <param name="color">The border color to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorderColor<T>(this T obj, Color color)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.BorderStyle = (obj.BorderStyle ?? Style.Plain).WithForeground(color);
return obj;
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IOverflowable"/>.
/// </summary>
public static class OverflowableExtensions
{
/// <summary>
/// Folds any overflowing text.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IOverflowable"/>.</typeparam>
/// <param name="obj">The overflowable object instance.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Fold<T>(this T obj)
where T : class, IOverflowable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Fold);
}
/// <summary>
/// Crops any overflowing text.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IOverflowable"/>.</typeparam>
/// <param name="obj">The overflowable object instance.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Crop<T>(this T obj)
where T : class, IOverflowable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Crop);
}
/// <summary>
/// Crops any overflowing text and adds an ellipsis to the end.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IOverflowable"/>.</typeparam>
/// <param name="obj">The overflowable object instance.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Ellipsis<T>(this T obj)
where T : class, IOverflowable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
return SetOverflow(obj, Overflow.Ellipsis);
}
/// <summary>
/// Sets the overflow strategy.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IOverflowable"/>.</typeparam>
/// <param name="obj">The overflowable object instance.</param>
/// <param name="overflow">The overflow strategy to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetOverflow<T>(this T obj, Overflow overflow)
where T : class, IOverflowable
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Overflow = overflow;
return obj;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Panel"/>.
/// </summary>
public static class PanelExtensions
{
/// <summary>
/// Sets the panel header.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="text">The header text.</param>
/// <param name="style">The header style.</param>
/// <param name="alignment">The header alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel SetHeader(this Panel panel, string text, Style? style = null, Justify? alignment = null)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return SetHeader(panel, new PanelHeader(text, style, alignment));
}
/// <summary>
/// Sets the panel header.
/// </summary>
/// <param name="panel">The panel.</param>
/// <param name="header">The header to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Panel SetHeader(this Panel panel, PanelHeader header)
{
if (panel is null)
{
throw new ArgumentNullException(nameof(panel));
}
panel.Header = header;
return panel;
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Style"/>.
/// </summary>
public static class StyleExtensions
{
/// <summary>
/// Creates a new style from the specified one with
/// the specified foreground color.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="color">The foreground color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithForeground(this Style style, Color color)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: color,
background: style.Background,
decoration: style.Decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified background color.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="color">The background color.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithBackground(this Style style, Color color)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: color,
decoration: style.Decoration);
}
/// <summary>
/// Creates a new style from the specified one with
/// the specified text decoration.
/// </summary>
/// <param name="style">The style.</param>
/// <param name="decoration">The text decoration.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Style WithDecoration(this Style style, Decoration decoration)
{
if (style is null)
{
throw new ArgumentNullException(nameof(style));
}
return new Style(
foreground: style.Foreground,
background: style.Background,
decoration: decoration);
}
}
}

View File

@@ -1,5 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console namespace Spectre.Console
{ {
@@ -8,13 +10,58 @@ namespace Spectre.Console
/// </summary> /// </summary>
public static class TableExtensions public static class TableExtensions
{ {
/// <summary>
/// Adds multiple columns to the table.
/// </summary>
/// <param name="table">The table to add the column to.</param>
/// <param name="columns">The columns to add.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddColumns(this Table table, params TableColumn[] columns)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
foreach (var column in columns)
{
table.AddColumn(column);
}
return table;
}
/// <summary>
/// Adds an empty row to the table.
/// </summary>
/// <param name="table">The table to add the row to.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddEmptyRow(this Table table)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
var columns = new IRenderable[table.ColumnCount];
Enumerable.Range(0, table.ColumnCount).ForEach(index => columns[index] = Text.Empty);
table.AddRow(columns);
return table;
}
/// <summary> /// <summary>
/// Adds a column to the table. /// Adds a column to the table.
/// </summary> /// </summary>
/// <param name="table">The table to add the column to.</param> /// <param name="table">The table to add the column to.</param>
/// <param name="column">The column to add.</param> /// <param name="column">The column to add.</param>
/// <returns>The added <see cref="TableColumn"/> instance.</returns> /// <param name="configure">Delegate that can be used to configure the added column.</param>
public static TableColumn AddColumn(this Table table, string column) /// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddColumn(this Table table, string column, Action<TableColumn>? configure = null)
{ {
if (table is null) if (table is null)
{ {
@@ -27,9 +74,10 @@ namespace Spectre.Console
} }
var tableColumn = new TableColumn(column); var tableColumn = new TableColumn(column);
table.AddColumn(tableColumn); configure?.Invoke(tableColumn);
return tableColumn; table.AddColumn(tableColumn);
return table;
} }
/// <summary> /// <summary>
@@ -37,7 +85,8 @@ namespace Spectre.Console
/// </summary> /// </summary>
/// <param name="table">The table to add the columns to.</param> /// <param name="table">The table to add the columns to.</param>
/// <param name="columns">The columns to add.</param> /// <param name="columns">The columns to add.</param>
public static void AddColumns(this Table table, params string[] columns) /// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddColumns(this Table table, params string[] columns)
{ {
if (table is null) if (table is null)
{ {
@@ -53,6 +102,8 @@ namespace Spectre.Console
{ {
AddColumn(table, column); AddColumn(table, column);
} }
return table;
} }
/// <summary> /// <summary>
@@ -60,7 +111,8 @@ namespace Spectre.Console
/// </summary> /// </summary>
/// <param name="table">The table to add the row to.</param> /// <param name="table">The table to add the row to.</param>
/// <param name="columns">The row columns to add.</param> /// <param name="columns">The row columns to add.</param>
public static void AddRow(this Table table, params string[] columns) /// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table AddRow(this Table table, params string[] columns)
{ {
if (table is null) if (table is null)
{ {
@@ -73,6 +125,7 @@ namespace Spectre.Console
} }
table.AddRow(columns.Select(column => new Markup(column)).ToArray()); table.AddRow(columns.Select(column => new Markup(column)).ToArray());
return table;
} }
/// <summary> /// <summary>

View File

@@ -10,16 +10,16 @@ namespace Spectre.Console
/// a "safe" border on legacy consoles that might not be able /// a "safe" border on legacy consoles that might not be able
/// to render non-ASCII characters. /// to render non-ASCII characters.
/// </summary> /// </summary>
bool SafeBorder { get; set; } bool UseSafeBorder { get; set; }
/// <summary> /// <summary>
/// Gets or sets the kind of border to use. /// Gets or sets the border.
/// </summary> /// </summary>
public BorderKind Border { get; set; } public Border Border { get; set; }
/// <summary> /// <summary>
/// Gets or sets the border color. /// Gets or sets the border style.
/// </summary> /// </summary>
public Color? BorderColor { get; set; } public Style? BorderStyle { get; set; }
} }
} }

View File

@@ -0,0 +1,13 @@
namespace Spectre.Console
{
/// <summary>
/// Represents something that can overflow.
/// </summary>
public interface IOverflowable
{
/// <summary>
/// Gets or sets the text overflow strategy.
/// </summary>
Overflow? Overflow { get; set; }
}
}

View File

@@ -21,7 +21,7 @@ namespace Spectre.Console.Internal
{ {
if (_out.IsStandardOut()) if (_out.IsStandardOut())
{ {
return System.Console.BufferWidth; return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth);
} }
return Constants.DefaultBufferWidth; return Constants.DefaultBufferWidth;
@@ -34,7 +34,7 @@ namespace Spectre.Console.Internal
{ {
if (_out.IsStandardOut()) if (_out.IsStandardOut())
{ {
return System.Console.BufferHeight; return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight);
} }
return Constants.DefaultBufferHeight; return Constants.DefaultBufferHeight;

View File

@@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Internal namespace Spectre.Console.Internal
{ {
internal static class ConsoleExtensions internal static class AnsiConsoleExtensions
{ {
public static IDisposable PushStyle(this IAnsiConsole console, Style style) public static IDisposable PushStyle(this IAnsiConsole console, Style style)
{ {

View File

@@ -24,7 +24,7 @@ namespace Spectre.Console.Internal
{ {
if (_out.IsStandardOut()) if (_out.IsStandardOut())
{ {
return System.Console.BufferWidth; return ConsoleHelper.GetSafeBufferWidth(Constants.DefaultBufferWidth);
} }
return Constants.DefaultBufferWidth; return Constants.DefaultBufferWidth;
@@ -37,7 +37,7 @@ namespace Spectre.Console.Internal
{ {
if (_out.IsStandardOut()) if (_out.IsStandardOut())
{ {
return System.Console.BufferHeight; return ConsoleHelper.GetSafeBufferHeight(Constants.DefaultBufferHeight);
} }
return Constants.DefaultBufferHeight; return Constants.DefaultBufferHeight;

View File

@@ -0,0 +1,43 @@
using System.IO;
namespace Spectre.Console.Internal
{
internal static class ConsoleHelper
{
public static int GetSafeBufferWidth(int defaultValue = Constants.DefaultBufferWidth)
{
try
{
var width = System.Console.BufferWidth;
if (width == 0)
{
width = defaultValue;
}
return width;
}
catch (IOException)
{
return defaultValue;
}
}
public static int GetSafeBufferHeight(int defaultValue = Constants.DefaultBufferWidth)
{
try
{
var height = System.Console.BufferHeight;
if (height == 0)
{
height = defaultValue;
}
return height;
}
catch (IOException)
{
return defaultValue;
}
}
}
}

View File

@@ -0,0 +1,24 @@
namespace Spectre.Console
{
/// <summary>
/// Represents text overflow.
/// </summary>
public enum Overflow
{
/// <summary>
/// Put any excess characters on the next line.
/// </summary>
Fold = 0,
/// <summary>
/// Truncates the text at the end of the line.
/// </summary>
Crop = 1,
/// <summary>
/// Truncates the text at the end of the line and
/// also inserts an ellipsis character.
/// </summary>
Ellipsis = 2,
}
}

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