Compare commits

...

20 Commits

Author SHA1 Message Date
Patrik Svensson
7ef1ac483a Fix overflow splitting bug
Closes #93
2020-10-17 12:37:20 +02:00
Takahito Yamatoya
c0875c912a fix incorrect link 2020-10-17 12:16:18 +02:00
Patrik Svensson
3f2ca49071 Add calendar control
Closes #101
2020-10-16 23:02:53 +02:00
Khalid Abuhakmeh
0a0380ae0a Don't throw when console is small
this just returns an empty collection when
take is 0. It leads to some strange output, but
it doesn't blow up.

 #93
2020-10-08 22:17:12 +02:00
Patrik Svensson
ae92c606bb Add support for remapping emojis
Closes #94
2020-10-07 17:33:05 +02:00
Patrik Svensson
68e92f3365 Add various exception improvements 2020-10-07 11:57:28 +02:00
Patrik Svensson
39a8588dc3 Rename ExceptionFormats.None 2020-10-07 11:57:28 +02:00
Patrik Svensson
3c3afe7439 Add support for rendering exceptions 2020-10-05 07:03:02 +02:00
Gary Ewan Park
971f9032ba (doc) Dynamically generate sections
Rather than hard-coding them.  Then, as the number of sections increase,
no additional changes will be required here.
2020-10-03 16:30:21 +02:00
Gary Ewan Park
5149557560 (doc) Remove hard-coded link
Rather than specifying spectre.console directly in the layout, figure out
the URL using GetLink method.
2020-10-03 16:30:21 +02:00
Khalid Abuhakmeh
e0947708c9 Generalized Search
Generalized the search to work with any table
with some basic classes and some updated
JavaScript, we can now search colors and emojis.
2020-10-02 19:39:02 +02:00
Patrik Svensson
b1db8a9403 Clean up border code
Removed caching that really didn't do anything anymore.
2020-10-01 23:05:02 +02:00
Khalid Abuhakmeh
a2f8652575 Add Search To Emoji Page
This adds a typeahead search feature
for the very large emoji table. It filters as you type to find if an emoji exists or not.

The JavaScript could be adapted to work on all tables in the future.
2020-10-01 22:00:39 +02:00
Khalid Abuhakmeh
672faa131f Update Docs Readme
Added some more context to the docs readme for
folks looking to contribute. Also added the license
so its clear what the license of the docs site is. As
a small tweak I removed a duplicate string value and
use the Constant.
2020-10-01 20:51:32 +02:00
Patrik Svensson
e429f6434b Update examples 2020-10-01 14:43:08 +02:00
Patrik Svensson
93ec7401c8 Add support for markdown tables
Closes #85

* Split Border into BoxBorder and TableBorder
* Change how different table parts are composed
* Add markdown table border
2020-10-01 14:43:08 +02:00
Patrik Svensson
697273917e Move console encoder to rendering namespace 2020-09-21 17:07:05 +02:00
Patrik Svensson
2943535973 Make segments immutable 2020-09-21 17:03:17 +02:00
Patrik Svensson
cd0d182f12 Add support for recording console output
This commit adds support for recording console output
as well as exporting it to either text or HTML. A user can
also provide their own encoder if they wish.
2020-09-21 13:33:28 +02:00
Patrik Svensson
b197f278ed Add support for rows
Closes #69
2020-09-20 19:17:33 +02:00
162 changed files with 5124 additions and 1295 deletions

View File

@@ -65,11 +65,13 @@ jobs:
run: |
dotnet tool restore
dotnet example info
dotnet example table
dotnet example grid
dotnet example panel
dotnet example tables
dotnet example grids
dotnet example panels
dotnet example colors
dotnet example emojis
dotnet example exceptions
dotnet example calendars
- name: Build
shell: bash

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@
[Pp]ackages/
/.artifacts/
/[Tt]ools/
.idea
.DS_Store
# Cakeup

View File

@@ -1,8 +1,50 @@
# Documentation
Preview the documentation locally by running the following
from your favourite shell:
To start contributing to the [Spectre.Console](https://github.com/spectresystems/spectre.console) documentation, you will need the [.NET Core SDK](https://dot.net) 3.1 or higher.
## Running Preview Site
The documentation site uses [Statiq](https://statiq.dev), a static site generator. To build the documentation site run the following in a command-line terminal.
```
> dotnet run -- preview --virtual-dir "spectre.console"
> dotnet run preview --virtual-dir "spectre.console"
```
After the build is complete, you can navigate to [http://localhost:5080/spectre.consle](http://localhost:5080/spectre.console).
**Note that the site runs under a virtual directory.**
## Editing Content
The documentation is written using [Markdown](https://www.markdownguide.org/basic-syntax/).
Markdown files can be found under the following directories:
- [/input](./input)
- [/appendix](./input/appendix)
## Editing Layout
Layout and styling can also be found in the [input](./input) directory. Look for Sass, Css, and Images under the [assets](./input/assets) directory.
## Custom Build Features
The documentation site has custom enhancements to Statiq located under the [./src](./src) directory. Enhancements to the build process include:
- [Extension Methods](./src/Extensions)
- [Models](./src/Models)
- [Pipelines](./src/Pipelines)
- [Shortcodes](./src/Shortcodes)
- [Utilities](./src/Utilities)
## License
MIT License
Copyright (c) 2020 Spectre Systems AB
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -5,8 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/spectre.console/assets/bootstrap/bootstrap.css" rel="stylesheet" />
<link href="/spectre.console/assets/css/styles.css" rel="stylesheet" />
<link href="@Context.GetLink("/assets/bootstrap/bootstrap.css")" rel="stylesheet" />
<link href="@Context.GetLink("/assets/css/styles.css")" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&family=Roboto+Slab:wght@400;700&family=Roboto:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap" rel="stylesheet" data-no-mirror>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.19.0/themes/prism.css">
@@ -29,7 +29,7 @@
<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>
<a class="navbar-brand" href="@Context.GetLink("/")"><img id="logo" src="@Context.GetLink("/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>

View File

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

View File

@@ -13,4 +13,19 @@ in markup text such as `AnsiConsole.Markup("[maroon on blue]Hello[/]")`.
# Standard colors
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
<input
class="form-control w-100 filter"
data-table="color-results"
type="text" placeholder="Search Colors..." autocomplete="off"
aria-label="Search Colors">
</div>
<?# ColorTable /?>
<script type="text/javascript" src="../assets/js/table-search.js"></script>

View File

@@ -30,9 +30,43 @@ var phrase = "Mmmm :birthday_cake:";
var rendered = Emoji.Replace(phrase);
```
# Remapping or adding an emoji
Sometimes you want to remap an existing emoji, or
add a completely new one. For this you can use the
`Emoji.Remap` method. This approach works both with
markup strings and `Emoji.Replace`.
```csharp
// Remap the emoji
Emoji.Remap("globe_showing_europe_africa", "😄");
// Render markup
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
// Replace emojis in string
var phrase = "Hello :globe_showing_europe_africa:!";
var rendered = Emoji.Replace(phrase);
```
# Emojis
_The images in the table below might not render correctly in your
browser for the same reasons mentioned in the `Compatibility` section._
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
<input
class="form-control w-100 filter"
data-table="emoji-results"
type="text" placeholder="Search Emojis..." autocomplete="off"
aria-label="Search Emojis">
</div>
<?# EmojiTable /?>
<script type="text/javascript" src="../assets/js/table-search.js"></script>

View File

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

View File

@@ -1,10 +0,0 @@
Title: Appendix
Order: 10
---
# Sections
* [Styles](xref:styles)
* [Colors](xref:colors)
* [Borders](xref:borders)
* [Emojis](xref:emojis)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

View File

@@ -0,0 +1,35 @@
$(document).ready(function () {
$('.filter').each(function () {
let input = this;
let table = document.getElementById(input.dataset.table);
input.onkeyup = function (event) {
if (event.key === "Enter") {
event.preventDefault();
event.stopPropagation();
return false;
}
let value = input.value.toUpperCase();
let rows = table.getElementsByClassName('search-row');
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
let match =
new RegExp(value, "i").test(row.textContent) ||
value === '';
if (match) {
row.style.display = 'table-row';
} else {
row.style.display = 'none';
}
}
}; // keyup
})
}); // ready

52
docs/input/exceptions.md Normal file
View File

@@ -0,0 +1,52 @@
Title: Exceptions
Order: 3
---
Exceptions isn't always readable when viewed in the terminal.
You can make exception a bit more readable by using the `WriteException` method.
```csharp
AnsiConsole.WriteException(ex);
```
<img src="assets/images/exception.png" style="max-width: 100%;">
## Shortening parts
You can also shorten specific parts of the exception to make it even
more readable, and make paths clickable hyperlinks. Whether or not
the hyperlinks are clickable is up to the terminal.
```csharp
AnsiConsole.WriteException(ex,
ExceptionFormat.ShortenPaths | ExceptionFormat.ShortenTypes |
ExceptionFormat.ShortenMethods | ExceptionFormat.ShowLinks);
```
<img src="assets/images/compact_exception.png" style="max-width: 100%;">
## Customizing exception output
In addition to shorten specific part of the exception, you can
also override the default styling.
```csharp
AnsiConsole.WriteException(ex, new ExceptionSettings
{
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
Style = new ExceptionStyle
{
Exception = Style.WithForeground(Color.Grey),
Message = Style.WithForeground(Color.White),
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
Parenthesis = Style.WithForeground(Color.Cornsilk1),
Method = Style.WithForeground(Color.Red),
ParameterName = Style.WithForeground(Color.Cornsilk1),
ParameterType = Style.WithForeground(Color.Red),
Path = Style.WithForeground(Color.Red),
LineNumber = Style.WithForeground(Color.Cornsilk1),
}
});
```
<img src="assets/images/custom_exception.png" style="max-width: 100%;">

View File

@@ -43,6 +43,12 @@ AnsiConsole.Markup("[[Hello]] "); // [Hello]
AnsiConsole.Markup("[red][[World]][/]"); // [World]
```
You can also use the `SafeMarkup` extension method.
```csharp
AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".SafeMarkup());
```
# Setting background color
You can set the background color in markup by prefixing the color with
@@ -61,10 +67,19 @@ To output an emoji as part of markup, you can use emoji shortcodes.
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
```
For a list of emoji, see the [Emojis](xref:styles) appendix section.
For a list of emoji, see the [Emojis](xref:emojis) appendix section.
# Colors
In the examples above, all colors was referenced by their name,
but you can also use the hex or rgb representation for colors in markdown.
```csharp
AnsiConsole.Markup("[red]Foo[/] ");
AnsiConsole.Markup("[#ff0000]Bar[/] ");
AnsiConsole.Markup("[rgb(255,0,0)]Baz[/] ");
```
For a list of colors, see the [Colors](xref:colors) appendix section.
# Styles

View File

@@ -20,10 +20,7 @@ namespace Docs
public static Bootstrapper ConfigureDeployment(this Bootstrapper bootstrapper, string deployBranch)
{
if (bootstrapper != null)
{
bootstrapper.AddSetting(Constants.Deployment.TargetBranch, deployBranch);
}
bootstrapper?.AddSetting(Constants.Deployment.TargetBranch, deployBranch);
return bootstrapper;
}
}

View File

@@ -9,15 +9,13 @@ namespace Docs.Pipelines
{
public class ColorsPipeline : Pipeline
{
public const string Url = "https://raw.githubusercontent.com/spectresystems/spectre.console/main/resources/scripts/Generator/Data/colors.json";
public ColorsPipeline()
{
InputModules = new ModuleList
{
new ExecuteConfig(
Config.FromContext(ctx => {
return new ReadWeb(Url);
return new ReadWeb(Constants.Colors.Url);
}))
};

View File

@@ -21,7 +21,7 @@ namespace Docs.Shortcodes
.First().Object;
// Headers
var table = new XElement("table", new XAttribute("class", "table"));
var table = new XElement("table", new XAttribute("class", "table"), new XAttribute("id", "color-results"));
var header = new XElement("tr", new XAttribute("class", "color-row"));
header.Add(new XElement("th", ""));
header.Add(new XElement("th", "#"));
@@ -44,7 +44,7 @@ namespace Docs.Shortcodes
var clr = new XElement("td", new XElement("code", color.ClrName));
// Create row
var row = new XElement("tr");
var row = new XElement("tr", new XAttribute("class", "search-row"));
row.Add(rep);
row.Add(name);
row.Add(number);

View File

@@ -18,8 +18,8 @@ namespace Docs.Shortcodes
.First().Object;
// Headers
var table = new XElement("table", new XAttribute("class", "table"));
var header = new XElement("tr", new XAttribute("class", "emoji-row"));
var table = new XElement("table", new XAttribute("class", "table"), new XAttribute("id", "emoji-results"));
var header = new XElement("tr", new XAttribute("class", "emoji-row-header"));
header.Add(new XElement("th", ""));
header.Add(new XElement("th", "Markup"));
header.Add(new XElement("th", "Constant"));
@@ -28,9 +28,9 @@ namespace Docs.Shortcodes
foreach (var emoji in emojis)
{
var code = emoji.Code.Replace("U+0000", "U+").Replace("U+000", "U+");
var icon = string.Format("&#x{0};", emoji.Code.Replace("U+", string.Empty));
var icon = $"&#x{emoji.Code.Replace("U+", string.Empty)};";
var row = new XElement("tr");
var row = new XElement("tr", new XAttribute("class", "search-row"));
row.Add(new XElement("td", icon));
row.Add(new XElement("td", new XElement("code", $":{emoji.Id}:")));
row.Add(new XElement("td", new XElement("code", emoji.Name)));

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Borders</Title>
<Description>Demonstrates the different kind of borders.</Description>
</PropertyGroup>

View File

@@ -1,40 +1,58 @@
using Spectre.Console;
using Spectre.Console.Rendering;
namespace Borders
namespace BordersExample
{
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),
};
// Render panel borders
AnsiConsole.WriteLine();
AnsiConsole.Render(new Columns(items).Collapse());
AnsiConsole.MarkupLine("[white bold underline]PANEL BORDERS[/]");
AnsiConsole.WriteLine();
RenderPanelBorders();
// Render table borders
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[white bold underline]TABLE BORDERS[/]");
AnsiConsole.WriteLine();
RenderTableBorders();
}
private static IRenderable Create(string name, Border border)
private static void RenderPanelBorders()
{
static IRenderable CreatePanel(string name, BoxBorder border)
{
return new Panel($"This is a panel with\nthe [yellow]{name}[/] border.")
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.SetBorder(border);
}
var items = new[]
{
CreatePanel("Ascii", BoxBorder.Ascii),
CreatePanel("Square", BoxBorder.Square),
CreatePanel("Rounded", BoxBorder.Rounded),
CreatePanel("Heavy", BoxBorder.Heavy),
CreatePanel("Double", BoxBorder.Double),
CreatePanel("None", BoxBorder.None),
};
AnsiConsole.Render(
new Padder(
new Columns(items).PadRight(2),
new Padding(2,0,0,0)));
}
private static void RenderTableBorders()
{
static IRenderable CreateTable(string name, TableBorder border)
{
var table = new Table().SetBorder(border);
table.AddColumns("[yellow]Header 1[/]", "[yellow]Header 2[/]");
table.AddColumn("[yellow]Header 1[/]");
table.AddColumn("[yellow]Header 2[/]", col => col.RightAligned());
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
@@ -43,5 +61,29 @@ namespace Borders
.SetBorderStyle(Style.Parse("grey"))
.NoBorder();
}
var items = new[]
{
CreateTable("Ascii", TableBorder.Ascii),
CreateTable("Ascii2", TableBorder.Ascii2),
CreateTable("AsciiDoubleHead", TableBorder.AsciiDoubleHead),
CreateTable("Horizontal", TableBorder.Horizontal),
CreateTable("Simple", TableBorder.Simple),
CreateTable("SimpleHeavy", TableBorder.SimpleHeavy),
CreateTable("Minimal", TableBorder.Minimal),
CreateTable("MinimalHeavyHead", TableBorder.MinimalHeavyHead),
CreateTable("MinimalDoubleHead", TableBorder.MinimalDoubleHead),
CreateTable("Square", TableBorder.Square),
CreateTable("Rounded", TableBorder.Rounded),
CreateTable("Heavy", TableBorder.Heavy),
CreateTable("HeavyEdge", TableBorder.HeavyEdge),
CreateTable("HeavyHead", TableBorder.HeavyHead),
CreateTable("Double", TableBorder.Double),
CreateTable("DoubleEdge", TableBorder.DoubleEdge),
CreateTable("Markdown", TableBorder.Markdown),
};
AnsiConsole.Render(new Columns(items).Collapse());
}
}
}

View File

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

View File

@@ -0,0 +1,68 @@
using System.Collections.Generic;
using Spectre.Console;
using Spectre.Console.Rendering;
namespace Calendars
{
public static class Program
{
public static void Main(string[] args)
{
AnsiConsole.WriteLine();
AnsiConsole.Render(
new Columns(GetCalendars())
.Collapse());
}
private static IEnumerable<IRenderable> GetCalendars()
{
yield return EmbedInPanel(
"Invariant calendar",
new Calendar(2020, 10)
.SimpleHeavyBorder()
.SetHighlightStyle(Style.Parse("red"))
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"Swedish calendar (sv-SE)",
new Calendar(2020, 10)
.RoundedBorder()
.SetHighlightStyle(Style.Parse("blue"))
.SetCulture("sv-SE")
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"German calendar (de-DE)",
new Calendar(2020, 10)
.MarkdownBorder()
.SetHighlightStyle(Style.Parse("yellow"))
.SetCulture("de-DE")
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
yield return EmbedInPanel(
"Italian calendar (de-DE)",
new Calendar(2020, 10)
.DoubleBorder()
.SetHighlightStyle(Style.Parse("green"))
.SetCulture("it-IT")
.AddCalendarEvent("An event", 2020, 9, 22)
.AddCalendarEvent("Another event", 2020, 10, 2)
.AddCalendarEvent("A third event", 2020, 10, 13));
}
private static IRenderable EmbedInPanel(string title, Calendar calendar)
{
return new Panel(calendar)
.Expand()
.RoundedBorder()
.SetBorderStyle(Style.Parse("grey"))
.SetHeader($" {title} ", Style.Parse("yellow"));
}
}
}

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Colors</Title>
<Description>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</Description>
</PropertyGroup>

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Columns</Title>
<Description>Demonstrates how to render data into columns.</Description>
</PropertyGroup>

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Emojis</Title>
<Description>Demonstrates how to render emojis.</Description>
</PropertyGroup>

View File

@@ -1,17 +1,24 @@
using System;
using Spectre.Console;
namespace Emojis
namespace EmojiExample
{
public static class Program
{
public static void Main(string[] args)
{
// Markup
// Show a known emoji
RenderEmoji();
// Show a remapped emoji
Emoji.Remap("globe_showing_europe_africa", Emoji.Known.GrinningFaceWithSmilingEyes);
RenderEmoji();
}
private static void RenderEmoji()
{
AnsiConsole.Render(
new Panel("[yellow]Hello :globe_showing_europe_africa:![/]")
.RoundedBorder()
.SetHeader("Markup"));
.RoundedBorder());
}
}
}

View File

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

View File

@@ -0,0 +1,66 @@
using System;
using System.Security.Authentication;
using Spectre.Console;
namespace Exceptions
{
public static class Program
{
public static void Main(string[] args)
{
try
{
DoMagic(42, null);
}
catch (Exception ex)
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Default[/]").Expand());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex);
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Compact[/]").Expand());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks);
AnsiConsole.WriteLine();
AnsiConsole.Render(new Panel("[u]Custom colors[/]").Expand());
AnsiConsole.WriteLine();
AnsiConsole.WriteException(ex, new ExceptionSettings
{
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
Style = new ExceptionStyle
{
Exception = Style.WithForeground(Color.Grey),
Message = Style.WithForeground(Color.White),
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
Parenthesis = Style.WithForeground(Color.Cornsilk1),
Method = Style.WithForeground(Color.Red),
ParameterName = Style.WithForeground(Color.Cornsilk1),
ParameterType = Style.WithForeground(Color.Red),
Path = Style.WithForeground(Color.Red),
LineNumber = Style.WithForeground(Color.Cornsilk1),
}
});
}
}
private static void DoMagic(int foo, string[,] bar)
{
try
{
CheckCredentials(foo, bar);
}
catch(Exception ex)
{
throw new InvalidOperationException("Whaaat?", ex);
}
}
private static void CheckCredentials(int qux, string[,] corgi)
{
throw new InvalidCredentialException("The credentials are invalid.");
}
}
}

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Grids</Title>
<Description>Demonstrates how to render grids in a console.</Description>
</PropertyGroup>

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Info</Title>
<Description>Displays the capabilities of the current console.</Description>
</PropertyGroup>

View File

@@ -1,6 +1,6 @@
using Spectre.Console;
namespace Info
namespace InfoExample
{
public static class Program
{
@@ -9,19 +9,20 @@ namespace Info
var grid = new Grid()
.AddColumn(new GridColumn().NoWrap().PadRight(4))
.AddColumn()
.AddRow("[b]:artist_palette: Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
.AddRow("[b]:nail_polish: Supports ansi?[/]", $"{GetEmoji(AnsiConsole.Capabilities.SupportsAnsi)}")
.AddRow("[b]:top_hat: Legacy console?[/]", $"{GetEmoji(AnsiConsole.Capabilities.LegacyConsole)}")
.AddRow("[b]:left_right_arrow: Buffer width[/]", $"{AnsiConsole.Console.Width}")
.AddRow("[b]:up_down_arrow: Buffer height[/]", $"{AnsiConsole.Console.Height}");
.AddRow("[b]Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsAnsi)}")
.AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Capabilities.LegacyConsole)}")
.AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Width}")
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Height}");
AnsiConsole.Render(
new Panel(grid)
.SetHeader("Information"));
}
private static string GetEmoji(bool value) => value
? ":thumbs_up:"
: ":thumbs_down:";
private static string YesNo(bool value)
{
return value ? "Yes" : "No";
}
}
}

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Links</Title>
<Description>Demonstrates how to render links in a console.</Description>
</PropertyGroup>

View File

@@ -1,6 +1,6 @@
using Spectre.Console;
namespace Links
namespace LinkExample
{
public static class Program
{

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Panels</Title>
<Description>Demonstrates how to render items in panels.</Description>
</PropertyGroup>

View File

@@ -13,7 +13,7 @@ namespace PanelExample
AnsiConsole.Render(
new Panel(
new Panel(content)
.SetBorder(Border.Rounded)));
.SetBorder(BoxBorder.Rounded)));
// Left adjusted panel with text
AnsiConsole.Render(

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Spectre.Console;
namespace TableExample
@@ -35,7 +36,7 @@ namespace TableExample
private static void RenderBigTable()
{
// Create the table.
var table = new Table().SetBorder(Border.Rounded);
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn("[red underline]Foo[/]");
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
@@ -57,7 +58,7 @@ namespace TableExample
private static void RenderNestedTable()
{
// Create simple table.
var simple = new Table().SetBorder(Border.Rounded).SetBorderColor(Color.Red);
var simple = new Table().SetBorder(TableBorder.Rounded).SetBorderColor(Color.Red);
simple.AddColumn(new TableColumn("[u]Foo[/]").Centered());
simple.AddColumn(new TableColumn("[u]Bar[/]"));
simple.AddColumn(new TableColumn("[u]Baz[/]"));
@@ -66,7 +67,7 @@ namespace TableExample
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
// Create other table.
var second = new Table().SetBorder(Border.Square).SetBorderColor(Color.Green);
var second = new Table().SetBorder(TableBorder.Square).SetBorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]"));
second.AddColumn(new TableColumn("[u]Baz[/]"));
@@ -74,7 +75,7 @@ namespace TableExample
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
var table = new Table().SetBorder(Border.Rounded);
var table = new Table().SetBorder(TableBorder.Rounded);
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]Baz[/]").SetBorderColor(Color.Blue)));

View File

@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Tables</Title>
<Description>Demonstrates how to render tables in a console.</Description>
</PropertyGroup>

View File

@@ -2,6 +2,6 @@
"projects": [ "src" ],
"sdk": {
"version": "3.1.301",
"rollForward": "latestMajor"
"rollForward": "latestPatch"
}
}

View File

@@ -87,3 +87,6 @@ dotnet_diagnostic.RCS1227.severity = none
# IDE0004: Remove Unnecessary Cast
dotnet_diagnostic.IDE0004.severity = warning
# CA1810: Initialize reference type static fields inline
dotnet_diagnostic.CA1810.severity = none

View File

@@ -24,3 +24,6 @@ dotnet_diagnostic.CA2000.severity = none
# SA1118: Parameter should not span multiple lines
dotnet_diagnostic.SA1118.severity = none
# CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = none

View File

@@ -0,0 +1,23 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Spectre.Console.Tests.Data
{
public static class TestExceptions
{
[SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "<Pending>")]
public static bool MethodThatThrows(int? number) => throw new InvalidOperationException("Throwing!");
public static void ThrowWithInnerException()
{
try
{
MethodThatThrows(null);
}
catch (Exception ex)
{
throw new InvalidOperationException("Something threw!", ex);
}
}
}
}

View File

@@ -1,13 +1,34 @@
using System;
using System.Text.RegularExpressions;
namespace Spectre.Console.Tests
{
public static class StringExtensions
{
private static readonly Regex _lineNumberRegex = new Regex(":\\d+", RegexOptions.Singleline);
private static readonly Regex _filenameRegex = new Regex("\\sin\\s.*cs:nn", RegexOptions.Multiline);
public static string NormalizeLineEndings(this string text)
{
return text?.Replace("\r\n", "\n", StringComparison.OrdinalIgnoreCase)
?.Replace("\r", string.Empty, StringComparison.OrdinalIgnoreCase);
}
public static string NormalizeStackTrace(this string text)
{
text = _lineNumberRegex.Replace(text, match =>
{
return ":nn";
});
return _filenameRegex.Replace(text, match =>
{
var value = match.Value;
var index = value.LastIndexOfAny(new[] { '\\', '/' });
var filename = value.Substring(index + 1, value.Length - index - 1);
return $" in /xyz/{filename}";
});
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
using Spectre.Console.Tests.Tools;
namespace Spectre.Console.Tests
@@ -36,9 +37,9 @@ namespace Spectre.Console.Tests
_writer?.Dispose();
}
public void Write(string text, Style style)
public void Write(Segment segment)
{
_console.Write(text, style);
_console.Write(segment);
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Tests.Tools
{
public sealed class MarkupConsoleFixture : IDisposable, IAnsiConsole
{
private readonly StringWriter _writer;
private readonly IAnsiConsole _console;
public string Output => _writer.ToString().TrimEnd('\n');
public Capabilities Capabilities => _console.Capabilities;
public Encoding Encoding => _console.Encoding;
public int Width { get; }
public int Height => _console.Height;
public MarkupConsoleFixture(ColorSystem system, AnsiSupport ansi = AnsiSupport.Yes, int width = 80)
{
_writer = new StringWriter();
_console = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = ansi,
ColorSystem = (ColorSystemSupport)system,
Out = _writer,
LinkIdentityGenerator = new TestLinkIdentityGenerator(),
});
Width = width;
}
public void Dispose()
{
_writer?.Dispose();
}
public void Write(Segment segment)
{
_console.Write(segment);
}
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Tests
{
@@ -40,9 +42,25 @@ namespace Spectre.Console.Tests
Writer.Dispose();
}
public void Write(string text, Style style)
public void Write(Segment segment)
{
Writer.Write(text);
if (segment is null)
{
throw new ArgumentNullException(nameof(segment));
}
Writer.Write(segment.Text);
}
public string[] WriteExceptionAndGetLines(Exception ex, ExceptionFormats formats = ExceptionFormats.Default)
{
this.WriteException(ex, formats);
return Output.NormalizeStackTrace()
.NormalizeLineEndings()
.Split(new char[] { '\n' })
.Select(line => line.TrimEnd())
.ToArray();
}
}
}

View File

@@ -0,0 +1,210 @@
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class BoxBorderTests
{
public sealed class NoBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.None.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.None);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().NoBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe(" Greeting ");
console.Lines[1].ShouldBe(" Hello World ");
console.Lines[2].ShouldBe(" ");
}
}
public sealed class AsciiBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Ascii.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Ascii);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().AsciiBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("+-Greeting----+");
console.Lines[1].ShouldBe("| Hello World |");
console.Lines[2].ShouldBe("+-------------+");
}
}
public sealed class DoubleBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Double.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Double);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().DoubleBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("╔═Greeting════╗");
console.Lines[1].ShouldBe("║ Hello World ║");
console.Lines[2].ShouldBe("╚═════════════╝");
}
}
public sealed class HeavyBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Heavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().HeavyBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┏━Greeting━━━━┓");
console.Lines[1].ShouldBe("┃ Hello World ┃");
console.Lines[2].ShouldBe("┗━━━━━━━━━━━━━┛");
}
}
public sealed class RoundedBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Rounded.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().RoundedBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("╭─Greeting────╮");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("╰─────────────╯");
}
}
public sealed class SquareBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Square.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().SquareBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌─Greeting────┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└─────────────┘");
}
}
private static class Fixture
{
public static Panel GetPanel()
{
return new Panel("Hello World")
.SetHeader("Greeting");
}
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class CalendarTests
{
[Fact]
public void Should_Render_Calendar_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10)
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
console.Lines[1].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
console.Lines[2].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
console.Lines[3].ShouldBe("│ │ │ │ │ 1 │ 2 │ 3* │");
console.Lines[4].ShouldBe("│ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
console.Lines[5].ShouldBe("│ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
console.Lines[6].ShouldBe("│ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
console.Lines[7].ShouldBe("│ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[9].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
}
[Fact]
public void Should_Render_Calendar_Correctly_For_Specific_Culture()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10, 15)
.SetCulture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
console.Lines[4].ShouldBe("│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │");
console.Lines[5].ShouldBe("│ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │");
console.Lines[6].ShouldBe("│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │");
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │");
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
}
[Fact]
public void Should_Render_List_Of_Events_If_Enabled()
{
// Given
var console = new PlainConsole(width: 80);
var calendar = new Calendar(2020, 10, 15)
.SetCulture("de-DE")
.AddCalendarEvent(new DateTime(2020, 9, 1))
.AddCalendarEvent(new DateTime(2020, 10, 3))
.AddCalendarEvent(new DateTime(2020, 10, 12));
// When
console.Render(calendar);
// Then
console.Lines.Count.ShouldBe(10);
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
console.Lines[4].ShouldBe("│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │");
console.Lines[5].ShouldBe("│ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │");
console.Lines[6].ShouldBe("│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │");
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │");
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
}
}
}

View File

@@ -228,6 +228,39 @@ namespace Spectre.Console.Tests.Unit
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Default_Color()
{
// Given, When
var result = Color.Default.ToMarkup();
// Then
result.ShouldBe("default");
}
[Fact]
public void Should_Return_Expected_Markup_For_Known_Color()
{
// Given, When
var result = Color.Red.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Custom_Color()
{
// Given, When
var result = new Color(255, 1, 12).ToMarkup();
// Then
result.ShouldBe("#FF010C");
}
}
public sealed class TheToStringMethod
{
[Fact]

View File

@@ -0,0 +1,99 @@
using System;
using Shouldly;
using Spectre.Console.Tests.Data;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class ExceptionTests
{
[Fact]
public void Should_Write_Exception()
{
// Given
var console = new PlainConsole(width: 1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteExceptionAndGetLines(dex);
// Then
result.Length.ShouldBe(4);
result[0].ShouldBe("System.InvalidOperationException: Throwing!");
result[1].ShouldBe(" at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn");
result[2].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception>b__0_0() in /xyz/ExceptionTests.cs:nn");
result[3].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn");
}
[Fact]
public void Should_Write_Exception_With_Shortened_Types()
{
// Given
var console = new PlainConsole(width: 1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteExceptionAndGetLines(dex, ExceptionFormats.ShortenTypes);
// Then
result.Length.ShouldBe(4);
result[0].ShouldBe("InvalidOperationException: Throwing!");
result[1].ShouldBe(" at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn");
result[2].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Shortened_Types>b__1_0() in /xyz/ExceptionTests.cs:nn");
result[3].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn");
}
[Fact]
public void Should_Write_Exception_With_Shortened_Methods()
{
// Given
var console = new PlainConsole(width: 1024);
var dex = GetException(() => TestExceptions.MethodThatThrows(null));
// When
var result = console.WriteExceptionAndGetLines(dex, ExceptionFormats.ShortenMethods);
// Then
result.Length.ShouldBe(4);
result[0].ShouldBe("System.InvalidOperationException: Throwing!");
result[1].ShouldBe(" at MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn");
result[2].ShouldBe(" at <Should_Write_Exception_With_Shortened_Methods>b__2_0() in /xyz/ExceptionTests.cs:nn");
result[3].ShouldBe(" at GetException(Action action) in /xyz/ExceptionTests.cs:nn");
}
[Fact]
public void Should_Write_Exception_With_Inner_Exception()
{
// Given
var console = new PlainConsole(width: 1024);
var dex = GetException(() => TestExceptions.ThrowWithInnerException());
// When
var result = console.WriteExceptionAndGetLines(dex);
// Then
result.Length.ShouldBe(7);
result[0].ShouldBe("System.InvalidOperationException: Something threw!");
result[1].ShouldBe(" System.InvalidOperationException: Throwing!");
result[2].ShouldBe(" at Spectre.Console.Tests.Data.TestExceptions.MethodThatThrows(Nullable`1 number) in /xyz/Exceptions.cs:nn");
result[3].ShouldBe(" at Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn");
result[4].ShouldBe(" at Spectre.Console.Tests.Data.TestExceptions.ThrowWithInnerException() in /xyz/Exceptions.cs:nn");
result[5].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.<>c.<Should_Write_Exception_With_Inner_Exception>b__3_0() in /xyz/ExceptionTests.cs:nn");
result[6].ShouldBe(" at Spectre.Console.Tests.Unit.ExceptionTests.GetException(Action action) in /xyz/ExceptionTests.cs:nn");
}
public static Exception GetException(Action action)
{
try
{
action?.Invoke();
}
catch (Exception e)
{
return e;
}
throw new InvalidOperationException("Exception harness failed");
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
namespace Spectre.Console.Tests.Unit
@@ -298,5 +300,33 @@ namespace Spectre.Console.Tests.Unit
console.Lines[3].ShouldBe("│ └─────────────┘ │");
console.Lines[4].ShouldBe("└─────────────────┘");
}
[Fact]
public void Should_Wrap_Content_Correctly()
{
// Given
var console = new PlainConsole(width: 84);
var rows = new List<IRenderable>();
var grid = new Grid();
grid.AddColumn(new GridColumn().PadLeft(2).PadRight(0));
grid.AddColumn(new GridColumn().PadLeft(1).PadRight(0));
grid.AddRow("at", "[grey]System.Runtime.CompilerServices.TaskAwaiter.[/][yellow]HandleNonSuccessAndDebuggerNotification[/]([blue]Task[/] task)");
rows.Add(grid);
var panel = new Panel(grid)
.Expand().RoundedBorder()
.SetBorderStyle(Style.WithForeground(Color.Grey))
.SetHeader("Short paths ", Style.WithForeground(Color.Grey));
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(4);
console.Lines[0].ShouldBe("╭─Short paths ─────────────────────────────────────────────────────────────────────╮");
console.Lines[1].ShouldBe("│ at System.Runtime.CompilerServices.TaskAwaiter. │");
console.Lines[2].ShouldBe("│ HandleNonSuccessAndDebuggerNotification(Task task) │");
console.Lines[3].ShouldBe("╰──────────────────────────────────────────────────────────────────────────────────╯");
}
}
}

View File

@@ -0,0 +1,66 @@
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class RecorderTests
{
[Fact]
public void Should_Export_Text_As_Expected()
{
// Given
var console = new PlainConsole();
var recorder = new Recorder(console);
recorder.Render(new Table()
.AddColumns("Foo", "Bar", "Qux")
.AddRow("Corgi", "Waldo", "Zap")
.AddRow(new Panel("Hello World").RoundedBorder()));
// When
var result = recorder.ExportText().Split(new[] { '\n' });
// Then
result.Length.ShouldBe(8);
result[0].ShouldBe("┌─────────────────┬───────┬─────┐");
result[1].ShouldBe("│ Foo │ Bar │ Qux │");
result[2].ShouldBe("├─────────────────┼───────┼─────┤");
result[3].ShouldBe("│ Corgi │ Waldo │ Zap │");
result[4].ShouldBe("│ ╭─────────────╮ │ │ │");
result[5].ShouldBe("│ │ Hello World │ │ │ │");
result[6].ShouldBe("│ ╰─────────────╯ │ │ │");
result[7].ShouldBe("└─────────────────┴───────┴─────┘");
}
[Fact]
public void Should_Export_Html_As_Expected()
{
// Given
var console = new PlainConsole();
var recorder = new Recorder(console);
recorder.Render(new Table()
.AddColumns("[red on black]Foo[/]", "[green bold]Bar[/]", "[blue italic]Qux[/]")
.AddRow("[invert underline]Corgi[/]", "[bold strikethrough]Waldo[/]", "[dim]Zap[/]")
.AddRow(new Panel("[blue]Hello World[/]")
.SetBorderColor(Color.Red).RoundedBorder()));
// When
var html = recorder.ExportHtml();
var result = html.Split(new[] { '\n' });
// Then
result.Length.ShouldBe(10);
result[0].ShouldBe("<pre style=\"font-size:90%;font-family:consolas,'Courier New',monospace\">");
result[1].ShouldBe("<span>┌─────────────────┬───────┬─────┐</span>");
result[2].ShouldBe("<span>│ </span><span style=\"color: #FF0000;background-color: #000000\">Foo</span><span> │ </span><span style=\"color: #008000;font-weight: bold;font-style: italic\">Bar</span><span> │ </span><span style=\"color: #0000FF\">Qux</span><span> │</span>");
result[3].ShouldBe("<span>├─────────────────┼───────┼─────┤</span>");
result[4].ShouldBe("<span>│ </span><span style=\"text-decoration: underline\">Corgi</span><span> │ </span><span style=\"font-weight: bold;font-style: italic;text-decoration: line-through\">Waldo</span><span> │ </span><span style=\"color: #7F7F7F\">Zap</span><span> │</span>");
result[5].ShouldBe("<span>│ </span><span style=\"color: #FF0000\">╭─────────────╮</span><span> │ │ │</span>");
result[6].ShouldBe("<span>│ </span><span style=\"color: #FF0000\">│</span><span> </span><span style=\"color: #0000FF\">Hello World</span><span> </span><span style=\"color: #FF0000\">│</span><span> │ │ │</span>");
result[7].ShouldBe("<span>│ </span><span style=\"color: #FF0000\">╰─────────────╯</span><span> │ │ │</span>");
result[8].ShouldBe("<span>└─────────────────┴───────┴─────┘</span>");
result[9].ShouldBe("</pre>");
}
}
}

View File

@@ -0,0 +1,96 @@
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class RowsTests
{
[Fact]
public void Should_Render_Rows()
{
// Given
var console = new PlainConsole(width: 60);
var rows = new Rows(
new IRenderable[]
{
new Markup("Hello"),
new Table()
.AddColumns("Foo", "Bar")
.AddRow("Baz", "Qux"),
new Markup("World"),
});
// When
console.Render(rows);
// Then
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe("Hello");
console.Lines[1].ShouldBe("┌─────┬─────┐");
console.Lines[2].ShouldBe("│ Foo │ Bar │");
console.Lines[3].ShouldBe("├─────┼─────┤");
console.Lines[4].ShouldBe("│ Baz │ Qux │");
console.Lines[5].ShouldBe("└─────┴─────┘");
console.Lines[6].ShouldBe("World");
}
[Fact]
public void Should_Render_Rows_Correctly_Inside_Other_Widget()
{
// Given
var console = new PlainConsole(width: 60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}), new Text("Qux"));
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe("┌─────────────┬─────┐");
console.Lines[1].ShouldBe("│ Foo │ Bar │");
console.Lines[2].ShouldBe("├─────────────┼─────┤");
console.Lines[3].ShouldBe("│ HELLO WORLD │ │");
console.Lines[4].ShouldBe("│ Hello │ Qux │");
console.Lines[5].ShouldBe("│ World │ │");
console.Lines[6].ShouldBe("└─────────────┴─────┘");
}
[Fact]
public void Should_Render_Rows_Correctly_Inside_Other_Widget_When_Expanded()
{
// Given
var console = new PlainConsole(width: 60);
var table = new Table()
.AddColumns("Foo", "Bar")
.AddRow("HELLO WORLD")
.AddRow(
new Rows(new IRenderable[]
{
new Markup("Hello"),
new Markup("World"),
}).Expand(), new Text("Qux"));
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(7);
console.Lines[0].ShouldBe("┌────────────────────────────────────────────────────┬─────┐");
console.Lines[1].ShouldBe("│ Foo │ Bar │");
console.Lines[2].ShouldBe("├────────────────────────────────────────────────────┼─────┤");
console.Lines[3].ShouldBe("│ HELLO WORLD │ │");
console.Lines[4].ShouldBe("│ Hello │ Qux │");
console.Lines[5].ShouldBe("│ World │ │");
console.Lines[6].ShouldBe("└────────────────────────────────────────────────────┴─────┘");
}
}
}

View File

@@ -317,5 +317,60 @@ namespace Spectre.Console.Tests.Unit
result.ShouldBeFalse();
}
}
public sealed class TheToMarkupMethod
{
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_Color()
{
// Given
var style = new Style(Color.Red);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color()
{
// Given
var style = new Style(Color.Red, Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color_And_Decoration()
{
// Given
var style = new Style(Color.Red, Color.Green, Decoration.Bold | Decoration.Underline);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("bold underline red on green");
}
[Fact]
public void Should_Return_Expected_Markup_For_Style_With_Only_Background_Color()
{
// Given
var style = new Style(background: Color.Green);
// When
var result = style.ToMarkup();
// Then
result.ShouldBe("default on green");
}
}
}
}

View File

@@ -4,7 +4,7 @@ using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class BorderTests
public sealed class TableBorderTests
{
public sealed class NoBorder
{
@@ -12,7 +12,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.None.Visible;
var visibility = TableBorder.None.Visible;
// Then
visibility.ShouldBeFalse();
@@ -24,10 +24,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.None.GetSafeBorder(safe: true);
var border = TableBorder.None.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.None);
border.ShouldBeSameAs(TableBorder.None);
}
}
@@ -55,7 +55,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii.Visible;
var visibility = TableBorder.Ascii.Visible;
// Then
visibility.ShouldBeTrue();
@@ -67,10 +67,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii.GetSafeBorder(safe: true);
var border = TableBorder.Ascii.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii);
border.ShouldBeSameAs(TableBorder.Ascii);
}
}
@@ -101,7 +101,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii2.Visible;
var visibility = TableBorder.Ascii2.Visible;
// Then
visibility.ShouldBeTrue();
@@ -113,10 +113,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii2.GetSafeBorder(safe: true);
var border = TableBorder.Ascii2.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii2);
border.ShouldBeSameAs(TableBorder.Ascii2);
}
}
@@ -147,7 +147,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.AsciiDoubleHead.Visible;
var visibility = TableBorder.AsciiDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
@@ -159,10 +159,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.AsciiDoubleHead.GetSafeBorder(safe: true);
var border = TableBorder.AsciiDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.AsciiDoubleHead);
border.ShouldBeSameAs(TableBorder.AsciiDoubleHead);
}
}
@@ -193,7 +193,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Square.Visible;
var visibility = TableBorder.Square.Visible;
// Then
visibility.ShouldBeTrue();
@@ -205,10 +205,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Square.GetSafeBorder(safe: true);
var border = TableBorder.Square.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@@ -239,7 +239,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Rounded.Visible;
var visibility = TableBorder.Rounded.Visible;
// Then
visibility.ShouldBeTrue();
@@ -251,10 +251,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Rounded.GetSafeBorder(safe: true);
var border = TableBorder.Rounded.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@@ -285,7 +285,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Minimal.Visible;
var visibility = TableBorder.Minimal.Visible;
// Then
visibility.ShouldBeTrue();
@@ -297,10 +297,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Minimal.GetSafeBorder(safe: true);
var border = TableBorder.Minimal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
border.ShouldBeSameAs(TableBorder.Minimal);
}
}
@@ -331,7 +331,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalHeavyHead.Visible;
var visibility = TableBorder.MinimalHeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
@@ -343,10 +343,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalHeavyHead.GetSafeBorder(safe: true);
var border = TableBorder.MinimalHeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
border.ShouldBeSameAs(TableBorder.Minimal);
}
}
@@ -377,7 +377,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalDoubleHead.Visible;
var visibility = TableBorder.MinimalDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
@@ -389,10 +389,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalDoubleHead.GetSafeBorder(safe: true);
var border = TableBorder.MinimalDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.MinimalDoubleHead);
border.ShouldBeSameAs(TableBorder.MinimalDoubleHead);
}
}
@@ -423,7 +423,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Simple.Visible;
var visibility = TableBorder.Simple.Visible;
// Then
visibility.ShouldBeTrue();
@@ -435,10 +435,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Simple.GetSafeBorder(safe: true);
var border = TableBorder.Simple.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
border.ShouldBeSameAs(TableBorder.Simple);
}
}
@@ -469,7 +469,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Horizontal.Visible;
var visibility = TableBorder.Horizontal.Visible;
// Then
visibility.ShouldBeTrue();
@@ -481,10 +481,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Horizontal.GetSafeBorder(safe: true);
var border = TableBorder.Horizontal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Horizontal);
border.ShouldBeSameAs(TableBorder.Horizontal);
}
}
@@ -515,7 +515,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.SimpleHeavy.Visible;
var visibility = TableBorder.SimpleHeavy.Visible;
// Then
visibility.ShouldBeTrue();
@@ -527,10 +527,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.SimpleHeavy.GetSafeBorder(safe: true);
var border = TableBorder.SimpleHeavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
border.ShouldBeSameAs(TableBorder.Simple);
}
}
@@ -561,7 +561,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Heavy.Visible;
var visibility = TableBorder.Heavy.Visible;
// Then
visibility.ShouldBeTrue();
@@ -573,10 +573,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Heavy.GetSafeBorder(safe: true);
var border = TableBorder.Heavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@@ -607,7 +607,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyEdge.Visible;
var visibility = TableBorder.HeavyEdge.Visible;
// Then
visibility.ShouldBeTrue();
@@ -619,10 +619,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyEdge.GetSafeBorder(safe: true);
var border = TableBorder.HeavyEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@@ -653,7 +653,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyHead.Visible;
var visibility = TableBorder.HeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
@@ -665,10 +665,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyHead.GetSafeBorder(safe: true);
var border = TableBorder.HeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@@ -699,7 +699,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Double.Visible;
var visibility = TableBorder.Double.Visible;
// Then
visibility.ShouldBeTrue();
@@ -711,10 +711,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Double.GetSafeBorder(safe: true);
var border = TableBorder.Double.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Double);
border.ShouldBeSameAs(TableBorder.Double);
}
}
@@ -745,7 +745,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.DoubleEdge.Visible;
var visibility = TableBorder.DoubleEdge.Visible;
// Then
visibility.ShouldBeTrue();
@@ -757,10 +757,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.DoubleEdge.GetSafeBorder(safe: true);
var border = TableBorder.DoubleEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.DoubleEdge);
border.ShouldBeSameAs(TableBorder.DoubleEdge);
}
}
@@ -785,12 +785,119 @@ namespace Spectre.Console.Tests.Unit
}
}
public sealed class MarkdownBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = TableBorder.Markdown.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = TableBorder.Markdown.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(TableBorder.Markdown);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().MarkdownBorder();
// 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(" ");
}
[Fact]
public void Should_Render_Left_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Left).MarkdownBorder();
// 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(" ");
}
[Fact]
public void Should_Render_Center_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Center).MarkdownBorder();
// 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(" ");
}
[Fact]
public void Should_Render_Right_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Right).MarkdownBorder();
// 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()
public static Table GetTable(Justify? header1 = null, Justify? header2 = null)
{
var table = new Table();
table.AddColumns("Header 1", "Header 2");
table.AddColumn("Header 1", c => c.Alignment = header1);
table.AddColumn("Header 2", c => c.Alignment = header2);
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return table;

View File

@@ -173,7 +173,7 @@ namespace Spectre.Console.Tests.Unit
{
// A simple table
var console = new PlainConsole(width: 80);
var table = new Table() { Border = Border.Rounded };
var table = new Table() { Border = TableBorder.Rounded };
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right });
@@ -183,7 +183,7 @@ namespace Spectre.Console.Tests.Unit
// Render a table in some panels.
console.Render(new Panel(new Panel(table)
{
Border = Border.Ascii,
Border = BoxBorder.Ascii,
}));
// Then
@@ -255,7 +255,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.Ascii };
var table = new Table { Border = TableBorder.Ascii };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
@@ -278,7 +278,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.Rounded };
var table = new Table { Border = TableBorder.Rounded };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
@@ -301,7 +301,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.None };
var table = new Table { Border = TableBorder.None };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");

View File

@@ -17,11 +17,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{F0575243-121F-4DEE-9F6B-246E26DC0844}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Table", "..\examples\Table\Table.csproj", "{94ECCBA8-7EBF-4B53-8379-52EB2327417E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tables", "..\examples\Tables\Tables.csproj", "{94ECCBA8-7EBF-4B53-8379-52EB2327417E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Panel", "..\examples\Panel\Panel.csproj", "{BFF37228-B376-4ADD-9657-4E501F929713}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Panels", "..\examples\Panels\Panels.csproj", "{BFF37228-B376-4ADD-9657-4E501F929713}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grid", "..\examples\Grid\Grid.csproj", "{C7FF6FDB-FB59-4517-8669-521C96AB7323}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grids", "..\examples\Grids\Grids.csproj", "{C7FF6FDB-FB59-4517-8669-521C96AB7323}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "..\examples\Colors\Colors.csproj", "{1F51C55C-BA4C-4856-9001-0F7924FFB179}"
EndProject
@@ -33,7 +33,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Borders", "..\examples\Bord
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Links", "..\examples\Links\Links.csproj", "{6AF8C93B-AA41-4F44-8B1B-B8D166576174}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emojis", "..\examples\Emojis\Emojis.csproj", "{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emojis", "..\examples\Emojis\Emojis.csproj", "{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exceptions", "..\examples\Exceptions\Exceptions.csproj", "{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D}"
ProjectSection(SolutionItems) = preProject
..\.github\workflows\ci.yaml = ..\.github\workflows\ci.yaml
..\.github\workflows\docs.yaml = ..\.github\workflows\docs.yaml
..\.github\workflows\publish.yaml = ..\.github\workflows\publish.yaml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calendars", "..\examples\Calendars\Calendars.csproj", "{57691C7D-683D-46E6-AA4F-57A8C5F65D25}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -177,6 +188,30 @@ Global
{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2}.Release|x64.Build.0 = Release|Any CPU
{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2}.Release|x86.ActiveCfg = Release|Any CPU
{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2}.Release|x86.Build.0 = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|x64.ActiveCfg = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|x64.Build.0 = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|x86.ActiveCfg = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Debug|x86.Build.0 = Debug|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|Any CPU.Build.0 = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x64.ActiveCfg = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x64.Build.0 = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x86.ActiveCfg = Release|Any CPU
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x86.Build.0 = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x64.ActiveCfg = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x64.Build.0 = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x86.ActiveCfg = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x86.Build.0 = Debug|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|Any CPU.Build.0 = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x64.ActiveCfg = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x64.Build.0 = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.ActiveCfg = Release|Any CPU
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -191,6 +226,9 @@ Global
{094245E6-4C94-485D-B5AC-3153E878B112} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{6AF8C93B-AA41-4F44-8B1B-B8D166576174} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@@ -0,0 +1,30 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// A console capable of writing ANSI escape sequences.
/// </summary>
public static partial class AnsiConsole
{
/// <summary>
/// Writes an exception to the console.
/// </summary>
/// <param name="exception">The exception to write to the console.</param>
/// <param name="format">The exception format options.</param>
public static void WriteException(Exception exception, ExceptionFormats format = ExceptionFormats.Default)
{
Console.WriteException(exception, format);
}
/// <summary>
/// Writes an exception to the console.
/// </summary>
/// <param name="exception">The exception to write to the console.</param>
/// <param name="settings">The exception settings.</param>
public static void WriteException(Exception exception, ExceptionSettings settings)
{
Console.WriteException(exception, settings);
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// A console capable of writing ANSI escape sequences.
/// </summary>
public static partial class AnsiConsole
{
/// <summary>
/// Starts recording the console output.
/// </summary>
public static void Record()
{
_recorder = new Recorder(_console.Value);
}
/// <summary>
/// Exports all recorded console output as text.
/// </summary>
/// <returns>The recorded output as text.</returns>
public static string ExportText()
{
if (_recorder == null)
{
throw new InvalidOperationException("Cannot export text since a recording hasn't been started.");
}
return _recorder.ExportText();
}
/// <summary>
/// Exports all recorded console output as HTML.
/// </summary>
/// <returns>The recorded output as HTML.</returns>
public static string ExportHtml()
{
if (_recorder == null)
{
throw new InvalidOperationException("Cannot export HTML since a recording hasn't been started.");
}
return _recorder.ExportHtml();
}
/// <summary>
/// Exports all recorded console output using a custom encoder.
/// </summary>
/// <param name="encoder">The encoder to use.</param>
/// <returns>The recorded output.</returns>
public static string ExportCustom(IAnsiConsoleEncoder encoder)
{
if (_recorder == null)
{
throw new InvalidOperationException("Cannot export HTML since a recording hasn't been started.");
}
if (encoder is null)
{
throw new ArgumentNullException(nameof(encoder));
}
return _recorder.Export(encoder);
}
}
}

View File

@@ -1,7 +1,3 @@
using System;
using System.IO;
using Spectre.Console.Internal;
namespace Spectre.Console
{
/// <summary>
@@ -9,9 +5,6 @@ namespace Spectre.Console
/// </summary>
public static partial class AnsiConsole
{
private static ConsoleColor _defaultForeground;
private static ConsoleColor _defaultBackground;
internal static Style CurrentStyle { get; private set; } = Style.Plain;
internal static bool Created { get; private set; }
@@ -42,20 +35,6 @@ namespace Spectre.Console
set => CurrentStyle = CurrentStyle.WithDecoration(value);
}
internal static void Initialize(TextWriter? @out)
{
if (@out?.IsStandardOut() ?? false)
{
Foreground = _defaultForeground = System.Console.ForegroundColor;
Background = _defaultBackground = System.Console.BackgroundColor;
}
else
{
Foreground = _defaultForeground = Color.Silver;
Background = _defaultBackground = Color.Black;
}
}
/// <summary>
/// Resets colors and text decorations.
/// </summary>
@@ -78,8 +57,7 @@ namespace Spectre.Console
/// </summary>
public static void ResetColors()
{
Foreground = _defaultForeground;
Background = _defaultBackground;
CurrentStyle = Style.Plain;
}
}
}

View File

@@ -16,15 +16,16 @@ namespace Spectre.Console
ColorSystem = ColorSystemSupport.Detect,
Out = System.Console.Out,
});
Initialize(System.Console.Out);
Created = true;
return console;
});
private static Recorder? _recorder;
/// <summary>
/// Gets the underlying <see cref="IAnsiConsole"/>.
/// </summary>
public static IAnsiConsole Console => _console.Value;
public static IAnsiConsole Console => _recorder ?? _console.Value;
/// <summary>
/// Gets the console's capabilities.

View File

@@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
{
private readonly Dictionary<BorderPart, string> _lookup;
/// <summary>
/// Gets a value indicating whether or not the border is visible.
/// </summary>
public virtual bool Visible { get; } = true;
/// <summary>
/// Gets the safe border for this border or <c>null</c> if none exist.
/// </summary>
public virtual Border? SafeBorder { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Border"/> class.
/// </summary>
protected Border()
{
_lookup = Initialize();
}
private Dictionary<BorderPart, string> Initialize()
{
var lookup = new Dictionary<BorderPart, string>();
foreach (BorderPart? part in Enum.GetValues(typeof(BorderPart)))
{
if (part == null)
{
continue;
}
var text = GetBoxPart(part.Value);
if (text.Length > 1)
{
throw new InvalidOperationException("A box part cannot contain more than one character.");
}
lookup.Add(part.Value, GetBoxPart(part.Value));
}
return lookup;
}
/// <summary>
/// Gets the string representation of a specific border part.
/// </summary>
/// <param name="part">The part to get a string representation for.</param>
/// <param name="count">The number of repetitions.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(BorderPart part, int count)
{
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBoxPart(part)[0], count));
}
/// <summary>
/// Gets the string representation of a specific border part.
/// </summary>
/// <param name="part">The part to get a string representation for.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(BorderPart part)
{
return _lookup[part].ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Gets the character representing the specified border part.
/// </summary>
/// <param name="part">The part to get the character representation for.</param>
/// <returns>A character representation of the specified border part.</returns>
protected abstract string GetBoxPart(BorderPart part);
}
}

View File

@@ -1,37 +0,0 @@
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

@@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border.
/// </summary>
public sealed class AsciiBorder : 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

@@ -1,37 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder { get; } = 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

@@ -1,37 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareBorder : 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,42 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class BoxBorder
{
/// <summary>
/// Gets an invisible border.
/// </summary>
public static BoxBorder None { get; } = new NoBoxBorder();
/// <summary>
/// Gets an ASCII border.
/// </summary>
public static BoxBorder Ascii { get; } = new AsciiBoxBorder();
/// <summary>
/// Gets a double border.
/// </summary>
[SuppressMessage("Naming", "CA1720:Identifier contains type name")]
public static BoxBorder Double { get; } = new DoubleBoxBorder();
/// <summary>
/// Gets a heavy border.
/// </summary>
public static BoxBorder Heavy { get; } = new HeavyBoxBorder();
/// <summary>
/// Gets a rounded border.
/// </summary>
public static BoxBorder Rounded { get; } = new RoundedBoxBorder();
/// <summary>
/// Gets a square border.
/// </summary>
public static BoxBorder Square { get; } = new SquareBoxBorder();
}
}

View File

@@ -0,0 +1,22 @@
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class BoxBorder
{
/// <summary>
/// Gets the safe border for this border or <c>null</c> if none exist.
/// </summary>
public virtual BoxBorder? SafeBorder { get; }
/// <summary>
/// Gets the string representation of the specified border part.
/// </summary>
/// <param name="part">The part to get the character representation for.</param>
/// <returns>A character representation of the specified border part.</returns>
public abstract string GetPart(BoxBorderPart part);
}
}

View File

@@ -60,6 +60,35 @@ namespace Spectre.Console
Number = null;
}
/// <summary>
/// Blends two colors.
/// </summary>
/// <param name="other">The other color.</param>
/// <param name="factor">The blend factor.</param>
/// <returns>The resulting color.</returns>
public Color Blend(Color other, float factor)
{
// https://github.com/willmcgugan/rich/blob/f092b1d04252e6f6812021c0f415dd1d7be6a16a/rich/color.py#L494
return new Color(
(byte)(R + ((other.R - R) * factor)),
(byte)(G + ((other.G - G) * factor)),
(byte)(B + ((other.B - B) * factor)));
}
/// <summary>
/// Gets the hexadecimal representation of the color.
/// </summary>
/// <returns>The hexadecimal representation of the color.</returns>
public string ToHex()
{
return string.Format(
CultureInfo.InvariantCulture,
"{0}{1}{2}",
R.ToString("X2", CultureInfo.InvariantCulture),
G.ToString("X2", CultureInfo.InvariantCulture),
B.ToString("X2", CultureInfo.InvariantCulture));
}
/// <inheritdoc/>
public override int GetHashCode()
{
@@ -216,9 +245,37 @@ namespace Spectre.Console
};
}
/// <summary>
/// Converts the color to a markup string.
/// </summary>
/// <returns>A <see cref="string"/> representing the color as markup.</returns>
public string ToMarkup()
{
if (IsDefault)
{
return "default";
}
if (Number != null)
{
var name = ColorTable.GetName(Number.Value);
if (!string.IsNullOrWhiteSpace(name))
{
return name;
}
}
return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2}", R, G, B);
}
/// <inheritdoc/>
public override string ToString()
{
if (IsDefault)
{
return "default";
}
if (Number != null)
{
var name = ColorTable.GetName(Number.Value);

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Spectre.Console
@@ -8,6 +10,35 @@ namespace Spectre.Console
public static partial class Emoji
{
private static readonly Regex _emojiCode = new Regex(@"(:(\S*?):)", RegexOptions.Compiled);
private static readonly Dictionary<string, string> _remappings;
static Emoji()
{
_remappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// Remaps a specific emoji tag with a new emoji.
/// </summary>
/// <param name="tag">The emoji tag.</param>
/// <param name="emoji">The emoji.</param>
public static void Remap(string tag, string emoji)
{
if (tag is null)
{
throw new ArgumentNullException(nameof(tag));
}
if (emoji is null)
{
throw new ArgumentNullException(nameof(emoji));
}
tag = tag.TrimStart(':').TrimEnd(':');
emoji = emoji.TrimStart(':').TrimEnd(':');
_remappings[tag] = emoji;
}
/// <summary>
/// Replaces emoji markup with corresponding unicode characters.
@@ -16,7 +47,23 @@ namespace Spectre.Console
/// <returns>A string with emoji codes replaced with actual emoji.</returns>
public static string Replace(string value)
{
static string ReplaceEmoji(Match match) => _emojis[match.Groups[2].Value];
static string ReplaceEmoji(Match match)
{
var key = match.Groups[2].Value;
if (_remappings.Count > 0 && _remappings.TryGetValue(key, out var remappedEmoji))
{
return remappedEmoji;
}
if (_emojis.TryGetValue(key, out var emoji))
{
return emoji;
}
return match.Value;
}
return _emojiCode.Replace(value, ReplaceEmoji);
}
}

View File

@@ -0,0 +1,41 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Represents how an exception is formatted.
/// </summary>
[Flags]
public enum ExceptionFormats
{
/// <summary>
/// The default formatting.
/// </summary>
Default = 0,
/// <summary>
/// Whether or not paths should be shortened.
/// </summary>
ShortenPaths = 1,
/// <summary>
/// Whether or not types should be shortened.
/// </summary>
ShortenTypes = 2,
/// <summary>
/// Whether or not methods should be shortened.
/// </summary>
ShortenMethods = 4,
/// <summary>
/// Whether or not to show paths as links in the terminal.
/// </summary>
ShowLinks = 8,
/// <summary>
/// Shortens everything that can be shortened.
/// </summary>
ShortenEverything = ShortenMethods | ShortenTypes | ShortenPaths,
}
}

View File

@@ -0,0 +1,27 @@
namespace Spectre.Console
{
/// <summary>
/// Exception settings.
/// </summary>
public sealed class ExceptionSettings
{
/// <summary>
/// Gets or sets the exception format.
/// </summary>
public ExceptionFormats Format { get; set; }
/// <summary>
/// Gets or sets the exception style.
/// </summary>
public ExceptionStyle Style { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionSettings"/> class.
/// </summary>
public ExceptionSettings()
{
Format = ExceptionFormats.Default;
Style = new ExceptionStyle();
}
}
}

View File

@@ -0,0 +1,58 @@
namespace Spectre.Console
{
/// <summary>
/// Represent an exception style.
/// </summary>
public sealed class ExceptionStyle
{
/// <summary>
/// Gets or sets the message color.
/// </summary>
public Style Message { get; set; } = new Style(Color.Red, Color.Default, Decoration.Bold);
/// <summary>
/// Gets or sets the exception color.
/// </summary>
public Style Exception { get; set; } = new Style(Color.White);
/// <summary>
/// Gets or sets the method color.
/// </summary>
public Style Method { get; set; } = new Style(Color.Yellow);
/// <summary>
/// Gets or sets the parameter type color.
/// </summary>
public Style ParameterType { get; set; } = new Style(Color.Blue);
/// <summary>
/// Gets or sets the parameter name color.
/// </summary>
public Style ParameterName { get; set; } = new Style(Color.Silver);
/// <summary>
/// Gets or sets the parenthesis color.
/// </summary>
public Style Parenthesis { get; set; } = new Style(Color.Silver);
/// <summary>
/// Gets or sets the path color.
/// </summary>
public Style Path { get; set; } = new Style(Color.Yellow, Color.Default, Decoration.Bold);
/// <summary>
/// Gets or sets the line number color.
/// </summary>
public Style LineNumber { get; set; } = new Style(Color.Blue);
/// <summary>
/// Gets or sets the color for dimmed text such as "at" or "in".
/// </summary>
public Style Dimmed { get; set; } = new Style(Color.Grey);
/// <summary>
/// Gets or sets the color for non emphasized items.
/// </summary>
public Style NonEmphasized { get; set; } = new Style(Color.Silver);
}
}

View File

@@ -0,0 +1,32 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary>
public static partial class AnsiConsoleExtensions
{
/// <summary>
/// Writes an exception to the console.
/// </summary>
/// <param name="console">The console.</param>
/// <param name="exception">The exception to write to the console.</param>
/// <param name="format">The exception format options.</param>
public static void WriteException(this IAnsiConsole console, Exception exception, ExceptionFormats format = ExceptionFormats.Default)
{
Render(console, exception.GetRenderable(format));
}
/// <summary>
/// Writes an exception to the console.
/// </summary>
/// <param name="console">The console.</param>
/// <param name="exception">The exception to write to the console.</param>
/// <param name="settings">The exception settings.</param>
public static void WriteException(this IAnsiConsole console, Exception exception, ExceptionSettings settings)
{
Render(console, exception.GetRenderable(settings));
}
}
}

View File

@@ -27,10 +27,9 @@ namespace Spectre.Console
}
var options = new RenderContext(console.Encoding, console.Capabilities.LegacyConsole);
var segments = renderable.Render(options, console.Width).Where(x => !(x.Text.Length == 0 && !x.IsLineBreak)).ToArray();
var segments = renderable.Render(options, console.Width).ToArray();
segments = Segment.Merge(segments).ToArray();
var current = Style.Plain;
foreach (var segment in segments)
{
if (string.IsNullOrEmpty(segment.Text))

View File

@@ -1,4 +1,5 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
@@ -7,6 +8,32 @@ namespace Spectre.Console
/// </summary>
public static partial class AnsiConsoleExtensions
{
/// <summary>
/// Creates a recorder for the specified console.
/// </summary>
/// <param name="console">The console to record.</param>
/// <returns>A recorder for the specified console.</returns>
public static Recorder CreateRecorder(this IAnsiConsole console)
{
return new Recorder(console);
}
/// <summary>
/// Writes the specified string value to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="text">The text to write.</param>
/// <param name="style">The text style.</param>
public static void Write(this IAnsiConsole console, string text, Style style)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Write(new Segment(text, style));
}
/// <summary>
/// Writes an empty line to the console.
/// </summary>
@@ -34,7 +61,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(console));
}
console.Write(text, style);
console.Write(new Segment(text, style));
console.WriteLine();
}
}

View File

@@ -3,7 +3,7 @@ using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Contains extension methods for <see cref="Border"/>.
/// Contains extension methods for <see cref="TableBorder"/>.
/// </summary>
public static class BorderExtensions
{
@@ -13,7 +13,7 @@ namespace Spectre.Console.Rendering
/// <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)
public static TableBorder GetSafeBorder(this TableBorder border, bool safe)
{
if (border is null)
{

View File

@@ -0,0 +1,31 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Contains extension methods for <see cref="BoxBorder"/>.
/// </summary>
public static class BoxExtensions
{
/// <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 BoxBorder GetSafeBorder(this BoxBorder border, bool safe)
{
if (border is null)
{
throw new ArgumentNullException(nameof(border));
}
if (safe && border.SafeBorder != null)
{
border = border.SafeBorder;
}
return border;
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Calendar"/>.
/// </summary>
public static class CalendarExtensions
{
/// <summary>
/// Adds a calendar event.
/// </summary>
/// <param name="calendar">The calendar to add the calendar event to.</param>
/// <param name="date">The calendar event date.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar AddCalendarEvent(this Calendar calendar, DateTime date)
{
return AddCalendarEvent(calendar, string.Empty, date.Year, date.Month, date.Day);
}
/// <summary>
/// Adds a calendar event.
/// </summary>
/// <param name="calendar">The calendar to add the calendar event to.</param>
/// <param name="description">The calendar event description.</param>
/// <param name="date">The calendar event date.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar AddCalendarEvent(this Calendar calendar, string description, DateTime date)
{
return AddCalendarEvent(calendar, description, date.Year, date.Month, date.Day);
}
/// <summary>
/// Adds a calendar event.
/// </summary>
/// <param name="calendar">The calendar to add the calendar event to.</param>
/// <param name="year">The year of the calendar event.</param>
/// <param name="month">The month of the calendar event.</param>
/// <param name="day">The day of the calendar event.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar AddCalendarEvent(this Calendar calendar, int year, int month, int day)
{
return AddCalendarEvent(calendar, string.Empty, year, month, day);
}
/// <summary>
/// Adds a calendar event.
/// </summary>
/// <param name="calendar">The calendar.</param>
/// <param name="description">The calendar event description.</param>
/// <param name="year">The year of the calendar event.</param>
/// <param name="month">The month of the calendar event.</param>
/// <param name="day">The day of the calendar event.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar AddCalendarEvent(this Calendar calendar, string description, int year, int month, int day)
{
if (calendar is null)
{
throw new ArgumentNullException(nameof(calendar));
}
calendar.CalendarEvents.Add(new CalendarEvent(description, year, month, day));
return calendar;
}
/// <summary>
/// Sets the calendar's highlight <see cref="Style"/>.
/// </summary>
/// <param name="calendar">The calendar.</param>
/// <param name="style">The highlight style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Calendar SetHighlightStyle(this Calendar calendar, Style? style)
{
if (calendar is null)
{
throw new ArgumentNullException(nameof(calendar));
}
calendar.HightlightStyle = style ?? Style.Plain;
return calendar;
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Exception"/>.
/// </summary>
public static class ExceptionExtensions
{
/// <summary>
/// Gets a <see cref="IRenderable"/> representation of the exception.
/// </summary>
/// <param name="exception">The exception to format.</param>
/// <param name="format">The exception format options.</param>
/// <returns>A <see cref="IRenderable"/> representing the exception.</returns>
public static IRenderable GetRenderable(this Exception exception, ExceptionFormats format = ExceptionFormats.Default)
{
if (exception is null)
{
throw new ArgumentNullException(nameof(exception));
}
return GetRenderable(exception, new ExceptionSettings
{
Format = format,
});
}
/// <summary>
/// Gets a <see cref="IRenderable"/> representation of the exception.
/// </summary>
/// <param name="exception">The exception to format.</param>
/// <param name="settings">The exception settings.</param>
/// <returns>A <see cref="IRenderable"/> representing the exception.</returns>
public static IRenderable GetRenderable(this Exception exception, ExceptionSettings settings)
{
if (exception is null)
{
throw new ArgumentNullException(nameof(exception));
}
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}
return ExceptionFormatter.Format(exception, settings);
}
}
}

View File

@@ -7,229 +7,6 @@ namespace Spectre.Console
/// </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>

View File

@@ -0,0 +1,101 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasBoxBorder"/>.
/// </summary>
public static class HasBoxBorderExtensions
{
/// <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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Ascii);
}
/// <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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Rounded);
}
/// <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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Heavy);
}
/// <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, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Double);
}
/// <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, BoxBorder border)
where T : class, IHasBoxBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Globalization;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasCulture"/>.
/// </summary>
public static class HasCultureExtensions
{
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="culture">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, CultureInfo culture)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (culture is null)
{
throw new ArgumentNullException(nameof(culture));
}
obj.Culture = culture;
return obj;
}
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, string name)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
}
/// <summary>
/// Sets the culture.
/// </summary>
/// <typeparam name="T">An object type with a culture.</typeparam>
/// <param name="obj">The object to set the culture for.</param>
/// <param name="name">The culture to set.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetCulture<T>(this T obj, int name)
where T : class, IHasCulture
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Culture = CultureInfo.GetCultureInfo(name);
return obj;
}
}
}

View File

@@ -0,0 +1,245 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasTableBorder"/>.
/// </summary>
public static class HasTableBorderExtensions
{
/// <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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.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, IHasTableBorder
{
return SetBorder(obj, TableBorder.DoubleEdge);
}
/// <summary>
/// Display a markdown 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 MarkdownBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Markdown);
}
/// <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, TableBorder border)
where T : class, IHasTableBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

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