Initial commit

This commit is contained in:
Patrik Svensson 2020-07-21 12:03:41 +02:00
commit 334dcddc1a
57 changed files with 4685 additions and 0 deletions

178
.editorconfig Normal file
View File

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

44
.github/workflows/ci.yaml vendored Normal file
View File

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

87
.github/workflows/publish.yaml vendored Normal file
View File

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

88
.gitignore vendored Normal file
View File

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

46
CODE_OF_CONDUCT.md Normal file
View File

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

21
LICENSE.md Normal file
View File

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

71
README.md Normal file
View File

@ -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`._

113
build.cake Normal file
View File

@ -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<string>("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<string>("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)

18
dotnet-tools.json Normal file
View File

@ -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"
]
}
}
}

7
global.json Normal file
View File

@ -0,0 +1,7 @@
{
"projects": [ "src" ],
"sdk": {
"version": "3.1.301",
"rollForward": "latestMajor"
}
}

77
src/.editorconfig Normal file
View File

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

47
src/Directory.Build.props Normal file
View File

@ -0,0 +1,47 @@
<Project>
<PropertyGroup Label="Settings">
<Deterministic>true</Deterministic>
<LangVersion>8.0</LangVersion>
<IncludeSymbols>true</IncludeSymbols>
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Label="Deterministic Build" Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Label="Package Information">
<Description>A library that makes it easier to create beautiful console applications.</Description>
<Company>Spectre Systems AB</Company>
<Copyright>Spectre Systems AB</Copyright>
<Authors>Patrik Svensson</Authors>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/spectresystems/spectre.console</RepositoryUrl>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageProjectUrl>https://github.com/spectresystems/spectre.console</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReleaseNotes>https://github.com/spectresystems/spectre.console/releases</PackageReleaseNotes>
</PropertyGroup>
<PropertyGroup Label="Source Link">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.113">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="2.3.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
<Project>
<Target Name="Versioning" BeforeTargets="MinVer">
<PropertyGroup Label="Build">
<MinVerDefaultPreReleasePhase>preview</MinVerDefaultPreReleasePhase>
<MinVerVerbosity>normal</MinVerVerbosity>
</PropertyGroup>
</Target>
</Project>

8
src/Sample/.editorconfig Normal file
View File

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

53
src/Sample/Program.cs Normal file
View File

@ -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();
}
}
}

14
src/Sample/Sample.csproj Normal file
View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@ -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();
}
}
}

View File

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

View File

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

View File

@ -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");
}
}
}

View File

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

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

73
src/Spectre.Console.sln Normal file
View File

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

View File

@ -0,0 +1,135 @@
using System;
using Spectre.Console.Internal;
namespace Spectre.Console
{
/// <summary>
/// A console capable of writing ANSI escape sequences.
/// </summary>
public static class AnsiConsole
{
private static readonly Lazy<IAnsiConsole> _console = new Lazy<IAnsiConsole>(() =>
{
return Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.Detect,
ColorSystem = ColorSystemSupport.Detect,
Out = System.Console.Out,
});
});
/// <summary>
/// Gets the current renderer.
/// </summary>
public static IAnsiConsole Console => _console.Value;
/// <summary>
/// Gets the console's capabilities.
/// </summary>
public static AnsiConsoleCapabilities Capabilities => Console.Capabilities;
/// <summary>
/// Gets the buffer width of the console.
/// </summary>
public static int Width
{
get => Console.Width;
}
/// <summary>
/// Gets the buffer height of the console.
/// </summary>
public static int Height
{
get => Console.Height;
}
/// <summary>
/// Gets or sets the foreground color.
/// </summary>
public static Color Foreground
{
get => Console.Foreground;
set => Console.SetColor(value, true);
}
/// <summary>
/// Gets or sets the background color.
/// </summary>
public static Color Background
{
get => Console.Background;
set => Console.SetColor(value, false);
}
/// <summary>
/// Gets or sets the style.
/// </summary>
public static Styles Style
{
get => Console.Style;
set => Console.Style = value;
}
/// <summary>
/// Creates a new <see cref="IAnsiConsole"/> instance
/// from the provided settings.
/// </summary>
/// <param name="settings">The settings to use.</param>
/// <returns>An <see cref="IAnsiConsole"/> instance.</returns>
public static IAnsiConsole Create(AnsiConsoleSettings settings)
{
return ConsoleBuilder.Build(settings);
}
/// <summary>
/// Resets colors and styles to the default ones.
/// </summary>
public static void Reset()
{
Console.Reset();
}
/// <summary>
/// Resets the current style back to the default one.
/// </summary>
public static void ResetStyle()
{
Console.ResetStyle();
}
/// <summary>
/// Resets the foreground and background colors to the default ones.
/// </summary>
public static void ResetColors()
{
Console.ResetColors();
}
/// <summary>
/// Writes the content to the console.
/// </summary>
/// <param name="content">The content to write.</param>
public static void Write(string content)
{
Console.Write(content);
}
/// <summary>
/// Writes an empty line to the console.
/// </summary>
public static void WriteLine()
{
Console.WriteLine();
}
/// <summary>
/// Writes a line to the console.
/// </summary>
/// <param name="content">The content to write.</param>
public static void WriteLine(string content)
{
Console.WriteLine(content);
}
}
}

View File

@ -0,0 +1,42 @@
namespace Spectre.Console
{
/// <summary>
/// Represents console capabilities.
/// </summary>
public sealed class AnsiConsoleCapabilities
{
/// <summary>
/// Gets a value indicating whether or not
/// the console supports Ansi.
/// </summary>
public bool SupportsAnsi { get; }
/// <summary>
/// Gets the color system.
/// </summary>
public ColorSystem ColorSystem { get; }
internal AnsiConsoleCapabilities(bool supportsAnsi, ColorSystem colorSystem)
{
SupportsAnsi = supportsAnsi;
ColorSystem = colorSystem;
}
/// <inheritdoc/>
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})";
}
}
}

View File

@ -0,0 +1,27 @@
using System.IO;
namespace Spectre.Console
{
/// <summary>
/// Settings used by <see cref="ConsoleBuilder"/>
/// when building a <see cref="IAnsiConsole"/>.
/// </summary>
public sealed class AnsiConsoleSettings
{
/// <summary>
/// Gets or sets a value indicating whether or
/// not ANSI escape sequences are supported.
/// </summary>
public AnsiSupport Ansi { get; set; }
/// <summary>
/// Gets or sets the color system to use.
/// </summary>
public ColorSystemSupport ColorSystem { get; set; }
/// <summary>
/// Gets or sets the out buffer.
/// </summary>
public TextWriter Out { get; set; }
}
}

View File

@ -0,0 +1,24 @@
namespace Spectre.Console
{
/// <summary>
/// Determines ANSI escape sequence support.
/// </summary>
public enum AnsiSupport
{
/// <summary>
/// ANSI escape sequence support should
/// be detected by the system.
/// </summary>
Detect = 0,
/// <summary>
/// ANSI escape sequences are supported.
/// </summary>
Yes = 1,
/// <summary>
/// ANSI escape sequences are not supported.
/// </summary>
No = 2,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
using System;
using System.Diagnostics;
using System.Linq;
using Spectre.Console.Internal;
namespace Spectre.Console
{
/// <summary>
/// Represents a color.
/// </summary>
public partial struct Color : IEquatable<Color>
{
/// <summary>
/// Gets the default color.
/// </summary>
public static Color Default { get; }
static Color()
{
Default = new Color(0, "default", 0, 0, 0, true);
}
/// <summary>
/// Gets the red component.
/// </summary>
public byte R { get; }
/// <summary>
/// Gets the green component.
/// </summary>
public byte G { get; }
/// <summary>
/// Gets the blue component.
/// </summary>
public byte B { get; }
/// <summary>
/// Gets the name of the color, if any.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the number of the color, if any.
/// </summary>
internal byte? Number { get; }
/// <summary>
/// Gets a value indicating whether or not this is the default color.
/// </summary>
internal bool IsDefault { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="red">The red component.</param>
/// <param name="green">The green component.</param>
/// <param name="blue">The blue component.</param>
public Color(byte red, byte green, byte blue)
{
R = red;
G = green;
B = blue;
IsDefault = false;
Name = null;
Number = null;
}
/// <inheritdoc/>
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;
}
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Color color && Equals(color);
}
/// <inheritdoc/>
public bool Equals(Color other)
{
return Number == other.Number || (R == other.R && G == other.G && B == other.B);
}
/// <summary>
/// Checks if two <see cref="Color"/> instances are equal.
/// </summary>
/// <param name="left">The first color instance to compare.</param>
/// <param name="right">The second color instance to compare.</param>
/// <returns><c>true</c> if the two colors are equal, otherwise <c>false</c>.</returns>
public static bool operator ==(Color left, Color right)
{
return left.Equals(right);
}
/// <summary>
/// Checks if two <see cref="Color"/> instances are not equal.
/// </summary>
/// <param name="left">The first color instance to compare.</param>
/// <param name="right">The second color instance to compare.</param>
/// <returns><c>true</c> if the two colors are not equal, otherwise <c>false</c>.</returns>
public static bool operator !=(Color left, Color right)
{
return !(left == right);
}
/// <summary>
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
/// </summary>
/// <param name="color">The color to convert.</param>
public static implicit operator Color(ConsoleColor color)
{
return FromConsoleColor(color);
}
/// <summary>
/// Convers a color number into a <see cref="Color"/>.
/// </summary>
/// <param name="number">The color number.</param>
/// <returns>The color representing the specified color number.</returns>
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);
}
/// <summary>
/// Convers a <see cref="Color"/> to a <see cref="ConsoleColor"/>.
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>A <see cref="ConsoleColor"/> representing the <see cref="Color"/>.</returns>
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."),
};
}
/// <summary>
/// Convers a <see cref="ConsoleColor"/> to a <see cref="Color"/>.
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>A <see cref="Color"/> representing the <see cref="ConsoleColor"/>.</returns>
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,
};
}
}
}

View File

@ -0,0 +1,33 @@
namespace Spectre.Console
{
/// <summary>
/// Represents a color system.
/// </summary>
public enum ColorSystem
{
/// <summary>
/// No colors.
/// </summary>
NoColors = 0,
/// <summary>
/// Legacy, 3-bit mode.
/// </summary>
Legacy = 1,
/// <summary>
/// Standard, 4-bit mode.
/// </summary>
Standard = 2,
/// <summary>
/// 8-bit mode.
/// </summary>
EightBit = 3,
/// <summary>
/// 24-bit mode.
/// </summary>
TrueColor = 4,
}
}

View File

@ -0,0 +1,38 @@
namespace Spectre.Console
{
/// <summary>
/// Determines what color system should be used.
/// </summary>
public enum ColorSystemSupport
{
/// <summary>
/// Try to detect the color system.
/// </summary>
Detect = -1,
/// <summary>
/// No colors.
/// </summary>
NoColors = 0,
/// <summary>
/// Legacy, 3-bit mode.
/// </summary>
Legacy = 1,
/// <summary>
/// Standard, 4-bit mode.
/// </summary>
Standard = 2,
/// <summary>
/// 8-bit mode.
/// </summary>
EightBit = 3,
/// <summary>
/// 24-bit mode.
/// </summary>
TrueColor = 4,
}
}

View File

@ -0,0 +1,83 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IAnsiConsole"/>.
/// </summary>
public static class ConsoleExtensions
{
/// <summary>
/// Resets both colors and style for the console.
/// </summary>
/// <param name="console">The console to reset.</param>
public static void Reset(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.ResetColors();
console.ResetStyle();
}
/// <summary>
/// Resets the current style back to the default one.
/// </summary>
/// <param name="console">The console to reset the style for.</param>
public static void ResetStyle(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Style = Styles.None;
}
/// <summary>
/// Resets the foreground and background colors to the default ones.
/// </summary>
/// <param name="console">The console to reset colors for.</param>
public static void ResetColors(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.Foreground = Color.Default;
console.Background = Color.Default;
}
/// <summary>
/// Writes an empty line to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
public static void WriteLine(this IAnsiConsole console)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(null);
}
/// <summary>
/// Writes a line to the console.
/// </summary>
/// <param name="console">The console to write to.</param>
/// <param name="content">The content to write.</param>
public static void WriteLine(this IAnsiConsole console, string content)
{
if (console is null)
{
throw new ArgumentNullException(nameof(console));
}
console.WriteLine(content);
}
}
}

View File

@ -0,0 +1,52 @@
namespace Spectre.Console
{
/// <summary>
/// Represents a console.
/// </summary>
public interface IAnsiConsole
{
/// <summary>
/// Gets the console's capabilities.
/// </summary>
public AnsiConsoleCapabilities Capabilities { get; }
/// <summary>
/// Gets the buffer width of the console.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the buffer height of the console.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets or sets the current style.
/// </summary>
Styles Style { get; set; }
/// <summary>
/// Gets or sets the current foreground.
/// </summary>
Color Foreground { get; set; }
/// <summary>
/// Gets or sets the current background.
/// </summary>
Color Background { get; set; }
/// <summary>
/// Writes a string followed by a line terminator to the console.
/// </summary>
/// <param name="text">The string to write.</param>
void Write(string text);
/// <summary>
/// Writes a string followed by a line terminator to the console.
/// </summary>
/// <param name="text">
/// The string to write. If value is null, only the line terminator is written.
/// </param>
void WriteLine(string text);
}
}

View File

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

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Spectre.Console.Internal
{
internal static class AnsiColorBuilder
{
public static IEnumerable<byte> GetAnsiCodes(ColorSystem system, Color color, bool foreground)
{
return system switch
{
ColorSystem.NoColors => Array.Empty<byte>(), // 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<byte> 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<byte> 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<byte> 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<byte> 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 };
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Spectre.Console.Internal
{
internal static class ColorPalette
{
public static IReadOnlyList<Color> Legacy { get; }
public static IReadOnlyList<Color> Standard { get; }
public static IReadOnlyList<Color> EightBit { get; }
static ColorPalette()
{
Legacy = new List<Color>
{
Color.Black, Color.Maroon, Color.Green, Color.Olive,
Color.Navy, Color.Purple, Color.Teal, Color.Silver,
};
Standard = new List<Color>(Legacy)
{
Color.Grey, Color.Red, Color.Lime, Color.Yellow,
Color.Blue, Color.Fuchsia, Color.Aqua, Color.White,
};
EightBit = new List<Color>(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<Color?>()
.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;
}
}
}

View File

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

View File

@ -0,0 +1,73 @@
using System;
namespace Spectre.Console.Internal
{
internal sealed class Composer : IRenderable
{
private readonly BlockElement _root;
/// <inheritdoc/>
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<Composer> 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<Composer> 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<Composer> action)
{
if (action is null)
{
return this;
}
var content = new Composer();
action(content);
_root.Append(new StyleElement(style, content));
return this;
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
_root.Render(renderer);
}
}
}

View File

@ -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;
/// <inheritdoc/>
public int Length => _element.Length;
public BackgroundElement(Color color, IRenderable element)
{
_color = color;
_element = element ?? throw new ArgumentNullException(nameof(element));
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
if (renderer is null)
{
throw new ArgumentNullException(nameof(renderer));
}
using (renderer.PushColor(_color, foreground: false))
{
_element.Render(renderer);
}
}
}
}

View File

@ -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<IRenderable> _elements;
/// <inheritdoc/>
public int Length { get; private set; }
public IReadOnlyList<IRenderable> Elements => _elements;
public BlockElement()
{
_elements = new List<IRenderable>();
}
public BlockElement Append(IRenderable element)
{
if (element != null)
{
_elements.Add(element);
Length += element.Length;
}
return this;
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
foreach (var element in _elements)
{
element.Render(renderer);
}
}
}
}

View File

@ -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;
/// <inheritdoc/>
public int Length => _element.Length;
public ForegroundElement(Color color, IRenderable element)
{
_color = color;
_element = element ?? throw new ArgumentNullException(nameof(element));
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
if (renderer is null)
{
throw new ArgumentNullException(nameof(renderer));
}
using (renderer.PushColor(_color, foreground: true))
{
_element.Render(renderer);
}
}
}
}

View File

@ -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
{
/// <inheritdoc/>
public int Length => 0;
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
renderer.Write(Environment.NewLine);
}
}
}

View File

@ -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;
/// <inheritdoc/>
public int Length => _element.Length;
public StyleElement(Styles style, IRenderable element)
{
_style = style;
_element = element ?? throw new ArgumentNullException(nameof(element));
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
if (renderer is null)
{
throw new ArgumentNullException(nameof(renderer));
}
using (renderer.PushStyle(_style))
{
_element.Render(renderer);
}
}
}
}

View File

@ -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;
/// <inheritdoc/>
public int Length => _text.Length;
public TextElement(string text)
{
_text = text ?? throw new System.ArgumentNullException(nameof(text));
}
/// <inheritdoc/>
public void Render(IAnsiConsole renderer)
{
renderer.Write(_text);
}
}
}

View File

@ -0,0 +1,19 @@
namespace Spectre.Console.Internal
{
/// <summary>
/// Represents something that can be rendered to a console.
/// </summary>
internal interface IRenderable
{
/// <summary>
/// Gets the length of the element.
/// </summary>
int Length { get; }
/// <summary>
/// Renders the element using the specified renderer.
/// </summary>
/// <param name="console">The renderer to use.</param>
void Render(IAnsiConsole console);
}
}

View File

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

View File

@ -0,0 +1,8 @@
namespace Spectre.Console.Internal
{
internal static class Constants
{
public const int DefaultBufferWidth = 80;
public const int DefaultBufferHeight = 9001;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemGroup>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,75 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Represents a style.
/// </summary>
/// <remarks>
/// Support for different styles is up to the terminal.
/// </remarks>
[Flags]
public enum Styles
{
/// <summary>
/// No style.
/// </summary>
None = 0,
/// <summary>
/// Bold text.
/// Not supported in every environment.
/// </summary>
Bold = 1 << 0,
/// <summary>
/// Dim or faint text.
/// Not supported in every environment.
/// </summary>
Dim = 1 << 1,
/// <summary>
/// Italic text.
/// Not supported in every environment.
/// </summary>
Italic = 1 << 2,
/// <summary>
/// Underlined text.
/// Not supported in every environment.
/// </summary>
Underline = 1 << 3,
/// <summary>
/// Swaps the foreground and background colors.
/// Not supported in every environment.
/// </summary>
Invert = 1 << 4,
/// <summary>
/// Hides the text.
/// Not supported in every environment.
/// </summary>
Conceal = 1 << 5,
/// <summary>
/// Makes text blink.
/// Normally less than 150 blinks per minute.
/// Not supported in every environment.
/// </summary>
SlowBlink = 1 << 6,
/// <summary>
/// Makes text blink.
/// Normally more than 150 blinks per minute.
/// Not supported in every environment.
/// </summary>
RapidBlink = 1 << 7,
/// <summary>
/// Shows text with a horizontal line through the center.
/// Not supported in every environment.
/// </summary>
Strikethrough = 1 << 8,
}
}

26
src/stylecop.json Normal file
View File

@ -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"
]
}
}
}