commit 334dcddc1aa62953ad6c2190344bf5b94820614c Author: Patrik Svensson Date: Tue Jul 21 12:03:41 2020 +0200 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6399c11 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,178 @@ +root = true + +[*] +charset = utf-8 +end_of_line = CRLF +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +[*.sln] +indent_style = tab + +[*.{csproj,vbproj,vcxproj,vcxproj.filters}] +indent_size = 2 + +[*.{xml,config,props,targets,nuspec,ruleset}] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sh] +end_of_line = lf + +[*.cs] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:refactoring +dotnet_style_qualification_for_property = false:refactoring +dotnet_style_qualification_for_method = false:refactoring +dotnet_style_qualification_for_event = false:refactoring + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Non-private readonly fields are PascalCase +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style +dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly +dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style +dotnet_naming_symbols.instance_fields.applicable_kinds = field +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.all_members.applicable_kinds = * +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Blocks are allowed +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +# warning RS0037: PublicAPI.txt is missing '#nullable enable' +dotnet_diagnostic.RS0037.severity = none \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..7f7a9f9 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,44 @@ +name: Continuous Integration +on: pull_request + +env: + # Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + # Disable sending usage data to Microsoft + DOTNET_CLI_TELEMETRY_OPTOUT: true + +jobs: + build: + name: Build + if: "!contains(github.event.head_commit.message, 'skip-ci')" + strategy: + matrix: + kind: ['linux', 'windows', 'macOS'] + include: + - kind: linux + os: ubuntu-latest + - kind: windows + os: windows-latest + - kind: macOS + os: macos-latest + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: 'Get Git tags' + run: git fetch --tags + shell: bash + + - name: Setup dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.301 + + - name: Build + shell: bash + run: | + dotnet tool restore + dotnet cake \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..e4a3ab5 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,87 @@ +name: Publish + +on: + push: + tags: + - '*' + branches: + - main + +env: + # Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + # Disable sending usage data to Microsoft + DOTNET_CLI_TELEMETRY_OPTOUT: true + +jobs: + + ################################################### + # BUILD + ################################################### + + build: + name: Build + if: "!contains(github.event.head_commit.message, 'skip-ci')" + strategy: + matrix: + kind: ['linux', 'windows', 'macOS'] + include: + - kind: linux + os: ubuntu-latest + - kind: windows + os: windows-latest + - kind: macOS + os: macos-latest + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: 'Get Git tags' + run: git fetch --tags + shell: bash + + - name: Setup dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.301 + + - name: Build + shell: bash + run: | + dotnet tool restore + dotnet cake + + ################################################### + # PUBLISH + ################################################### + + publish: + name: Publish + needs: [build] + if: "!contains(github.event.head_commit.message, 'skip-ci')" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: 'Get Git tags' + run: git fetch --tags + shell: bash + + - name: Setup dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.301 + + - name: Publish + shell: bash + run: | + dotnet tool restore + dotnet cake --target="publish" \ + --nuget-key="${{secrets.NUGET_API_KEY}}" \ + --github-key="${{secrets.GITHUB_TOKEN}}" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5900336 --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# Misc folders +[Bb]in/ +[Oo]bj/ +[Tt]emp/ +[Pp]ackages/ +/.artifacts/ +/[Tt]ools/ + +# Cakeup +cakeup-x86_64-latest.exe + +# .NET Core CLI +/.dotnet/ +/.packages/ +dotnet-install.sh* +*.lock.json + +# Visual Studio +.vs/ +.vscode/ +launchSettings.json +*.sln.ide/ + +# Rider +src/.idea/**/workspace.xml +src/.idea/**/tasks.xml +src/.idea/dictionaries +src/.idea/**/dataSources/ +src/.idea/**/dataSources.ids +src/.idea/**/dataSources.xml +src/.idea/**/dataSources.local.xml +src/.idea/**/sqlDataSources.xml +src/.idea/**/dynamic.xml +src/.idea/**/uiDesigner.xml + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.userprefs +*.GhostDoc.xml +*StyleCop.Cache + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml +_NCrunch_* + +# NuGet Packages Directory +packages + +# Windows +Thumbs.db diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2fc5457 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@spectresystems.se. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..146231b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Spectre Systems AB + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..627d9e9 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# `Spectre.Console` + +A .NET Standard 2.0 library that makes it easier to create beautiful console applications. +It is heavily inspired by the excellent [Rich library](https://github.com/willmcgugan/rich) +for Python. + +## Features + +* Written with unit testing in mind. +* Supports the most common SRG parameters when it comes to text + styling such as bold, dim, italic, underline, strikethrough, + and blinking text. +* Supports 3/4/8/24-bit colors in the terminal. + The library will detect the capabilities of the current terminal + and downgrade colors as needed. + +## Example + +![Example](https://spectresystems.se/assets/open-source/spectre-console/example.png) + +## Usage + +The `Spectre.Console` API is stateful and is not thread-safe. +If you need to write to the console from different threads, make sure that +you take appropriate precautions, just like when you use the +regular `System.Console` API. + +If the current terminal does not support ANSI escape sequences, +`Spectre.Console` will fallback to using the `System.Console` API. + +_NOTE: This library is currently under development and API's +might change or get removed at any point up until a 1.0 release._ + +### Using the static API + +The static API is perfect when you just want to output text +like you usually do with the `System.Console` API, but prettier. + +```csharp +AnsiConsole.Foreground = Color.CornflowerBlue; +AnsiConsole.Style = Styles.Underline | Styles.Bold; +AnsiConsole.WriteLine("Hello World!"); + +AnsiConsole.Reset(); +AnsiConsole.WriteLine("Good bye!"); +AnsiConsole.WriteLine(); +``` + +If you want to get a reference to the default `IAnsiConsole`, +you can access it via `AnsiConsole.Console`. + +### Creating a 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. + +```csharp +IAnsiConsole console = AnsiConsole.Create( + new AnsiConsoleSettings() + { + Ansi = AnsiSupport.Yes, + ColorSystem = ColorSystemSupport.TrueColor, + Out = new StringWriter(), + }); +``` + +_NOTE: Even if you can specify a specific color system to use +when manually creating a console, remember that the user's terminal +might not be able to use it, so unless you're creating an IAnsiConsole +for testing, always use `ColorSystemSupport.Detect` and `AnsiSupport.Detect`._ diff --git a/build.cake b/build.cake new file mode 100644 index 0000000..0ad4eb5 --- /dev/null +++ b/build.cake @@ -0,0 +1,113 @@ +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Release"); + +//////////////////////////////////////////////////////////////// +// Tasks + +Task("Build") + .Does(context => +{ + DotNetCoreBuild("./src/Spectre.Console.sln", new DotNetCoreBuildSettings { + Configuration = configuration, + NoIncremental = context.HasArgument("rebuild"), + MSBuildSettings = new DotNetCoreMSBuildSettings() + .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error) + }); +}); + +Task("Test") + .IsDependentOn("Build") + .Does(context => +{ + DotNetCoreTest("./src/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetCoreTestSettings { + Configuration = configuration, + NoRestore = true, + NoBuild = true, + }); +}); + +Task("Package") + .IsDependentOn("Test") + .Does(context => +{ + context.CleanDirectory("./.artifacts"); + + context.DotNetCorePack($"./src/Spectre.Console.sln", new DotNetCorePackSettings { + Configuration = configuration, + NoRestore = true, + NoBuild = true, + OutputDirectory = "./.artifacts", + MSBuildSettings = new DotNetCoreMSBuildSettings() + .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error) + .WithProperty("SymbolPackageFormat", "snupkg") + }); +}); + +Task("Publish-GitHub") + .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions") + .IsDependentOn("Package") + .Does(context => +{ + var apiKey = Argument("github-key", null); + if(string.IsNullOrWhiteSpace(apiKey)) { + throw new CakeException("No GitHub API key was provided."); + } + + // Publish to GitHub Packages + var exitCode = 0; + foreach(var file in context.GetFiles("./.artifacts/*.nupkg")) + { + context.Information("Publishing {0}...", file.GetFilename().FullPath); + exitCode += StartProcess("dotnet", + new ProcessSettings { + Arguments = new ProcessArgumentBuilder() + .Append("gpr") + .Append("push") + .AppendQuoted(file.FullPath) + .AppendSwitchSecret("-k", " ", apiKey) + } + ); + } + + if(exitCode != 0) + { + throw new CakeException("Could not push GitHub packages."); + } +}); + +Task("Publish-NuGet") + .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions") + .IsDependentOn("Package") + .Does(context => +{ + var apiKey = Argument("nuget-key", null); + if(string.IsNullOrWhiteSpace(apiKey)) { + throw new CakeException("No NuGet API key was provided."); + } + + // Publish to GitHub Packages + foreach(var file in context.GetFiles("./.artifacts/*.nupkg")) + { + context.Information("Publishing {0}...", file.GetFilename().FullPath); + DotNetCoreNuGetPush(file.FullPath, new DotNetCoreNuGetPushSettings + { + Source = "https://api.nuget.org/v3/index.json", + ApiKey = apiKey, + }); + } +}); + +//////////////////////////////////////////////////////////////// +// Targets + +Task("Publish") + .IsDependentOn("Publish-GitHub") + .IsDependentOn("Publish-NuGet"); + +Task("Default") + .IsDependentOn("Package"); + +//////////////////////////////////////////////////////////////// +// Execution + +RunTarget(target) \ No newline at end of file diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 0000000..f697e67 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "0.38.4", + "commands": [ + "dotnet-cake" + ] + }, + "gpr": { + "version": "0.1.224", + "commands": [ + "gpr" + ] + } + } +} \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..7c77738 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "projects": [ "src" ], + "sdk": { + "version": "3.1.301", + "rollForward": "latestMajor" + } +} \ No newline at end of file diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 0000000..369239d --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,77 @@ +root = false + +[*.cs] +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = warning + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Public members should come before private members +dotnet_diagnostic.SA1202.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1404: Code analysis suppressions should have justification +dotnet_diagnostic.SA1404.severity = none + +# SA1516: Elements should be separated by a blank line +dotnet_diagnostic.SA1516.severity = none + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none + +# CSA1204: Static members should appear before non-static members +dotnet_diagnostic.SA1204.severity = none + +# IDE0052: Remove unread private members +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0063: Use simple 'using' statement +csharp_prefer_simple_using_statement = false:suggestion + +# IDE0018: Variable declaration can be inlined +dotnet_diagnostic.IDE0018.severity = warning + +# SA1625: Element documenation should not be copied and pasted +dotnet_diagnostic.SA1625.severity = none + +# IDE0005: Using directive is unnecessary +dotnet_diagnostic.IDE0005.severity = warning + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1404: Code analysis suppression should have justification +dotnet_diagnostic.SA1404.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# CA1814: Prefer jagged arrays over multidimensional +dotnet_diagnostic.CA1814.severity = none + +# RCS1194: Implement exception constructors. +dotnet_diagnostic.RCS1194.severity = none + +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = none + +# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly +dotnet_diagnostic.CA1826.severity = none diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..16f3ec3 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,47 @@ + + + true + 8.0 + true + true + true + + + + true + + + + A library that makes it easier to create beautiful console applications. + Spectre Systems AB + Spectre Systems AB + Patrik Svensson + git + https://github.com/spectresystems/spectre.console + True + https://github.com/spectresystems/spectre.console + MIT + https://github.com/spectresystems/spectre.console/releases + + + + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + All + + + All + + + \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 0000000..4b6ff93 --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + preview + normal + + + \ No newline at end of file diff --git a/src/Sample/.editorconfig b/src/Sample/.editorconfig new file mode 100644 index 0000000..ce57c1b --- /dev/null +++ b/src/Sample/.editorconfig @@ -0,0 +1,8 @@ +root = false +[*.cs] + +# Default severity for all analyzer diagnostics +dotnet_analyzer_diagnostic.severity = none + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none diff --git a/src/Sample/Program.cs b/src/Sample/Program.cs new file mode 100644 index 0000000..c322fab --- /dev/null +++ b/src/Sample/Program.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using Spectre.Console; + +namespace Sample +{ + public static class Program + { + public static void Main(string[] args) + { + // Use the static API to write some things to the console. + AnsiConsole.Foreground = Color.Chartreuse2; + AnsiConsole.Style = Styles.Underline | Styles.Bold; + AnsiConsole.WriteLine("Hello World!"); + AnsiConsole.Reset(); + AnsiConsole.WriteLine($"Capabilities: {AnsiConsole.Capabilities}"); + AnsiConsole.WriteLine($"Width={AnsiConsole.Width}, Height={AnsiConsole.Height}"); + AnsiConsole.WriteLine("Good bye!"); + AnsiConsole.WriteLine(); + + // We can get the default console via the static API. + var console = AnsiConsole.Console; + + // Or you can build it yourself the old fashion way. + console = AnsiConsole.Create( + new AnsiConsoleSettings() + { + Ansi = AnsiSupport.Yes, + ColorSystem = ColorSystemSupport.Standard, + Out = Console.Out, + }); + + // In this case, we will find the closest colors + // and downgrade them to the specified color system. + console.Foreground = Color.Chartreuse2; + console.Style = Styles.Underline | Styles.Bold; + console.WriteLine("Hello World!"); + console.ResetColors(); + console.ResetStyle(); + console.WriteLine($"Capabilities: {console.Capabilities}"); + console.WriteLine($"Width={AnsiConsole.Width}, Height={AnsiConsole.Height}"); + console.WriteLine("Good bye!"); + console.WriteLine(); + } + } +} + + + diff --git a/src/Sample/Sample.csproj b/src/Sample/Sample.csproj new file mode 100644 index 0000000..faf35b1 --- /dev/null +++ b/src/Sample/Sample.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.1 + false + false + + + + + + + diff --git a/src/Spectre.Console.Tests/.editorconfig b/src/Spectre.Console.Tests/.editorconfig new file mode 100644 index 0000000..1cc45d2 --- /dev/null +++ b/src/Spectre.Console.Tests/.editorconfig @@ -0,0 +1,23 @@ +root = false +[*.cs] + +# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none + +# SA1210: Using directives should be ordered alphabetically by namespace +dotnet_diagnostic.SA1210.severity = none + +# CA1034: Nested types should not be visible +dotnet_diagnostic.CA1034.severity = none + +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = none diff --git a/src/Spectre.Console.Tests/AnsiConsoleFixture.cs b/src/Spectre.Console.Tests/AnsiConsoleFixture.cs new file mode 100644 index 0000000..ee0bbd5 --- /dev/null +++ b/src/Spectre.Console.Tests/AnsiConsoleFixture.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Spectre.Console.Tests +{ + public sealed class AnsiConsoleFixture : IDisposable + { + private readonly StringWriter _writer; + + public IAnsiConsole Console { get; } + + public string Output => _writer.ToString(); + + public AnsiConsoleFixture(ColorSystem system) + { + _writer = new StringWriter(); + + Console = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Yes, + ColorSystem = (ColorSystemSupport)system, + Out = _writer, + }); + } + + public void Dispose() + { + _writer?.Dispose(); + } + } +} diff --git a/src/Spectre.Console.Tests/AnsiConsoleTests.Colors.cs b/src/Spectre.Console.Tests/AnsiConsoleTests.Colors.cs new file mode 100644 index 0000000..9b71eeb --- /dev/null +++ b/src/Spectre.Console.Tests/AnsiConsoleTests.Colors.cs @@ -0,0 +1,216 @@ +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests +{ + public partial class AnsiConsoleTests + { + public sealed class TrueColor + { + [Theory] + [InlineData(true, "\u001b[38;2;128;0;128mHello\u001b[0m")] + [InlineData(false, "\u001b[48;2;128;0;128mHello\u001b[0m")] + public void Should_Return_Correct_Code(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + fixture.Console.SetColor(new Color(128, 0, 128), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, "\u001b[38;5;5mHello\u001b[0m")] + [InlineData(false, "\u001b[48;5;5mHello\u001b[0m")] + public void Should_Return_Eight_Bit_Ansi_Code_For_Known_Colors(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + fixture.Console.SetColor(Color.Purple, foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + } + + public sealed class EightBit + { + [Theory] + [InlineData(true, "\u001b[38;5;3mHello\u001b[0m")] + [InlineData(false, "\u001b[48;5;3mHello\u001b[0m")] + public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + fixture.Console.SetColor(Color.Olive, foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, "\u001b[38;5;3mHello\u001b[0m")] + [InlineData(false, "\u001b[48;5;3mHello\u001b[0m")] + public void Should_Map_TrueColor_To_Nearest_Eight_Bit_Color_If_Possible(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + fixture.Console.SetColor(new Color(128, 128, 0), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, "\u001b[38;5;3mHello\u001b[0m")] + [InlineData(false, "\u001b[48;5;3mHello\u001b[0m")] + public void Should_Estimate_TrueColor_To_Nearest_Eight_Bit_Color(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.EightBit); + fixture.Console.SetColor(new Color(126, 127, 0), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + } + + public sealed class Standard + { + [Theory] + [InlineData(true, "\u001b[33mHello\u001b[0m")] + [InlineData(false, "\u001b[43mHello\u001b[0m")] + public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.SetColor(Color.Olive, foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, 128, 128, 128, "\u001b[90mHello\u001b[0m")] + [InlineData(false, 128, 128, 128, "\u001b[100mHello\u001b[0m")] + [InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")] + [InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")] + public void Should_Map_TrueColor_To_Nearest_Four_Bit_Color_If_Possible( + bool foreground, + byte r, byte g, byte b, + string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.SetColor(new Color(r, g, b), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, 112, 120, 128, "\u001b[90mHello\u001b[0m")] + [InlineData(false, 112, 120, 128, "\u001b[100mHello\u001b[0m")] + [InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")] + [InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")] + public void Should_Estimate_TrueColor_To_Nearest_Four_Bit_Color( + bool foreground, + byte r, byte g, byte b, + string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.SetColor(new Color(r, g, b), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + } + + public sealed class Legacy + { + [Theory] + [InlineData(true, "\u001b[33mHello\u001b[0m")] + [InlineData(false, "\u001b[43mHello\u001b[0m")] + public void Should_Return_Correct_Code_For_Known_Color(bool foreground, string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + fixture.Console.SetColor(Color.Olive, foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, 128, 128, 128, "\u001b[37mHello\u001b[0m")] + [InlineData(false, 128, 128, 128, "\u001b[47mHello\u001b[0m")] + [InlineData(true, 0, 128, 0, "\u001b[32mHello\u001b[0m")] + [InlineData(false, 0, 128, 0, "\u001b[42mHello\u001b[0m")] + public void Should_Map_TrueColor_To_Nearest_Three_Bit_Color_If_Possible( + bool foreground, + byte r, byte g, byte b, + string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + fixture.Console.SetColor(new Color(r, g, b), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [Theory] + [InlineData(true, 112, 120, 128, "\u001b[36mHello\u001b[0m")] + [InlineData(false, 112, 120, 128, "\u001b[46mHello\u001b[0m")] + [InlineData(true, 0, 120, 12, "\u001b[32mHello\u001b[0m")] + [InlineData(false, 0, 120, 12, "\u001b[42mHello\u001b[0m")] + public void Should_Estimate_TrueColor_To_Nearest_Three_Bit_Color( + bool foreground, + byte r, byte g, byte b, + string expected) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Legacy); + fixture.Console.SetColor(new Color(r, g, b), foreground); + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe(expected); + } + } + } +} diff --git a/src/Spectre.Console.Tests/AnsiConsoleTests.Style.cs b/src/Spectre.Console.Tests/AnsiConsoleTests.Style.cs new file mode 100644 index 0000000..2d7f9a1 --- /dev/null +++ b/src/Spectre.Console.Tests/AnsiConsoleTests.Style.cs @@ -0,0 +1,47 @@ +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests +{ + 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) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + fixture.Console.Style = style; + + // When + fixture.Console.Write("Hello World"); + + // Then + fixture.Output.ShouldBe(expected); + } + + [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) + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.TrueColor); + fixture.Console.Style = style; + + // When + fixture.Console.Write("Hello World"); + + // Then + fixture.Output.ShouldBe(expected); + } + } +} diff --git a/src/Spectre.Console.Tests/AnsiConsoleTests.cs b/src/Spectre.Console.Tests/AnsiConsoleTests.cs new file mode 100644 index 0000000..2f380ab --- /dev/null +++ b/src/Spectre.Console.Tests/AnsiConsoleTests.cs @@ -0,0 +1,72 @@ +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests +{ + public partial class AnsiConsoleTests + { + [Fact] + public void Should_Combine_Style_And_Colors() + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.Foreground = Color.RoyalBlue1; + fixture.Console.Background = Color.NavajoWhite1; + fixture.Console.Style = Styles.Italic; + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe("\u001b[3;90;47mHello\u001b[0m"); + } + + [Fact] + public void Should_Not_Include_Foreground_If_Set_To_Default_Color() + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.Foreground = Color.Default; + fixture.Console.Background = Color.NavajoWhite1; + fixture.Console.Style = Styles.Italic; + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe("\u001b[3;47mHello\u001b[0m"); + } + + [Fact] + public void Should_Not_Include_Background_If_Set_To_Default_Color() + { + // Given + var fixture = new AnsiConsoleFixture(ColorSystem.Standard); + fixture.Console.Foreground = Color.RoyalBlue1; + fixture.Console.Background = Color.Default; + fixture.Console.Style = Styles.Italic; + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe("\u001b[3;90mHello\u001b[0m"); + } + + [Fact] + public void Should_Not_Include_Style_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; + + // When + fixture.Console.Write("Hello"); + + // Then + fixture.Output.ShouldBe("\u001b[90;47mHello\u001b[0m"); + } + } +} diff --git a/src/Spectre.Console.Tests/Extensions/ConsoleExtensions.cs b/src/Spectre.Console.Tests/Extensions/ConsoleExtensions.cs new file mode 100644 index 0000000..01282ee --- /dev/null +++ b/src/Spectre.Console.Tests/Extensions/ConsoleExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Spectre.Console.Tests +{ + public static class ConsoleExtensions + { + public static void SetColor(this IAnsiConsole console, Color color, bool foreground) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + if (foreground) + { + console.Foreground = color; + } + else + { + console.Background = color; + } + } + } +} diff --git a/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj new file mode 100644 index 0000000..c59f346 --- /dev/null +++ b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + diff --git a/src/Spectre.Console.sln b/src/Spectre.Console.sln new file mode 100644 index 0000000..6b86d1c --- /dev/null +++ b/src/Spectre.Console.sln @@ -0,0 +1,73 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30225.117 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console", "Spectre.Console\Spectre.Console.csproj", "{80DCBEF3-99D6-46C0-9C5B-42B4534D9113}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Tests", "Spectre.Console.Tests\Spectre.Console.Tests.csproj", "{9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{272E6092-BD31-4EB6-A9FF-F4179F91958F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20595AD4-8D75-4AF8-B6BC-9C38C160423F}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + stylecop.json = stylecop.json + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|x64.ActiveCfg = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|x64.Build.0 = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|x86.ActiveCfg = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Debug|x86.Build.0 = Debug|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|Any CPU.Build.0 = Release|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|x64.ActiveCfg = Release|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|x64.Build.0 = Release|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|x86.ActiveCfg = Release|Any CPU + {80DCBEF3-99D6-46C0-9C5B-42B4534D9113}.Release|x86.Build.0 = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|x64.ActiveCfg = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|x64.Build.0 = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|x86.ActiveCfg = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Debug|x86.Build.0 = Debug|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|Any CPU.Build.0 = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x64.ActiveCfg = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x64.Build.0 = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.ActiveCfg = Release|Any CPU + {9F1AC4C1-766E-4421-8A78-B28F5BCDD94F}.Release|x86.Build.0 = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x64.ActiveCfg = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x64.Build.0 = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x86.ActiveCfg = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Debug|x86.Build.0 = Debug|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|Any CPU.Build.0 = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x64.ActiveCfg = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x64.Build.0 = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x86.ActiveCfg = Release|Any CPU + {272E6092-BD31-4EB6-A9FF-F4179F91958F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C} + EndGlobalSection +EndGlobal diff --git a/src/Spectre.Console/AnsiConsole.cs b/src/Spectre.Console/AnsiConsole.cs new file mode 100644 index 0000000..cb5ecd5 --- /dev/null +++ b/src/Spectre.Console/AnsiConsole.cs @@ -0,0 +1,135 @@ +using System; +using Spectre.Console.Internal; + +namespace Spectre.Console +{ + /// + /// A console capable of writing ANSI escape sequences. + /// + public static class AnsiConsole + { + private static readonly Lazy _console = new Lazy(() => + { + return Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = System.Console.Out, + }); + }); + + /// + /// Gets the current renderer. + /// + public static IAnsiConsole Console => _console.Value; + + /// + /// Gets the console's capabilities. + /// + public static AnsiConsoleCapabilities Capabilities => Console.Capabilities; + + /// + /// Gets the buffer width of the console. + /// + public static int Width + { + get => Console.Width; + } + + /// + /// Gets the buffer height of the console. + /// + public static int Height + { + get => Console.Height; + } + + /// + /// Gets or sets the foreground color. + /// + public static Color Foreground + { + get => Console.Foreground; + set => Console.SetColor(value, true); + } + + /// + /// Gets or sets the background color. + /// + public static Color Background + { + get => Console.Background; + set => Console.SetColor(value, false); + } + + /// + /// Gets or sets the style. + /// + public static Styles Style + { + get => Console.Style; + set => Console.Style = value; + } + + /// + /// Creates a new instance + /// from the provided settings. + /// + /// The settings to use. + /// An instance. + public static IAnsiConsole Create(AnsiConsoleSettings settings) + { + return ConsoleBuilder.Build(settings); + } + + /// + /// Resets colors and styles to the default ones. + /// + public static void Reset() + { + Console.Reset(); + } + + /// + /// Resets the current style back to the default one. + /// + public static void ResetStyle() + { + Console.ResetStyle(); + } + + /// + /// Resets the foreground and background colors to the default ones. + /// + public static void ResetColors() + { + Console.ResetColors(); + } + + /// + /// Writes the content to the console. + /// + /// The content to write. + public static void Write(string content) + { + Console.Write(content); + } + + /// + /// Writes an empty line to the console. + /// + public static void WriteLine() + { + Console.WriteLine(); + } + + /// + /// Writes a line to the console. + /// + /// The content to write. + public static void WriteLine(string content) + { + Console.WriteLine(content); + } + } +} diff --git a/src/Spectre.Console/AnsiConsoleCapabilities.cs b/src/Spectre.Console/AnsiConsoleCapabilities.cs new file mode 100644 index 0000000..1c3a6f3 --- /dev/null +++ b/src/Spectre.Console/AnsiConsoleCapabilities.cs @@ -0,0 +1,42 @@ +namespace Spectre.Console +{ + /// + /// Represents console capabilities. + /// + public sealed class AnsiConsoleCapabilities + { + /// + /// Gets a value indicating whether or not + /// the console supports Ansi. + /// + public bool SupportsAnsi { get; } + + /// + /// Gets the color system. + /// + public ColorSystem ColorSystem { get; } + + internal AnsiConsoleCapabilities(bool supportsAnsi, ColorSystem colorSystem) + { + SupportsAnsi = supportsAnsi; + ColorSystem = colorSystem; + } + + /// + public override string ToString() + { + var supportsAnsi = SupportsAnsi ? "Yes" : "No"; + var bits = ColorSystem switch + { + ColorSystem.NoColors => "1 bit", + ColorSystem.Legacy => "3 bits", + ColorSystem.Standard => "4 bits", + ColorSystem.EightBit => "8 bits", + ColorSystem.TrueColor => "24 bits", + _ => "?" + }; + + return $"ANSI={supportsAnsi}, Colors={ColorSystem} ({bits})"; + } + } +} diff --git a/src/Spectre.Console/AnsiConsoleSettings.cs b/src/Spectre.Console/AnsiConsoleSettings.cs new file mode 100644 index 0000000..ccb34f8 --- /dev/null +++ b/src/Spectre.Console/AnsiConsoleSettings.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Spectre.Console +{ + /// + /// Settings used by + /// when building a . + /// + public sealed class AnsiConsoleSettings + { + /// + /// Gets or sets a value indicating whether or + /// not ANSI escape sequences are supported. + /// + public AnsiSupport Ansi { get; set; } + + /// + /// Gets or sets the color system to use. + /// + public ColorSystemSupport ColorSystem { get; set; } + + /// + /// Gets or sets the out buffer. + /// + public TextWriter Out { get; set; } + } +} diff --git a/src/Spectre.Console/AnsiSupport.cs b/src/Spectre.Console/AnsiSupport.cs new file mode 100644 index 0000000..8a85026 --- /dev/null +++ b/src/Spectre.Console/AnsiSupport.cs @@ -0,0 +1,24 @@ +namespace Spectre.Console +{ + /// + /// Determines ANSI escape sequence support. + /// + public enum AnsiSupport + { + /// + /// ANSI escape sequence support should + /// be detected by the system. + /// + Detect = 0, + + /// + /// ANSI escape sequences are supported. + /// + Yes = 1, + + /// + /// ANSI escape sequences are not supported. + /// + No = 2, + } +} diff --git a/src/Spectre.Console/Color.Known.cs b/src/Spectre.Console/Color.Known.cs new file mode 100644 index 0000000..b225823 --- /dev/null +++ b/src/Spectre.Console/Color.Known.cs @@ -0,0 +1,1352 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console +{ + /// + /// Represents a color. + /// + public partial struct Color + { + internal Color(byte number, string name, byte red, byte green, byte blue, bool isDefault = false) + : this(red, green, blue) + { + Name = name; + Number = number; + IsDefault = isDefault; + } + + /// + /// Gets the color "Black" (RGB 0,0,0). + /// + public static Color Black { get; } = new Color(0, "black", 0, 0, 0); + + /// + /// Gets the color "Maroon" (RGB 128,0,0). + /// + public static Color Maroon { get; } = new Color(1, "maroon", 128, 0, 0); + + /// + /// Gets the color "Green" (RGB 0,128,0). + /// + public static Color Green { get; } = new Color(2, "green", 0, 128, 0); + + /// + /// Gets the color "Olive" (RGB 128,128,0). + /// + public static Color Olive { get; } = new Color(3, "olive", 128, 128, 0); + + /// + /// Gets the color "Navy" (RGB 0,0,128). + /// + public static Color Navy { get; } = new Color(4, "navy", 0, 0, 128); + + /// + /// Gets the color "Purple" (RGB 128,0,128). + /// + public static Color Purple { get; } = new Color(5, "purple", 128, 0, 128); + + /// + /// Gets the color "Teal" (RGB 0,128,128). + /// + public static Color Teal { get; } = new Color(6, "teal", 0, 128, 128); + + /// + /// Gets the color "Silver" (RGB 192,192,192). + /// + public static Color Silver { get; } = new Color(7, "silver", 192, 192, 192); + + /// + /// Gets the color "Grey" (RGB 128,128,128). + /// + public static Color Grey { get; } = new Color(8, "grey", 128, 128, 128); + + /// + /// Gets the color "Red" (RGB 255,0,0). + /// + public static Color Red { get; } = new Color(9, "red", 255, 0, 0); + + /// + /// Gets the color "Lime" (RGB 0,255,0). + /// + public static Color Lime { get; } = new Color(10, "lime", 0, 255, 0); + + /// + /// Gets the color "Yellow" (RGB 255,255,0). + /// + public static Color Yellow { get; } = new Color(11, "yellow", 255, 255, 0); + + /// + /// Gets the color "Blue" (RGB 0,0,255). + /// + public static Color Blue { get; } = new Color(12, "blue", 0, 0, 255); + + /// + /// Gets the color "Fuchsia" (RGB 255,0,255). + /// + public static Color Fuchsia { get; } = new Color(13, "fuchsia", 255, 0, 255); + + /// + /// Gets the color "Aqua" (RGB 0,255,255). + /// + public static Color Aqua { get; } = new Color(14, "aqua", 0, 255, 255); + + /// + /// Gets the color "White" (RGB 255,255,255). + /// + public static Color White { get; } = new Color(15, "white", 255, 255, 255); + + /// + /// Gets the color "Grey0" (RGB 0,0,0). + /// + public static Color Grey0 { get; } = new Color(16, "grey0", 0, 0, 0); + + /// + /// Gets the color "NavyBlue" (RGB 0,0,95). + /// + public static Color NavyBlue { get; } = new Color(17, "navyblue", 0, 0, 95); + + /// + /// Gets the color "DarkBlue" (RGB 0,0,135). + /// + public static Color DarkBlue { get; } = new Color(18, "darkblue", 0, 0, 135); + + /// + /// Gets the color "Blue3" (RGB 0,0,175). + /// + public static Color Blue3 { get; } = new Color(19, "blue3", 0, 0, 175); + + /// + /// Gets the color "Blue3_1" (RGB 0,0,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Blue3_1 { get; } = new Color(20, "blue3_1", 0, 0, 215); + + /// + /// Gets the color "Blue1" (RGB 0,0,255). + /// + public static Color Blue1 { get; } = new Color(21, "blue1", 0, 0, 255); + + /// + /// Gets the color "DarkGreen" (RGB 0,95,0). + /// + public static Color DarkGreen { get; } = new Color(22, "darkgreen", 0, 95, 0); + + /// + /// Gets the color "DeepSkyBlue4" (RGB 0,95,95). + /// + public static Color DeepSkyBlue4 { get; } = new Color(23, "deepskyblue4", 0, 95, 95); + + /// + /// Gets the color "DeepSkyBlue4_1" (RGB 0,95,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepSkyBlue4_1 { get; } = new Color(24, "deepskyblue4_1", 0, 95, 135); + + /// + /// Gets the color "DeepSkyBlue4_2" (RGB 0,95,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepSkyBlue4_2 { get; } = new Color(25, "deepskyblue4_2", 0, 95, 175); + + /// + /// Gets the color "DodgerBlue3" (RGB 0,95,215). + /// + public static Color DodgerBlue3 { get; } = new Color(26, "dodgerblue3", 0, 95, 215); + + /// + /// Gets the color "DodgerBlue2" (RGB 0,95,255). + /// + public static Color DodgerBlue2 { get; } = new Color(27, "dodgerblue2", 0, 95, 255); + + /// + /// Gets the color "Green4" (RGB 0,135,0). + /// + public static Color Green4 { get; } = new Color(28, "green4", 0, 135, 0); + + /// + /// Gets the color "SpringGreen4" (RGB 0,135,95). + /// + public static Color SpringGreen4 { get; } = new Color(29, "springgreen4", 0, 135, 95); + + /// + /// Gets the color "Turquoise4" (RGB 0,135,135). + /// + public static Color Turquoise4 { get; } = new Color(30, "turquoise4", 0, 135, 135); + + /// + /// Gets the color "DeepSkyBlue3" (RGB 0,135,175). + /// + public static Color DeepSkyBlue3 { get; } = new Color(31, "deepskyblue3", 0, 135, 175); + + /// + /// Gets the color "DeepSkyBlue3_1" (RGB 0,135,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepSkyBlue3_1 { get; } = new Color(32, "deepskyblue3_1", 0, 135, 215); + + /// + /// Gets the color "DodgerBlue1" (RGB 0,135,255). + /// + public static Color DodgerBlue1 { get; } = new Color(33, "dodgerblue1", 0, 135, 255); + + /// + /// Gets the color "Green3" (RGB 0,175,0). + /// + public static Color Green3 { get; } = new Color(34, "green3", 0, 175, 0); + + /// + /// Gets the color "SpringGreen3" (RGB 0,175,95). + /// + public static Color SpringGreen3 { get; } = new Color(35, "springgreen3", 0, 175, 95); + + /// + /// Gets the color "DarkCyan" (RGB 0,175,135). + /// + public static Color DarkCyan { get; } = new Color(36, "darkcyan", 0, 175, 135); + + /// + /// Gets the color "LightSeaGreen" (RGB 0,175,175). + /// + public static Color LightSeaGreen { get; } = new Color(37, "lightseagreen", 0, 175, 175); + + /// + /// Gets the color "DeepSkyBlue2" (RGB 0,175,215). + /// + public static Color DeepSkyBlue2 { get; } = new Color(38, "deepskyblue2", 0, 175, 215); + + /// + /// Gets the color "DeepSkyBlue1" (RGB 0,175,255). + /// + public static Color DeepSkyBlue1 { get; } = new Color(39, "deepskyblue1", 0, 175, 255); + + /// + /// Gets the color "Green3_1" (RGB 0,215,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Green3_1 { get; } = new Color(40, "green3_1", 0, 215, 0); + + /// + /// Gets the color "SpringGreen3_1" (RGB 0,215,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color SpringGreen3_1 { get; } = new Color(41, "springgreen3_1", 0, 215, 95); + + /// + /// Gets the color "SpringGreen2" (RGB 0,215,135). + /// + public static Color SpringGreen2 { get; } = new Color(42, "springgreen2", 0, 215, 135); + + /// + /// Gets the color "Cyan3" (RGB 0,215,175). + /// + public static Color Cyan3 { get; } = new Color(43, "cyan3", 0, 215, 175); + + /// + /// Gets the color "DarkTurquoise" (RGB 0,215,215). + /// + public static Color DarkTurquoise { get; } = new Color(44, "darkturquoise", 0, 215, 215); + + /// + /// Gets the color "Turquoise2" (RGB 0,215,255). + /// + public static Color Turquoise2 { get; } = new Color(45, "turquoise2", 0, 215, 255); + + /// + /// Gets the color "Green1" (RGB 0,255,0). + /// + public static Color Green1 { get; } = new Color(46, "green1", 0, 255, 0); + + /// + /// Gets the color "SpringGreen2_1" (RGB 0,255,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color SpringGreen2_1 { get; } = new Color(47, "springgreen2_1", 0, 255, 95); + + /// + /// Gets the color "SpringGreen1" (RGB 0,255,135). + /// + public static Color SpringGreen1 { get; } = new Color(48, "springgreen1", 0, 255, 135); + + /// + /// Gets the color "MediumSpringGreen" (RGB 0,255,175). + /// + public static Color MediumSpringGreen { get; } = new Color(49, "mediumspringgreen", 0, 255, 175); + + /// + /// Gets the color "Cyan2" (RGB 0,255,215). + /// + public static Color Cyan2 { get; } = new Color(50, "cyan2", 0, 255, 215); + + /// + /// Gets the color "Cyan1" (RGB 0,255,255). + /// + public static Color Cyan1 { get; } = new Color(51, "cyan1", 0, 255, 255); + + /// + /// Gets the color "DarkRed" (RGB 95,0,0). + /// + public static Color DarkRed { get; } = new Color(52, "darkred", 95, 0, 0); + + /// + /// Gets the color "DeepPink4" (RGB 95,0,95). + /// + public static Color DeepPink4 { get; } = new Color(53, "deeppink4", 95, 0, 95); + + /// + /// Gets the color "Purple4" (RGB 95,0,135). + /// + public static Color Purple4 { get; } = new Color(54, "purple4", 95, 0, 135); + + /// + /// Gets the color "Purple4_1" (RGB 95,0,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Purple4_1 { get; } = new Color(55, "purple4_1", 95, 0, 175); + + /// + /// Gets the color "Purple3" (RGB 95,0,215). + /// + public static Color Purple3 { get; } = new Color(56, "purple3", 95, 0, 215); + + /// + /// Gets the color "BlueViolet" (RGB 95,0,255). + /// + public static Color BlueViolet { get; } = new Color(57, "blueviolet", 95, 0, 255); + + /// + /// Gets the color "Orange4" (RGB 95,95,0). + /// + public static Color Orange4 { get; } = new Color(58, "orange4", 95, 95, 0); + + /// + /// Gets the color "Grey37" (RGB 95,95,95). + /// + public static Color Grey37 { get; } = new Color(59, "grey37", 95, 95, 95); + + /// + /// Gets the color "MediumPurple4" (RGB 95,95,135). + /// + public static Color MediumPurple4 { get; } = new Color(60, "mediumpurple4", 95, 95, 135); + + /// + /// Gets the color "SlateBlue3" (RGB 95,95,175). + /// + public static Color SlateBlue3 { get; } = new Color(61, "slateblue3", 95, 95, 175); + + /// + /// Gets the color "SlateBlue3_1" (RGB 95,95,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color SlateBlue3_1 { get; } = new Color(62, "slateblue3_1", 95, 95, 215); + + /// + /// Gets the color "RoyalBlue1" (RGB 95,95,255). + /// + public static Color RoyalBlue1 { get; } = new Color(63, "royalblue1", 95, 95, 255); + + /// + /// Gets the color "Chartreuse4" (RGB 95,135,0). + /// + public static Color Chartreuse4 { get; } = new Color(64, "chartreuse4", 95, 135, 0); + + /// + /// Gets the color "DarkSeaGreen4" (RGB 95,135,95). + /// + public static Color DarkSeaGreen4 { get; } = new Color(65, "darkseagreen4", 95, 135, 95); + + /// + /// Gets the color "PaleTurquoise4" (RGB 95,135,135). + /// + public static Color PaleTurquoise4 { get; } = new Color(66, "paleturquoise4", 95, 135, 135); + + /// + /// Gets the color "SteelBlue" (RGB 95,135,175). + /// + public static Color SteelBlue { get; } = new Color(67, "steelblue", 95, 135, 175); + + /// + /// Gets the color "SteelBlue3" (RGB 95,135,215). + /// + public static Color SteelBlue3 { get; } = new Color(68, "steelblue3", 95, 135, 215); + + /// + /// Gets the color "CornflowerBlue" (RGB 95,135,255). + /// + public static Color CornflowerBlue { get; } = new Color(69, "cornflowerblue", 95, 135, 255); + + /// + /// Gets the color "Chartreuse3" (RGB 95,175,0). + /// + public static Color Chartreuse3 { get; } = new Color(70, "chartreuse3", 95, 175, 0); + + /// + /// Gets the color "DarkSeaGreen4_1" (RGB 95,175,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkSeaGreen4_1 { get; } = new Color(71, "darkseagreen4_1", 95, 175, 95); + + /// + /// Gets the color "CadetBlue" (RGB 95,175,135). + /// + public static Color CadetBlue { get; } = new Color(72, "cadetblue", 95, 175, 135); + + /// + /// Gets the color "CadetBlue_1" (RGB 95,175,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color CadetBlue_1 { get; } = new Color(73, "cadetblue_1", 95, 175, 175); + + /// + /// Gets the color "SkyBlue3" (RGB 95,175,215). + /// + public static Color SkyBlue3 { get; } = new Color(74, "skyblue3", 95, 175, 215); + + /// + /// Gets the color "SteelBlue1" (RGB 95,175,255). + /// + public static Color SteelBlue1 { get; } = new Color(75, "steelblue1", 95, 175, 255); + + /// + /// Gets the color "Chartreuse3_1" (RGB 95,215,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Chartreuse3_1 { get; } = new Color(76, "chartreuse3_1", 95, 215, 0); + + /// + /// Gets the color "PaleGreen3" (RGB 95,215,95). + /// + public static Color PaleGreen3 { get; } = new Color(77, "palegreen3", 95, 215, 95); + + /// + /// Gets the color "SeaGreen3" (RGB 95,215,135). + /// + public static Color SeaGreen3 { get; } = new Color(78, "seagreen3", 95, 215, 135); + + /// + /// Gets the color "Aquamarine3" (RGB 95,215,175). + /// + public static Color Aquamarine3 { get; } = new Color(79, "aquamarine3", 95, 215, 175); + + /// + /// Gets the color "MediumTurquoise" (RGB 95,215,215). + /// + public static Color MediumTurquoise { get; } = new Color(80, "mediumturquoise", 95, 215, 215); + + /// + /// Gets the color "SteelBlue1_1" (RGB 95,215,255). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color SteelBlue1_1 { get; } = new Color(81, "steelblue1_1", 95, 215, 255); + + /// + /// Gets the color "Chartreuse2" (RGB 95,255,0). + /// + public static Color Chartreuse2 { get; } = new Color(82, "chartreuse2", 95, 255, 0); + + /// + /// Gets the color "SeaGreen2" (RGB 95,255,95). + /// + public static Color SeaGreen2 { get; } = new Color(83, "seagreen2", 95, 255, 95); + + /// + /// Gets the color "SeaGreen1" (RGB 95,255,135). + /// + public static Color SeaGreen1 { get; } = new Color(84, "seagreen1", 95, 255, 135); + + /// + /// Gets the color "SeaGreen1_1" (RGB 95,255,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color SeaGreen1_1 { get; } = new Color(85, "seagreen1_1", 95, 255, 175); + + /// + /// Gets the color "Aquamarine1" (RGB 95,255,215). + /// + public static Color Aquamarine1 { get; } = new Color(86, "aquamarine1", 95, 255, 215); + + /// + /// Gets the color "DarkSlateGray2" (RGB 95,255,255). + /// + public static Color DarkSlateGray2 { get; } = new Color(87, "darkslategray2", 95, 255, 255); + + /// + /// Gets the color "DarkRed_1" (RGB 135,0,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkRed_1 { get; } = new Color(88, "darkred_1", 135, 0, 0); + + /// + /// Gets the color "DeepPink4_1" (RGB 135,0,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepPink4_1 { get; } = new Color(89, "deeppink4_1", 135, 0, 95); + + /// + /// Gets the color "DarkMagenta" (RGB 135,0,135). + /// + public static Color DarkMagenta { get; } = new Color(90, "darkmagenta", 135, 0, 135); + + /// + /// Gets the color "DarkMagenta_1" (RGB 135,0,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkMagenta_1 { get; } = new Color(91, "darkmagenta_1", 135, 0, 175); + + /// + /// Gets the color "DarkViolet" (RGB 135,0,215). + /// + public static Color DarkViolet { get; } = new Color(92, "darkviolet", 135, 0, 215); + + /// + /// Gets the color "Purple_1" (RGB 135,0,255). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Purple_1 { get; } = new Color(93, "purple_1", 135, 0, 255); + + /// + /// Gets the color "Orange4_1" (RGB 135,95,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Orange4_1 { get; } = new Color(94, "orange4_1", 135, 95, 0); + + /// + /// Gets the color "LightPink4" (RGB 135,95,95). + /// + public static Color LightPink4 { get; } = new Color(95, "lightpink4", 135, 95, 95); + + /// + /// Gets the color "Plum4" (RGB 135,95,135). + /// + public static Color Plum4 { get; } = new Color(96, "plum4", 135, 95, 135); + + /// + /// Gets the color "MediumPurple3" (RGB 135,95,175). + /// + public static Color MediumPurple3 { get; } = new Color(97, "mediumpurple3", 135, 95, 175); + + /// + /// Gets the color "MediumPurple3_1" (RGB 135,95,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color MediumPurple3_1 { get; } = new Color(98, "mediumpurple3_1", 135, 95, 215); + + /// + /// Gets the color "SlateBlue1" (RGB 135,95,255). + /// + public static Color SlateBlue1 { get; } = new Color(99, "slateblue1", 135, 95, 255); + + /// + /// Gets the color "Yellow4" (RGB 135,135,0). + /// + public static Color Yellow4 { get; } = new Color(100, "yellow4", 135, 135, 0); + + /// + /// Gets the color "Wheat4" (RGB 135,135,95). + /// + public static Color Wheat4 { get; } = new Color(101, "wheat4", 135, 135, 95); + + /// + /// Gets the color "Grey53" (RGB 135,135,135). + /// + public static Color Grey53 { get; } = new Color(102, "grey53", 135, 135, 135); + + /// + /// Gets the color "LightSlateGrey" (RGB 135,135,175). + /// + public static Color LightSlateGrey { get; } = new Color(103, "lightslategrey", 135, 135, 175); + + /// + /// Gets the color "MediumPurple" (RGB 135,135,215). + /// + public static Color MediumPurple { get; } = new Color(104, "mediumpurple", 135, 135, 215); + + /// + /// Gets the color "LightSlateBlue" (RGB 135,135,255). + /// + public static Color LightSlateBlue { get; } = new Color(105, "lightslateblue", 135, 135, 255); + + /// + /// Gets the color "Yellow4_1" (RGB 135,175,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Yellow4_1 { get; } = new Color(106, "yellow4_1", 135, 175, 0); + + /// + /// Gets the color "DarkOliveGreen3" (RGB 135,175,95). + /// + public static Color DarkOliveGreen3 { get; } = new Color(107, "darkolivegreen3", 135, 175, 95); + + /// + /// Gets the color "DarkSeaGreen" (RGB 135,175,135). + /// + public static Color DarkSeaGreen { get; } = new Color(108, "darkseagreen", 135, 175, 135); + + /// + /// Gets the color "LightSkyBlue3" (RGB 135,175,175). + /// + public static Color LightSkyBlue3 { get; } = new Color(109, "lightskyblue3", 135, 175, 175); + + /// + /// Gets the color "LightSkyBlue3_1" (RGB 135,175,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color LightSkyBlue3_1 { get; } = new Color(110, "lightskyblue3_1", 135, 175, 215); + + /// + /// Gets the color "SkyBlue2" (RGB 135,175,255). + /// + public static Color SkyBlue2 { get; } = new Color(111, "skyblue2", 135, 175, 255); + + /// + /// Gets the color "Chartreuse2_1" (RGB 135,215,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Chartreuse2_1 { get; } = new Color(112, "chartreuse2_1", 135, 215, 0); + + /// + /// Gets the color "DarkOliveGreen3_1" (RGB 135,215,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkOliveGreen3_1 { get; } = new Color(113, "darkolivegreen3_1", 135, 215, 95); + + /// + /// Gets the color "PaleGreen3_1" (RGB 135,215,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color PaleGreen3_1 { get; } = new Color(114, "palegreen3_1", 135, 215, 135); + + /// + /// Gets the color "DarkSeaGreen3" (RGB 135,215,175). + /// + public static Color DarkSeaGreen3 { get; } = new Color(115, "darkseagreen3", 135, 215, 175); + + /// + /// Gets the color "DarkSlateGray3" (RGB 135,215,215). + /// + public static Color DarkSlateGray3 { get; } = new Color(116, "darkslategray3", 135, 215, 215); + + /// + /// Gets the color "SkyBlue1" (RGB 135,215,255). + /// + public static Color SkyBlue1 { get; } = new Color(117, "skyblue1", 135, 215, 255); + + /// + /// Gets the color "Chartreuse1" (RGB 135,255,0). + /// + public static Color Chartreuse1 { get; } = new Color(118, "chartreuse1", 135, 255, 0); + + /// + /// Gets the color "LightGreen" (RGB 135,255,95). + /// + public static Color LightGreen { get; } = new Color(119, "lightgreen", 135, 255, 95); + + /// + /// Gets the color "LightGreen_1" (RGB 135,255,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color LightGreen_1 { get; } = new Color(120, "lightgreen_1", 135, 255, 135); + + /// + /// Gets the color "PaleGreen1" (RGB 135,255,175). + /// + public static Color PaleGreen1 { get; } = new Color(121, "palegreen1", 135, 255, 175); + + /// + /// Gets the color "Aquamarine1_1" (RGB 135,255,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Aquamarine1_1 { get; } = new Color(122, "aquamarine1_1", 135, 255, 215); + + /// + /// Gets the color "DarkSlateGray1" (RGB 135,255,255). + /// + public static Color DarkSlateGray1 { get; } = new Color(123, "darkslategray1", 135, 255, 255); + + /// + /// Gets the color "Red3" (RGB 175,0,0). + /// + public static Color Red3 { get; } = new Color(124, "red3", 175, 0, 0); + + /// + /// Gets the color "DeepPink4_2" (RGB 175,0,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepPink4_2 { get; } = new Color(125, "deeppink4_2", 175, 0, 95); + + /// + /// Gets the color "MediumVioletRed" (RGB 175,0,135). + /// + public static Color MediumVioletRed { get; } = new Color(126, "mediumvioletred", 175, 0, 135); + + /// + /// Gets the color "Magenta3" (RGB 175,0,175). + /// + public static Color Magenta3 { get; } = new Color(127, "magenta3", 175, 0, 175); + + /// + /// Gets the color "DarkViolet_1" (RGB 175,0,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkViolet_1 { get; } = new Color(128, "darkviolet_1", 175, 0, 215); + + /// + /// Gets the color "Purple_2" (RGB 175,0,255). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Purple_2 { get; } = new Color(129, "purple_2", 175, 0, 255); + + /// + /// Gets the color "DarkOrange3" (RGB 175,95,0). + /// + public static Color DarkOrange3 { get; } = new Color(130, "darkorange3", 175, 95, 0); + + /// + /// Gets the color "IndianRed" (RGB 175,95,95). + /// + public static Color IndianRed { get; } = new Color(131, "indianred", 175, 95, 95); + + /// + /// Gets the color "HotPink3" (RGB 175,95,135). + /// + public static Color HotPink3 { get; } = new Color(132, "hotpink3", 175, 95, 135); + + /// + /// Gets the color "MediumOrchid3" (RGB 175,95,175). + /// + public static Color MediumOrchid3 { get; } = new Color(133, "mediumorchid3", 175, 95, 175); + + /// + /// Gets the color "MediumOrchid" (RGB 175,95,215). + /// + public static Color MediumOrchid { get; } = new Color(134, "mediumorchid", 175, 95, 215); + + /// + /// Gets the color "MediumPurple2" (RGB 175,95,255). + /// + public static Color MediumPurple2 { get; } = new Color(135, "mediumpurple2", 175, 95, 255); + + /// + /// Gets the color "DarkGoldenrod" (RGB 175,135,0). + /// + public static Color DarkGoldenrod { get; } = new Color(136, "darkgoldenrod", 175, 135, 0); + + /// + /// Gets the color "LightSalmon3" (RGB 175,135,95). + /// + public static Color LightSalmon3 { get; } = new Color(137, "lightsalmon3", 175, 135, 95); + + /// + /// Gets the color "RosyBrown" (RGB 175,135,135). + /// + public static Color RosyBrown { get; } = new Color(138, "rosybrown", 175, 135, 135); + + /// + /// Gets the color "Grey63" (RGB 175,135,175). + /// + public static Color Grey63 { get; } = new Color(139, "grey63", 175, 135, 175); + + /// + /// Gets the color "MediumPurple2_1" (RGB 175,135,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color MediumPurple2_1 { get; } = new Color(140, "mediumpurple2_1", 175, 135, 215); + + /// + /// Gets the color "MediumPurple1" (RGB 175,135,255). + /// + public static Color MediumPurple1 { get; } = new Color(141, "mediumpurple1", 175, 135, 255); + + /// + /// Gets the color "Gold3" (RGB 175,175,0). + /// + public static Color Gold3 { get; } = new Color(142, "gold3", 175, 175, 0); + + /// + /// Gets the color "DarkKhaki" (RGB 175,175,95). + /// + public static Color DarkKhaki { get; } = new Color(143, "darkkhaki", 175, 175, 95); + + /// + /// Gets the color "NavajoWhite3" (RGB 175,175,135). + /// + public static Color NavajoWhite3 { get; } = new Color(144, "navajowhite3", 175, 175, 135); + + /// + /// Gets the color "Grey69" (RGB 175,175,175). + /// + public static Color Grey69 { get; } = new Color(145, "grey69", 175, 175, 175); + + /// + /// Gets the color "LightSteelBlue3" (RGB 175,175,215). + /// + public static Color LightSteelBlue3 { get; } = new Color(146, "lightsteelblue3", 175, 175, 215); + + /// + /// Gets the color "LightSteelBlue" (RGB 175,175,255). + /// + public static Color LightSteelBlue { get; } = new Color(147, "lightsteelblue", 175, 175, 255); + + /// + /// Gets the color "Yellow3" (RGB 175,215,0). + /// + public static Color Yellow3 { get; } = new Color(148, "yellow3", 175, 215, 0); + + /// + /// Gets the color "DarkOliveGreen3_2" (RGB 175,215,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkOliveGreen3_2 { get; } = new Color(149, "darkolivegreen3_2", 175, 215, 95); + + /// + /// Gets the color "DarkSeaGreen3_1" (RGB 175,215,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkSeaGreen3_1 { get; } = new Color(150, "darkseagreen3_1", 175, 215, 135); + + /// + /// Gets the color "DarkSeaGreen2" (RGB 175,215,175). + /// + public static Color DarkSeaGreen2 { get; } = new Color(151, "darkseagreen2", 175, 215, 175); + + /// + /// Gets the color "LightCyan3" (RGB 175,215,215). + /// + public static Color LightCyan3 { get; } = new Color(152, "lightcyan3", 175, 215, 215); + + /// + /// Gets the color "LightSkyBlue1" (RGB 175,215,255). + /// + public static Color LightSkyBlue1 { get; } = new Color(153, "lightskyblue1", 175, 215, 255); + + /// + /// Gets the color "GreenYellow" (RGB 175,255,0). + /// + public static Color GreenYellow { get; } = new Color(154, "greenyellow", 175, 255, 0); + + /// + /// Gets the color "DarkOliveGreen2" (RGB 175,255,95). + /// + public static Color DarkOliveGreen2 { get; } = new Color(155, "darkolivegreen2", 175, 255, 95); + + /// + /// Gets the color "PaleGreen1_1" (RGB 175,255,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color PaleGreen1_1 { get; } = new Color(156, "palegreen1_1", 175, 255, 135); + + /// + /// Gets the color "DarkSeaGreen2_1" (RGB 175,255,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkSeaGreen2_1 { get; } = new Color(157, "darkseagreen2_1", 175, 255, 175); + + /// + /// Gets the color "DarkSeaGreen1" (RGB 175,255,215). + /// + public static Color DarkSeaGreen1 { get; } = new Color(158, "darkseagreen1", 175, 255, 215); + + /// + /// Gets the color "PaleTurquoise1" (RGB 175,255,255). + /// + public static Color PaleTurquoise1 { get; } = new Color(159, "paleturquoise1", 175, 255, 255); + + /// + /// Gets the color "Red3_1" (RGB 215,0,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Red3_1 { get; } = new Color(160, "red3_1", 215, 0, 0); + + /// + /// Gets the color "DeepPink3" (RGB 215,0,95). + /// + public static Color DeepPink3 { get; } = new Color(161, "deeppink3", 215, 0, 95); + + /// + /// Gets the color "DeepPink3_1" (RGB 215,0,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepPink3_1 { get; } = new Color(162, "deeppink3_1", 215, 0, 135); + + /// + /// Gets the color "Magenta3_1" (RGB 215,0,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Magenta3_1 { get; } = new Color(163, "magenta3_1", 215, 0, 175); + + /// + /// Gets the color "Magenta3_2" (RGB 215,0,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Magenta3_2 { get; } = new Color(164, "magenta3_2", 215, 0, 215); + + /// + /// Gets the color "Magenta2" (RGB 215,0,255). + /// + public static Color Magenta2 { get; } = new Color(165, "magenta2", 215, 0, 255); + + /// + /// Gets the color "DarkOrange3_1" (RGB 215,95,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkOrange3_1 { get; } = new Color(166, "darkorange3_1", 215, 95, 0); + + /// + /// Gets the color "IndianRed_1" (RGB 215,95,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color IndianRed_1 { get; } = new Color(167, "indianred_1", 215, 95, 95); + + /// + /// Gets the color "HotPink3_1" (RGB 215,95,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color HotPink3_1 { get; } = new Color(168, "hotpink3_1", 215, 95, 135); + + /// + /// Gets the color "HotPink2" (RGB 215,95,175). + /// + public static Color HotPink2 { get; } = new Color(169, "hotpink2", 215, 95, 175); + + /// + /// Gets the color "Orchid" (RGB 215,95,215). + /// + public static Color Orchid { get; } = new Color(170, "orchid", 215, 95, 215); + + /// + /// Gets the color "MediumOrchid1" (RGB 215,95,255). + /// + public static Color MediumOrchid1 { get; } = new Color(171, "mediumorchid1", 215, 95, 255); + + /// + /// Gets the color "Orange3" (RGB 215,135,0). + /// + public static Color Orange3 { get; } = new Color(172, "orange3", 215, 135, 0); + + /// + /// Gets the color "LightSalmon3_1" (RGB 215,135,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color LightSalmon3_1 { get; } = new Color(173, "lightsalmon3_1", 215, 135, 95); + + /// + /// Gets the color "LightPink3" (RGB 215,135,135). + /// + public static Color LightPink3 { get; } = new Color(174, "lightpink3", 215, 135, 135); + + /// + /// Gets the color "Pink3" (RGB 215,135,175). + /// + public static Color Pink3 { get; } = new Color(175, "pink3", 215, 135, 175); + + /// + /// Gets the color "Plum3" (RGB 215,135,215). + /// + public static Color Plum3 { get; } = new Color(176, "plum3", 215, 135, 215); + + /// + /// Gets the color "Violet" (RGB 215,135,255). + /// + public static Color Violet { get; } = new Color(177, "violet", 215, 135, 255); + + /// + /// Gets the color "Gold3_1" (RGB 215,175,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Gold3_1 { get; } = new Color(178, "gold3_1", 215, 175, 0); + + /// + /// Gets the color "LightGoldenrod3" (RGB 215,175,95). + /// + public static Color LightGoldenrod3 { get; } = new Color(179, "lightgoldenrod3", 215, 175, 95); + + /// + /// Gets the color "Tan" (RGB 215,175,135). + /// + public static Color Tan { get; } = new Color(180, "tan", 215, 175, 135); + + /// + /// Gets the color "MistyRose3" (RGB 215,175,175). + /// + public static Color MistyRose3 { get; } = new Color(181, "mistyrose3", 215, 175, 175); + + /// + /// Gets the color "Thistle3" (RGB 215,175,215). + /// + public static Color Thistle3 { get; } = new Color(182, "thistle3", 215, 175, 215); + + /// + /// Gets the color "Plum2" (RGB 215,175,255). + /// + public static Color Plum2 { get; } = new Color(183, "plum2", 215, 175, 255); + + /// + /// Gets the color "Yellow3_1" (RGB 215,215,0). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Yellow3_1 { get; } = new Color(184, "yellow3_1", 215, 215, 0); + + /// + /// Gets the color "Khaki3" (RGB 215,215,95). + /// + public static Color Khaki3 { get; } = new Color(185, "khaki3", 215, 215, 95); + + /// + /// Gets the color "LightGoldenrod2" (RGB 215,215,135). + /// + public static Color LightGoldenrod2 { get; } = new Color(186, "lightgoldenrod2", 215, 215, 135); + + /// + /// Gets the color "LightYellow3" (RGB 215,215,175). + /// + public static Color LightYellow3 { get; } = new Color(187, "lightyellow3", 215, 215, 175); + + /// + /// Gets the color "Grey84" (RGB 215,215,215). + /// + public static Color Grey84 { get; } = new Color(188, "grey84", 215, 215, 215); + + /// + /// Gets the color "LightSteelBlue1" (RGB 215,215,255). + /// + public static Color LightSteelBlue1 { get; } = new Color(189, "lightsteelblue1", 215, 215, 255); + + /// + /// Gets the color "Yellow2" (RGB 215,255,0). + /// + public static Color Yellow2 { get; } = new Color(190, "yellow2", 215, 255, 0); + + /// + /// Gets the color "DarkOliveGreen1" (RGB 215,255,95). + /// + public static Color DarkOliveGreen1 { get; } = new Color(191, "darkolivegreen1", 215, 255, 95); + + /// + /// Gets the color "DarkOliveGreen1_1" (RGB 215,255,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkOliveGreen1_1 { get; } = new Color(192, "darkolivegreen1_1", 215, 255, 135); + + /// + /// Gets the color "DarkSeaGreen1_1" (RGB 215,255,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DarkSeaGreen1_1 { get; } = new Color(193, "darkseagreen1_1", 215, 255, 175); + + /// + /// Gets the color "Honeydew2" (RGB 215,255,215). + /// + public static Color Honeydew2 { get; } = new Color(194, "honeydew2", 215, 255, 215); + + /// + /// Gets the color "LightCyan1" (RGB 215,255,255). + /// + public static Color LightCyan1 { get; } = new Color(195, "lightcyan1", 215, 255, 255); + + /// + /// Gets the color "Red1" (RGB 255,0,0). + /// + public static Color Red1 { get; } = new Color(196, "red1", 255, 0, 0); + + /// + /// Gets the color "DeepPink2" (RGB 255,0,95). + /// + public static Color DeepPink2 { get; } = new Color(197, "deeppink2", 255, 0, 95); + + /// + /// Gets the color "DeepPink1" (RGB 255,0,135). + /// + public static Color DeepPink1 { get; } = new Color(198, "deeppink1", 255, 0, 135); + + /// + /// Gets the color "DeepPink1_1" (RGB 255,0,175). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color DeepPink1_1 { get; } = new Color(199, "deeppink1_1", 255, 0, 175); + + /// + /// Gets the color "Magenta2_1" (RGB 255,0,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color Magenta2_1 { get; } = new Color(200, "magenta2_1", 255, 0, 215); + + /// + /// Gets the color "Magenta1" (RGB 255,0,255). + /// + public static Color Magenta1 { get; } = new Color(201, "magenta1", 255, 0, 255); + + /// + /// Gets the color "OrangeRed1" (RGB 255,95,0). + /// + public static Color OrangeRed1 { get; } = new Color(202, "orangered1", 255, 95, 0); + + /// + /// Gets the color "IndianRed1" (RGB 255,95,95). + /// + public static Color IndianRed1 { get; } = new Color(203, "indianred1", 255, 95, 95); + + /// + /// Gets the color "IndianRed1_1" (RGB 255,95,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color IndianRed1_1 { get; } = new Color(204, "indianred1_1", 255, 95, 135); + + /// + /// Gets the color "HotPink" (RGB 255,95,175). + /// + public static Color HotPink { get; } = new Color(205, "hotpink", 255, 95, 175); + + /// + /// Gets the color "HotPink_1" (RGB 255,95,215). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color HotPink_1 { get; } = new Color(206, "hotpink_1", 255, 95, 215); + + /// + /// Gets the color "MediumOrchid1_1" (RGB 255,95,255). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color MediumOrchid1_1 { get; } = new Color(207, "mediumorchid1_1", 255, 95, 255); + + /// + /// Gets the color "DarkOrange" (RGB 255,135,0). + /// + public static Color DarkOrange { get; } = new Color(208, "darkorange", 255, 135, 0); + + /// + /// Gets the color "Salmon1" (RGB 255,135,95). + /// + public static Color Salmon1 { get; } = new Color(209, "salmon1", 255, 135, 95); + + /// + /// Gets the color "LightCoral" (RGB 255,135,135). + /// + public static Color LightCoral { get; } = new Color(210, "lightcoral", 255, 135, 135); + + /// + /// Gets the color "PaleVioletRed1" (RGB 255,135,175). + /// + public static Color PaleVioletRed1 { get; } = new Color(211, "palevioletred1", 255, 135, 175); + + /// + /// Gets the color "Orchid2" (RGB 255,135,215). + /// + public static Color Orchid2 { get; } = new Color(212, "orchid2", 255, 135, 215); + + /// + /// Gets the color "Orchid1" (RGB 255,135,255). + /// + public static Color Orchid1 { get; } = new Color(213, "orchid1", 255, 135, 255); + + /// + /// Gets the color "Orange1" (RGB 255,175,0). + /// + public static Color Orange1 { get; } = new Color(214, "orange1", 255, 175, 0); + + /// + /// Gets the color "SandyBrown" (RGB 255,175,95). + /// + public static Color SandyBrown { get; } = new Color(215, "sandybrown", 255, 175, 95); + + /// + /// Gets the color "LightSalmon1" (RGB 255,175,135). + /// + public static Color LightSalmon1 { get; } = new Color(216, "lightsalmon1", 255, 175, 135); + + /// + /// Gets the color "LightPink1" (RGB 255,175,175). + /// + public static Color LightPink1 { get; } = new Color(217, "lightpink1", 255, 175, 175); + + /// + /// Gets the color "Pink1" (RGB 255,175,215). + /// + public static Color Pink1 { get; } = new Color(218, "pink1", 255, 175, 215); + + /// + /// Gets the color "Plum1" (RGB 255,175,255). + /// + public static Color Plum1 { get; } = new Color(219, "plum1", 255, 175, 255); + + /// + /// Gets the color "Gold1" (RGB 255,215,0). + /// + public static Color Gold1 { get; } = new Color(220, "gold1", 255, 215, 0); + + /// + /// Gets the color "LightGoldenrod2_1" (RGB 255,215,95). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color LightGoldenrod2_1 { get; } = new Color(221, "lightgoldenrod2_1", 255, 215, 95); + + /// + /// Gets the color "LightGoldenrod2_2" (RGB 255,215,135). + /// + [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")] + public static Color LightGoldenrod2_2 { get; } = new Color(222, "lightgoldenrod2_2", 255, 215, 135); + + /// + /// Gets the color "NavajoWhite1" (RGB 255,215,175). + /// + public static Color NavajoWhite1 { get; } = new Color(223, "navajowhite1", 255, 215, 175); + + /// + /// Gets the color "MistyRose1" (RGB 255,215,215). + /// + public static Color MistyRose1 { get; } = new Color(224, "mistyrose1", 255, 215, 215); + + /// + /// Gets the color "Thistle1" (RGB 255,215,255). + /// + public static Color Thistle1 { get; } = new Color(225, "thistle1", 255, 215, 255); + + /// + /// Gets the color "Yellow1" (RGB 255,255,0). + /// + public static Color Yellow1 { get; } = new Color(226, "yellow1", 255, 255, 0); + + /// + /// Gets the color "LightGoldenrod1" (RGB 255,255,95). + /// + public static Color LightGoldenrod1 { get; } = new Color(227, "lightgoldenrod1", 255, 255, 95); + + /// + /// Gets the color "Khaki1" (RGB 255,255,135). + /// + public static Color Khaki1 { get; } = new Color(228, "khaki1", 255, 255, 135); + + /// + /// Gets the color "Wheat1" (RGB 255,255,175). + /// + public static Color Wheat1 { get; } = new Color(229, "wheat1", 255, 255, 175); + + /// + /// Gets the color "Cornsilk1" (RGB 255,255,215). + /// + public static Color Cornsilk1 { get; } = new Color(230, "cornsilk1", 255, 255, 215); + + /// + /// Gets the color "Grey100" (RGB 255,255,255). + /// + public static Color Grey100 { get; } = new Color(231, "grey100", 255, 255, 255); + + /// + /// Gets the color "Grey3" (RGB 8,8,8). + /// + public static Color Grey3 { get; } = new Color(232, "grey3", 8, 8, 8); + + /// + /// Gets the color "Grey7" (RGB 18,18,18). + /// + public static Color Grey7 { get; } = new Color(233, "grey7", 18, 18, 18); + + /// + /// Gets the color "Grey11" (RGB 28,28,28). + /// + public static Color Grey11 { get; } = new Color(234, "grey11", 28, 28, 28); + + /// + /// Gets the color "Grey15" (RGB 38,38,38). + /// + public static Color Grey15 { get; } = new Color(235, "grey15", 38, 38, 38); + + /// + /// Gets the color "Grey19" (RGB 48,48,48). + /// + public static Color Grey19 { get; } = new Color(236, "grey19", 48, 48, 48); + + /// + /// Gets the color "Grey23" (RGB 58,58,58). + /// + public static Color Grey23 { get; } = new Color(237, "grey23", 58, 58, 58); + + /// + /// Gets the color "Grey27" (RGB 68,68,68). + /// + public static Color Grey27 { get; } = new Color(238, "grey27", 68, 68, 68); + + /// + /// Gets the color "Grey30" (RGB 78,78,78). + /// + public static Color Grey30 { get; } = new Color(239, "grey30", 78, 78, 78); + + /// + /// Gets the color "Grey35" (RGB 88,88,88). + /// + public static Color Grey35 { get; } = new Color(240, "grey35", 88, 88, 88); + + /// + /// Gets the color "Grey39" (RGB 98,98,98). + /// + public static Color Grey39 { get; } = new Color(241, "grey39", 98, 98, 98); + + /// + /// Gets the color "Grey42" (RGB 108,108,108). + /// + public static Color Grey42 { get; } = new Color(242, "grey42", 108, 108, 108); + + /// + /// Gets the color "Grey46" (RGB 118,118,118). + /// + public static Color Grey46 { get; } = new Color(243, "grey46", 118, 118, 118); + + /// + /// Gets the color "Grey50" (RGB 128,128,128). + /// + public static Color Grey50 { get; } = new Color(244, "grey50", 128, 128, 128); + + /// + /// Gets the color "Grey54" (RGB 138,138,138). + /// + public static Color Grey54 { get; } = new Color(245, "grey54", 138, 138, 138); + + /// + /// Gets the color "Grey58" (RGB 148,148,148). + /// + public static Color Grey58 { get; } = new Color(246, "grey58", 148, 148, 148); + + /// + /// Gets the color "Grey62" (RGB 158,158,158). + /// + public static Color Grey62 { get; } = new Color(247, "grey62", 158, 158, 158); + + /// + /// Gets the color "Grey66" (RGB 168,168,168). + /// + public static Color Grey66 { get; } = new Color(248, "grey66", 168, 168, 168); + + /// + /// Gets the color "Grey70" (RGB 178,178,178). + /// + public static Color Grey70 { get; } = new Color(249, "grey70", 178, 178, 178); + + /// + /// Gets the color "Grey74" (RGB 188,188,188). + /// + public static Color Grey74 { get; } = new Color(250, "grey74", 188, 188, 188); + + /// + /// Gets the color "Grey78" (RGB 198,198,198). + /// + public static Color Grey78 { get; } = new Color(251, "grey78", 198, 198, 198); + + /// + /// Gets the color "Grey82" (RGB 208,208,208). + /// + public static Color Grey82 { get; } = new Color(252, "grey82", 208, 208, 208); + + /// + /// Gets the color "Grey85" (RGB 218,218,218). + /// + public static Color Grey85 { get; } = new Color(253, "grey85", 218, 218, 218); + + /// + /// Gets the color "Grey89" (RGB 228,228,228). + /// + public static Color Grey89 { get; } = new Color(254, "grey89", 228, 228, 228); + + /// + /// Gets the color "Grey93" (RGB 238,238,238). + /// + public static Color Grey93 { get; } = new Color(255, "grey93", 238, 238, 238); + } +} diff --git a/src/Spectre.Console/Color.cs b/src/Spectre.Console/Color.cs new file mode 100644 index 0000000..a111970 --- /dev/null +++ b/src/Spectre.Console/Color.cs @@ -0,0 +1,211 @@ +using System; +using System.Diagnostics; +using System.Linq; +using Spectre.Console.Internal; + +namespace Spectre.Console +{ + /// + /// Represents a color. + /// + public partial struct Color : IEquatable + { + /// + /// Gets the default color. + /// + public static Color Default { get; } + + static Color() + { + Default = new Color(0, "default", 0, 0, 0, true); + } + + /// + /// Gets the red component. + /// + public byte R { get; } + + /// + /// Gets the green component. + /// + public byte G { get; } + + /// + /// Gets the blue component. + /// + public byte B { get; } + + /// + /// Gets the name of the color, if any. + /// + public string Name { get; } + + /// + /// Gets the number of the color, if any. + /// + internal byte? Number { get; } + + /// + /// Gets a value indicating whether or not this is the default color. + /// + internal bool IsDefault { get; } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + public Color(byte red, byte green, byte blue) + { + R = red; + G = green; + B = blue; + IsDefault = false; + Name = null; + Number = null; + } + + /// + public override int GetHashCode() + { + unchecked + { + var hash = (int)2166136261; + hash = (hash * 16777619) ^ R.GetHashCode(); + hash = (hash * 16777619) ^ G.GetHashCode(); + hash = (hash * 16777619) ^ B.GetHashCode(); + return hash; + } + } + + /// + public override bool Equals(object obj) + { + return obj is Color color && Equals(color); + } + + /// + public bool Equals(Color other) + { + return Number == other.Number || (R == other.R && G == other.G && B == other.B); + } + + /// + /// Checks if two instances are equal. + /// + /// The first color instance to compare. + /// The second color instance to compare. + /// true if the two colors are equal, otherwise false. + public static bool operator ==(Color left, Color right) + { + return left.Equals(right); + } + + /// + /// Checks if two instances are not equal. + /// + /// The first color instance to compare. + /// The second color instance to compare. + /// true if the two colors are not equal, otherwise false. + public static bool operator !=(Color left, Color right) + { + return !(left == right); + } + + /// + /// Convers a to a . + /// + /// The color to convert. + public static implicit operator Color(ConsoleColor color) + { + return FromConsoleColor(color); + } + + /// + /// Convers a color number into a . + /// + /// The color number. + /// The color representing the specified color number. + public static Color FromColorNumber(int number) + { + if (number < 0 || number > 255) + { + throw new InvalidOperationException("Color number must be between 0 and 255"); + } + + return ColorPalette.EightBit.First(x => x.Number == number); + } + + /// + /// Convers a to a . + /// + /// The color to convert. + /// A representing the . + public static ConsoleColor ToConsoleColor(Color color) + { + if (color.IsDefault) + { + return (ConsoleColor)(-1); + } + + if (color.Number == null || color.Number.Value >= 16) + { + color = ColorPalette.ExactOrClosest(ColorSystem.Standard, color); + } + + // Should not happen, but this will make things easier if we mess things up... + Debug.Assert(color.Number >= 0 && color.Number < 16, "Color does not fall inside the standard palette range."); + + return color.Number.Value switch + { + 0 => ConsoleColor.Black, + 1 => ConsoleColor.DarkRed, + 2 => ConsoleColor.DarkGreen, + 3 => ConsoleColor.DarkYellow, + 4 => ConsoleColor.DarkBlue, + 5 => ConsoleColor.DarkMagenta, + 6 => ConsoleColor.DarkCyan, + 7 => ConsoleColor.Gray, + 8 => ConsoleColor.DarkGray, + 9 => ConsoleColor.Red, + 10 => ConsoleColor.Green, + 11 => ConsoleColor.Yellow, + 12 => ConsoleColor.Blue, + 13 => ConsoleColor.Magenta, + 14 => ConsoleColor.Cyan, + 15 => ConsoleColor.White, + _ => throw new InvalidOperationException("Cannot convert color to console color."), + }; + } + + /// + /// Convers a to a . + /// + /// The color to convert. + /// A representing the . + public static Color FromConsoleColor(ConsoleColor color) + { + return color switch + { + ConsoleColor.Black => Black, + ConsoleColor.Blue => Blue, + ConsoleColor.Cyan => Aqua, + ConsoleColor.DarkBlue => Navy, + ConsoleColor.DarkCyan => Teal, + ConsoleColor.DarkGray => Grey, + ConsoleColor.DarkGreen => Green, + ConsoleColor.DarkMagenta => Purple, + ConsoleColor.DarkRed => Maroon, + ConsoleColor.DarkYellow => Olive, + ConsoleColor.Gray => Silver, + ConsoleColor.Green => Lime, + ConsoleColor.Magenta => Fuchsia, + ConsoleColor.Red => Red, + ConsoleColor.White => White, + ConsoleColor.Yellow => Yellow, + _ => Default, + }; + } + } +} diff --git a/src/Spectre.Console/ColorSystem.cs b/src/Spectre.Console/ColorSystem.cs new file mode 100644 index 0000000..f0e301d --- /dev/null +++ b/src/Spectre.Console/ColorSystem.cs @@ -0,0 +1,33 @@ +namespace Spectre.Console +{ + /// + /// Represents a color system. + /// + public enum ColorSystem + { + /// + /// No colors. + /// + NoColors = 0, + + /// + /// Legacy, 3-bit mode. + /// + Legacy = 1, + + /// + /// Standard, 4-bit mode. + /// + Standard = 2, + + /// + /// 8-bit mode. + /// + EightBit = 3, + + /// + /// 24-bit mode. + /// + TrueColor = 4, + } +} diff --git a/src/Spectre.Console/ColorSystemSupport.cs b/src/Spectre.Console/ColorSystemSupport.cs new file mode 100644 index 0000000..cdff656 --- /dev/null +++ b/src/Spectre.Console/ColorSystemSupport.cs @@ -0,0 +1,38 @@ +namespace Spectre.Console +{ + /// + /// Determines what color system should be used. + /// + public enum ColorSystemSupport + { + /// + /// Try to detect the color system. + /// + Detect = -1, + + /// + /// No colors. + /// + NoColors = 0, + + /// + /// Legacy, 3-bit mode. + /// + Legacy = 1, + + /// + /// Standard, 4-bit mode. + /// + Standard = 2, + + /// + /// 8-bit mode. + /// + EightBit = 3, + + /// + /// 24-bit mode. + /// + TrueColor = 4, + } +} diff --git a/src/Spectre.Console/ConsoleExtensions.cs b/src/Spectre.Console/ConsoleExtensions.cs new file mode 100644 index 0000000..9e9529a --- /dev/null +++ b/src/Spectre.Console/ConsoleExtensions.cs @@ -0,0 +1,83 @@ +using System; + +namespace Spectre.Console +{ + /// + /// Contains extension methods for . + /// + public static class ConsoleExtensions + { + /// + /// Resets both colors and style for the console. + /// + /// The console to reset. + public static void Reset(this IAnsiConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.ResetColors(); + console.ResetStyle(); + } + + /// + /// Resets the current style back to the default one. + /// + /// The console to reset the style for. + public static void ResetStyle(this IAnsiConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.Style = Styles.None; + } + + /// + /// Resets the foreground and background colors to the default ones. + /// + /// The console to reset colors for. + public static void ResetColors(this IAnsiConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.Foreground = Color.Default; + console.Background = Color.Default; + } + + /// + /// Writes an empty line to the console. + /// + /// The console to write to. + public static void WriteLine(this IAnsiConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.WriteLine(null); + } + + /// + /// Writes a line to the console. + /// + /// The console to write to. + /// The content to write. + public static void WriteLine(this IAnsiConsole console, string content) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.WriteLine(content); + } + } +} diff --git a/src/Spectre.Console/IAnsiConsole.cs b/src/Spectre.Console/IAnsiConsole.cs new file mode 100644 index 0000000..6e6fd31 --- /dev/null +++ b/src/Spectre.Console/IAnsiConsole.cs @@ -0,0 +1,52 @@ +namespace Spectre.Console +{ + /// + /// Represents a console. + /// + public interface IAnsiConsole + { + /// + /// Gets the console's capabilities. + /// + public AnsiConsoleCapabilities Capabilities { get; } + + /// + /// Gets the buffer width of the console. + /// + public int Width { get; } + + /// + /// Gets the buffer height of the console. + /// + public int Height { get; } + + /// + /// Gets or sets the current style. + /// + Styles Style { get; set; } + + /// + /// Gets or sets the current foreground. + /// + Color Foreground { get; set; } + + /// + /// Gets or sets the current background. + /// + Color Background { get; set; } + + /// + /// Writes a string followed by a line terminator to the console. + /// + /// The string to write. + void Write(string text); + + /// + /// Writes a string followed by a line terminator to the console. + /// + /// + /// The string to write. If value is null, only the line terminator is written. + /// + void WriteLine(string text); + } +} diff --git a/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs new file mode 100644 index 0000000..651ccb3 --- /dev/null +++ b/src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs @@ -0,0 +1,44 @@ +using System.Linq; + +namespace Spectre.Console.Internal +{ + internal static class AnsiBuilder + { + public static string GetAnsi( + ColorSystem system, + string text, + Styles style, + Color foreground, + Color background) + { + var codes = AnsiStyleBuilder.GetAnsiCodes(style); + + // Got foreground? + if (foreground != Color.Default) + { + codes = codes.Concat(AnsiColorBuilder.GetAnsiCodes(system, foreground, foreground: true)); + } + + // Got background? + if (background != Color.Default) + { + codes = codes.Concat(AnsiColorBuilder.GetAnsiCodes(system, background, foreground: false)); + } + + var result = codes.ToArray(); + if (result.Length == 0) + { + return text; + } + + var lol = string.Concat( + "\u001b[", + string.Join(";", result), + "m", + text, + "\u001b[0m"); + + return lol; + } + } +} diff --git a/src/Spectre.Console/Internal/Ansi/AnsiColorBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiColorBuilder.cs new file mode 100644 index 0000000..86b3f13 --- /dev/null +++ b/src/Spectre.Console/Internal/Ansi/AnsiColorBuilder.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Spectre.Console.Internal +{ + internal static class AnsiColorBuilder + { + public static IEnumerable GetAnsiCodes(ColorSystem system, Color color, bool foreground) + { + return system switch + { + ColorSystem.NoColors => Array.Empty(), // No colors + ColorSystem.TrueColor => GetTrueColor(color, foreground), // 24-bit + ColorSystem.EightBit => GetEightBit(color, foreground), // 8-bit + ColorSystem.Standard => GetFourBit(color, foreground), // 4-bit + ColorSystem.Legacy => GetThreeBit(color, foreground), // 3-bit + _ => throw new InvalidOperationException("Could not determine ANSI color."), + }; + } + + private static IEnumerable GetThreeBit(Color color, bool foreground) + { + var number = color.Number; + if (number == null || color.Number >= 8) + { + number = ColorPalette.ExactOrClosest(ColorSystem.Legacy, color).Number; + } + + Debug.Assert(number >= 0 && number < 8, "Invalid range for 4-bit color"); + + var mod = foreground ? 30 : 40; + return new byte[] { (byte)(number.Value + mod) }; + } + + private static IEnumerable GetFourBit(Color color, bool foreground) + { + var number = color.Number; + if (number == null || color.Number >= 16) + { + number = ColorPalette.ExactOrClosest(ColorSystem.Standard, color).Number; + } + + Debug.Assert(number >= 0 && number < 16, "Invalid range for 4-bit color"); + + var mod = number < 8 ? (foreground ? 30 : 40) : (foreground ? 82 : 92); + return new byte[] { (byte)(number.Value + mod) }; + } + + private static IEnumerable GetEightBit(Color color, bool foreground) + { + var number = color.Number ?? ColorPalette.ExactOrClosest(ColorSystem.EightBit, color).Number; + Debug.Assert(number >= 0 && number <= 255, "Invalid range for 8-bit color"); + + var mod = foreground ? (byte)38 : (byte)48; + return new byte[] { mod, 5, (byte)number }; + } + + private static IEnumerable GetTrueColor(Color color, bool foreground) + { + if (color.Number != null) + { + return GetEightBit(color, foreground); + } + + var mod = foreground ? (byte)38 : (byte)48; + return new byte[] { mod, 2, color.R, color.G, color.B }; + } + } +} diff --git a/src/Spectre.Console/Internal/Ansi/AnsiDetector.cs b/src/Spectre.Console/Internal/Ansi/AnsiDetector.cs new file mode 100644 index 0000000..2c61bf3 --- /dev/null +++ b/src/Spectre.Console/Internal/Ansi/AnsiDetector.cs @@ -0,0 +1,129 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Portions of this code was ported from the supports-ansi project by Qingrong Ke +// https://github.com/keqingrong/supports-ansi/blob/master/index.js +///////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace Spectre.Console.Internal +{ + internal static class AnsiDetector + { + private static readonly Regex[] Regexes = new[] + { + new Regex("^xterm"), // xterm, PuTTY, Mintty + new Regex("^rxvt"), // RXVT + new Regex("^eterm"), // Eterm + new Regex("^screen"), // GNU screen, tmux + new Regex("tmux"), // tmux + new Regex("^vt100"), // DEC VT series + new Regex("^vt102"), // DEC VT series + new Regex("^vt220"), // DEC VT series + new Regex("^vt320"), // DEC VT series + new Regex("ansi"), // ANSI + new Regex("scoansi"), // SCO ANSI + new Regex("cygwin"), // Cygwin, MinGW + new Regex("linux"), // Linux console + new Regex("konsole"), // Konsole + new Regex("bvterm"), // Bitvise SSH Client + }; + + public static bool SupportsAnsi(bool upgrade) + { + // Github action doesn't setup a correct PTY but supports ANSI. + if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTION"))) + { + return true; + } + + // Running on Windows? + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Running under ConEmu? + var conEmu = Environment.GetEnvironmentVariable("ConEmuANSI"); + if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return Windows.SupportsAnsi(upgrade); + } + + // Check if the terminal is of type ANSI/VT100/xterm compatible. + var term = Environment.GetEnvironmentVariable("TERM"); + if (!string.IsNullOrWhiteSpace(term)) + { + if (Regexes.Any(regex => regex.IsMatch(term))) + { + return true; + } + } + + return false; + } + + [SuppressMessage("Design", "CA1060:Move pinvokes to native methods class")] + internal static class Windows + { + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")] + private const int STD_OUTPUT_HANDLE = -11; + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")] + private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore")] + private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; + + [DllImport("kernel32.dll")] + private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll")] + public static extern uint GetLastError(); + + [SuppressMessage("Design", "CA1031:Do not catch general exception types")] + public static bool SupportsAnsi(bool upgrade) + { + try + { + var @out = GetStdHandle(STD_OUTPUT_HANDLE); + if (!GetConsoleMode(@out, out uint mode)) + { + // Could not get console mode. + return false; + } + + if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) + { + if (!upgrade) + { + return false; + } + + // Try enable ANSI support. + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + if (!SetConsoleMode(@out, mode)) + { + // Enabling failed. + return false; + } + } + + return true; + } + catch + { + // All we know here is that we don't support ANSI. + return false; + } + } + } + } +} diff --git a/src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs b/src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs new file mode 100644 index 0000000..59cf885 --- /dev/null +++ b/src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace Spectre.Console.Internal +{ + internal static class AnsiStyleBuilder + { + // TODO: Rewrite this to not yield + public static IEnumerable GetAnsiCodes(Styles style) + { + if ((style & Styles.Bold) != 0) + { + yield return 1; + } + + if ((style & Styles.Dim) != 0) + { + yield return 2; + } + + if ((style & Styles.Italic) != 0) + { + yield return 3; + } + + if ((style & Styles.Underline) != 0) + { + yield return 4; + } + + if ((style & Styles.SlowBlink) != 0) + { + yield return 5; + } + + if ((style & Styles.RapidBlink) != 0) + { + yield return 6; + } + + if ((style & Styles.Invert) != 0) + { + yield return 7; + } + + if ((style & Styles.Conceal) != 0) + { + yield return 8; + } + + if ((style & Styles.Strikethrough) != 0) + { + yield return 9; + } + } + } +} diff --git a/src/Spectre.Console/Internal/Colors/ColorPalette.cs b/src/Spectre.Console/Internal/Colors/ColorPalette.cs new file mode 100644 index 0000000..91a644f --- /dev/null +++ b/src/Spectre.Console/Internal/Colors/ColorPalette.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Spectre.Console.Internal +{ + internal static class ColorPalette + { + public static IReadOnlyList Legacy { get; } + public static IReadOnlyList Standard { get; } + public static IReadOnlyList EightBit { get; } + + static ColorPalette() + { + Legacy = new List + { + Color.Black, Color.Maroon, Color.Green, Color.Olive, + Color.Navy, Color.Purple, Color.Teal, Color.Silver, + }; + + Standard = new List(Legacy) + { + Color.Grey, Color.Red, Color.Lime, Color.Yellow, + Color.Blue, Color.Fuchsia, Color.Aqua, Color.White, + }; + + EightBit = new List(Standard) + { + Color.Grey0, Color.NavyBlue, Color.DarkBlue, Color.Blue3, + Color.Blue3_1, Color.Blue1, Color.DarkGreen, Color.DeepSkyBlue4, + Color.DeepSkyBlue4_1, Color.DeepSkyBlue4_2, Color.DodgerBlue3, Color.DodgerBlue2, + Color.Green4, Color.SpringGreen4, Color.Turquoise4, Color.DeepSkyBlue3, + Color.DeepSkyBlue3_1, Color.DodgerBlue1, Color.Green3, Color.SpringGreen3, + Color.DarkCyan, Color.LightSeaGreen, Color.DeepSkyBlue2, Color.DeepSkyBlue1, + Color.Green3_1, Color.SpringGreen3_1, Color.SpringGreen2, Color.Cyan3, + Color.DarkTurquoise, Color.Turquoise2, Color.Green1, Color.SpringGreen2_1, + Color.SpringGreen1, Color.MediumSpringGreen, Color.Cyan2, Color.Cyan1, + Color.DarkRed, Color.DeepPink4, Color.Purple4, Color.Purple4_1, + Color.Purple3, Color.BlueViolet, Color.Orange4, Color.Grey37, + Color.MediumPurple4, Color.SlateBlue3, Color.SlateBlue3_1, Color.RoyalBlue1, + Color.Chartreuse4, Color.DarkSeaGreen4, Color.PaleTurquoise4, Color.SteelBlue, + Color.SteelBlue3, Color.CornflowerBlue, Color.Chartreuse3, Color.DarkSeaGreen4_1, + Color.CadetBlue, Color.CadetBlue_1, Color.SkyBlue3, Color.SteelBlue1, + Color.Chartreuse3_1, Color.PaleGreen3, Color.SeaGreen3, Color.Aquamarine3, + Color.MediumTurquoise, Color.SteelBlue1_1, Color.Chartreuse2, Color.SeaGreen2, + Color.SeaGreen1, Color.SeaGreen1_1, Color.Aquamarine1, Color.DarkSlateGray2, + Color.DarkRed_1, Color.DeepPink4_1, Color.DarkMagenta, Color.DarkMagenta_1, + Color.DarkViolet, Color.Purple_1, Color.Orange4_1, Color.LightPink4, + Color.Plum4, Color.MediumPurple3, Color.MediumPurple3_1, Color.SlateBlue1, + Color.Yellow4, Color.Wheat4, Color.Grey53, Color.LightSlateGrey, + Color.MediumPurple, Color.LightSlateBlue, Color.Yellow4_1, Color.DarkOliveGreen3, + Color.DarkSeaGreen, Color.LightSkyBlue3, Color.LightSkyBlue3_1, Color.SkyBlue2, + Color.Chartreuse2_1, Color.DarkOliveGreen3_1, Color.PaleGreen3_1, Color.DarkSeaGreen3, + Color.DarkSlateGray3, Color.SkyBlue1, Color.Chartreuse1, Color.LightGreen, + Color.LightGreen_1, Color.PaleGreen1, Color.Aquamarine1_1, Color.DarkSlateGray1, + Color.Red3, Color.DeepPink4_2, Color.MediumVioletRed, Color.Magenta3, + Color.DarkViolet_1, Color.Purple_2, Color.DarkOrange3, Color.IndianRed, + Color.HotPink3, Color.MediumOrchid3, Color.MediumOrchid, Color.MediumPurple2, + Color.DarkGoldenrod, Color.LightSalmon3, Color.RosyBrown, Color.Grey63, + Color.MediumPurple2_1, Color.MediumPurple1, Color.Gold3, Color.DarkKhaki, + Color.NavajoWhite3, Color.Grey69, Color.LightSteelBlue3, Color.LightSteelBlue, + Color.Yellow3, Color.DarkOliveGreen3_2, Color.DarkSeaGreen3_1, Color.DarkSeaGreen2, + Color.LightCyan3, Color.LightSkyBlue1, Color.GreenYellow, Color.DarkOliveGreen2, + Color.PaleGreen1_1, Color.DarkSeaGreen2_1, Color.DarkSeaGreen1, Color.PaleTurquoise1, + Color.Red3_1, Color.DeepPink3, Color.DeepPink3_1, Color.Magenta3_1, + Color.Magenta3_2, Color.Magenta2, Color.DarkOrange3_1, Color.IndianRed_1, + Color.HotPink3_1, Color.HotPink2, Color.Orchid, Color.MediumOrchid1, + Color.Orange3, Color.LightSalmon3_1, Color.LightPink3, Color.Pink3, + Color.Plum3, Color.Violet, Color.Gold3_1, Color.LightGoldenrod3, + Color.Tan, Color.MistyRose3, Color.Thistle3, Color.Plum2, + Color.Yellow3_1, Color.Khaki3, Color.LightGoldenrod2, Color.LightYellow3, + Color.Grey84, Color.LightSteelBlue1, Color.Yellow2, Color.DarkOliveGreen1, + Color.DarkOliveGreen1_1, Color.DarkSeaGreen1_1, Color.Honeydew2, Color.LightCyan1, + Color.Red1, Color.DeepPink2, Color.DeepPink1, Color.DeepPink1_1, + Color.Magenta2_1, Color.Magenta1, Color.OrangeRed1, Color.IndianRed1, + Color.IndianRed1_1, Color.HotPink, Color.HotPink_1, Color.MediumOrchid1_1, + Color.DarkOrange, Color.Salmon1, Color.LightCoral, Color.PaleVioletRed1, + Color.Orchid2, Color.Orchid1, Color.Orange1, Color.SandyBrown, + Color.LightSalmon1, Color.LightPink1, Color.Pink1, Color.Plum1, + Color.Gold1, Color.LightGoldenrod2_1, Color.LightGoldenrod2_2, Color.NavajoWhite1, + Color.MistyRose1, Color.Thistle1, Color.Yellow1, Color.LightGoldenrod1, + Color.Khaki1, Color.Wheat1, Color.Cornsilk1, Color.Grey100, + Color.Grey3, Color.Grey7, Color.Grey11, Color.Grey15, + Color.Grey19, Color.Grey23, Color.Grey27, Color.Grey30, + Color.Grey35, Color.Grey39, Color.Grey42, Color.Grey46, + Color.Grey50, Color.Grey54, Color.Grey58, Color.Grey62, + Color.Grey66, Color.Grey70, Color.Grey74, Color.Grey78, + Color.Grey82, Color.Grey85, Color.Grey89, Color.Grey93, + }; + } + + internal static Color ExactOrClosest(ColorSystem system, Color color) + { + var exact = Exact(system, color); + if (exact != null) + { + return exact.Value; + } + + return Closest(system, color); + } + + private static Color? Exact(ColorSystem system, Color color) + { + if (system == ColorSystem.TrueColor) + { + return color; + } + + var palette = system switch + { + ColorSystem.Legacy => Legacy, + ColorSystem.Standard => Standard, + ColorSystem.EightBit => EightBit, + _ => throw new NotSupportedException(), + }; + + return palette + .Where(c => c.Equals(color)) + .Cast() + .FirstOrDefault(); + } + + private static Color Closest(ColorSystem system, Color color) + { + if (system == ColorSystem.TrueColor) + { + return color; + } + + var palette = system switch + { + ColorSystem.Legacy => Legacy, + ColorSystem.Standard => Standard, + ColorSystem.EightBit => EightBit, + _ => throw new NotSupportedException(), + }; + + // https://stackoverflow.com/a/9085524 + static double Distance(Color first, Color second) + { + var rmean = ((float)first.R + second.R) / 2; + var r = first.R - second.R; + var g = first.G - second.G; + var b = first.B - second.B; + return Math.Sqrt( + ((int)((512 + rmean) * r * r) >> 8) + + (4 * g * g) + + ((int)((767 - rmean) * b * b) >> 8)); + } + + return Enumerable.Range(0, int.MaxValue) + .Zip(palette, (id, other) => (Distance: Distance(other, color), Id: id, Color: other)) + .OrderBy(x => x.Distance) + .FirstOrDefault().Color; + } + } +} diff --git a/src/Spectre.Console/Internal/Colors/ColorSystemDetector.cs b/src/Spectre.Console/Internal/Colors/ColorSystemDetector.cs new file mode 100644 index 0000000..e6ca217 --- /dev/null +++ b/src/Spectre.Console/Internal/Colors/ColorSystemDetector.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Spectre.Console.Internal +{ + internal static class ColorSystemDetector + { + // Adapted from https://github.com/willmcgugan/rich/blob/f0c29052c22d1e49579956a9207324d9072beed7/rich/console.py#L391 + // which I think is battletested enough to trust. + public static ColorSystem Detect(bool supportsAnsi) + { + if (Environment.GetEnvironmentVariables().Contains("NO_COLOR")) + { + // No colors supported + return ColorSystem.NoColors; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (supportsAnsi) + { + var regex = new Regex("^Microsoft Windows (?'major'[0-9]*).(?'minor'[0-9]*).(?'build'[0-9]*)$"); + var match = regex.Match(RuntimeInformation.OSDescription); + if (match.Success && int.TryParse(match.Groups["major"].Value, out var major)) + { + if (major > 10) + { + // Future Patrik will thank me. + return ColorSystem.TrueColor; + } + + if (major == 10 && int.TryParse(match.Groups["build"].Value, out var build) && build >= 15063) + { + return ColorSystem.TrueColor; + } + } + } + } + else + { + var colorTerm = Environment.GetEnvironmentVariable("COLORTERM"); + if (!string.IsNullOrWhiteSpace(colorTerm)) + { + if (colorTerm.Equals("truecolor", StringComparison.OrdinalIgnoreCase) || + colorTerm.Equals("24bit", StringComparison.OrdinalIgnoreCase)) + { + return ColorSystem.TrueColor; + } + } + } + + return ColorSystem.EightBit; + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Composer.cs b/src/Spectre.Console/Internal/Composition/Composer.cs new file mode 100644 index 0000000..a63d4b6 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Composer.cs @@ -0,0 +1,73 @@ +using System; + +namespace Spectre.Console.Internal +{ + internal sealed class Composer : IRenderable + { + private readonly BlockElement _root; + + /// + public int Length => _root.Length; + + public Composer() + { + _root = new BlockElement(); + } + + public static Composer New() + { + return new Composer(); + } + + public Composer Text(string text) + { + _root.Append(new TextElement(text)); + return this; + } + + public Composer Foreground(Color color, Action action) + { + if (action is null) + { + return this; + } + + var content = new Composer(); + action(content); + _root.Append(new ForegroundElement(color, content)); + return this; + } + + public Composer Background(Color color, Action action) + { + if (action is null) + { + return this; + } + + var content = new Composer(); + action(content); + _root.Append(new BackgroundElement(color, content)); + return this; + } + + public Composer Style(Styles style, Action action) + { + if (action is null) + { + return this; + } + + var content = new Composer(); + action(content); + _root.Append(new StyleElement(style, content)); + return this; + } + + /// + public void Render(IAnsiConsole renderer) + { + _root.Render(renderer); + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/BackgroundElement.cs b/src/Spectre.Console/Internal/Composition/Elements/BackgroundElement.cs new file mode 100644 index 0000000..764ba29 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/BackgroundElement.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class BackgroundElement : IRenderable + { + private readonly Color _color; + private readonly IRenderable _element; + + /// + public int Length => _element.Length; + + public BackgroundElement(Color color, IRenderable element) + { + _color = color; + _element = element ?? throw new ArgumentNullException(nameof(element)); + } + + /// + public void Render(IAnsiConsole renderer) + { + if (renderer is null) + { + throw new ArgumentNullException(nameof(renderer)); + } + + using (renderer.PushColor(_color, foreground: false)) + { + _element.Render(renderer); + } + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/BlockElement.cs b/src/Spectre.Console/Internal/Composition/Elements/BlockElement.cs new file mode 100644 index 0000000..f38c3e5 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/BlockElement.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class BlockElement : IRenderable + { + private readonly List _elements; + + /// + public int Length { get; private set; } + + public IReadOnlyList Elements => _elements; + + public BlockElement() + { + _elements = new List(); + } + + public BlockElement Append(IRenderable element) + { + if (element != null) + { + _elements.Add(element); + Length += element.Length; + } + + return this; + } + + /// + public void Render(IAnsiConsole renderer) + { + foreach (var element in _elements) + { + element.Render(renderer); + } + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/ForegroundElement.cs b/src/Spectre.Console/Internal/Composition/Elements/ForegroundElement.cs new file mode 100644 index 0000000..70080eb --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/ForegroundElement.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class ForegroundElement : IRenderable + { + private readonly Color _color; + private readonly IRenderable _element; + + /// + public int Length => _element.Length; + + public ForegroundElement(Color color, IRenderable element) + { + _color = color; + _element = element ?? throw new ArgumentNullException(nameof(element)); + } + + /// + public void Render(IAnsiConsole renderer) + { + if (renderer is null) + { + throw new ArgumentNullException(nameof(renderer)); + } + + using (renderer.PushColor(_color, foreground: true)) + { + _element.Render(renderer); + } + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/LineBreakElement.cs b/src/Spectre.Console/Internal/Composition/Elements/LineBreakElement.cs new file mode 100644 index 0000000..a6b4ec9 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/LineBreakElement.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class LineBreakElement : IRenderable + { + /// + public int Length => 0; + + /// + public void Render(IAnsiConsole renderer) + { + renderer.Write(Environment.NewLine); + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/StyleElement.cs b/src/Spectre.Console/Internal/Composition/Elements/StyleElement.cs new file mode 100644 index 0000000..804f993 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/StyleElement.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class StyleElement : IRenderable + { + private readonly Styles _style; + private readonly IRenderable _element; + + /// + public int Length => _element.Length; + + public StyleElement(Styles style, IRenderable element) + { + _style = style; + _element = element ?? throw new ArgumentNullException(nameof(element)); + } + + /// + public void Render(IAnsiConsole renderer) + { + if (renderer is null) + { + throw new ArgumentNullException(nameof(renderer)); + } + + using (renderer.PushStyle(_style)) + { + _element.Render(renderer); + } + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/Elements/TextElement.cs b/src/Spectre.Console/Internal/Composition/Elements/TextElement.cs new file mode 100644 index 0000000..dbb0eeb --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/Elements/TextElement.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + [SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Not used (yet)")] + internal sealed class TextElement : IRenderable + { + private readonly string _text; + + /// + public int Length => _text.Length; + + public TextElement(string text) + { + _text = text ?? throw new System.ArgumentNullException(nameof(text)); + } + + /// + public void Render(IAnsiConsole renderer) + { + renderer.Write(_text); + } + } +} diff --git a/src/Spectre.Console/Internal/Composition/IRenderable.cs b/src/Spectre.Console/Internal/Composition/IRenderable.cs new file mode 100644 index 0000000..8f85b78 --- /dev/null +++ b/src/Spectre.Console/Internal/Composition/IRenderable.cs @@ -0,0 +1,19 @@ +namespace Spectre.Console.Internal +{ + /// + /// Represents something that can be rendered to a console. + /// + internal interface IRenderable + { + /// + /// Gets the length of the element. + /// + int Length { get; } + + /// + /// Renders the element using the specified renderer. + /// + /// The renderer to use. + void Render(IAnsiConsole console); + } +} diff --git a/src/Spectre.Console/Internal/ConsoleBuilder.cs b/src/Spectre.Console/Internal/ConsoleBuilder.cs new file mode 100644 index 0000000..2d21f98 --- /dev/null +++ b/src/Spectre.Console/Internal/ConsoleBuilder.cs @@ -0,0 +1,36 @@ +using System; +using Spectre.Console.Internal; + +namespace Spectre.Console +{ + internal static class ConsoleBuilder + { + public static IAnsiConsole Build(AnsiConsoleSettings settings) + { + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var buffer = settings.Out ?? System.Console.Out; + + var supportsAnsi = settings.Ansi == AnsiSupport.Detect + ? AnsiDetector.SupportsAnsi(true) + : settings.Ansi == AnsiSupport.Yes; + + var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect + ? ColorSystemDetector.Detect(supportsAnsi) + : (ColorSystem)settings.ColorSystem; + + if (supportsAnsi) + { + return new AnsiConsoleRenderer(buffer, colorSystem) + { + Style = Styles.None, + }; + } + + return new FallbackConsoleRenderer(buffer, colorSystem); + } + } +} diff --git a/src/Spectre.Console/Internal/Constants.cs b/src/Spectre.Console/Internal/Constants.cs new file mode 100644 index 0000000..76a2cf0 --- /dev/null +++ b/src/Spectre.Console/Internal/Constants.cs @@ -0,0 +1,8 @@ +namespace Spectre.Console.Internal +{ + internal static class Constants + { + public const int DefaultBufferWidth = 80; + public const int DefaultBufferHeight = 9001; + } +} diff --git a/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs b/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs new file mode 100644 index 0000000..ae01a5d --- /dev/null +++ b/src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs @@ -0,0 +1,101 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Spectre.Console.Internal +{ + internal static class ConsoleExtensions + { + public static IDisposable PushColor(this IAnsiConsole console, Color color, bool foreground) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + var current = console.Foreground; + console.SetColor(color, foreground); + return new ColorScope(console, current, foreground); + } + + public static IDisposable PushStyle(this IAnsiConsole console, Styles style) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + var current = console.Style; + console.Style = style; + return new StyleScope(console, current); + } + + public static void SetColor(this IAnsiConsole console, Color color, bool foreground) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + if (foreground) + { + console.Foreground = color; + } + else + { + console.Background = color; + } + } + } + + internal sealed class ColorScope : IDisposable + { + private readonly IAnsiConsole _console; + private readonly Color _color; + private readonly bool _foreground; + + public ColorScope(IAnsiConsole console, Color color, bool foreground) + { + _console = console ?? throw new ArgumentNullException(nameof(console)); + _color = color; + _foreground = foreground; + } + + [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")] + [SuppressMessage("Performance", "CA1821:Remove empty Finalizers")] + ~ColorScope() + { + throw new InvalidOperationException("Color scope was not disposed."); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + _console.SetColor(_color, _foreground); + } + } + + internal sealed class StyleScope : IDisposable + { + private readonly IAnsiConsole _console; + private readonly Styles _style; + + public StyleScope(IAnsiConsole console, Styles color) + { + _console = console ?? throw new ArgumentNullException(nameof(console)); + _style = color; + } + + [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")] + [SuppressMessage("Performance", "CA1821:Remove empty Finalizers")] + ~StyleScope() + { + throw new InvalidOperationException("Style scope was not disposed."); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + _console.Style = _style; + } + } +} diff --git a/src/Spectre.Console/Internal/Extensions/TextWriterExtensions.cs b/src/Spectre.Console/Internal/Extensions/TextWriterExtensions.cs new file mode 100644 index 0000000..8589a60 --- /dev/null +++ b/src/Spectre.Console/Internal/Extensions/TextWriterExtensions.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace Spectre.Console.Internal +{ + internal static class TextWriterExtensions + { + [SuppressMessage("Design", "CA1031:Do not catch general exception types")] + public static bool IsStandardOut(this TextWriter writer) + { + try + { + return writer == System.Console.Out; + } + catch + { + return false; + } + } + } +} diff --git a/src/Spectre.Console/Internal/Rendering/AnsiConsoleRenderer.cs b/src/Spectre.Console/Internal/Rendering/AnsiConsoleRenderer.cs new file mode 100644 index 0000000..0c9a11d --- /dev/null +++ b/src/Spectre.Console/Internal/Rendering/AnsiConsoleRenderer.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; + +namespace Spectre.Console.Internal +{ + internal sealed class AnsiConsoleRenderer : IAnsiConsole + { + private readonly TextWriter _out; + private readonly ColorSystem _system; + + public AnsiConsoleCapabilities Capabilities { get; } + public Styles Style { get; set; } + public Color Foreground { get; set; } + public Color Background { get; set; } + + public int Width + { + get + { + if (_out.IsStandardOut()) + { + return System.Console.BufferWidth; + } + + return Constants.DefaultBufferWidth; + } + } + + public int Height + { + get + { + if (_out.IsStandardOut()) + { + return System.Console.BufferHeight; + } + + return Constants.DefaultBufferHeight; + } + } + + public AnsiConsoleRenderer(TextWriter @out, ColorSystem system) + { + _out = @out ?? throw new ArgumentNullException(nameof(@out)); + _system = system; + + Capabilities = new AnsiConsoleCapabilities(true, system); + 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; + } + } + + public void Write(string text) + { + if (string.IsNullOrEmpty(text)) + { + return; + } + + _out.Write(AnsiBuilder.GetAnsi( + _system, + text, + Style, + Foreground, + Background)); + } + + public void WriteLine(string text) + { + if (text == null) + { + _out.WriteLine(); + } + else + { + _out.WriteLine( + AnsiBuilder.GetAnsi( + _system, + text, + Style, + Foreground, + Background)); + } + } + } +} \ No newline at end of file diff --git a/src/Spectre.Console/Internal/Rendering/FallbackConsoleRenderer.cs b/src/Spectre.Console/Internal/Rendering/FallbackConsoleRenderer.cs new file mode 100644 index 0000000..d001e00 --- /dev/null +++ b/src/Spectre.Console/Internal/Rendering/FallbackConsoleRenderer.cs @@ -0,0 +1,121 @@ +using System; +using System.IO; + +namespace Spectre.Console.Internal +{ + internal sealed class FallbackConsoleRenderer : IAnsiConsole + { + private readonly ConsoleColor _defaultForeground; + private readonly ConsoleColor _defaultBackground; + + private readonly TextWriter _out; + private readonly ColorSystem _system; + private ConsoleColor _foreground; + private ConsoleColor _background; + + public AnsiConsoleCapabilities Capabilities { get; } + + public int Width + { + get + { + if (_out.IsStandardOut()) + { + return System.Console.BufferWidth; + } + + return Constants.DefaultBufferWidth; + } + } + + public int Height + { + get + { + if (_out.IsStandardOut()) + { + return System.Console.BufferHeight; + } + + return Constants.DefaultBufferHeight; + } + } + + public Styles Style { get; set; } + + public Color Foreground + { + get => _foreground; + set + { + _foreground = Color.ToConsoleColor(value); + if (_system != ColorSystem.NoColors && _out.IsStandardOut()) + { + if ((int)_foreground == -1) + { + _foreground = _defaultForeground; + } + + System.Console.ForegroundColor = _foreground; + } + } + } + + public Color Background + { + get => _background; + set + { + _background = Color.ToConsoleColor(value); + if (_system != ColorSystem.NoColors && _out.IsStandardOut()) + { + if ((int)_background == -1) + { + _background = _defaultBackground; + } + + if (_system != ColorSystem.NoColors) + { + System.Console.BackgroundColor = _background; + } + } + } + } + + public FallbackConsoleRenderer(TextWriter @out, ColorSystem system) + { + _out = @out; + _system = system; + + Capabilities = new AnsiConsoleCapabilities(false, _system); + + if (_out.IsStandardOut()) + { + _defaultForeground = System.Console.ForegroundColor; + _defaultBackground = System.Console.BackgroundColor; + } + else + { + _defaultForeground = ConsoleColor.Gray; + _defaultBackground = ConsoleColor.Black; + } + } + + public void Write(string text) + { + _out.Write(text); + } + + public void WriteLine(string text) + { + if (text == null) + { + _out.WriteLine(); + } + else + { + _out.WriteLine(text); + } + } + } +} diff --git a/src/Spectre.Console/Spectre.Console.csproj b/src/Spectre.Console/Spectre.Console.csproj new file mode 100644 index 0000000..3ba142c --- /dev/null +++ b/src/Spectre.Console/Spectre.Console.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + + + + + + + + + Color.cs + + + + + + + + diff --git a/src/Spectre.Console/Styles.cs b/src/Spectre.Console/Styles.cs new file mode 100644 index 0000000..cb3fedd --- /dev/null +++ b/src/Spectre.Console/Styles.cs @@ -0,0 +1,75 @@ +using System; + +namespace Spectre.Console +{ + /// + /// Represents a style. + /// + /// + /// Support for different styles is up to the terminal. + /// + [Flags] + public enum Styles + { + /// + /// No style. + /// + None = 0, + + /// + /// Bold text. + /// Not supported in every environment. + /// + Bold = 1 << 0, + + /// + /// Dim or faint text. + /// Not supported in every environment. + /// + Dim = 1 << 1, + + /// + /// Italic text. + /// Not supported in every environment. + /// + Italic = 1 << 2, + + /// + /// Underlined text. + /// Not supported in every environment. + /// + Underline = 1 << 3, + + /// + /// Swaps the foreground and background colors. + /// Not supported in every environment. + /// + Invert = 1 << 4, + + /// + /// Hides the text. + /// Not supported in every environment. + /// + Conceal = 1 << 5, + + /// + /// Makes text blink. + /// Normally less than 150 blinks per minute. + /// Not supported in every environment. + /// + SlowBlink = 1 << 6, + + /// + /// Makes text blink. + /// Normally more than 150 blinks per minute. + /// Not supported in every environment. + /// + RapidBlink = 1 << 7, + + /// + /// Shows text with a horizontal line through the center. + /// Not supported in every environment. + /// + Strikethrough = 1 << 8, + } +} diff --git a/src/stylecop.json b/src/stylecop.json new file mode 100644 index 0000000..4a7219f --- /dev/null +++ b/src/stylecop.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "documentExposedElements": true, + "documentInternalElements": false, + "documentPrivateElements": false, + "documentPrivateFields": false + }, + "layoutRules": { + "newlineAtEndOfFile": "allow", + "allowConsecutiveUsings": true + }, + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace", + "systemUsingDirectivesFirst": true, + "elementOrder": [ + "kind", + "accessibility", + "constant", + "static", + "readonly" + ] + } + } +} \ No newline at end of file