mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-14 16:02:50 +08:00
Initial commit
This commit is contained in:
commit
334dcddc1a
178
.editorconfig
Normal file
178
.editorconfig
Normal 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
44
.github/workflows/ci.yaml
vendored
Normal 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
87
.github/workflows/publish.yaml
vendored
Normal 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
88
.gitignore
vendored
Normal 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
46
CODE_OF_CONDUCT.md
Normal 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
21
LICENSE.md
Normal 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
71
README.md
Normal 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
|
||||
|
||||

|
||||
|
||||
## 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
113
build.cake
Normal 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
18
dotnet-tools.json
Normal 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
7
global.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"projects": [ "src" ],
|
||||
"sdk": {
|
||||
"version": "3.1.301",
|
||||
"rollForward": "latestMajor"
|
||||
}
|
||||
}
|
77
src/.editorconfig
Normal file
77
src/.editorconfig
Normal 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
47
src/Directory.Build.props
Normal 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>
|
8
src/Directory.Build.targets
Normal file
8
src/Directory.Build.targets
Normal 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
8
src/Sample/.editorconfig
Normal 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
53
src/Sample/Program.cs
Normal 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
14
src/Sample/Sample.csproj
Normal 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>
|
23
src/Spectre.Console.Tests/.editorconfig
Normal file
23
src/Spectre.Console.Tests/.editorconfig
Normal 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
|
31
src/Spectre.Console.Tests/AnsiConsoleFixture.cs
Normal file
31
src/Spectre.Console.Tests/AnsiConsoleFixture.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
216
src/Spectre.Console.Tests/AnsiConsoleTests.Colors.cs
Normal file
216
src/Spectre.Console.Tests/AnsiConsoleTests.Colors.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
src/Spectre.Console.Tests/AnsiConsoleTests.Style.cs
Normal file
47
src/Spectre.Console.Tests/AnsiConsoleTests.Style.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests
|
||||
{
|
||||
public partial class AnsiConsoleTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(Styles.Bold, "\u001b[1mHello World[0m")]
|
||||
[InlineData(Styles.Dim, "\u001b[2mHello World[0m")]
|
||||
[InlineData(Styles.Italic, "\u001b[3mHello World[0m")]
|
||||
[InlineData(Styles.Underline, "\u001b[4mHello World[0m")]
|
||||
[InlineData(Styles.Invert, "\u001b[7mHello World[0m")]
|
||||
[InlineData(Styles.Conceal, "\u001b[8mHello World[0m")]
|
||||
[InlineData(Styles.SlowBlink, "\u001b[5mHello World[0m")]
|
||||
[InlineData(Styles.RapidBlink, "\u001b[6mHello World[0m")]
|
||||
[InlineData(Styles.Strikethrough, "\u001b[9mHello World[0m")]
|
||||
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[0m")]
|
||||
[InlineData(Styles.Bold | Styles.Underline | Styles.Conceal, "\u001b[1;4;8mHello World[0m")]
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
72
src/Spectre.Console.Tests/AnsiConsoleTests.cs
Normal file
72
src/Spectre.Console.Tests/AnsiConsoleTests.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
24
src/Spectre.Console.Tests/Extensions/ConsoleExtensions.cs
Normal file
24
src/Spectre.Console.Tests/Extensions/ConsoleExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
src/Spectre.Console.Tests/Spectre.Console.Tests.csproj
Normal file
21
src/Spectre.Console.Tests/Spectre.Console.Tests.csproj
Normal 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
73
src/Spectre.Console.sln
Normal 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
|
135
src/Spectre.Console/AnsiConsole.cs
Normal file
135
src/Spectre.Console/AnsiConsole.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
42
src/Spectre.Console/AnsiConsoleCapabilities.cs
Normal file
42
src/Spectre.Console/AnsiConsoleCapabilities.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
27
src/Spectre.Console/AnsiConsoleSettings.cs
Normal file
27
src/Spectre.Console/AnsiConsoleSettings.cs
Normal 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; }
|
||||
}
|
||||
}
|
24
src/Spectre.Console/AnsiSupport.cs
Normal file
24
src/Spectre.Console/AnsiSupport.cs
Normal 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,
|
||||
}
|
||||
}
|
1352
src/Spectre.Console/Color.Known.cs
Normal file
1352
src/Spectre.Console/Color.Known.cs
Normal file
File diff suppressed because it is too large
Load Diff
211
src/Spectre.Console/Color.cs
Normal file
211
src/Spectre.Console/Color.cs
Normal 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
33
src/Spectre.Console/ColorSystem.cs
Normal file
33
src/Spectre.Console/ColorSystem.cs
Normal 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,
|
||||
}
|
||||
}
|
38
src/Spectre.Console/ColorSystemSupport.cs
Normal file
38
src/Spectre.Console/ColorSystemSupport.cs
Normal 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,
|
||||
}
|
||||
}
|
83
src/Spectre.Console/ConsoleExtensions.cs
Normal file
83
src/Spectre.Console/ConsoleExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
52
src/Spectre.Console/IAnsiConsole.cs
Normal file
52
src/Spectre.Console/IAnsiConsole.cs
Normal 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);
|
||||
}
|
||||
}
|
44
src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs
Normal file
44
src/Spectre.Console/Internal/Ansi/AnsiBuilder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
70
src/Spectre.Console/Internal/Ansi/AnsiColorBuilder.cs
Normal file
70
src/Spectre.Console/Internal/Ansi/AnsiColorBuilder.cs
Normal 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 };
|
||||
}
|
||||
}
|
||||
}
|
129
src/Spectre.Console/Internal/Ansi/AnsiDetector.cs
Normal file
129
src/Spectre.Console/Internal/Ansi/AnsiDetector.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs
Normal file
56
src/Spectre.Console/Internal/Ansi/AnsiStyleBuilder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
src/Spectre.Console/Internal/Colors/ColorPalette.cs
Normal file
158
src/Spectre.Console/Internal/Colors/ColorPalette.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
55
src/Spectre.Console/Internal/Colors/ColorSystemDetector.cs
Normal file
55
src/Spectre.Console/Internal/Colors/ColorSystemDetector.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
73
src/Spectre.Console/Internal/Composition/Composer.cs
Normal file
73
src/Spectre.Console/Internal/Composition/Composer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
19
src/Spectre.Console/Internal/Composition/IRenderable.cs
Normal file
19
src/Spectre.Console/Internal/Composition/IRenderable.cs
Normal 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);
|
||||
}
|
||||
}
|
36
src/Spectre.Console/Internal/ConsoleBuilder.cs
Normal file
36
src/Spectre.Console/Internal/ConsoleBuilder.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
8
src/Spectre.Console/Internal/Constants.cs
Normal file
8
src/Spectre.Console/Internal/Constants.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const int DefaultBufferWidth = 80;
|
||||
public const int DefaultBufferHeight = 9001;
|
||||
}
|
||||
}
|
101
src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs
Normal file
101
src/Spectre.Console/Internal/Extensions/ConsoleExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
src/Spectre.Console/Internal/Rendering/AnsiConsoleRenderer.cs
Normal file
100
src/Spectre.Console/Internal/Rendering/AnsiConsoleRenderer.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
src/Spectre.Console/Spectre.Console.csproj
Normal file
21
src/Spectre.Console/Spectre.Console.csproj
Normal 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>
|
75
src/Spectre.Console/Styles.cs
Normal file
75
src/Spectre.Console/Styles.cs
Normal 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
26
src/stylecop.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user