From 98cf63f485542186fc364b18df12ae8dcdea3bdd Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Mon, 3 Aug 2020 22:33:08 +0200 Subject: [PATCH] Rename `Style` and `Appearance` * Renames Style -> Decoration * Renames Appearance -> Style * Adds Style.Parse and Style.TryParse --- README.md | 9 +- src/Sample/Program.cs | 6 +- .../Fixtures/ConsoleWithWidth.cs | 2 +- .../Fixtures/PlainConsole.cs | 2 +- .../Unit/AnsiConsoleTests.Style.cs | 30 +-- .../Unit/AnsiConsoleTests.cs | 12 +- .../Unit/AppearanceTests.cs | 24 -- .../Unit/Composition/SegmentTests.cs | 8 +- .../Unit/Composition/TextTests.cs | 4 +- src/Spectre.Console.Tests/Unit/StyleTests.cs | 237 ++++++++++++++++++ src/Spectre.Console/AnsiConsole.cs | 18 +- src/Spectre.Console/Appearance.cs | 108 -------- src/Spectre.Console/Composition/Segment.cs | 26 +- src/Spectre.Console/Composition/Text.cs | 38 +-- .../ConsoleExtensions.Rendering.cs | 4 +- src/Spectre.Console/ConsoleExtensions.cs | 14 +- .../{Styles.cs => Decoration.cs} | 10 +- src/Spectre.Console/IAnsiConsole.cs | 4 +- .../Internal/Ansi/AnsiBuilder.cs | 4 +- ...yleBuilder.cs => AnsiDecorationBuilder.cs} | 22 +- .../Internal/AnsiConsoleRenderer.cs | 20 +- .../Internal/ConsoleBuilder.cs | 2 +- .../Internal/DecorationTable.cs | 35 +++ .../Extensions/AppearanceExtensions.cs | 18 -- .../Internal/Extensions/ConsoleExtensions.cs | 57 +++-- .../Internal/Extensions/StyleExtensions.cs | 29 +++ .../Internal/FallbackConsoleRenderer.cs | 2 +- src/Spectre.Console/Internal/StyleTable.cs | 34 --- .../Internal/Text/Markup/MarkupParser.cs | 14 +- .../Internal/Text/Markup/MarkupStyleParser.cs | 65 ----- .../Internal/Text/StyleParser.cs | 104 ++++++++ src/Spectre.Console/Style.cs | 134 ++++++++++ 32 files changed, 691 insertions(+), 405 deletions(-) delete mode 100644 src/Spectre.Console.Tests/Unit/AppearanceTests.cs create mode 100644 src/Spectre.Console.Tests/Unit/StyleTests.cs delete mode 100644 src/Spectre.Console/Appearance.cs rename src/Spectre.Console/{Styles.cs => Decoration.cs} (86%) rename src/Spectre.Console/Internal/Ansi/{AnsiStyleBuilder.cs => AnsiDecorationBuilder.cs} (52%) create mode 100644 src/Spectre.Console/Internal/DecorationTable.cs delete mode 100644 src/Spectre.Console/Internal/Extensions/AppearanceExtensions.cs create mode 100644 src/Spectre.Console/Internal/Extensions/StyleExtensions.cs delete mode 100644 src/Spectre.Console/Internal/StyleTable.cs delete mode 100644 src/Spectre.Console/Internal/Text/Markup/MarkupStyleParser.cs create mode 100644 src/Spectre.Console/Internal/Text/StyleParser.cs create mode 100644 src/Spectre.Console/Style.cs diff --git a/README.md b/README.md index 520480a..ce2b155 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,11 @@ like you usually do with the `System.Console` API, but prettier. ```csharp AnsiConsole.Foreground = Color.CornflowerBlue; -AnsiConsole.Style = Styles.Underline | Styles.Bold; +AnsiConsole.Decoration = Decoration.Underline | Decoration.Bold; AnsiConsole.WriteLine("Hello World!"); AnsiConsole.Reset(); -AnsiConsole.MarkupLine("[yellow]{0}[/] [underline]world[/]!", "Goodbye"); +AnsiConsole.MarkupLine("[bold yellow on red]{0}[/] [underline]world[/]!", "Goodbye"); ``` If you want to get a reference to the default `IAnsiConsole`, @@ -64,7 +64,10 @@ you can access it via `AnsiConsole.Console`. Sometimes it's useful to explicitly create a console with specific capabilities, such as during unit testing when you want control -over the environment your code runs in. +over the environment your code runs in. + +It's recommended to not use `AnsiConsole` in code that run as +part of a unit test. ```csharp IAnsiConsole console = AnsiConsole.Create( diff --git a/src/Sample/Program.cs b/src/Sample/Program.cs index 508f470..65ae7a3 100644 --- a/src/Sample/Program.cs +++ b/src/Sample/Program.cs @@ -9,7 +9,7 @@ namespace Sample { // Use the static API to write some things to the console. AnsiConsole.Foreground = Color.Chartreuse2; - AnsiConsole.Style = Styles.Underline | Styles.Bold; + AnsiConsole.Decoration = Decoration.Underline | Decoration.Bold; AnsiConsole.WriteLine("Hello World!"); AnsiConsole.Reset(); AnsiConsole.MarkupLine("Capabilities: [yellow underline]{0}[/]", AnsiConsole.Capabilities); @@ -40,10 +40,10 @@ namespace Sample // and downgrade them to the specified color system. console.WriteLine(); console.Foreground = Color.Chartreuse2; - console.Style = Styles.Underline | Styles.Bold; + console.Decoration = Decoration.Underline | Decoration.Bold; console.WriteLine("Hello World!"); console.ResetColors(); - console.ResetStyle(); + console.ResetDecoration(); console.MarkupLine("Capabilities: [yellow underline]{0}[/]", console.Capabilities); console.MarkupLine("Width=[yellow]{0}[/], Height=[yellow]{1}[/]", console.Width, console.Height); console.MarkupLine("[white on red]Good[/] [red]bye[/]!"); diff --git a/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs b/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs index 3429478..dde146c 100644 --- a/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs +++ b/src/Spectre.Console.Tests/Fixtures/ConsoleWithWidth.cs @@ -13,7 +13,7 @@ namespace Spectre.Console.Tests public Encoding Encoding => _console.Encoding; - public Styles Style { get => _console.Style; set => _console.Style = value; } + public Decoration Decoration { get => _console.Decoration; set => _console.Decoration = value; } public Color Foreground { get => _console.Foreground; set => _console.Foreground = value; } public Color Background { get => _console.Background; set => _console.Background = value; } diff --git a/src/Spectre.Console.Tests/Fixtures/PlainConsole.cs b/src/Spectre.Console.Tests/Fixtures/PlainConsole.cs index fb84731..4d52aa1 100644 --- a/src/Spectre.Console.Tests/Fixtures/PlainConsole.cs +++ b/src/Spectre.Console.Tests/Fixtures/PlainConsole.cs @@ -13,7 +13,7 @@ namespace Spectre.Console.Tests public int Width { get; } public int Height { get; } - public Styles Style { get; set; } + public Decoration Decoration { get; set; } public Color Foreground { get; set; } public Color Background { get; set; } diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs index 624d52e..ced16e6 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Style.cs @@ -6,20 +6,20 @@ namespace Spectre.Console.Tests.Unit public partial class AnsiConsoleTests { [Theory] - [InlineData(Styles.Bold, "\u001b[1mHello World")] - [InlineData(Styles.Dim, "\u001b[2mHello World")] - [InlineData(Styles.Italic, "\u001b[3mHello World")] - [InlineData(Styles.Underline, "\u001b[4mHello World")] - [InlineData(Styles.Invert, "\u001b[7mHello World")] - [InlineData(Styles.Conceal, "\u001b[8mHello World")] - [InlineData(Styles.SlowBlink, "\u001b[5mHello World")] - [InlineData(Styles.RapidBlink, "\u001b[6mHello World")] - [InlineData(Styles.Strikethrough, "\u001b[9mHello World")] - public void Should_Write_Style_Correctly(Styles style, string expected) + [InlineData(Decoration.Bold, "\u001b[1mHello World")] + [InlineData(Decoration.Dim, "\u001b[2mHello World")] + [InlineData(Decoration.Italic, "\u001b[3mHello World")] + [InlineData(Decoration.Underline, "\u001b[4mHello World")] + [InlineData(Decoration.Invert, "\u001b[7mHello World")] + [InlineData(Decoration.Conceal, "\u001b[8mHello World")] + [InlineData(Decoration.SlowBlink, "\u001b[5mHello World")] + [InlineData(Decoration.RapidBlink, "\u001b[6mHello World")] + [InlineData(Decoration.Strikethrough, "\u001b[9mHello World")] + public void Should_Write_Decorated_Text_Correctly(Decoration decoration, string expected) { // Given var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); - fixture.Console.Style = style; + fixture.Console.Decoration = decoration; // When fixture.Console.Write("Hello World"); @@ -29,13 +29,13 @@ namespace Spectre.Console.Tests.Unit } [Theory] - [InlineData(Styles.Bold | Styles.Underline, "\u001b[1;4mHello World")] - [InlineData(Styles.Bold | Styles.Underline | Styles.Conceal, "\u001b[1;4;8mHello World")] - public void Should_Write_Combined_Styles_Correctly(Styles style, string expected) + [InlineData(Decoration.Bold | Decoration.Underline, "\u001b[1;4mHello World")] + [InlineData(Decoration.Bold | Decoration.Underline | Decoration.Conceal, "\u001b[1;4;8mHello World")] + public void Should_Write_Text_With_Multiple_Decorations_Correctly(Decoration decoration, string expected) { // Given var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); - fixture.Console.Style = style; + fixture.Console.Decoration = decoration; // When fixture.Console.Write("Hello World"); diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs index aeecdfc..b1505a5 100644 --- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs +++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.cs @@ -8,13 +8,13 @@ namespace Spectre.Console.Tests.Unit public partial class AnsiConsoleTests { [Fact] - public void Should_Combine_Style_And_Colors() + public void Should_Combine_Decoration_And_Colors() { // Given var fixture = new AnsiConsoleFixture(ColorSystem.Standard); fixture.Console.Foreground = Color.RoyalBlue1; fixture.Console.Background = Color.NavajoWhite1; - fixture.Console.Style = Styles.Italic; + fixture.Console.Decoration = Decoration.Italic; // When fixture.Console.Write("Hello"); @@ -30,7 +30,7 @@ namespace Spectre.Console.Tests.Unit var fixture = new AnsiConsoleFixture(ColorSystem.Standard); fixture.Console.Foreground = Color.Default; fixture.Console.Background = Color.NavajoWhite1; - fixture.Console.Style = Styles.Italic; + fixture.Console.Decoration = Decoration.Italic; // When fixture.Console.Write("Hello"); @@ -46,7 +46,7 @@ namespace Spectre.Console.Tests.Unit var fixture = new AnsiConsoleFixture(ColorSystem.Standard); fixture.Console.Foreground = Color.RoyalBlue1; fixture.Console.Background = Color.Default; - fixture.Console.Style = Styles.Italic; + fixture.Console.Decoration = Decoration.Italic; // When fixture.Console.Write("Hello"); @@ -56,13 +56,13 @@ namespace Spectre.Console.Tests.Unit } [Fact] - public void Should_Not_Include_Style_If_Set_To_None() + public void Should_Not_Include_Decoration_If_Set_To_None() { // Given var fixture = new AnsiConsoleFixture(ColorSystem.Standard); fixture.Console.Foreground = Color.RoyalBlue1; fixture.Console.Background = Color.NavajoWhite1; - fixture.Console.Style = Styles.None; + fixture.Console.Decoration = Decoration.None; // When fixture.Console.Write("Hello"); diff --git a/src/Spectre.Console.Tests/Unit/AppearanceTests.cs b/src/Spectre.Console.Tests/Unit/AppearanceTests.cs deleted file mode 100644 index 3261fa3..0000000 --- a/src/Spectre.Console.Tests/Unit/AppearanceTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Shouldly; -using Xunit; - -namespace Spectre.Console.Tests.Unit -{ - public sealed class AppearanceTests - { - [Fact] - public void Should_Combine_Two_Appearances_As_Expected() - { - // Given - var first = new Appearance(Color.White, Color.Yellow, Styles.Bold | Styles.Italic); - var other = new Appearance(Color.Green, Color.Silver, Styles.Underline); - - // When - var result = first.Combine(other); - - // Then - result.Foreground.ShouldBe(Color.Green); - result.Background.ShouldBe(Color.Silver); - result.Style.ShouldBe(Styles.Bold | Styles.Italic | Styles.Underline); - } - } -} diff --git a/src/Spectre.Console.Tests/Unit/Composition/SegmentTests.cs b/src/Spectre.Console.Tests/Unit/Composition/SegmentTests.cs index 0e97b2a..dd49299 100644 --- a/src/Spectre.Console.Tests/Unit/Composition/SegmentTests.cs +++ b/src/Spectre.Console.Tests/Unit/Composition/SegmentTests.cs @@ -12,17 +12,17 @@ namespace Spectre.Console.Tests.Unit public void Should_Split_Segment_Correctly() { // Given - var appearance = new Appearance(Color.Red, Color.Green, Styles.Bold); - var segment = new Segment("Foo Bar", appearance); + var style = new Style(Color.Red, Color.Green, Decoration.Bold); + var segment = new Segment("Foo Bar", style); // When var (first, second) = segment.Split(3); // Then first.Text.ShouldBe("Foo"); - first.Appearance.ShouldBe(appearance); + first.Style.ShouldBe(style); second.Text.ShouldBe(" Bar"); - second.Appearance.ShouldBe(appearance); + second.Style.ShouldBe(style); } } diff --git a/src/Spectre.Console.Tests/Unit/Composition/TextTests.cs b/src/Spectre.Console.Tests/Unit/Composition/TextTests.cs index 3db878c..b94c6ac 100644 --- a/src/Spectre.Console.Tests/Unit/Composition/TextTests.cs +++ b/src/Spectre.Console.Tests/Unit/Composition/TextTests.cs @@ -45,7 +45,7 @@ namespace Spectre.Console.Tests.Unit // Given var fixture = new AnsiConsoleFixture(ColorSystem.Standard); var text = Text.New("Hello World"); - text.Stylize(start: 3, end: 8, new Appearance(style: Styles.Underline)); + text.Stylize(start: 3, end: 8, new Style(decoration: Decoration.Underline)); // When fixture.Console.Render(text); @@ -62,7 +62,7 @@ namespace Spectre.Console.Tests.Unit // Given var fixture = new AnsiConsoleFixture(ColorSystem.Standard, width: 5); var text = Text.New("Hello World"); - text.Stylize(start: 3, end: 8, new Appearance(style: Styles.Underline)); + text.Stylize(start: 3, end: 8, new Style(decoration: Decoration.Underline)); // When fixture.Console.Render(text); diff --git a/src/Spectre.Console.Tests/Unit/StyleTests.cs b/src/Spectre.Console.Tests/Unit/StyleTests.cs new file mode 100644 index 0000000..204016f --- /dev/null +++ b/src/Spectre.Console.Tests/Unit/StyleTests.cs @@ -0,0 +1,237 @@ +using System; +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests.Unit +{ + public sealed class StyleTests + { + [Fact] + public void Should_Combine_Two_Styles_As_Expected() + { + // Given + var first = new Style(Color.White, Color.Yellow, Decoration.Bold | Decoration.Italic); + var other = new Style(Color.Green, Color.Silver, Decoration.Underline); + + // When + var result = first.Combine(other); + + // Then + result.Foreground.ShouldBe(Color.Green); + result.Background.ShouldBe(Color.Silver); + result.Decoration.ShouldBe(Decoration.Bold | Decoration.Italic | Decoration.Underline); + } + + public sealed class TheParseMethod + { + [Fact] + public void Default_Keyword_Should_Return_Default_Style() + { + // Given, When + var result = Style.Parse("default"); + + // Then + result.ShouldNotBeNull(); + result.Foreground.ShouldBe(Color.Default); + result.Background.ShouldBe(Color.Default); + result.Decoration.ShouldBe(Decoration.None); + } + + [Theory] + [InlineData("bold", Decoration.Bold)] + [InlineData("dim", Decoration.Dim)] + [InlineData("italic", Decoration.Italic)] + [InlineData("underline", Decoration.Underline)] + [InlineData("invert", Decoration.Invert)] + [InlineData("conceal", Decoration.Conceal)] + [InlineData("slowblink", Decoration.SlowBlink)] + [InlineData("rapidblink", Decoration.RapidBlink)] + [InlineData("strikethrough", Decoration.Strikethrough)] + public void Should_Parse_Decoration(string text, Decoration decoration) + { + // Given, When + var result = Style.Parse(text); + + // Then + result.ShouldNotBeNull(); + result.Decoration.ShouldBe(decoration); + } + + [Fact] + public void Should_Parse_Text_And_Decoration() + { + // Given, When + var result = Style.Parse("bold underline blue on green"); + + // Then + result.ShouldNotBeNull(); + result.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline); + result.Foreground.ShouldBe(Color.Blue); + result.Background.ShouldBe(Color.Green); + } + + [Fact] + public void Should_Parse_Background_If_Foreground_Is_Set_To_Default() + { + // Given, When + var result = Style.Parse("default on green"); + + // Then + result.ShouldNotBeNull(); + result.Decoration.ShouldBe(Decoration.None); + result.Foreground.ShouldBe(Color.Default); + result.Background.ShouldBe(Color.Green); + } + + [Fact] + public void Should_Throw_If_Foreground_Is_Set_Twice() + { + // Given, When + var result = Record.Exception(() => Style.Parse("green yellow")); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldBe("A foreground color has already been set."); + } + + [Fact] + public void Should_Throw_If_Background_Is_Set_Twice() + { + // Given, When + var result = Record.Exception(() => Style.Parse("green on blue yellow")); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldBe("A background color has already been set."); + } + + [Fact] + public void Should_Throw_If_Color_Name_Could_Not_Be_Found() + { + // Given, When + var result = Record.Exception(() => Style.Parse("bold lol")); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldBe("Could not find color or style 'lol'."); + } + + [Fact] + public void Should_Throw_If_Background_Color_Name_Could_Not_Be_Found() + { + // Given, When + var result = Record.Exception(() => Style.Parse("blue on lol")); + + // Then + result.ShouldBeOfType(); + result.Message.ShouldBe("Could not find color 'lol'."); + } + } + + public sealed class TheTryParseMethod + { + [Fact] + public void Default_Keyword_Should_Return_Default_Style() + { + // Given, When + var result = Style.TryParse("default", out var style); + + // Then + result.ShouldBeTrue(); + style.ShouldNotBeNull(); + style.Foreground.ShouldBe(Color.Default); + style.Background.ShouldBe(Color.Default); + style.Decoration.ShouldBe(Decoration.None); + } + + [Theory] + [InlineData("bold", Decoration.Bold)] + [InlineData("dim", Decoration.Dim)] + [InlineData("italic", Decoration.Italic)] + [InlineData("underline", Decoration.Underline)] + [InlineData("invert", Decoration.Invert)] + [InlineData("conceal", Decoration.Conceal)] + [InlineData("slowblink", Decoration.SlowBlink)] + [InlineData("rapidblink", Decoration.RapidBlink)] + [InlineData("strikethrough", Decoration.Strikethrough)] + public void Should_Parse_Decoration(string text, Decoration decoration) + { + // Given, When + var result = Style.TryParse(text, out var style); + + // Then + result.ShouldBeTrue(); + style.ShouldNotBeNull(); + style.Decoration.ShouldBe(decoration); + } + + [Fact] + public void Should_Parse_Text_And_Decoration() + { + // Given, When + var result = Style.TryParse("bold underline blue on green", out var style); + + // Then + result.ShouldBeTrue(); + style.ShouldNotBeNull(); + style.Decoration.ShouldBe(Decoration.Bold | Decoration.Underline); + style.Foreground.ShouldBe(Color.Blue); + style.Background.ShouldBe(Color.Green); + } + + [Fact] + public void Should_Parse_Background_If_Foreground_Is_Set_To_Default() + { + // Given, When + var result = Style.TryParse("default on green", out var style); + + // Then + result.ShouldBeTrue(); + style.ShouldNotBeNull(); + style.Decoration.ShouldBe(Decoration.None); + style.Foreground.ShouldBe(Color.Default); + style.Background.ShouldBe(Color.Green); + } + + [Fact] + public void Should_Throw_If_Foreground_Is_Set_Twice() + { + // Given, When + var result = Style.TryParse("green yellow", out var style); + + // Then + result.ShouldBeFalse(); + } + + [Fact] + public void Should_Throw_If_Background_Is_Set_Twice() + { + // Given, When + var result = Style.TryParse("green on blue yellow", out var style); + + // Then + result.ShouldBeFalse(); + } + + [Fact] + public void Should_Throw_If_Color_Name_Could_Not_Be_Found() + { + // Given, When + var result = Style.TryParse("bold lol", out var style); + + // Then + result.ShouldBeFalse(); + } + + [Fact] + public void Should_Throw_If_Background_Color_Name_Could_Not_Be_Found() + { + // Given, When + var result = Style.TryParse("blue on lol", out var style); + + // Then + result.ShouldBeFalse(); + } + } + } +} diff --git a/src/Spectre.Console/AnsiConsole.cs b/src/Spectre.Console/AnsiConsole.cs index a1e5612..31e0254 100644 --- a/src/Spectre.Console/AnsiConsole.cs +++ b/src/Spectre.Console/AnsiConsole.cs @@ -63,12 +63,12 @@ namespace Spectre.Console } /// - /// Gets or sets the style. + /// Gets or sets the text decoration. /// - public static Styles Style + public static Decoration Decoration { - get => Console.Style; - set => Console.Style = value; + get => Console.Decoration; + set => Console.Decoration = value; } /// @@ -83,7 +83,7 @@ namespace Spectre.Console } /// - /// Resets colors and styles to the default ones. + /// Resets colors and text decorations. /// public static void Reset() { @@ -91,15 +91,15 @@ namespace Spectre.Console } /// - /// Resets the current style back to the default one. + /// Resets the current applied text decorations. /// - public static void ResetStyle() + public static void ResetDecoration() { - Console.ResetStyle(); + Console.ResetDecoration(); } /// - /// Resets the foreground and background colors to the default ones. + /// Resets the current applied foreground and background colors. /// public static void ResetColors() { diff --git a/src/Spectre.Console/Appearance.cs b/src/Spectre.Console/Appearance.cs deleted file mode 100644 index 8ec4fb7..0000000 --- a/src/Spectre.Console/Appearance.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; - -namespace Spectre.Console -{ - /// - /// Represents color and style. - /// - public sealed class Appearance : IEquatable - { - /// - /// Gets the foreground color. - /// - public Color Foreground { get; } - - /// - /// Gets the background color. - /// - public Color Background { get; } - - /// - /// Gets the style. - /// - public Styles Style { get; } - - /// - /// Gets an with the - /// default color and without style. - /// - public static Appearance Plain { get; } = new Appearance(); - - private Appearance() - : this(null, null, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The foreground color. - /// The background color. - /// The style. - public Appearance(Color? foreground = null, Color? background = null, Styles? style = null) - { - Foreground = foreground ?? Color.Default; - Background = background ?? Color.Default; - Style = style ?? Styles.None; - } - - /// - /// Combines this appearance with another one. - /// - /// The item to combine with this. - /// A new appearance representing a combination of this and the other one. - public Appearance Combine(Appearance other) - { - if (other is null) - { - throw new ArgumentNullException(nameof(other)); - } - - var foreground = Foreground; - if (!other.Foreground.IsDefault) - { - foreground = other.Foreground; - } - - var background = Background; - if (!other.Background.IsDefault) - { - background = other.Background; - } - - return new Appearance(foreground, background, Style | other.Style); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hash = (int)2166136261; - hash = (hash * 16777619) ^ Foreground.GetHashCode(); - hash = (hash * 16777619) ^ Background.GetHashCode(); - hash = (hash * 16777619) ^ Style.GetHashCode(); - return hash; - } - } - - /// - public override bool Equals(object obj) - { - return Equals(obj as Appearance); - } - - /// - public bool Equals(Appearance other) - { - if (other == null) - { - return false; - } - - return Foreground.Equals(other.Foreground) && - Background.Equals(other.Background) && - Style == other.Style; - } - } -} diff --git a/src/Spectre.Console/Composition/Segment.cs b/src/Spectre.Console/Composition/Segment.cs index b01cffa..bdc9f65 100644 --- a/src/Spectre.Console/Composition/Segment.cs +++ b/src/Spectre.Console/Composition/Segment.cs @@ -25,16 +25,16 @@ namespace Spectre.Console.Composition public bool IsLineBreak { get; } /// - /// Gets the appearance of the segment. + /// Gets the segment style. /// - public Appearance Appearance { get; } + public Style Style { get; } /// /// Initializes a new instance of the class. /// /// The segment text. public Segment(string text) - : this(text, Appearance.Plain) + : this(text, Style.Plain) { } @@ -42,16 +42,16 @@ namespace Spectre.Console.Composition /// Initializes a new instance of the class. /// /// The segment text. - /// The segment appearance. - public Segment(string text, Appearance appearance) - : this(text, appearance, false) + /// The segment style. + public Segment(string text, Style style) + : this(text, style, false) { } - private Segment(string text, Appearance appearance, bool lineBreak) + private Segment(string text, Style style, bool lineBreak) { Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text)); - Appearance = appearance; + Style = style; IsLineBreak = lineBreak; } @@ -61,7 +61,7 @@ namespace Spectre.Console.Composition /// A segment that represents an implicit line break. public static Segment LineBreak() { - return new Segment("\n", Appearance.Plain, true); + return new Segment("\n", Style.Plain, true); } /// @@ -81,7 +81,7 @@ namespace Spectre.Console.Composition /// A new segment without any trailing line endings. public Segment StripLineEndings() { - return new Segment(Text.TrimEnd('\n'), Appearance); + return new Segment(Text.TrimEnd('\n'), Style); } /// @@ -102,8 +102,8 @@ namespace Spectre.Console.Composition } return ( - new Segment(Text.Substring(0, offset), Appearance), - new Segment(Text.Substring(offset, Text.Length - offset), Appearance)); + new Segment(Text.Substring(0, offset), Style), + new Segment(Text.Substring(offset, Text.Length - offset), Style)); } /// @@ -178,7 +178,7 @@ namespace Spectre.Console.Composition { if (parts[0].Length > 0) { - line.Add(new Segment(parts[0], segment.Appearance)); + line.Add(new Segment(parts[0], segment.Style)); } } diff --git a/src/Spectre.Console/Composition/Text.cs b/src/Spectre.Console/Composition/Text.cs index fcac206..c4ac9e2 100644 --- a/src/Spectre.Console/Composition/Text.cs +++ b/src/Spectre.Console/Composition/Text.cs @@ -9,7 +9,7 @@ using Spectre.Console.Internal; namespace Spectre.Console { /// - /// Represents text with color and style. + /// Represents text with color and decorations. /// [SuppressMessage("Naming", "CA1724:Type names should not match namespaces")] public sealed class Text : IRenderable @@ -21,13 +21,13 @@ namespace Spectre.Console { public int Start { get; } public int End { get; } - public Appearance Appearance { get; } + public Style Style { get; } - public Span(int start, int end, Appearance appearance) + public Span(int start, int end, Style style) { Start = start; End = end; - Appearance = appearance ?? Appearance.Plain; + Style = style ?? Style.Plain; } } @@ -47,21 +47,21 @@ namespace Spectre.Console /// The text. /// The foreground. /// The background. - /// The style. + /// The text decoration. /// A instance. public static Text New( - string text, Color? foreground = null, Color? background = null, Styles? style = null) + string text, Color? foreground = null, Color? background = null, Decoration? decoration = null) { - var result = MarkupParser.Parse(text, new Appearance(foreground, background, style)); + var result = MarkupParser.Parse(text, new Style(foreground, background, decoration)); return result; } /// - /// Appends some text with a style. + /// Appends some text with the specified color and decorations. /// /// The text to append. - /// The appearance of the text. - public void Append(string text, Appearance appearance) + /// The text style. + public void Append(string text, Style style) { if (text == null) { @@ -73,7 +73,7 @@ namespace Spectre.Console _text += text; - Stylize(start, end, appearance); + Stylize(start, end, style); } /// @@ -81,8 +81,8 @@ namespace Spectre.Console /// /// The start position. /// The end position. - /// The color and style to apply. - public void Stylize(int start, int end, Appearance appearance) + /// The style to apply. + public void Stylize(int start, int end, Style style) { if (start >= end) { @@ -92,7 +92,7 @@ namespace Spectre.Console start = Math.Max(start, 0); end = Math.Min(end, _text.Length); - _spans.Add(new Span(start, end, appearance)); + _spans.Add(new Span(start, end, style)); } /// @@ -149,7 +149,7 @@ namespace Spectre.Console } result.Add(Segment.LineBreak()); - queue.Enqueue(new Segment(second.Text.Substring(1), second.Appearance)); + queue.Enqueue(new Segment(second.Text.Substring(1), second.Style)); } } @@ -162,8 +162,8 @@ namespace Spectre.Console // https://github.com/willmcgugan/rich/blob/eb2f0d5277c159d8693636ec60c79c5442fd2e43/rich/text.py#L492 // Create the style map. - var styleMap = _spans.SelectIndex((span, index) => (span, index)).ToDictionary(x => x.index + 1, x => x.span.Appearance); - styleMap[0] = Appearance.Plain; + var styleMap = _spans.SelectIndex((span, index) => (span, index)).ToDictionary(x => x.index + 1, x => x.span.Style); + styleMap[0] = Style.Plain; // Create a span list. var spans = new List<(int Offset, bool Leaving, int Style)>(); @@ -173,7 +173,7 @@ namespace Spectre.Console spans.Add((_text.Length, true, 0)); spans = spans.OrderBy(x => x.Offset).ThenBy(x => !x.Leaving).ToList(); - // Keep track of applied appearances using a stack + // Keep track of applied styles using a stack var styleStack = new Stack(); // Now build the segments. @@ -195,7 +195,7 @@ namespace Spectre.Console { // Build the current style from the stack var styleIndices = styleStack.OrderBy(index => index).ToArray(); - var currentStyle = Appearance.Plain.Combine(styleIndices.Select(index => styleMap[index])); + var currentStyle = Style.Plain.Combine(styleIndices.Select(index => styleMap[index])); // Create segment var text = _text.Substring(offset, Math.Min(_text.Length - offset, nextOffset - offset)); diff --git a/src/Spectre.Console/ConsoleExtensions.Rendering.cs b/src/Spectre.Console/ConsoleExtensions.Rendering.cs index 2589645..9ad1e04 100644 --- a/src/Spectre.Console/ConsoleExtensions.Rendering.cs +++ b/src/Spectre.Console/ConsoleExtensions.Rendering.cs @@ -28,9 +28,9 @@ namespace Spectre.Console foreach (var segment in renderable.Render(console.Encoding, console.Width)) { - if (!segment.Appearance.Equals(Appearance.Plain)) + if (!segment.Style.Equals(Style.Plain)) { - using (var appearance = console.PushAppearance(segment.Appearance)) + using (var style = console.PushStyle(segment.Style)) { console.Write(segment.Text); } diff --git a/src/Spectre.Console/ConsoleExtensions.cs b/src/Spectre.Console/ConsoleExtensions.cs index 082f40c..9a75a1c 100644 --- a/src/Spectre.Console/ConsoleExtensions.cs +++ b/src/Spectre.Console/ConsoleExtensions.cs @@ -8,7 +8,7 @@ namespace Spectre.Console public static partial class ConsoleExtensions { /// - /// Resets both colors and style for the console. + /// Resets colors and text decorations. /// /// The console to reset. public static void Reset(this IAnsiConsole console) @@ -19,25 +19,25 @@ namespace Spectre.Console } console.ResetColors(); - console.ResetStyle(); + console.ResetDecoration(); } /// - /// Resets the current style back to the default one. + /// Resets the current applied text decorations. /// - /// The console to reset the style for. - public static void ResetStyle(this IAnsiConsole console) + /// The console to reset the text decorations for. + public static void ResetDecoration(this IAnsiConsole console) { if (console is null) { throw new ArgumentNullException(nameof(console)); } - console.Style = Styles.None; + console.Decoration = Decoration.None; } /// - /// Resets the foreground and background colors to the default ones. + /// Resets the current applied foreground and background colors. /// /// The console to reset colors for. public static void ResetColors(this IAnsiConsole console) diff --git a/src/Spectre.Console/Styles.cs b/src/Spectre.Console/Decoration.cs similarity index 86% rename from src/Spectre.Console/Styles.cs rename to src/Spectre.Console/Decoration.cs index cb3fedd..4336eb9 100644 --- a/src/Spectre.Console/Styles.cs +++ b/src/Spectre.Console/Decoration.cs @@ -1,18 +1,20 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Spectre.Console { /// - /// Represents a style. + /// Represents text decoration. /// /// - /// Support for different styles is up to the terminal. + /// Support for text decorations is up to the terminal. /// [Flags] - public enum Styles + [SuppressMessage("Naming", "CA1714:Flags enums should have plural names")] + public enum Decoration { /// - /// No style. + /// No text decoration. /// None = 0, diff --git a/src/Spectre.Console/IAnsiConsole.cs b/src/Spectre.Console/IAnsiConsole.cs index 78fd832..14e97b1 100644 --- a/src/Spectre.Console/IAnsiConsole.cs +++ b/src/Spectre.Console/IAnsiConsole.cs @@ -28,9 +28,9 @@ namespace Spectre.Console Encoding Encoding { get; } /// - /// Gets or sets the current style. + /// Gets or sets the current text decoration. /// - Styles Style { get; set; } + Decoration Decoration { get; set; } /// /// Gets or sets the current foreground. diff --git a/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs index 651ccb3..abdf13f 100644 --- a/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs +++ b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs @@ -7,11 +7,11 @@ namespace Spectre.Console.Internal public static string GetAnsi( ColorSystem system, string text, - Styles style, + Decoration decoration, Color foreground, Color background) { - var codes = AnsiStyleBuilder.GetAnsiCodes(style); + var codes = AnsiDecorationBuilder.GetAnsiCodes(decoration); // Got foreground? if (foreground != Color.Default) diff --git a/src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiDecorationBuilder.cs similarity index 52% rename from src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs rename to src/Spectre.Console/Internal/Ansi/AnsiDecorationBuilder.cs index 59cf885..65732fa 100644 --- a/src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs +++ b/src/Spectre.Console/Internal/Ansi/AnsiDecorationBuilder.cs @@ -2,52 +2,52 @@ using System.Collections.Generic; namespace Spectre.Console.Internal { - internal static class AnsiStyleBuilder + internal static class AnsiDecorationBuilder { // TODO: Rewrite this to not yield - public static IEnumerable GetAnsiCodes(Styles style) + public static IEnumerable GetAnsiCodes(Decoration decoration) { - if ((style & Styles.Bold) != 0) + if ((decoration & Decoration.Bold) != 0) { yield return 1; } - if ((style & Styles.Dim) != 0) + if ((decoration & Decoration.Dim) != 0) { yield return 2; } - if ((style & Styles.Italic) != 0) + if ((decoration & Decoration.Italic) != 0) { yield return 3; } - if ((style & Styles.Underline) != 0) + if ((decoration & Decoration.Underline) != 0) { yield return 4; } - if ((style & Styles.SlowBlink) != 0) + if ((decoration & Decoration.SlowBlink) != 0) { yield return 5; } - if ((style & Styles.RapidBlink) != 0) + if ((decoration & Decoration.RapidBlink) != 0) { yield return 6; } - if ((style & Styles.Invert) != 0) + if ((decoration & Decoration.Invert) != 0) { yield return 7; } - if ((style & Styles.Conceal) != 0) + if ((decoration & Decoration.Conceal) != 0) { yield return 8; } - if ((style & Styles.Strikethrough) != 0) + if ((decoration & Decoration.Strikethrough) != 0) { yield return 9; } diff --git a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs b/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs index 8f689ed..47cdfec 100644 --- a/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs +++ b/src/Spectre.Console/Internal/AnsiConsoleRenderer.cs @@ -11,7 +11,7 @@ namespace Spectre.Console.Internal public Capabilities Capabilities { get; } public Encoding Encoding { get; } - public Styles Style { get; set; } + public Decoration Decoration { get; set; } public Color Foreground { get; set; } public Color Background { get; set; } @@ -50,21 +50,7 @@ namespace Spectre.Console.Internal Encoding = @out.IsStandardOut() ? System.Console.OutputEncoding : Encoding.UTF8; Foreground = Color.Default; Background = Color.Default; - Style = Styles.None; - } - - public void Reset(bool colors, bool styles) - { - if (colors) - { - Foreground = Color.Default; - Background = Color.Default; - } - - if (styles) - { - Style = Styles.None; - } + Decoration = Decoration.None; } public void Write(string text) @@ -77,7 +63,7 @@ namespace Spectre.Console.Internal _out.Write(AnsiBuilder.GetAnsi( _system, text.NormalizeLineEndings(native: true), - Style, + Decoration, Foreground, Background)); } diff --git a/src/Spectre.Console/Internal/ConsoleBuilder.cs b/src/Spectre.Console/Internal/ConsoleBuilder.cs index 24b14ec..a8abcf8 100644 --- a/src/Spectre.Console/Internal/ConsoleBuilder.cs +++ b/src/Spectre.Console/Internal/ConsoleBuilder.cs @@ -25,7 +25,7 @@ namespace Spectre.Console.Internal { return new AnsiConsoleRenderer(buffer, colorSystem) { - Style = Styles.None, + Decoration = Decoration.None, }; } diff --git a/src/Spectre.Console/Internal/DecorationTable.cs b/src/Spectre.Console/Internal/DecorationTable.cs new file mode 100644 index 0000000..c8a9f7b --- /dev/null +++ b/src/Spectre.Console/Internal/DecorationTable.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + internal static class DecorationTable + { + private static readonly Dictionary _lookup; + + [SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline")] + static DecorationTable() + { + _lookup = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "none", Decoration.None }, + { "bold", Decoration.Bold }, + { "dim", Decoration.Dim }, + { "italic", Decoration.Italic }, + { "underline", Decoration.Underline }, + { "invert", Decoration.Invert }, + { "conceal", Decoration.Conceal }, + { "slowblink", Decoration.SlowBlink }, + { "rapidblink", Decoration.RapidBlink }, + { "strikethrough", Decoration.Strikethrough }, + }; + } + + public static Decoration? GetDecoration(string name) + { + _lookup.TryGetValue(name, out var result); + return result; + } + } +} diff --git a/src/Spectre.Console/Internal/Extensions/AppearanceExtensions.cs b/src/Spectre.Console/Internal/Extensions/AppearanceExtensions.cs deleted file mode 100644 index 421ac93..0000000 --- a/src/Spectre.Console/Internal/Extensions/AppearanceExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; - -namespace Spectre.Console.Internal -{ - internal static class AppearanceExtensions - { - public static Appearance Combine(this Appearance appearance, IEnumerable source) - { - var current = appearance; - foreach (var item in source) - { - current = current.Combine(item); - } - - return current; - } - } -} diff --git a/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs b/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs index daf4e1d..7f653f9 100644 --- a/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs +++ b/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs @@ -5,18 +5,23 @@ namespace Spectre.Console.Internal { internal static class ConsoleExtensions { - public static IDisposable PushAppearance(this IAnsiConsole console, Appearance appearance) + public static IDisposable PushStyle(this IAnsiConsole console, Style style) { if (console is null) { throw new ArgumentNullException(nameof(console)); } - var current = new Appearance(console.Foreground, console.Background, console.Style); - console.SetColor(appearance.Foreground, true); - console.SetColor(appearance.Background, false); - console.Style = appearance.Style; - return new AppearanceScope(console, current); + if (style is null) + { + throw new ArgumentNullException(nameof(style)); + } + + var current = new Style(console.Foreground, console.Background, console.Decoration); + console.SetColor(style.Foreground, true); + console.SetColor(style.Background, false); + console.Decoration = style.Decoration; + return new StyleScope(console, current); } public static IDisposable PushColor(this IAnsiConsole console, Color color, bool foreground) @@ -31,16 +36,16 @@ namespace Spectre.Console.Internal return new ColorScope(console, current, foreground); } - public static IDisposable PushStyle(this IAnsiConsole console, Styles style) + public static IDisposable PushDecoration(this IAnsiConsole console, Decoration decoration) { if (console is null) { throw new ArgumentNullException(nameof(console)); } - var current = console.Style; - console.Style = style; - return new StyleScope(console, current); + var current = console.Decoration; + console.Decoration = decoration; + return new DecorationScope(console, current); } public static void SetColor(this IAnsiConsole console, Color color, bool foreground) @@ -61,30 +66,30 @@ namespace Spectre.Console.Internal } } - internal sealed class AppearanceScope : IDisposable + internal sealed class StyleScope : IDisposable { private readonly IAnsiConsole _console; - private readonly Appearance _apperance; + private readonly Style _style; - public AppearanceScope(IAnsiConsole console, Appearance appearance) + public StyleScope(IAnsiConsole console, Style style) { _console = console ?? throw new ArgumentNullException(nameof(console)); - _apperance = appearance; + _style = style ?? throw new ArgumentNullException(nameof(style)); } [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")] [SuppressMessage("Performance", "CA1821:Remove empty Finalizers")] - ~AppearanceScope() + ~StyleScope() { - throw new InvalidOperationException("Appearance scope was not disposed."); + throw new InvalidOperationException("Style scope was not disposed."); } public void Dispose() { GC.SuppressFinalize(this); - _console.SetColor(_apperance.Foreground, true); - _console.SetColor(_apperance.Background, false); - _console.Style = _apperance.Style; + _console.SetColor(_style.Foreground, true); + _console.SetColor(_style.Background, false); + _console.Decoration = _style.Decoration; } } @@ -115,28 +120,28 @@ namespace Spectre.Console.Internal } } - internal sealed class StyleScope : IDisposable + internal sealed class DecorationScope : IDisposable { private readonly IAnsiConsole _console; - private readonly Styles _style; + private readonly Decoration _decoration; - public StyleScope(IAnsiConsole console, Styles color) + public DecorationScope(IAnsiConsole console, Decoration decoration) { _console = console ?? throw new ArgumentNullException(nameof(console)); - _style = color; + _decoration = decoration; } [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")] [SuppressMessage("Performance", "CA1821:Remove empty Finalizers")] - ~StyleScope() + ~DecorationScope() { - throw new InvalidOperationException("Style scope was not disposed."); + throw new InvalidOperationException("Decoration scope was not disposed."); } public void Dispose() { GC.SuppressFinalize(this); - _console.Style = _style; + _console.Decoration = _decoration; } } } diff --git a/src/Spectre.Console/Internal/Extensions/StyleExtensions.cs b/src/Spectre.Console/Internal/Extensions/StyleExtensions.cs new file mode 100644 index 0000000..2b09e4a --- /dev/null +++ b/src/Spectre.Console/Internal/Extensions/StyleExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace Spectre.Console.Internal +{ + internal static class StyleExtensions + { + public static Style Combine(this Style style, IEnumerable