Compare commits

...

6 Commits

Author SHA1 Message Date
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
104 changed files with 2825 additions and 1149 deletions

View File

@@ -65,9 +65,9 @@ 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

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;
```

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

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

@@ -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,7 +1,6 @@
using System;
using Spectre.Console;
namespace Emojis
namespace EmojiExample
{
public static class Program
{

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

@@ -35,7 +35,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 +57,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 +66,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 +74,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

@@ -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

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Tests
{
@@ -40,9 +41,14 @@ 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);
}
}
}

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,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

@@ -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,7 @@ 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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

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

@@ -21,10 +21,12 @@ namespace Spectre.Console
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,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

@@ -9,45 +9,40 @@ namespace Spectre.Console
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
public abstract partial class BoxBorder
{
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;
private readonly Dictionary<BoxBorderPart, string> _lookup;
/// <summary>
/// Gets the safe border for this border or <c>null</c> if none exist.
/// </summary>
public virtual Border? SafeBorder { get; }
public virtual BoxBorder? SafeBorder { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Border"/> class.
/// Initializes a new instance of the <see cref="BoxBorder"/> class.
/// </summary>
protected Border()
protected BoxBorder()
{
_lookup = Initialize();
}
private Dictionary<BorderPart, string> Initialize()
private Dictionary<BoxBorderPart, string> Initialize()
{
var lookup = new Dictionary<BorderPart, string>();
foreach (BorderPart? part in Enum.GetValues(typeof(BorderPart)))
var lookup = new Dictionary<BoxBorderPart, string>();
foreach (BoxBorderPart? part in Enum.GetValues(typeof(BoxBorderPart)))
{
if (part == null)
{
continue;
}
var text = GetBoxPart(part.Value);
var text = GetBorderPart(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));
lookup.Add(part.Value, GetBorderPart(part.Value));
}
return lookup;
@@ -59,10 +54,10 @@ namespace Spectre.Console
/// <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)
public string GetPart(BoxBorderPart part, int count)
{
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBoxPart(part)[0], count));
return string.Join(string.Empty, Enumerable.Repeat(GetBorderPart(part)[0], count));
}
/// <summary>
@@ -70,7 +65,7 @@ namespace Spectre.Console
/// </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)
public string GetPart(BoxBorderPart part)
{
return _lookup[part].ToString(CultureInfo.InvariantCulture);
}
@@ -80,6 +75,6 @@ namespace Spectre.Console
/// </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);
protected abstract string GetBorderPart(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()
{

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

@@ -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,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;
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using Spectre.Console.Internal;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="Recorder"/>.
/// </summary>
public static class RecorderExtensions
{
private static readonly TextEncoder _textEncoder = new TextEncoder();
private static readonly HtmlEncoder _htmlEncoder = new HtmlEncoder();
/// <summary>
/// Exports the recorded content as text.
/// </summary>
/// <param name="recorder">The recorder.</param>
/// <returns>The recorded content as text.</returns>
public static string ExportText(this Recorder recorder)
{
if (recorder is null)
{
throw new ArgumentNullException(nameof(recorder));
}
return recorder.Export(_textEncoder);
}
/// <summary>
/// Exports the recorded content as HTML.
/// </summary>
/// <param name="recorder">The recorder.</param>
/// <returns>The recorded content as HTML.</returns>
public static string ExportHtml(this Recorder recorder)
{
if (recorder is null)
{
throw new ArgumentNullException(nameof(recorder));
}
return recorder.Export(_htmlEncoder);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
@@ -30,8 +31,7 @@ namespace Spectre.Console
/// <summary>
/// Writes a string followed by a line terminator to the console.
/// </summary>
/// <param name="text">The string to write.</param>
/// <param name="style">The style to use.</param>
void Write(string text, Style style);
/// <param name="segment">The segment to write.</param>
void Write(Segment segment);
}
}

View File

@@ -13,12 +13,7 @@ namespace Spectre.Console
bool UseSafeBorder { get; set; }
/// <summary>
/// Gets or sets the border.
/// </summary>
public Border Border { get; set; }
/// <summary>
/// Gets or sets the border style.
/// Gets or sets the box style.
/// </summary>
public Style? BorderStyle { get; set; }
}

View File

@@ -0,0 +1,13 @@
namespace Spectre.Console
{
/// <summary>
/// Represents something that has a box border.
/// </summary>
public interface IHasBoxBorder : IHasBorder
{
/// <summary>
/// Gets or sets the box.
/// </summary>
public BoxBorder Border { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace Spectre.Console
{
/// <summary>
/// Represents something that has a border.
/// </summary>
public interface IHasTableBorder : IHasBorder
{
/// <summary>
/// Gets or sets the border.
/// </summary>
public TableBorder Border { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
@@ -48,21 +49,14 @@ namespace Spectre.Console.Internal
_ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
}
public void Write(string text, Style style)
public void Write(Segment segment)
{
if (string.IsNullOrEmpty(text))
{
return;
}
style ??= Style.Plain;
var parts = text.NormalizeLineEndings().Split(new[] { '\n' });
var parts = segment.Text.NormalizeLineEndings().Split(new[] { '\n' });
foreach (var (_, _, last, part) in parts.Enumerate())
{
if (!string.IsNullOrEmpty(part))
{
_out.Write(_ansiBuilder.GetAnsi(part, style));
_out.Write(_ansiBuilder.GetAnsi(part, segment.Style));
}
if (!last)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Spectre.Console.Rendering;
@@ -80,27 +81,24 @@ namespace Spectre.Console.Internal
return result.ToArray();
}
// https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/
public static int GetDeterministicHashCode(this string str)
public static string Multiply(this string text, int count)
{
unchecked
if (text is null)
{
var hash1 = (5381 << 16) + 5381;
var hash2 = hash1;
for (var i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
{
break;
throw new ArgumentNullException(nameof(text));
}
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
if (count <= 0)
{
return string.Empty;
}
return hash1 + (hash2 * 1566083941);
}
if (count == 1)
{
return text;
}
return string.Concat(Enumerable.Repeat(text, count));
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
@@ -61,14 +62,14 @@ namespace Spectre.Console.Internal
Capabilities = capabilities;
}
public void Write(string text, Style style)
public void Write(Segment segment)
{
if (_lastStyle?.Equals(style) != true)
if (_lastStyle?.Equals(segment.Style) != true)
{
SetStyle(style);
SetStyle(segment.Style);
}
_out.Write(text.NormalizeLineEndings(native: true));
_out.Write(segment.Text.NormalizeLineEndings(native: true));
}
private void SetStyle(Style style)

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class HtmlEncoder : IAnsiConsoleEncoder
{
public string Encode(IEnumerable<Segment> segments)
{
var builder = new StringBuilder();
builder.Append("<pre style=\"font-size:90%;font-family:consolas,'Courier New',monospace\">\n");
foreach (var (_, first, _, segment) in segments.Enumerate())
{
if (segment.Text == "\n" && !first)
{
builder.Append('\n');
continue;
}
var parts = segment.Text.Split(new[] { '\n' }, StringSplitOptions.None);
foreach (var (_, _, last, line) in parts.Enumerate())
{
if (string.IsNullOrEmpty(line))
{
continue;
}
builder.Append("<span");
if (!segment.Style.Equals(Style.Plain))
{
builder.Append(" style=\"");
builder.Append(BuildCss(segment.Style));
builder.Append('"');
}
builder.Append('>');
builder.Append(line);
builder.Append("</span>");
if (parts.Length > 1 && !last)
{
builder.Append('\n');
}
}
}
builder.Append("</pre>");
return builder.ToString().TrimEnd('\n');
}
private static string BuildCss(Style style)
{
var css = new List<string>();
var foreground = style.Foreground;
var background = style.Background;
if ((style.Decoration & Decoration.Invert) != 0)
{
var temp = foreground;
foreground = background;
background = temp;
}
if ((style.Decoration & Decoration.Dim) != 0)
{
var blender = background;
if (blender.Equals(Color.Default))
{
blender = Color.White;
}
foreground = foreground.Blend(blender, 0.5f);
}
if (!foreground.Equals(Color.Default))
{
css.Add($"color: #{foreground.ToHex()}");
}
if (!background.Equals(Color.Default))
{
css.Add($"background-color: #{background.ToHex()}");
}
if ((style.Decoration & Decoration.Bold) != 0)
{
css.Add("font-weight: bold");
}
if ((style.Decoration & Decoration.Bold) != 0)
{
css.Add("font-style: italic");
}
if ((style.Decoration & Decoration.Underline) != 0)
{
css.Add("text-decoration: underline");
}
if ((style.Decoration & Decoration.Strikethrough) != 0)
{
css.Add("text-decoration: line-through");
}
return string.Join(";", css);
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console.Internal
{
internal sealed class TextEncoder : IAnsiConsoleEncoder
{
public string Encode(IEnumerable<Segment> segments)
{
var builder = new StringBuilder();
foreach (var segment in Segment.Merge(segments))
{
builder.Append(segment.Text);
}
return builder.ToString().TrimEnd('\n');
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// A console recorder used to record output from a console.
/// </summary>
public sealed class Recorder : IAnsiConsole, IDisposable
{
private readonly IAnsiConsole _console;
private readonly List<Segment> _recorded;
/// <inheritdoc/>
public Capabilities Capabilities => _console.Capabilities;
/// <inheritdoc/>
public Encoding Encoding => _console.Encoding;
/// <inheritdoc/>
public int Width => _console.Width;
/// <inheritdoc/>
public int Height => _console.Height;
/// <summary>
/// Initializes a new instance of the <see cref="Recorder"/> class.
/// </summary>
/// <param name="console">The console to record output for.</param>
public Recorder(IAnsiConsole console)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
_recorded = new List<Segment>();
}
/// <inheritdoc/>
public void Dispose()
{
// Only used for scoping.
}
/// <inheritdoc/>
public void Write(Segment segment)
{
_recorded.Add(segment);
_console.Write(segment);
}
/// <summary>
/// Exports the recorded data.
/// </summary>
/// <param name="encoder">The encoder.</param>
/// <returns>The recorded data represented as a string.</returns>
public string Export(IAnsiConsoleEncoder encoder)
{
if (encoder is null)
{
throw new ArgumentNullException(nameof(encoder));
}
return encoder.Encode(_recorded);
}
}
}

View File

@@ -0,0 +1,48 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents the different parts of a box border.
/// </summary>
public enum BoxBorderPart
{
/// <summary>
/// The top left part of a box.
/// </summary>
TopLeft,
/// <summary>
/// The top part of a box.
/// </summary>
Top,
/// <summary>
/// The top right part of a box.
/// </summary>
TopRight,
/// <summary>
/// The left part of a box.
/// </summary>
Left,
/// <summary>
/// The right part of a box.
/// </summary>
Right,
/// <summary>
/// The bottom left part of a box.
/// </summary>
BottomLeft,
/// <summary>
/// The bottom part of a box.
/// </summary>
Bottom,
/// <summary>
/// The bottom right part of a box.
/// </summary>
BottomRight,
}
}

View File

@@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border.
/// </summary>
public sealed class AsciiBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "+",
BoxBorderPart.Top => "-",
BoxBorderPart.TopRight => "+",
BoxBorderPart.Left => "|",
BoxBorderPart.Right => "|",
BoxBorderPart.BottomLeft => "+",
BoxBorderPart.Bottom => "-",
BoxBorderPart.BottomRight => "+",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a double border.
/// </summary>
public sealed class DoubleBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "╔",
BoxBorderPart.Top => "═",
BoxBorderPart.TopRight => "╗",
BoxBorderPart.Left => "║",
BoxBorderPart.Right => "║",
BoxBorderPart.BottomLeft => "╚",
BoxBorderPart.Bottom => "═",
BoxBorderPart.BottomRight => "╝",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a heavy border.
/// </summary>
public sealed class HeavyBoxBorder : BoxBorder
{
/// <inheritdoc/>
public override BoxBorder? SafeBorder => BoxBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "┏",
BoxBorderPart.Top => "━",
BoxBorderPart.TopRight => "┓",
BoxBorderPart.Left => "┃",
BoxBorderPart.Right => "┃",
BoxBorderPart.BottomLeft => "┗",
BoxBorderPart.Bottom => "━",
BoxBorderPart.BottomRight => "┛",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -0,0 +1,14 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an invisible border.
/// </summary>
public sealed class NoBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return " ";
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedBoxBorder : BoxBorder
{
/// <inheritdoc/>
public override BoxBorder? SafeBorder => BoxBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "╭",
BoxBorderPart.Top => "─",
BoxBorderPart.TopRight => "╮",
BoxBorderPart.Left => "│",
BoxBorderPart.Right => "│",
BoxBorderPart.BottomLeft => "╰",
BoxBorderPart.Bottom => "─",
BoxBorderPart.BottomRight => "╯",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "┌",
BoxBorderPart.Top => "─",
BoxBorderPart.TopRight => "┐",
BoxBorderPart.Left => "│",
BoxBorderPart.Right => "│",
BoxBorderPart.BottomLeft => "└",
BoxBorderPart.Bottom => "─",
BoxBorderPart.BottomRight => "┘",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -1,9 +1,9 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents the different border parts.
/// Represents the different parts of a table border.
/// </summary>
public enum BorderPart
public enum TableBorderPart
{
/// <summary>
/// The top left part of a header.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Internal;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a Markdown border.
/// </summary>
public sealed class MarkdownTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => "|",
TableBorderPart.HeaderSeparator => "|",
TableBorderPart.HeaderRight => "|",
TableBorderPart.HeaderBottomLeft => "|",
TableBorderPart.HeaderBottom => "-",
TableBorderPart.HeaderBottomSeparator => "|",
TableBorderPart.HeaderBottomRight => "|",
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
/// <inheritdoc/>
public override string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (part != TablePart.Separator)
{
return base.GetColumnRow(part, widths, columns);
}
var (left, center, separator, right) = GetTableParts(part);
var builder = new StringBuilder();
builder.Append(left);
foreach (var (columnIndex, _, lastColumn, columnWidth) in widths.Enumerate())
{
var padding = columns[columnIndex].Padding;
if (padding.Left > 0)
{
// Left padding
builder.Append(" ".Multiply(padding.Left));
}
var justification = columns[columnIndex].Alignment;
if (justification == null)
{
// No alignment
builder.Append(center.Multiply(columnWidth));
}
else if (justification.Value == Justify.Left)
{
// Left
builder.Append(':');
builder.Append(center.Multiply(columnWidth - 1));
}
else if (justification.Value == Justify.Center)
{
// Centered
builder.Append(':');
builder.Append(center.Multiply(columnWidth - 2));
builder.Append(':');
}
else if (justification.Value == Justify.Right)
{
// Right
builder.Append(center.Multiply(columnWidth - 1));
builder.Append(':');
}
// Right padding
if (padding.Right > 0)
{
builder.Append(" ".Multiply(padding.Right));
}
if (!lastColumn)
{
builder.Append(separator);
}
}
builder.Append(right);
return builder.ToString();
}
}
}

View File

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

View File

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

View File

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

View File

@@ -3,13 +3,13 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Represents an invisible border.
/// </summary>
public sealed class NoBorder : Border
public sealed class NoTableBorder : TableBorder
{
/// <inheritdoc/>
public override bool Visible => false;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
protected override string GetBorderPart(TableBorderPart part)
{
return " ";
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "╭",
TableBorderPart.HeaderTop => "─",
TableBorderPart.HeaderTopSeparator => "┬",
TableBorderPart.HeaderTopRight => "╮",
TableBorderPart.HeaderLeft => "│",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "│",
TableBorderPart.HeaderBottomLeft => "├",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "┤",
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterBottomLeft => "╰",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",
TableBorderPart.FooterBottomRight => "╯",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "┌",
TableBorderPart.HeaderTop => "─",
TableBorderPart.HeaderTopSeparator => "┬",
TableBorderPart.HeaderTopRight => "┐",
TableBorderPart.HeaderLeft => "│",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "│",
TableBorderPart.HeaderBottomLeft => "├",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "┤",
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",
TableBorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a console encoder that can encode
/// recorded segments into a string.
/// </summary>
public interface IAnsiConsoleEncoder
{
/// <summary>
/// Encodes the specified segments.
/// </summary>
/// <param name="segments">The segments to encode.</param>
/// <returns>The encoded string.</returns>
string Encode(IEnumerable<Segment> segments);
}
}

View File

@@ -15,7 +15,7 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Gets the segment text.
/// </summary>
public string Text { get; private set; }
public string Text { get; }
/// <summary>
/// Gets a value indicating whether or not this is an expicit line break
@@ -38,12 +38,12 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Gets a segment representing a line break.
/// </summary>
public static Segment LineBreak => new Segment(Environment.NewLine, Style.Plain, true);
public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true);
/// <summary>
/// Gets an empty segment.
/// </summary>
public static Segment Empty => new Segment(string.Empty, Style.Plain, false);
public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false);
/// <summary>
/// Initializes a new instance of the <see cref="Segment"/> class.
@@ -72,7 +72,7 @@ namespace Spectre.Console.Rendering
}
Text = text.NormalizeLineEndings();
Style = style;
Style = style ?? throw new ArgumentNullException(nameof(style));
IsLineBreak = lineBreak;
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
}
@@ -241,12 +241,10 @@ namespace Spectre.Console.Rendering
// Same style?
if (previous.Style.Equals(segment.Style))
{
// Modify the content of the previous segment
previous.Text += segment.Text;
previous = new Segment(previous.Text + segment.Text, previous.Style);
}
else
{
// Push the current one to the results.
result.Add(previous);
previous = segment;
}
@@ -260,6 +258,15 @@ namespace Spectre.Console.Rendering
return result;
}
/// <summary>
/// Clones the segment.
/// </summary>
/// <returns>A new segment that's identical to this one.</returns>
public Segment Clone()
{
return new Segment(Text, Style);
}
/// <summary>
/// Splits an overflowing segment into several new segments.
/// </summary>

View File

@@ -0,0 +1,23 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents different parts of a table.
/// </summary>
public enum TablePart
{
/// <summary>
/// The top of a table.
/// </summary>
Top,
/// <summary>
/// The separator between the header and the cells.
/// </summary>
Separator,
/// <summary>
/// The bottom of a table.
/// </summary>
Bottom,
}
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@@ -14,12 +14,15 @@
<Compile Update="AnsiConsole.*.cs">
<DependentUpon>AnsiConsole.cs</DependentUpon>
</Compile>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
<Compile Update="Border.*.cs">
<DependentUpon>Border.cs</DependentUpon>
</Compile>
<Compile Update="BoxBorder.*.cs">
<DependentUpon>BoxBorder.cs</DependentUpon>
</Compile>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
<Compile Update="Emoji.*.cs">
<DependentUpon>Emoji.cs</DependentUpon>
</Compile>
@@ -39,6 +42,12 @@
</ItemGroup>
<ItemGroup>
<Compile Update="BoxBorder.Known.cs">
<DependentUpon>BoxBorder.cs</DependentUpon>
</Compile>
<Compile Update="TableBorder.Known.cs">
<DependentUpon>TableBorder.cs</DependentUpon>
</Compile>
<Compile Update="Extensions\AnsiConsoleExtensions.Markup.cs">
<DependentUpon>AnsiConsoleExtensions.cs</DependentUpon>
</Compile>

View File

@@ -6,92 +6,97 @@ namespace Spectre.Console
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
public abstract partial class TableBorder
{
/// <summary>
/// Gets an invisible border.
/// </summary>
public static Border None { get; } = new NoBorder();
public static TableBorder None { get; } = new NoTableBorder();
/// <summary>
/// Gets an ASCII border.
/// </summary>
public static Border Ascii { get; } = new AsciiBorder();
public static TableBorder Ascii { get; } = new AsciiTableBorder();
/// <summary>
/// Gets another ASCII border.
/// Gets an ASCII border.
/// </summary>
public static Border Ascii2 { get; } = new Ascii2Border();
public static TableBorder Ascii2 { get; } = new Ascii2TableBorder();
/// <summary>
/// Gets an ASCII border with a double header border.
/// </summary>
public static Border AsciiDoubleHead { get; } = new AsciiDoubleHeadBorder();
public static TableBorder AsciiDoubleHead { get; } = new AsciiDoubleHeadTableBorder();
/// <summary>
/// Gets a square border.
/// </summary>
public static Border Square { get; } = new SquareBorder();
public static TableBorder Square { get; } = new SquareTableBorder();
/// <summary>
/// Gets a rounded border.
/// </summary>
public static Border Rounded { get; } = new RoundedBorder();
public static TableBorder Rounded { get; } = new RoundedTableBorder();
/// <summary>
/// Gets a minimal border.
/// </summary>
public static Border Minimal { get; } = new MinimalBorder();
public static TableBorder Minimal { get; } = new MinimalTableBorder();
/// <summary>
/// Gets a minimal border with a heavy head.
/// </summary>
public static Border MinimalHeavyHead { get; } = new MinimalHeavyHeadBorder();
public static TableBorder MinimalHeavyHead { get; } = new MinimalHeavyHeadTableBorder();
/// <summary>
/// Gets a minimal border with a double header border.
/// </summary>
public static Border MinimalDoubleHead { get; } = new MinimalDoubleHeadBorder();
public static TableBorder MinimalDoubleHead { get; } = new MinimalDoubleHeadTableBorder();
/// <summary>
/// Gets a simple border.
/// </summary>
public static Border Simple { get; } = new SimpleBorder();
public static TableBorder Simple { get; } = new SimpleTableBorder();
/// <summary>
/// Gets a simple border with heavy lines.
/// </summary>
public static Border SimpleHeavy { get; } = new SimpleHeavyBorder();
public static TableBorder SimpleHeavy { get; } = new SimpleHeavyTableBorder();
/// <summary>
/// Gets a horizontal border.
/// </summary>
public static Border Horizontal { get; } = new HorizontalBorder();
public static TableBorder Horizontal { get; } = new HorizontalTableBorder();
/// <summary>
/// Gets a heavy border.
/// </summary>
public static Border Heavy { get; } = new HeavyBorder();
public static TableBorder Heavy { get; } = new HeavyTableBorder();
/// <summary>
/// Gets a border with a heavy edge.
/// </summary>
public static Border HeavyEdge { get; } = new HeavyEdgeBorder();
public static TableBorder HeavyEdge { get; } = new HeavyEdgeTableBorder();
/// <summary>
/// Gets a border with a heavy header.
/// </summary>
public static Border HeavyHead { get; } = new HeavyHeadBorder();
public static TableBorder HeavyHead { get; } = new HeavyHeadTableBorder();
/// <summary>
/// Gets a double border.
/// </summary>
[SuppressMessage("Naming", "CA1720:Identifier contains type name")]
public static Border Double { get; } = new DoubleBorder();
public static TableBorder Double { get; } = new DoubleTableBorder();
/// <summary>
/// Gets a border with a double edge.
/// </summary>
public static Border DoubleEdge { get; } = new DoubleEdgeBorder();
public static TableBorder DoubleEdge { get; } = new DoubleEdgeTableBorder();
/// <summary>
/// Gets a markdown border.
/// </summary>
public static TableBorder Markdown { get; } = new MarkdownTableBorder();
}
}

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class TableBorder
{
private readonly Dictionary<TableBorderPart, 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 TableBorder? SafeBorder { get; }
/// <summary>
/// Initializes a new instance of the <see cref="TableBorder"/> class.
/// </summary>
protected TableBorder()
{
_lookup = Initialize();
}
private Dictionary<TableBorderPart, string> Initialize()
{
var lookup = new Dictionary<TableBorderPart, string>();
foreach (TableBorderPart? part in Enum.GetValues(typeof(TableBorderPart)))
{
if (part == null)
{
continue;
}
var text = GetBorderPart(part.Value);
if (text.Length > 1)
{
throw new InvalidOperationException("A box part cannot contain more than one character.");
}
lookup.Add(part.Value, GetBorderPart(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(TableBorderPart part, int count)
{
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBorderPart(part)[0], count));
}
/// <summary>
/// Gets a whole column row for the specific column row part.
/// </summary>
/// <param name="part">The column row part.</param>
/// <param name="widths">The column widths.</param>
/// <param name="columns">The columns.</param>
/// <returns>A string representing the column row.</returns>
public virtual string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (widths is null)
{
throw new ArgumentNullException(nameof(widths));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
var (left, center, separator, right) = GetTableParts(part);
var builder = new StringBuilder();
builder.Append(left);
foreach (var (columnIndex, _, lastColumn, columnWidth) in widths.Enumerate())
{
var padding = columns[columnIndex].Padding;
var centerWidth = padding.Left + columnWidth + padding.Right;
builder.Append(center.Multiply(centerWidth));
if (!lastColumn)
{
builder.Append(separator);
}
}
builder.Append(right);
return builder.ToString();
}
/// <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(TableBorderPart 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 GetBorderPart(TableBorderPart part);
/// <summary>
/// Gets the table parts used to render a specific table row.
/// </summary>
/// <param name="part">The table part.</param>
/// <returns>The table parts used to render the specific table row.</returns>
protected (string Left, string Center, string Separator, string Right)
GetTableParts(TablePart part)
{
return part switch
{
// Top part
TablePart.Top =>
(GetPart(TableBorderPart.HeaderTopLeft), GetPart(TableBorderPart.HeaderTop),
GetPart(TableBorderPart.HeaderTopSeparator), GetPart(TableBorderPart.HeaderTopRight)),
// Separator between header and cells
TablePart.Separator =>
(GetPart(TableBorderPart.HeaderBottomLeft), GetPart(TableBorderPart.HeaderBottom),
GetPart(TableBorderPart.HeaderBottomSeparator), GetPart(TableBorderPart.HeaderBottomRight)),
// Bottom part
TablePart.Bottom =>
(GetPart(TableBorderPart.FooterBottomLeft), GetPart(TableBorderPart.FooterBottom),
GetPart(TableBorderPart.FooterBottomSeparator), GetPart(TableBorderPart.FooterBottomRight)),
// Unknown
_ => throw new NotSupportedException("Unknown column row part"),
};
}
}
}

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