mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ef1ac483a | ||
|
|
c0875c912a | ||
|
|
3f2ca49071 | ||
|
|
0a0380ae0a | ||
|
|
ae92c606bb | ||
|
|
68e92f3365 | ||
|
|
39a8588dc3 |
1
.github/workflows/ci.yaml
vendored
1
.github/workflows/ci.yaml
vendored
@@ -71,6 +71,7 @@ jobs:
|
||||
dotnet example colors
|
||||
dotnet example emojis
|
||||
dotnet example exceptions
|
||||
dotnet example calendars
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
|
||||
@@ -30,6 +30,25 @@ var phrase = "Mmmm :birthday_cake:";
|
||||
var rendered = Emoji.Replace(phrase);
|
||||
```
|
||||
|
||||
# Remapping or adding an emoji
|
||||
|
||||
Sometimes you want to remap an existing emoji, or
|
||||
add a completely new one. For this you can use the
|
||||
`Emoji.Remap` method. This approach works both with
|
||||
markup strings and `Emoji.Replace`.
|
||||
|
||||
```csharp
|
||||
// Remap the emoji
|
||||
Emoji.Remap("globe_showing_europe_africa", "😄");
|
||||
|
||||
// Render markup
|
||||
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
|
||||
|
||||
// Replace emojis in string
|
||||
var phrase = "Hello :globe_showing_europe_africa:!";
|
||||
var rendered = Emoji.Replace(phrase);
|
||||
```
|
||||
|
||||
# Emojis
|
||||
|
||||
_The images in the table below might not render correctly in your
|
||||
|
||||
BIN
docs/input/assets/images/custom_exception.png
Normal file
BIN
docs/input/assets/images/custom_exception.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
@@ -9,9 +9,9 @@ You can make exception a bit more readable by using the `WriteException` method.
|
||||
AnsiConsole.WriteException(ex);
|
||||
```
|
||||
|
||||
<img src="assets/images/exception.png" style="max-width: 100%; margin-bottom: 20px">
|
||||
|
||||
<img src="assets/images/exception.png" style="max-width: 100%;">
|
||||
|
||||
## Shortening parts
|
||||
|
||||
You can also shorten specific parts of the exception to make it even
|
||||
more readable, and make paths clickable hyperlinks. Whether or not
|
||||
@@ -24,3 +24,29 @@ AnsiConsole.WriteException(ex,
|
||||
```
|
||||
|
||||
<img src="assets/images/compact_exception.png" style="max-width: 100%;">
|
||||
|
||||
## Customizing exception output
|
||||
|
||||
In addition to shorten specific part of the exception, you can
|
||||
also override the default styling.
|
||||
|
||||
```csharp
|
||||
AnsiConsole.WriteException(ex, new ExceptionSettings
|
||||
{
|
||||
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
|
||||
Style = new ExceptionStyle
|
||||
{
|
||||
Exception = Style.WithForeground(Color.Grey),
|
||||
Message = Style.WithForeground(Color.White),
|
||||
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
|
||||
Parenthesis = Style.WithForeground(Color.Cornsilk1),
|
||||
Method = Style.WithForeground(Color.Red),
|
||||
ParameterName = Style.WithForeground(Color.Cornsilk1),
|
||||
ParameterType = Style.WithForeground(Color.Red),
|
||||
Path = Style.WithForeground(Color.Red),
|
||||
LineNumber = Style.WithForeground(Color.Cornsilk1),
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
<img src="assets/images/custom_exception.png" style="max-width: 100%;">
|
||||
|
||||
@@ -43,6 +43,12 @@ AnsiConsole.Markup("[[Hello]] "); // [Hello]
|
||||
AnsiConsole.Markup("[red][[World]][/]"); // [World]
|
||||
```
|
||||
|
||||
You can also use the `SafeMarkup` extension method.
|
||||
|
||||
```csharp
|
||||
AnsiConsole.Markup("[red]{0}[/]", "Hello [World]".SafeMarkup());
|
||||
```
|
||||
|
||||
# Setting background color
|
||||
|
||||
You can set the background color in markup by prefixing the color with
|
||||
@@ -61,7 +67,7 @@ To output an emoji as part of markup, you can use emoji shortcodes.
|
||||
AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
|
||||
```
|
||||
|
||||
For a list of emoji, see the [Emojis](xref:styles) appendix section.
|
||||
For a list of emoji, see the [Emojis](xref:emojis) appendix section.
|
||||
|
||||
# Colors
|
||||
|
||||
|
||||
15
examples/Calendars/Calendars.csproj
Normal file
15
examples/Calendars/Calendars.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Title>Calendars</Title>
|
||||
<Description>Demonstrates how to render calendars.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
68
examples/Calendars/Program.cs
Normal file
68
examples/Calendars/Program.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Collections.Generic;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Calendars
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(
|
||||
new Columns(GetCalendars())
|
||||
.Collapse());
|
||||
}
|
||||
|
||||
private static IEnumerable<IRenderable> GetCalendars()
|
||||
{
|
||||
yield return EmbedInPanel(
|
||||
"Invariant calendar",
|
||||
new Calendar(2020, 10)
|
||||
.SimpleHeavyBorder()
|
||||
.SetHighlightStyle(Style.Parse("red"))
|
||||
.AddCalendarEvent("An event", 2020, 9, 22)
|
||||
.AddCalendarEvent("Another event", 2020, 10, 2)
|
||||
.AddCalendarEvent("A third event", 2020, 10, 13));
|
||||
|
||||
yield return EmbedInPanel(
|
||||
"Swedish calendar (sv-SE)",
|
||||
new Calendar(2020, 10)
|
||||
.RoundedBorder()
|
||||
.SetHighlightStyle(Style.Parse("blue"))
|
||||
.SetCulture("sv-SE")
|
||||
.AddCalendarEvent("An event", 2020, 9, 22)
|
||||
.AddCalendarEvent("Another event", 2020, 10, 2)
|
||||
.AddCalendarEvent("A third event", 2020, 10, 13));
|
||||
|
||||
yield return EmbedInPanel(
|
||||
"German calendar (de-DE)",
|
||||
new Calendar(2020, 10)
|
||||
.MarkdownBorder()
|
||||
.SetHighlightStyle(Style.Parse("yellow"))
|
||||
.SetCulture("de-DE")
|
||||
.AddCalendarEvent("An event", 2020, 9, 22)
|
||||
.AddCalendarEvent("Another event", 2020, 10, 2)
|
||||
.AddCalendarEvent("A third event", 2020, 10, 13));
|
||||
|
||||
yield return EmbedInPanel(
|
||||
"Italian calendar (de-DE)",
|
||||
new Calendar(2020, 10)
|
||||
.DoubleBorder()
|
||||
.SetHighlightStyle(Style.Parse("green"))
|
||||
.SetCulture("it-IT")
|
||||
.AddCalendarEvent("An event", 2020, 9, 22)
|
||||
.AddCalendarEvent("Another event", 2020, 10, 2)
|
||||
.AddCalendarEvent("A third event", 2020, 10, 13));
|
||||
}
|
||||
|
||||
private static IRenderable EmbedInPanel(string title, Calendar calendar)
|
||||
{
|
||||
return new Panel(calendar)
|
||||
.Expand()
|
||||
.RoundedBorder()
|
||||
.SetBorderStyle(Style.Parse("grey"))
|
||||
.SetHeader($" {title} ", Style.Parse("yellow"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,19 @@ namespace EmojiExample
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Markup
|
||||
// Show a known emoji
|
||||
RenderEmoji();
|
||||
|
||||
// Show a remapped emoji
|
||||
Emoji.Remap("globe_showing_europe_africa", Emoji.Known.GrinningFaceWithSmilingEyes);
|
||||
RenderEmoji();
|
||||
}
|
||||
|
||||
private static void RenderEmoji()
|
||||
{
|
||||
AnsiConsole.Render(
|
||||
new Panel("[yellow]Hello :globe_showing_europe_africa:![/]")
|
||||
.RoundedBorder()
|
||||
.SetHeader("Markup"));
|
||||
.RoundedBorder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,35 @@ namespace Exceptions
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Panel("[u]Default[/]").Expand());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex);
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Panel("[u]Compact[/]").Expand());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks);
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.Render(new Panel("[u]Custom colors[/]").Expand());
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.WriteException(ex, new ExceptionSettings
|
||||
{
|
||||
Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks,
|
||||
Style = new ExceptionStyle
|
||||
{
|
||||
Exception = Style.WithForeground(Color.Grey),
|
||||
Message = Style.WithForeground(Color.White),
|
||||
NonEmphasized = Style.WithForeground(Color.Cornsilk1),
|
||||
Parenthesis = Style.WithForeground(Color.Cornsilk1),
|
||||
Method = Style.WithForeground(Color.Red),
|
||||
ParameterName = Style.WithForeground(Color.Cornsilk1),
|
||||
ParameterType = Style.WithForeground(Color.Red),
|
||||
Path = Style.WithForeground(Color.Red),
|
||||
LineNumber = Style.WithForeground(Color.Cornsilk1),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace TableExample
|
||||
|
||||
@@ -86,4 +86,7 @@ dotnet_diagnostic.RCS1057.severity = none
|
||||
dotnet_diagnostic.RCS1227.severity = none
|
||||
|
||||
# IDE0004: Remove Unnecessary Cast
|
||||
dotnet_diagnostic.IDE0004.severity = warning
|
||||
dotnet_diagnostic.IDE0004.severity = warning
|
||||
|
||||
# CA1810: Initialize reference type static fields inline
|
||||
dotnet_diagnostic.CA1810.severity = none
|
||||
@@ -52,7 +52,7 @@ namespace Spectre.Console.Tests
|
||||
Writer.Write(segment.Text);
|
||||
}
|
||||
|
||||
public string[] WriteExceptionAndGetLines(Exception ex, ExceptionFormats formats = ExceptionFormats.None)
|
||||
public string[] WriteExceptionAndGetLines(Exception ex, ExceptionFormats formats = ExceptionFormats.Default)
|
||||
{
|
||||
this.WriteException(ex, formats);
|
||||
|
||||
|
||||
92
src/Spectre.Console.Tests/Unit/CalendarTests.cs
Normal file
92
src/Spectre.Console.Tests/Unit/CalendarTests.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
public sealed class CalendarTests
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Render_Calendar_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10)
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(10);
|
||||
console.Lines[0].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
|
||||
console.Lines[1].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
|
||||
console.Lines[2].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
|
||||
console.Lines[3].ShouldBe("│ │ │ │ │ 1 │ 2 │ 3* │");
|
||||
console.Lines[4].ShouldBe("│ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
|
||||
console.Lines[5].ShouldBe("│ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
|
||||
console.Lines[6].ShouldBe("│ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
|
||||
console.Lines[7].ShouldBe("│ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
|
||||
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
|
||||
console.Lines[9].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Render_Calendar_Correctly_For_Specific_Culture()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10, 15)
|
||||
.SetCulture("de-DE")
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(10);
|
||||
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
|
||||
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
|
||||
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
|
||||
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
|
||||
console.Lines[4].ShouldBe("│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │");
|
||||
console.Lines[5].ShouldBe("│ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │");
|
||||
console.Lines[6].ShouldBe("│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │");
|
||||
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │");
|
||||
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
|
||||
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Render_List_Of_Events_If_Enabled()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10, 15)
|
||||
.SetCulture("de-DE")
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(10);
|
||||
console.Lines[0].ShouldBe("┌─────┬────┬────┬────┬────┬────┬────┐");
|
||||
console.Lines[1].ShouldBe("│ Mo │ Di │ Mi │ Do │ Fr │ Sa │ So │");
|
||||
console.Lines[2].ShouldBe("├─────┼────┼────┼────┼────┼────┼────┤");
|
||||
console.Lines[3].ShouldBe("│ │ │ │ 1 │ 2 │ 3* │ 4 │");
|
||||
console.Lines[4].ShouldBe("│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │");
|
||||
console.Lines[5].ShouldBe("│ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │");
|
||||
console.Lines[6].ShouldBe("│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │");
|
||||
console.Lines[7].ShouldBe("│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │");
|
||||
console.Lines[8].ShouldBe("│ │ │ │ │ │ │ │");
|
||||
console.Lines[9].ShouldBe("└─────┴────┴────┴────┴────┴────┴────┘");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,6 +228,39 @@ namespace Spectre.Console.Tests.Unit
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TheToMarkupMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Default_Color()
|
||||
{
|
||||
// Given, When
|
||||
var result = Color.Default.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("default");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Known_Color()
|
||||
{
|
||||
// Given, When
|
||||
var result = Color.Red.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("red");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Custom_Color()
|
||||
{
|
||||
// Given, When
|
||||
var result = new Color(255, 1, 12).ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("#FF010C");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TheToStringMethod
|
||||
{
|
||||
[Fact]
|
||||
|
||||
@@ -317,5 +317,60 @@ namespace Spectre.Console.Tests.Unit
|
||||
result.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TheToMarkupMethod
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Style_With_Foreground_Color()
|
||||
{
|
||||
// Given
|
||||
var style = new Style(Color.Red);
|
||||
|
||||
// When
|
||||
var result = style.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("red");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color()
|
||||
{
|
||||
// Given
|
||||
var style = new Style(Color.Red, Color.Green);
|
||||
|
||||
// When
|
||||
var result = style.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("red on green");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Style_With_Foreground_And_Background_Color_And_Decoration()
|
||||
{
|
||||
// Given
|
||||
var style = new Style(Color.Red, Color.Green, Decoration.Bold | Decoration.Underline);
|
||||
|
||||
// When
|
||||
var result = style.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("bold underline red on green");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Return_Expected_Markup_For_Style_With_Only_Background_Color()
|
||||
{
|
||||
// Given
|
||||
var style = new Style(background: Color.Green);
|
||||
|
||||
// When
|
||||
var result = style.ToMarkup();
|
||||
|
||||
// Then
|
||||
result.ShouldBe("default on green");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{C3E2CB
|
||||
..\.github\workflows\publish.yaml = ..\.github\workflows\publish.yaml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calendars", "..\examples\Calendars\Calendars.csproj", "{57691C7D-683D-46E6-AA4F-57A8C5F65D25}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -198,6 +200,18 @@ Global
|
||||
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x64.Build.0 = Release|Any CPU
|
||||
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32}.Release|x86.Build.0 = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x64.Build.0 = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -214,6 +228,7 @@ Global
|
||||
{1EABB956-957F-4C1A-8AC0-FD19C8F3C2F2} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||
{90C081A7-7C1D-4A4A-82B6-8FF473C3EA32} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
|
||||
{57691C7D-683D-46E6-AA4F-57A8C5F65D25} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
||||
|
||||
@@ -12,9 +12,19 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to write to the console.</param>
|
||||
/// <param name="format">The exception format options.</param>
|
||||
public static void WriteException(Exception exception, ExceptionFormats format = ExceptionFormats.None)
|
||||
public static void WriteException(Exception exception, ExceptionFormats format = ExceptionFormats.Default)
|
||||
{
|
||||
Console.WriteException(exception, format);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an exception to the console.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to write to the console.</param>
|
||||
/// <param name="settings">The exception settings.</param>
|
||||
public static void WriteException(Exception exception, ExceptionSettings settings)
|
||||
{
|
||||
Console.WriteException(exception, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,8 +249,13 @@ namespace Spectre.Console
|
||||
/// Converts the color to a markup string.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="string"/> representing the color as markup.</returns>
|
||||
public string ToMarkupString()
|
||||
public string ToMarkup()
|
||||
{
|
||||
if (IsDefault)
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
|
||||
if (Number != null)
|
||||
{
|
||||
var name = ColorTable.GetName(Number.Value);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Spectre.Console
|
||||
@@ -8,6 +10,35 @@ namespace Spectre.Console
|
||||
public static partial class Emoji
|
||||
{
|
||||
private static readonly Regex _emojiCode = new Regex(@"(:(\S*?):)", RegexOptions.Compiled);
|
||||
private static readonly Dictionary<string, string> _remappings;
|
||||
|
||||
static Emoji()
|
||||
{
|
||||
_remappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remaps a specific emoji tag with a new emoji.
|
||||
/// </summary>
|
||||
/// <param name="tag">The emoji tag.</param>
|
||||
/// <param name="emoji">The emoji.</param>
|
||||
public static void Remap(string tag, string emoji)
|
||||
{
|
||||
if (tag is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tag));
|
||||
}
|
||||
|
||||
if (emoji is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(emoji));
|
||||
}
|
||||
|
||||
tag = tag.TrimStart(':').TrimEnd(':');
|
||||
emoji = emoji.TrimStart(':').TrimEnd(':');
|
||||
|
||||
_remappings[tag] = emoji;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces emoji markup with corresponding unicode characters.
|
||||
@@ -19,6 +50,12 @@ namespace Spectre.Console
|
||||
static string ReplaceEmoji(Match match)
|
||||
{
|
||||
var key = match.Groups[2].Value;
|
||||
|
||||
if (_remappings.Count > 0 && _remappings.TryGetValue(key, out var remappedEmoji))
|
||||
{
|
||||
return remappedEmoji;
|
||||
}
|
||||
|
||||
if (_emojis.TryGetValue(key, out var emoji))
|
||||
{
|
||||
return emoji;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// The default formatting.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not paths should be shortened.
|
||||
|
||||
27
src/Spectre.Console/ExceptionSettings.cs
Normal file
27
src/Spectre.Console/ExceptionSettings.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception settings.
|
||||
/// </summary>
|
||||
public sealed class ExceptionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the exception format.
|
||||
/// </summary>
|
||||
public ExceptionFormats Format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception style.
|
||||
/// </summary>
|
||||
public ExceptionStyle Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExceptionSettings"/> class.
|
||||
/// </summary>
|
||||
public ExceptionSettings()
|
||||
{
|
||||
Format = ExceptionFormats.Default;
|
||||
Style = new ExceptionStyle();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/Spectre.Console/ExceptionStyle.cs
Normal file
58
src/Spectre.Console/ExceptionStyle.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent an exception style.
|
||||
/// </summary>
|
||||
public sealed class ExceptionStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the message color.
|
||||
/// </summary>
|
||||
public Style Message { get; set; } = new Style(Color.Red, Color.Default, Decoration.Bold);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception color.
|
||||
/// </summary>
|
||||
public Style Exception { get; set; } = new Style(Color.White);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method color.
|
||||
/// </summary>
|
||||
public Style Method { get; set; } = new Style(Color.Yellow);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter type color.
|
||||
/// </summary>
|
||||
public Style ParameterType { get; set; } = new Style(Color.Blue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter name color.
|
||||
/// </summary>
|
||||
public Style ParameterName { get; set; } = new Style(Color.Silver);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parenthesis color.
|
||||
/// </summary>
|
||||
public Style Parenthesis { get; set; } = new Style(Color.Silver);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path color.
|
||||
/// </summary>
|
||||
public Style Path { get; set; } = new Style(Color.Yellow, Color.Default, Decoration.Bold);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the line number color.
|
||||
/// </summary>
|
||||
public Style LineNumber { get; set; } = new Style(Color.Blue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color for dimmed text such as "at" or "in".
|
||||
/// </summary>
|
||||
public Style Dimmed { get; set; } = new Style(Color.Grey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color for non emphasized items.
|
||||
/// </summary>
|
||||
public Style NonEmphasized { get; set; } = new Style(Color.Silver);
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,20 @@ namespace Spectre.Console
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="exception">The exception to write to the console.</param>
|
||||
/// <param name="format">The exception format options.</param>
|
||||
public static void WriteException(this IAnsiConsole console, Exception exception, ExceptionFormats format = ExceptionFormats.None)
|
||||
public static void WriteException(this IAnsiConsole console, Exception exception, ExceptionFormats format = ExceptionFormats.Default)
|
||||
{
|
||||
Render(console, exception.GetRenderable(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an exception to the console.
|
||||
/// </summary>
|
||||
/// <param name="console">The console.</param>
|
||||
/// <param name="exception">The exception to write to the console.</param>
|
||||
/// <param name="settings">The exception settings.</param>
|
||||
public static void WriteException(this IAnsiConsole console, Exception exception, ExceptionSettings settings)
|
||||
{
|
||||
Render(console, exception.GetRenderable(settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
src/Spectre.Console/Extensions/CalendarExtensions.cs
Normal file
83
src/Spectre.Console/Extensions/CalendarExtensions.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="Calendar"/>.
|
||||
/// </summary>
|
||||
public static class CalendarExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a calendar event.
|
||||
/// </summary>
|
||||
/// <param name="calendar">The calendar to add the calendar event to.</param>
|
||||
/// <param name="date">The calendar event date.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Calendar AddCalendarEvent(this Calendar calendar, DateTime date)
|
||||
{
|
||||
return AddCalendarEvent(calendar, string.Empty, date.Year, date.Month, date.Day);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a calendar event.
|
||||
/// </summary>
|
||||
/// <param name="calendar">The calendar to add the calendar event to.</param>
|
||||
/// <param name="description">The calendar event description.</param>
|
||||
/// <param name="date">The calendar event date.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Calendar AddCalendarEvent(this Calendar calendar, string description, DateTime date)
|
||||
{
|
||||
return AddCalendarEvent(calendar, description, date.Year, date.Month, date.Day);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a calendar event.
|
||||
/// </summary>
|
||||
/// <param name="calendar">The calendar to add the calendar event to.</param>
|
||||
/// <param name="year">The year of the calendar event.</param>
|
||||
/// <param name="month">The month of the calendar event.</param>
|
||||
/// <param name="day">The day of the calendar event.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Calendar AddCalendarEvent(this Calendar calendar, int year, int month, int day)
|
||||
{
|
||||
return AddCalendarEvent(calendar, string.Empty, year, month, day);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a calendar event.
|
||||
/// </summary>
|
||||
/// <param name="calendar">The calendar.</param>
|
||||
/// <param name="description">The calendar event description.</param>
|
||||
/// <param name="year">The year of the calendar event.</param>
|
||||
/// <param name="month">The month of the calendar event.</param>
|
||||
/// <param name="day">The day of the calendar event.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Calendar AddCalendarEvent(this Calendar calendar, string description, int year, int month, int day)
|
||||
{
|
||||
if (calendar is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(calendar));
|
||||
}
|
||||
|
||||
calendar.CalendarEvents.Add(new CalendarEvent(description, year, month, day));
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the calendar's highlight <see cref="Style"/>.
|
||||
/// </summary>
|
||||
/// <param name="calendar">The calendar.</param>
|
||||
/// <param name="style">The highlight style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Calendar SetHighlightStyle(this Calendar calendar, Style? style)
|
||||
{
|
||||
if (calendar is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(calendar));
|
||||
}
|
||||
|
||||
calendar.HightlightStyle = style ?? Style.Plain;
|
||||
return calendar;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,38 @@ namespace Spectre.Console
|
||||
/// <param name="exception">The exception to format.</param>
|
||||
/// <param name="format">The exception format options.</param>
|
||||
/// <returns>A <see cref="IRenderable"/> representing the exception.</returns>
|
||||
public static IRenderable GetRenderable(this Exception exception, ExceptionFormats format = ExceptionFormats.None)
|
||||
public static IRenderable GetRenderable(this Exception exception, ExceptionFormats format = ExceptionFormats.Default)
|
||||
{
|
||||
return ExceptionFormatter.Format(exception, format);
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
return GetRenderable(exception, new ExceptionSettings
|
||||
{
|
||||
Format = format,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IRenderable"/> representation of the exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to format.</param>
|
||||
/// <param name="settings">The exception settings.</param>
|
||||
/// <returns>A <see cref="IRenderable"/> representing the exception.</returns>
|
||||
public static IRenderable GetRenderable(this Exception exception, ExceptionSettings settings)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
if (settings is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
return ExceptionFormatter.Format(exception, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
src/Spectre.Console/Extensions/HasCultureExtensions.cs
Normal file
73
src/Spectre.Console/Extensions/HasCultureExtensions.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="IHasCulture"/>.
|
||||
/// </summary>
|
||||
public static class HasCultureExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the culture.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An object type with a culture.</typeparam>
|
||||
/// <param name="obj">The object to set the culture for.</param>
|
||||
/// <param name="culture">The culture to set.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static T SetCulture<T>(this T obj, CultureInfo culture)
|
||||
where T : class, IHasCulture
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
if (culture is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(culture));
|
||||
}
|
||||
|
||||
obj.Culture = culture;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the culture.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An object type with a culture.</typeparam>
|
||||
/// <param name="obj">The object to set the culture for.</param>
|
||||
/// <param name="name">The culture to set.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static T SetCulture<T>(this T obj, string name)
|
||||
where T : class, IHasCulture
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.Culture = CultureInfo.GetCultureInfo(name);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the culture.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An object type with a culture.</typeparam>
|
||||
/// <param name="obj">The object to set the culture for.</param>
|
||||
/// <param name="name">The culture to set.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static T SetCulture<T>(this T obj, int name)
|
||||
where T : class, IHasCulture
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
obj.Culture = CultureInfo.GetCultureInfo(name);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/Spectre.Console/IHasCulture.cs
Normal file
15
src/Spectre.Console/IHasCulture.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents something that has a culture.
|
||||
/// </summary>
|
||||
public interface IHasCulture
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the culture.
|
||||
/// </summary>
|
||||
CultureInfo Culture { get; set; }
|
||||
}
|
||||
}
|
||||
88
src/Spectre.Console/Internal/Collections/ListWithCallback.cs
Normal file
88
src/Spectre.Console/Internal/Collections/ListWithCallback.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console.Internal.Collections
|
||||
{
|
||||
internal sealed class ListWithCallback<T> : IList<T>
|
||||
{
|
||||
private readonly List<T> _list;
|
||||
private readonly Action _callback;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => _list[index];
|
||||
set => _list[index] = value;
|
||||
}
|
||||
|
||||
public int Count => _list.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public ListWithCallback(Action callback)
|
||||
{
|
||||
_list = new List<T>();
|
||||
_callback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
_list.Add(item);
|
||||
_callback();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_list.Clear();
|
||||
_callback();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return _list.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
_list.CopyTo(array, arrayIndex);
|
||||
_callback();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return _list.GetEnumerator();
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
return _list.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
_list.Insert(index, item);
|
||||
_callback();
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
var result = _list.Remove(item);
|
||||
if (result)
|
||||
{
|
||||
_callback();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
_list.RemoveAt(index);
|
||||
_callback();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal static class DecorationTable
|
||||
{
|
||||
private static readonly Dictionary<string, Decoration?> _lookup;
|
||||
private static readonly Dictionary<Decoration, string> _reverseLookup;
|
||||
|
||||
[SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline")]
|
||||
static DecorationTable()
|
||||
@@ -28,6 +30,21 @@ namespace Spectre.Console.Internal
|
||||
{ "strikethrough", Decoration.Strikethrough },
|
||||
{ "s", Decoration.Strikethrough },
|
||||
};
|
||||
|
||||
_reverseLookup = new Dictionary<Decoration, string>();
|
||||
foreach (var (name, decoration) in _lookup)
|
||||
{
|
||||
// Cannot happen, but the compiler thinks so...
|
||||
if (decoration == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_reverseLookup.ContainsKey(decoration.Value))
|
||||
{
|
||||
_reverseLookup[decoration.Value] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Decoration? GetDecoration(string name)
|
||||
@@ -35,5 +52,23 @@ namespace Spectre.Console.Internal
|
||||
_lookup.TryGetValue(name, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<string> GetMarkupNames(Decoration decoration)
|
||||
{
|
||||
var result = new List<string>();
|
||||
|
||||
Enum.GetValues(typeof(Decoration))
|
||||
.Cast<Decoration>()
|
||||
.Where(flag => (decoration & flag) != 0)
|
||||
.ForEach(flag =>
|
||||
{
|
||||
if (flag != Decoration.None && _reverseLookup.TryGetValue(flag, out var name))
|
||||
{
|
||||
result.Add(name);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,7 @@ namespace Spectre.Console
|
||||
{
|
||||
internal static class ExceptionFormatter
|
||||
{
|
||||
private static readonly Color _typeColor = Color.White;
|
||||
private static readonly Color _methodColor = Color.Yellow;
|
||||
private static readonly Color _parameterColor = Color.Blue;
|
||||
private static readonly Color _pathColor = Color.Yellow;
|
||||
private static readonly Color _dimmedColor = Color.Grey;
|
||||
|
||||
public static IRenderable Format(Exception exception, ExceptionFormats format)
|
||||
public static IRenderable Format(Exception exception, ExceptionSettings settings)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
@@ -27,10 +21,10 @@ namespace Spectre.Console
|
||||
return new Text(exception.ToString());
|
||||
}
|
||||
|
||||
return GetException(info, format);
|
||||
return GetException(info, settings);
|
||||
}
|
||||
|
||||
private static IRenderable GetException(ExceptionInfo info, ExceptionFormats format)
|
||||
private static IRenderable GetException(ExceptionInfo info, ExceptionSettings settings)
|
||||
{
|
||||
if (info is null)
|
||||
{
|
||||
@@ -39,20 +33,20 @@ namespace Spectre.Console
|
||||
|
||||
return new Rows(new IRenderable[]
|
||||
{
|
||||
GetMessage(info, format),
|
||||
GetStackFrames(info, format),
|
||||
GetMessage(info, settings),
|
||||
GetStackFrames(info, settings),
|
||||
}).Expand();
|
||||
}
|
||||
|
||||
private static Markup GetMessage(ExceptionInfo ex, ExceptionFormats format)
|
||||
private static Markup GetMessage(ExceptionInfo ex, ExceptionSettings settings)
|
||||
{
|
||||
var shortenTypes = (format & ExceptionFormats.ShortenTypes) != 0;
|
||||
var type = Emphasize(ex.Type, new[] { '.' }, _typeColor.ToMarkupString(), shortenTypes);
|
||||
var message = $"[b red]{ex.Message.SafeMarkup()}[/]";
|
||||
var shortenTypes = (settings.Format & ExceptionFormats.ShortenTypes) != 0;
|
||||
var type = Emphasize(ex.Type, new[] { '.' }, settings.Style.Exception, shortenTypes, settings);
|
||||
var message = $"[{settings.Style.Message.ToMarkup()}]{ex.Message.SafeMarkup()}[/]";
|
||||
return new Markup(string.Concat(type, ": ", message));
|
||||
}
|
||||
|
||||
private static Grid GetStackFrames(ExceptionInfo ex, ExceptionFormats format)
|
||||
private static Grid GetStackFrames(ExceptionInfo ex, ExceptionSettings settings)
|
||||
{
|
||||
var grid = new Grid();
|
||||
grid.AddColumn(new GridColumn().PadLeft(2).PadRight(0).NoWrap());
|
||||
@@ -63,7 +57,7 @@ namespace Spectre.Console
|
||||
{
|
||||
grid.AddRow(
|
||||
Text.Empty,
|
||||
GetException(ex.Inner, format));
|
||||
GetException(ex.Inner, settings));
|
||||
}
|
||||
|
||||
// Stack frames
|
||||
@@ -72,47 +66,57 @@ namespace Spectre.Console
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// Method
|
||||
var shortenMethods = (format & ExceptionFormats.ShortenMethods) != 0;
|
||||
builder.Append(Emphasize(frame.Method, new[] { '.' }, _methodColor.ToMarkupString(), shortenMethods));
|
||||
builder.Append('(');
|
||||
builder.Append(string.Join(", ", frame.Parameters.Select(x => $"[{_parameterColor.ToMarkupString()}]{x.Type.SafeMarkup()}[/] {x.Name}")));
|
||||
builder.Append(')');
|
||||
var shortenMethods = (settings.Format & ExceptionFormats.ShortenMethods) != 0;
|
||||
builder.Append(Emphasize(frame.Method, new[] { '.' }, settings.Style.Method, shortenMethods, settings));
|
||||
builder.Append('[').Append(settings.Style.Parenthesis.ToMarkup()).Append(']').Append('(').Append("[/]");
|
||||
AppendParameters(builder, frame, settings);
|
||||
builder.Append('[').Append(settings.Style.Parenthesis.ToMarkup()).Append(']').Append(')').Append("[/]");
|
||||
|
||||
if (frame.Path != null)
|
||||
{
|
||||
builder.Append(" [").Append(_dimmedColor.ToMarkupString()).Append("]in[/] ");
|
||||
builder.Append(" [").Append(settings.Style.Dimmed.ToMarkup()).Append("]in[/] ");
|
||||
|
||||
// Path
|
||||
AppendPath(builder, frame, format);
|
||||
AppendPath(builder, frame, settings);
|
||||
|
||||
// Line number
|
||||
if (frame.LineNumber != null)
|
||||
{
|
||||
builder.Append(':');
|
||||
builder.Append('[').Append(_parameterColor.ToMarkupString()).Append(']').Append(frame.LineNumber).Append("[/]");
|
||||
builder.Append('[').Append(settings.Style.Dimmed.ToMarkup()).Append("]:[/]");
|
||||
builder.Append('[').Append(settings.Style.LineNumber.ToMarkup()).Append(']').Append(frame.LineNumber).Append("[/]");
|
||||
}
|
||||
}
|
||||
|
||||
grid.AddRow($"[{_dimmedColor.ToMarkupString()}]at[/]", builder.ToString());
|
||||
grid.AddRow(
|
||||
$"[{settings.Style.Dimmed.ToMarkup()}]at[/]",
|
||||
builder.ToString());
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
private static void AppendPath(StringBuilder builder, StackFrameInfo frame, ExceptionFormats format)
|
||||
private static void AppendParameters(StringBuilder builder, StackFrameInfo frame, ExceptionSettings settings)
|
||||
{
|
||||
var typeColor = settings.Style.ParameterType.ToMarkup();
|
||||
var nameColor = settings.Style.ParameterName.ToMarkup();
|
||||
var parameters = frame.Parameters.Select(x => $"[{typeColor}]{x.Type.SafeMarkup()}[/] [{nameColor}]{x.Name}[/]");
|
||||
builder.Append(string.Join(", ", parameters));
|
||||
}
|
||||
|
||||
private static void AppendPath(StringBuilder builder, StackFrameInfo frame, ExceptionSettings settings)
|
||||
{
|
||||
if (frame?.Path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void RenderLink()
|
||||
void AppendPath()
|
||||
{
|
||||
var shortenPaths = (format & ExceptionFormats.ShortenPaths) != 0;
|
||||
builder.Append(Emphasize(frame.Path, new[] { '/', '\\' }, $"b {_pathColor.ToMarkupString()}", shortenPaths));
|
||||
var shortenPaths = (settings.Format & ExceptionFormats.ShortenPaths) != 0;
|
||||
builder.Append(Emphasize(frame.Path, new[] { '/', '\\' }, settings.Style.Path, shortenPaths, settings));
|
||||
}
|
||||
|
||||
if ((format & ExceptionFormats.ShowLinks) != 0)
|
||||
if ((settings.Format & ExceptionFormats.ShowLinks) != 0)
|
||||
{
|
||||
var hasLink = frame.TryGetUri(out var uri);
|
||||
if (hasLink && uri != null)
|
||||
@@ -120,7 +124,7 @@ namespace Spectre.Console
|
||||
builder.Append("[link=").Append(uri.AbsoluteUri).Append(']');
|
||||
}
|
||||
|
||||
RenderLink();
|
||||
AppendPath();
|
||||
|
||||
if (hasLink && uri != null)
|
||||
{
|
||||
@@ -129,11 +133,11 @@ namespace Spectre.Console
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderLink();
|
||||
AppendPath();
|
||||
}
|
||||
}
|
||||
|
||||
private static string Emphasize(string input, char[] separators, string color, bool compact)
|
||||
private static string Emphasize(string input, char[] separators, Style color, bool compact, ExceptionSettings settings)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
@@ -143,10 +147,12 @@ namespace Spectre.Console
|
||||
{
|
||||
if (!compact)
|
||||
{
|
||||
builder.Append("[silver]").Append(type, 0, index + 1).Append("[/]");
|
||||
builder.Append('[').Append(settings.Style.NonEmphasized.ToMarkup()).Append(']')
|
||||
.Append(type, 0, index + 1).Append("[/]");
|
||||
}
|
||||
|
||||
builder.Append('[').Append(color).Append(']').Append(type, index + 1, type.Length - index - 1).Append("[/]");
|
||||
builder.Append('[').Append(color.ToMarkup()).Append(']')
|
||||
.Append(type, index + 1, type.Length - index - 1).Append("[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal static class DayOfWeekExtensions
|
||||
{
|
||||
public static string GetAbbreviatedDayName(this DayOfWeek day, CultureInfo culture)
|
||||
{
|
||||
culture ??= CultureInfo.InvariantCulture;
|
||||
var name = culture.DateTimeFormat.GetAbbreviatedDayName(day);
|
||||
|
||||
if (name.Length > 0 && char.IsLower(name[0]))
|
||||
{
|
||||
name = string.Format(culture, "{0}{1}", char.ToUpper(name[0], culture), name.Substring(1));
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static DayOfWeek GetNextWeekDay(this DayOfWeek day)
|
||||
{
|
||||
var next = (int)day + 1;
|
||||
if (next > (int)DayOfWeek.Saturday)
|
||||
{
|
||||
return DayOfWeek.Sunday;
|
||||
}
|
||||
|
||||
return (DayOfWeek)next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value)
|
||||
{
|
||||
key = tuple.Key;
|
||||
value = tuple.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,11 +302,11 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
// How many characters should we take?
|
||||
var take = Math.Min(width, totalLength - index);
|
||||
if (take == 0)
|
||||
if (take <= 0)
|
||||
{
|
||||
// This shouldn't really occur, but I don't like
|
||||
// never ending loops if it does...
|
||||
throw new InvalidOperationException("Text folding failed since 'take' was zero.");
|
||||
return new List<Segment>();
|
||||
}
|
||||
|
||||
result.Add(new Segment(segment.Text.Substring(index, take), segment.Style));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Spectre.Console.Internal;
|
||||
|
||||
@@ -25,7 +26,7 @@ namespace Spectre.Console
|
||||
public Decoration Decoration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the link.
|
||||
/// Gets the link associated with the style.
|
||||
/// </summary>
|
||||
public string? Link { get; }
|
||||
|
||||
@@ -191,6 +192,41 @@ namespace Spectre.Console
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the markup representation of this style.
|
||||
/// </summary>
|
||||
/// <returns>The markup representation of this style.</returns>
|
||||
public string ToMarkup()
|
||||
{
|
||||
var builder = new List<string>();
|
||||
|
||||
if (Decoration != Decoration.None)
|
||||
{
|
||||
var result = DecorationTable.GetMarkupNames(Decoration);
|
||||
if (result.Count != 0)
|
||||
{
|
||||
builder.AddRange(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (Foreground != Color.Default)
|
||||
{
|
||||
builder.Add(Foreground.ToMarkup());
|
||||
}
|
||||
|
||||
if (Background != Color.Default)
|
||||
{
|
||||
if (builder.Count == 0)
|
||||
{
|
||||
builder.Add("default");
|
||||
}
|
||||
|
||||
builder.Add("on " + Background.ToMarkup());
|
||||
}
|
||||
|
||||
return string.Join(" ", builder);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
|
||||
274
src/Spectre.Console/Widgets/Calendar.cs
Normal file
274
src/Spectre.Console/Widgets/Calendar.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Internal;
|
||||
using Spectre.Console.Internal.Collections;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// A renderable calendar.
|
||||
/// </summary>
|
||||
public sealed class Calendar : Renderable, IHasCulture, IHasTableBorder
|
||||
{
|
||||
private const int NumberOfWeekDays = 7;
|
||||
private const int ExpectedRowCount = 6;
|
||||
|
||||
private readonly ListWithCallback<CalendarEvent> _calendarEvents;
|
||||
|
||||
private int _year;
|
||||
private int _month;
|
||||
private int _day;
|
||||
private IRenderable? _table;
|
||||
private TableBorder _border;
|
||||
private bool _useSafeBorder;
|
||||
private Style? _borderStyle;
|
||||
private bool _dirty;
|
||||
private CultureInfo _culture;
|
||||
private Style _highlightStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar year.
|
||||
/// </summary>
|
||||
public int Year
|
||||
{
|
||||
get => _year;
|
||||
set => MarkAsDirty(() => _year = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar month.
|
||||
/// </summary>
|
||||
public int Month
|
||||
{
|
||||
get => _month;
|
||||
set => MarkAsDirty(() => _month = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar day.
|
||||
/// </summary>
|
||||
public int Day
|
||||
{
|
||||
get => _day;
|
||||
set => MarkAsDirty(() => _day = value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TableBorder Border
|
||||
{
|
||||
get => _border;
|
||||
set => MarkAsDirty(() => _border = value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UseSafeBorder
|
||||
{
|
||||
get => _useSafeBorder;
|
||||
set => MarkAsDirty(() => _useSafeBorder = value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Style? BorderStyle
|
||||
{
|
||||
get => _borderStyle;
|
||||
set => MarkAsDirty(() => _borderStyle = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar's <see cref="CultureInfo"/>.
|
||||
/// </summary>
|
||||
public CultureInfo Culture
|
||||
{
|
||||
get => _culture;
|
||||
set => MarkAsDirty(() => _culture = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar's highlight <see cref="Style"/>.
|
||||
/// </summary>
|
||||
public Style HightlightStyle
|
||||
{
|
||||
get => _highlightStyle;
|
||||
set => MarkAsDirty(() => _highlightStyle = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list containing all calendar events.
|
||||
/// </summary>
|
||||
public IList<CalendarEvent> CalendarEvents => _calendarEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Calendar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="date">The calendar date.</param>
|
||||
public Calendar(DateTime date)
|
||||
: this(date.Year, date.Month, date.Day)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Calendar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="year">The calendar year.</param>
|
||||
/// <param name="month">The calendar month.</param>
|
||||
public Calendar(int year, int month)
|
||||
: this(year, month, 1)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Calendar"/> class.
|
||||
/// </summary>
|
||||
/// <param name="year">The calendar year.</param>
|
||||
/// <param name="month">The calendar month.</param>
|
||||
/// <param name="day">The calendar day.</param>
|
||||
public Calendar(int year, int month, int day)
|
||||
{
|
||||
_year = year;
|
||||
_month = month;
|
||||
_day = day;
|
||||
_table = null;
|
||||
_border = TableBorder.Square;
|
||||
_useSafeBorder = true;
|
||||
_borderStyle = null;
|
||||
_dirty = true;
|
||||
_culture = CultureInfo.InvariantCulture;
|
||||
_highlightStyle = new Style(foreground: Color.Blue);
|
||||
_calendarEvents = new ListWithCallback<CalendarEvent>(() => _dirty = true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||
{
|
||||
var table = GetTable();
|
||||
return table.Measure(context, maxWidth);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
return GetTable().Render(context, maxWidth);
|
||||
}
|
||||
|
||||
private IRenderable GetTable()
|
||||
{
|
||||
// Table needs to be built?
|
||||
if (_dirty || _table == null)
|
||||
{
|
||||
_table = BuildTable();
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
return _table;
|
||||
}
|
||||
|
||||
private IRenderable BuildTable()
|
||||
{
|
||||
var culture = Culture ?? CultureInfo.InvariantCulture;
|
||||
|
||||
var table = new Table
|
||||
{
|
||||
Border = _border,
|
||||
UseSafeBorder = _useSafeBorder,
|
||||
BorderStyle = _borderStyle,
|
||||
};
|
||||
|
||||
// Add columns
|
||||
foreach (var order in GetWeekdays())
|
||||
{
|
||||
table.AddColumn(new TableColumn(order.GetAbbreviatedDayName(culture)));
|
||||
}
|
||||
|
||||
var row = new List<IRenderable>();
|
||||
|
||||
var currentDay = 1;
|
||||
var weekday = culture.DateTimeFormat.FirstDayOfWeek;
|
||||
var weekdays = BuildWeekDayTable();
|
||||
|
||||
var daysInMonth = DateTime.DaysInMonth(Year, Month);
|
||||
while (currentDay <= daysInMonth)
|
||||
{
|
||||
if (weekdays[currentDay - 1] == weekday)
|
||||
{
|
||||
if (_calendarEvents.Any(e => e.Month == Month && e.Day == currentDay))
|
||||
{
|
||||
row.Add(new Markup(currentDay.ToString(CultureInfo.InvariantCulture) + "*", _highlightStyle));
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Add(new Text(currentDay.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
currentDay++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add empty cell
|
||||
row.Add(Text.Empty);
|
||||
}
|
||||
|
||||
if (row.Count == NumberOfWeekDays)
|
||||
{
|
||||
// Flush row
|
||||
table.AddRow(row.ToArray());
|
||||
row.Clear();
|
||||
}
|
||||
|
||||
weekday = weekday.GetNextWeekDay();
|
||||
}
|
||||
|
||||
if (row.Count > 0)
|
||||
{
|
||||
// Flush row
|
||||
table.AddRow(row.ToArray());
|
||||
row.Clear();
|
||||
}
|
||||
|
||||
// We want all calendars to have the same height.
|
||||
if (table.RowCount < ExpectedRowCount)
|
||||
{
|
||||
var diff = Math.Max(0, ExpectedRowCount - table.RowCount);
|
||||
for (var i = 0; i < diff; i++)
|
||||
{
|
||||
table.AddEmptyRow();
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private void MarkAsDirty(Action action)
|
||||
{
|
||||
action();
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
private DayOfWeek[] GetWeekdays()
|
||||
{
|
||||
var culture = Culture ?? CultureInfo.InvariantCulture;
|
||||
|
||||
var days = new DayOfWeek[7];
|
||||
days[0] = culture.DateTimeFormat.FirstDayOfWeek;
|
||||
for (var i = 1; i < 7; i++)
|
||||
{
|
||||
days[i] = days[i - 1].GetNextWeekDay();
|
||||
}
|
||||
|
||||
return days;
|
||||
}
|
||||
|
||||
private DayOfWeek[] BuildWeekDayTable()
|
||||
{
|
||||
var result = new List<DayOfWeek>();
|
||||
for (var day = 0; day < DateTime.DaysInMonth(Year, Month); day++)
|
||||
{
|
||||
result.Add(new DateTime(Year, Month, day + 1).DayOfWeek);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/Spectre.Console/Widgets/CalendarEvent.cs
Normal file
54
src/Spectre.Console/Widgets/CalendarEvent.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a calendar event.
|
||||
/// </summary>
|
||||
public sealed class CalendarEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the description of the calendar event.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the year of the calendar event.
|
||||
/// </summary>
|
||||
public int Year { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the month of the calendar event.
|
||||
/// </summary>
|
||||
public int Month { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the day of the calendar event.
|
||||
/// </summary>
|
||||
public int Day { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CalendarEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="year">The year of the calendar event.</param>
|
||||
/// <param name="month">The month of the calendar event.</param>
|
||||
/// <param name="day">The day of the calendar event.</param>
|
||||
public CalendarEvent(int year, int month, int day)
|
||||
: this(string.Empty, year, month, day)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CalendarEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="description">The calendar event description.</param>
|
||||
/// <param name="year">The year of the calendar event.</param>
|
||||
/// <param name="month">The month of the calendar event.</param>
|
||||
/// <param name="day">The day of the calendar event.</param>
|
||||
public CalendarEvent(string description, int year, int month, int day)
|
||||
{
|
||||
Description = description ?? string.Empty;
|
||||
Year = year;
|
||||
Month = month;
|
||||
Day = day;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,29 @@ namespace Spectre.Console
|
||||
_items = new List<IRenderable>(items.Select(item => new Markup(item)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||
{
|
||||
var maxPadding = Math.Max(Padding.Left, Padding.Right);
|
||||
|
||||
var itemWidths = _items.Select(item => item.Measure(context, maxWidth).Max).ToArray();
|
||||
var columnCount = CalculateColumnCount(maxWidth, itemWidths, _items.Count, maxPadding);
|
||||
|
||||
var rows = _items.Count / columnCount;
|
||||
var greatestWidth = 0;
|
||||
for (var row = 0; row < rows; row += columnCount)
|
||||
{
|
||||
var widths = itemWidths.Skip(row * columnCount).Take(columnCount).ToList();
|
||||
var totalWidth = widths.Sum() + (maxPadding * (widths.Count - 1));
|
||||
if (totalWidth > greatestWidth)
|
||||
{
|
||||
greatestWidth = totalWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return new Measurement(greatestWidth, greatestWidth);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user