Initial commit
This commit is contained in:
Коммит
ea97f296c0
|
@ -0,0 +1,175 @@
|
|||
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
|
|
@ -0,0 +1 @@
|
|||
github: patriksvensson
|
|
@ -0,0 +1,73 @@
|
|||
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:
|
||||
|
||||
###################################################
|
||||
# DOCS
|
||||
###################################################
|
||||
|
||||
docs:
|
||||
name: Documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '3.1.301' # SDK Version to use.
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
cd docs
|
||||
dotnet run --configuration Release
|
||||
|
||||
###################################################
|
||||
# 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
|
|
@ -0,0 +1,113 @@
|
|||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'src/**'
|
||||
|
||||
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:
|
||||
|
||||
###################################################
|
||||
# DOCS
|
||||
###################################################
|
||||
|
||||
docs:
|
||||
name: Documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '3.1.301' # SDK Version to use.
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
cd docs
|
||||
dotnet run --configuration Release
|
||||
|
||||
###################################################
|
||||
# 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}}"
|
|
@ -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
|
|
@ -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.
|
|
@ -0,0 +1,51 @@
|
|||
# Wcwidth
|
||||
|
||||
This is a port of the [Python port](https://github.com/jquast/wcwidth)
|
||||
written by Jeff Quast, which originally was written by Markus Kuhn.
|
||||
|
||||
* Python port: https://github.com/jquast/wcwidth (MIT)
|
||||
* Original: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
|
||||
## Usage
|
||||
|
||||
```csharp
|
||||
using Wcwidth;
|
||||
|
||||
// Get the width
|
||||
var width = Wcwidth.GetWidth('コ');
|
||||
|
||||
// It should be 2 cells wide
|
||||
Debug.Assert(width == 2);
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
We're using [Cake](https://github.com/cake-build/cake) as a
|
||||
[dotnet tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools)
|
||||
for building. So make sure that you've restored Cake by running
|
||||
the following in the repository root:
|
||||
|
||||
```
|
||||
> dotnet tool restore
|
||||
```
|
||||
|
||||
After that, running the build is as easy as writing:
|
||||
|
||||
```
|
||||
> dotnet cake
|
||||
```
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
This code is a port of https://github.com/jquast/wcwidth,
|
||||
licensed under [MIT](https://github.com/jquast/wcwidth/blob/dc720a9a4c3c6ae6c5b16a552cfe5186dde22551/LICENSE).
|
||||
|
||||
This code was originally derived directly from C code of the same name,
|
||||
whose latest version is available at http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c:
|
||||
|
||||
```
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
```
|
|
@ -0,0 +1,112 @@
|
|||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Tasks
|
||||
|
||||
Task("Build")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetCoreBuild("./src/Wcwidth.sln", new DotNetCoreBuildSettings {
|
||||
Configuration = configuration,
|
||||
NoIncremental = context.HasArgument("rebuild"),
|
||||
MSBuildSettings = new DotNetCoreMSBuildSettings()
|
||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Test")
|
||||
.IsDependentOn("Build")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetCoreTest("./src/Wcwidth.sln", new DotNetCoreTestSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
});
|
||||
});
|
||||
|
||||
Task("Package")
|
||||
.IsDependentOn("Test")
|
||||
.Does(context =>
|
||||
{
|
||||
context.CleanDirectory("./.artifacts");
|
||||
|
||||
context.DotNetCorePack($"./src/Wcwidth.sln", new DotNetCorePackSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
OutputDirectory = "./.artifacts",
|
||||
MSBuildSettings = new DotNetCoreMSBuildSettings()
|
||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||
});
|
||||
});
|
||||
|
||||
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)
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"cake.tool": {
|
||||
"version": "0.38.4",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
]
|
||||
},
|
||||
"gpr": {
|
||||
"version": "0.1.224",
|
||||
"commands": [
|
||||
"gpr"
|
||||
]
|
||||
},
|
||||
"dotnet-example": {
|
||||
"version": "0.8.0",
|
||||
"commands": [
|
||||
"dotnet-example"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"projects": [ "src" ],
|
||||
"sdk": {
|
||||
"version": "3.1.301",
|
||||
"rollForward": "latestMajor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Output
|
||||
Data
|
|
@ -0,0 +1,28 @@
|
|||
##########################################################
|
||||
# Script that generates known colors and lookup tables.
|
||||
##########################################################
|
||||
|
||||
$Output = Join-Path $PSScriptRoot "Output"
|
||||
$Data = Join-Path $PSScriptRoot "Data"
|
||||
$Source = Join-Path $PSScriptRoot "/../src/Wcwidth/Tables"
|
||||
|
||||
if(!(Test-Path $Output -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $Output | Out-Null
|
||||
}
|
||||
if(!(Test-Path $Data -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $Data | Out-Null
|
||||
}
|
||||
|
||||
# Generate the files
|
||||
Push-Location Generator
|
||||
&dotnet run -- "$Output" --data "$Data"
|
||||
if(!$?) {
|
||||
Pop-Location
|
||||
Throw "An error occured when generating code."
|
||||
}
|
||||
Pop-Location
|
||||
|
||||
# Copy the files to the correct location
|
||||
Copy-Item (Join-Path "$Output" "ZeroTable.Generated.cs") -Destination "$Source/ZeroTable.Generated.cs"
|
||||
Copy-Item (Join-Path "$Output" "WideTable.Generated.cs") -Destination "$Source/WideTable.Generated.cs"
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Spectre.Cli;
|
||||
using Spectre.Console;
|
||||
using Spectre.IO;
|
||||
using Wcwidth;
|
||||
|
||||
namespace Generator.Commands
|
||||
{
|
||||
public sealed class TableGeneratorCommand : AsyncCommand<TableGeneratorSettings>
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly List<TableGenerator> _generators;
|
||||
|
||||
public TableGeneratorCommand()
|
||||
{
|
||||
_fileSystem = new FileSystem();
|
||||
_generators = new List<TableGenerator>
|
||||
{
|
||||
new ZeroTableGenerator(),
|
||||
new WideTableGenerator(),
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, TableGeneratorSettings settings)
|
||||
{
|
||||
// Get the output path
|
||||
var output = new DirectoryPath(settings.Output);
|
||||
if (!_fileSystem.Directory.Exists(settings.Output))
|
||||
{
|
||||
_fileSystem.Directory.Create(settings.Output);
|
||||
}
|
||||
|
||||
// Get the data path
|
||||
var data = settings.Data != null
|
||||
? new DirectoryPath(settings.Data)
|
||||
: output;
|
||||
|
||||
// Get all versions
|
||||
var versions = GetVersions();
|
||||
|
||||
foreach (var generator in _generators)
|
||||
{
|
||||
// Generate the source
|
||||
AnsiConsole.MarkupLine("⏳ Generating [yellow]{0}[/]...", generator.ClassName);
|
||||
var result = await generator.Build(data, versions);
|
||||
|
||||
// Write the generated source to disk
|
||||
var file = output.CombineWithFilePath($"{generator.ClassName}.Generated.cs");
|
||||
AnsiConsole.MarkupLine("💾 Saving [yellow]{0}[/]...", file.GetFilename().FullPath);
|
||||
File.WriteAllText(file.FullPath, result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<string> GetVersions()
|
||||
{
|
||||
var result = new List<string>();
|
||||
foreach (var field in typeof(Unicode).GetFields().Where(x => x.IsStatic))
|
||||
{
|
||||
var attr = field.GetCustomAttribute<DescriptionAttribute>();
|
||||
if (attr == null || string.IsNullOrWhiteSpace(attr.Description))
|
||||
{
|
||||
throw new InvalidOperationException("Unicode version enum is missing version attribute.");
|
||||
}
|
||||
|
||||
result.Add(attr.Description);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Spectre.Cli;
|
||||
|
||||
namespace Generator.Commands
|
||||
{
|
||||
public sealed class TableGeneratorSettings : CommandSettings
|
||||
{
|
||||
[CommandArgument(0, "<OUTPUT>")]
|
||||
public string Output { get; set; }
|
||||
|
||||
[CommandOption("-d|--data <PATH>")]
|
||||
public string Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Spectre.Cli" Version="0.43.0" />
|
||||
<PackageReference Include="Scriban" Version="2.1.3" />
|
||||
<PackageReference Include="Spectre.IO" Version="0.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Wcwidth\Wcwidth.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Templates\Table.template">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30413.136
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generator", "Generator.csproj", "{64A7A6EB-C4EA-4537-A181-B0C11CF21CE4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{64A7A6EB-C4EA-4537-A181-B0C11CF21CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{64A7A6EB-C4EA-4537-A181-B0C11CF21CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{64A7A6EB-C4EA-4537-A181-B0C11CF21CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{64A7A6EB-C4EA-4537-A181-B0C11CF21CE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {37F50EC9-9829-4283-B046-E72EA93276CC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Generator.Models;
|
||||
using Generator.Templating;
|
||||
using Generator.Utilities;
|
||||
using Scriban;
|
||||
using Scriban.Runtime;
|
||||
using Spectre.Console;
|
||||
using Spectre.IO;
|
||||
using ScribanTemplate = Scriban.Template;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public abstract class TableGenerator
|
||||
{
|
||||
public abstract string ClassName { get; }
|
||||
|
||||
protected abstract string DataFilename { get; }
|
||||
protected virtual string Template { get; } = "Templates/Table.template";
|
||||
|
||||
public async Task<string> Build(DirectoryPath data, IEnumerable<string> versions)
|
||||
{
|
||||
// Compile the template
|
||||
var template = ScribanTemplate.Parse(File.ReadAllText(Template));
|
||||
|
||||
// Get data
|
||||
var model = new ScriptObject();
|
||||
model["data"] = await GetData(data, versions, Filter);
|
||||
model["name"] = ClassName;
|
||||
|
||||
// Prepare context
|
||||
var context = new TemplateContext();
|
||||
context.PushGlobal(model);
|
||||
context.PushGlobal(new ScribanHelpers());
|
||||
context.LoopLimit = 100000;
|
||||
|
||||
// Render template
|
||||
return template.Render(context);
|
||||
}
|
||||
|
||||
protected abstract string GetUrl(string version);
|
||||
protected abstract bool Filter(string category);
|
||||
|
||||
private async Task<IEnumerable<UnicodeDataTable>> GetData(
|
||||
DirectoryPath data,
|
||||
IEnumerable<string> versions,
|
||||
Func<string, bool> predicate)
|
||||
{
|
||||
var result = new List<UnicodeDataTable>();
|
||||
foreach (var version in versions)
|
||||
{
|
||||
var url = GetUrl(version);
|
||||
var filename = data.CombineWithFilePath($"{DataFilename}_{version}.txt");
|
||||
|
||||
using var stream = await UnicodeDataRetriever.GetData(filename, url);
|
||||
result.Add(UnicodeDataParser.Parse(url, version, stream, predicate));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public sealed class WideTableGenerator : TableGenerator
|
||||
{
|
||||
public override string ClassName => "WideTable";
|
||||
protected override string DataFilename => "EastAsianWidth";
|
||||
|
||||
protected override string GetUrl(string version)
|
||||
{
|
||||
return $"http://www.unicode.org/Public/{version}/ucd/EastAsianWidth.txt";
|
||||
}
|
||||
|
||||
protected override bool Filter(string category)
|
||||
{
|
||||
return category.Equals("W", StringComparison.OrdinalIgnoreCase) ||
|
||||
category.Equals("F", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public sealed class ZeroTableGenerator : TableGenerator
|
||||
{
|
||||
public override string ClassName => "ZeroTable";
|
||||
protected override string DataFilename => "DerivedGeneralCategory";
|
||||
|
||||
protected override string GetUrl(string version)
|
||||
{
|
||||
return $"http://www.unicode.org/Public/{version}/ucd/extracted/DerivedGeneralCategory.txt";
|
||||
}
|
||||
|
||||
protected override bool Filter(string category)
|
||||
{
|
||||
return category.Equals("Me", StringComparison.OrdinalIgnoreCase) ||
|
||||
category.Equals("Mn", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Generator.Commands;
|
||||
using Spectre.Cli;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
var app = new CommandApp<TableGeneratorCommand>();
|
||||
return app.Run(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Scriban.Runtime;
|
||||
|
||||
namespace Generator.Templating
|
||||
{
|
||||
public class ScribanHelpers : ScriptObject
|
||||
{
|
||||
public static string StringVer(string version)
|
||||
{
|
||||
return version.Replace(".", "_");
|
||||
}
|
||||
|
||||
public static string HexPad(int value, int pad)
|
||||
{
|
||||
return string.Format("0x{0}", value.ToString("X").PadLeft(pad, '0'));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Generated {{ date.now | date.to_string `%F %R` }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wcwidth
|
||||
{
|
||||
internal static partial class {{ name }}
|
||||
{
|
||||
private static uint[,] GenerateTable(Unicode version)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
{{- for item in data }}
|
||||
Unicode.Version_{{ item.version | string_ver }} => Unicode_{{ item.version | string_ver }}(),
|
||||
{{- end }}
|
||||
_ => throw new InvalidOperationException("Unknown Unicode version"),
|
||||
};
|
||||
}
|
||||
{{~ for item in data }}
|
||||
// Source: {{ item.source }}
|
||||
private static uint[,] Unicode_{{ item.version | string.replace "." "_" }}()
|
||||
{
|
||||
return new uint[,]
|
||||
{
|
||||
{{~ for range in item.ranges ~}}
|
||||
{%{{}%} {{ range.start | hex_pad 6 }}, {{ range.end | hex_pad 6 }} {%{}}%},
|
||||
{{~ end ~}}
|
||||
};
|
||||
}
|
||||
{{~ end ~}}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Generator.Models;
|
||||
|
||||
namespace Generator.Utilities
|
||||
{
|
||||
public static class UnicodeDataParser
|
||||
{
|
||||
private static readonly Regex _regex;
|
||||
|
||||
static UnicodeDataParser()
|
||||
{
|
||||
_regex = new Regex("^(?<start>[0-9A-F]{4,6})(..(?<end>[0-9A-F]{4,6})?)?\\s*;(?<property>[A-Za-z ]*)#(?<comment>.*$)");
|
||||
}
|
||||
|
||||
public static UnicodeDataTable Parse(string source, string version, Stream stream,
|
||||
Func<string, bool> predicate)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (version is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
}
|
||||
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
var result = new List<int>();
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
while (true)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var range = ParseLine(line, predicate);
|
||||
if (range != null)
|
||||
{
|
||||
result.AddRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
return UnicodeDataTableBuilder.Build(source, version, result);
|
||||
}
|
||||
|
||||
private static IEnumerable<int> ParseLine(string line, Func<string, bool> predicate)
|
||||
{
|
||||
var match = _regex.Match(line);
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Could not parse line");
|
||||
}
|
||||
|
||||
var property = match.GetGroupValue("property", string.Empty).Trim();
|
||||
if (predicate(property))
|
||||
{
|
||||
var (start, end) = GetStartAndEnd(match);
|
||||
return Enumerable.Range(start, end - start + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static (int, int) GetStartAndEnd(Match match)
|
||||
{
|
||||
var start = match.GetGroupValue("start")?.Trim();
|
||||
if (start == null)
|
||||
{
|
||||
throw new InvalidOperationException("Missing start code point");
|
||||
}
|
||||
|
||||
var end = match.GetGroupValue("end", start).Trim();
|
||||
|
||||
return (
|
||||
int.Parse(start, NumberStyles.HexNumber),
|
||||
int.Parse(end, NumberStyles.HexNumber));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Generator.Models
|
||||
{
|
||||
public sealed class UnicodeDataRange
|
||||
{
|
||||
public int Start { get; set; }
|
||||
public int End { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Spectre.Console;
|
||||
using Spectre.IO;
|
||||
|
||||
namespace Generator.Utilities
|
||||
{
|
||||
public static class UnicodeDataRetriever
|
||||
{
|
||||
public static async Task<Stream> GetData(
|
||||
FilePath filename, string url)
|
||||
{
|
||||
if (!File.Exists(filename.FullPath))
|
||||
{
|
||||
AnsiConsole.MarkupLine($"🌍 Downloading [yellow]{url}[/]...");
|
||||
using var client = new HttpClient();
|
||||
var content = await client.GetStringAsync(url);
|
||||
await File.WriteAllTextAsync(filename.FullPath, content);
|
||||
}
|
||||
|
||||
return File.OpenRead(filename.FullPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Generator.Models
|
||||
{
|
||||
public sealed class UnicodeDataTable
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string Source { get; set; }
|
||||
public List<UnicodeDataRange> Ranges { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Generator.Models;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace Generator.Utilities
|
||||
{
|
||||
public static class UnicodeDataTableBuilder
|
||||
{
|
||||
public static UnicodeDataTable Build(
|
||||
string source,
|
||||
string version,
|
||||
List<int> values)
|
||||
{
|
||||
var ranges = CollapseRanges(values);
|
||||
|
||||
return new UnicodeDataTable
|
||||
{
|
||||
Source = source,
|
||||
Version = version,
|
||||
Ranges = ranges.OrderBy(x => x.Item1).Select((value) =>
|
||||
{
|
||||
return new UnicodeDataRange
|
||||
{
|
||||
Start = value.Item1,
|
||||
End = value.Item2
|
||||
};
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private static Deque<(int, int)> CollapseRanges(List<int> values)
|
||||
{
|
||||
var queue = new Deque<(int, int)>();
|
||||
|
||||
var start = values[0];
|
||||
var end = values[0];
|
||||
|
||||
foreach (var (index, value) in values.Enumerate())
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
queue.Append((value, value));
|
||||
continue;
|
||||
}
|
||||
|
||||
(start, end) = queue.Pop();
|
||||
if (end == value - 1)
|
||||
{
|
||||
queue.Append((start, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
queue.Append((start, end));
|
||||
queue.Append((value, value));
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Generator.Utilities
|
||||
{
|
||||
public sealed class Deque<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly List<T> _items;
|
||||
|
||||
public Deque()
|
||||
{
|
||||
_items = new List<T>();
|
||||
}
|
||||
|
||||
public void Append(T item)
|
||||
{
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
var last = _items[_items.Count - 1];
|
||||
_items.RemoveAt(_items.Count - 1);
|
||||
return last;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<(int Index, T Item)> Enumerate<T>(this IEnumerable<T> source)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
return Enumerate(source.GetEnumerator());
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<(int Index, T Item)> Enumerate<T>(this IEnumerator<T> source)
|
||||
{
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var last = !source.MoveNext();
|
||||
T current;
|
||||
|
||||
for (var index = 0; !last; index++)
|
||||
{
|
||||
current = source.Current;
|
||||
last = !source.MoveNext();
|
||||
yield return (index, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
public static class MatchExtensions
|
||||
{
|
||||
public static string GetGroupValue(this Match match, string group, string defaultValue = null)
|
||||
{
|
||||
return match.Groups[group].Success
|
||||
? match.Groups[group].Value
|
||||
: defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
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
|
||||
|
||||
# RCS1079: Throwing of new NotImplementedException.
|
||||
dotnet_diagnostic.RCS1079.severity = warning
|
||||
|
||||
# RCS1057: Add empty line between declarations.
|
||||
dotnet_diagnostic.RCS1057.severity = none
|
||||
|
||||
# IDE0004: Remove Unnecessary Cast
|
||||
dotnet_diagnostic.IDE0004.severity = warning
|
||||
|
||||
# CA1810: Initialize reference type static fields inline
|
||||
dotnet_diagnostic.CA1810.severity = none
|
||||
|
||||
# SA1636: File header copyright text should match
|
||||
dotnet_diagnostic.SA1636.severity = none
|
||||
|
||||
# CA1724: Namespace conflict
|
||||
dotnet_diagnostic.CA1724.severity = none
|
|
@ -0,0 +1,45 @@
|
|||
<Project>
|
||||
<PropertyGroup Label="Settings">
|
||||
<Deterministic>true</Deterministic>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>embedded</DebugType>
|
||||
<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 .NET library that calculates the width of Unicode characters.</Description>
|
||||
<Authors>Patrik Svensson</Authors>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/patriksvensson/wcwidth</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/patriksvensson/wcwidth</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReleaseNotes>https://github.com/patriksvensson/wcwidth/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="3.3.0">
|
||||
<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>
|
|
@ -0,0 +1,8 @@
|
|||
<Project>
|
||||
<Target Name="Versioning" BeforeTargets="MinVer">
|
||||
<PropertyGroup Label="Build">
|
||||
<MinVerDefaultPreReleasePhase>preview</MinVerDefaultPreReleasePhase>
|
||||
<MinVerVerbosity>normal</MinVerVerbosity>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,14 @@
|
|||
root = false
|
||||
|
||||
[*.cs]
|
||||
# CS1591: Missing XML comment for publicly visible type or member
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
|
||||
# SA1600: Elements should be documented
|
||||
dotnet_diagnostic.SA1600.severity = none
|
||||
|
||||
# SA1633: File should have header
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
|
||||
# CA1707: Identifiers should not contain underscores
|
||||
dotnet_diagnostic.CA1707.severity = none
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.2" />
|
||||
<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="..\Wcwidth\Wcwidth.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,120 @@
|
|||
using System.Linq;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Wcwidth.Tests
|
||||
{
|
||||
public sealed class WcwidthTests
|
||||
{
|
||||
[Fact]
|
||||
public void Test_Favorite_Emoji()
|
||||
{
|
||||
// Given
|
||||
var length = "💩".Select(c => Wcwidth.GetWidth(c)).Sum();
|
||||
|
||||
// Then
|
||||
length.ShouldBe(length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Hello_Japanese()
|
||||
{
|
||||
char c2 = 'コ';
|
||||
var foo = Wcwidth.GetWidth(c2);
|
||||
|
||||
// Given
|
||||
const string phrase = "コンニチハ, セカイ!";
|
||||
var expected = new[] { 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Null_Width()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "abc\x0000def";
|
||||
var expected = new[] { 1, 1, 1, 0, 1, 1, 1 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Control_C0_Width()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "\x1b[0m";
|
||||
var expected = new[] { -1, 1, 1, 1 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Combining_Width()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "--\u05bf--";
|
||||
var expected = new[] { 1, 1, 0, 1, 1 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Combining_Cafe_Width()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "cafe\u0301";
|
||||
var expected = new[] { 1, 1, 1, 1, 0 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Combining_Enclosing()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "\u0410\u0488";
|
||||
var expected = new[] { 1, 0 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_Combining_Spacing()
|
||||
{
|
||||
// Given
|
||||
const string phrase = "\u1B13\u1B28\u1B2E\u1B44";
|
||||
var expected = new[] { 1, 1, 1, 1 };
|
||||
|
||||
// When
|
||||
var length = phrase.Select(c => Wcwidth.GetWidth(c));
|
||||
|
||||
// Then
|
||||
length.ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30330.147
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wcwidth", "Wcwidth\Wcwidth.csproj", "{97277350-2069-4863-B0DE-07E929629958}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wcwidth.Tests", "Wcwidth.Tests\Wcwidth.Tests.csproj", "{A90C8C46-58DF-41B8-B273-B24396E8424C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9C3DA7B2-0055-453E-BFF7-F4D24B438433}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
..\README.md = ..\README.md
|
||||
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
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|x64.Build.0 = Release|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{97277350-2069-4863-B0DE-07E929629958}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A90C8C46-58DF-41B8-B273-B24396E8424C}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {91DC3E44-F01D-459A-A956-418FE23E6723}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,14 @@
|
|||
namespace Wcwidth
|
||||
{
|
||||
internal readonly struct Interval
|
||||
{
|
||||
public int Start { get; }
|
||||
public int End { get; }
|
||||
|
||||
public Interval(int start, int end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Wcwidth
|
||||
{
|
||||
internal static partial class WideTable
|
||||
{
|
||||
private static readonly Dictionary<Unicode, uint[,]> _lookup;
|
||||
private static readonly object _lock;
|
||||
|
||||
static WideTable()
|
||||
{
|
||||
_lookup = new Dictionary<Unicode, uint[,]>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public static uint[,] GetTable(Unicode version)
|
||||
{
|
||||
if (!_lookup.TryGetValue(version, out var table))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_lookup.TryGetValue(version, out table))
|
||||
{
|
||||
return table;
|
||||
}
|
||||
|
||||
// Generate the table for the version dynamically
|
||||
// since we don't want to load everything into memory.
|
||||
table = GenerateTable(version);
|
||||
_lookup[version] = table;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Wcwidth
|
||||
{
|
||||
internal static partial class ZeroTable
|
||||
{
|
||||
private static readonly Dictionary<Unicode, uint[,]> _lookup;
|
||||
private static readonly object _lock;
|
||||
|
||||
static ZeroTable()
|
||||
{
|
||||
_lookup = new Dictionary<Unicode, uint[,]>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public static uint[,] GetTable(Unicode version)
|
||||
{
|
||||
if (!_lookup.TryGetValue(version, out var table))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_lookup.TryGetValue(version, out table))
|
||||
{
|
||||
return table;
|
||||
}
|
||||
|
||||
// Generate the table for the version dynamically
|
||||
// since we don't want to load everything into memory.
|
||||
table = GenerateTable(version);
|
||||
_lookup[version] = table;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Wcwidth
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Unicode version.
|
||||
/// </summary>
|
||||
[SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores")]
|
||||
public enum Unicode
|
||||
{
|
||||
/// <summary>
|
||||
/// Unicode version 4.1.0.
|
||||
/// </summary>
|
||||
[Description("4.1.0")]
|
||||
Version_4_1_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 5.0.0.
|
||||
/// </summary>
|
||||
[Description("5.0.0")]
|
||||
Version_5_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 5.1.0.
|
||||
/// </summary>
|
||||
[Description("5.1.0")]
|
||||
Version_5_1_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 5.2.0.
|
||||
/// </summary>
|
||||
[Description("5.2.0")]
|
||||
Version_5_2_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 6.0.0.
|
||||
/// </summary>
|
||||
[Description("6.0.0")]
|
||||
Version_6_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 6.1.0.
|
||||
/// </summary>
|
||||
[Description("6.1.0")]
|
||||
Version_6_1_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 6.2.0.
|
||||
/// </summary>
|
||||
[Description("6.2.0")]
|
||||
Version_6_2_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 6.3.0.
|
||||
/// </summary>
|
||||
[Description("6.3.0")]
|
||||
Version_6_3_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 7.0.0.
|
||||
/// </summary>
|
||||
[Description("7.0.0")]
|
||||
Version_7_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 8.0.0.
|
||||
/// </summary>
|
||||
[Description("8.0.0")]
|
||||
Version_8_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 9.0.0.
|
||||
/// </summary>
|
||||
[Description("9.0.0")]
|
||||
Version_9_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 10.0.0.
|
||||
/// </summary>
|
||||
[Description("10.0.0")]
|
||||
Version_10_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 11.0.0.
|
||||
/// </summary>
|
||||
[Description("11.0.0")]
|
||||
Version_11_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 12.0.0.
|
||||
/// </summary>
|
||||
[Description("12.0.0")]
|
||||
Version_12_0_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 12.1.0.
|
||||
/// </summary>
|
||||
[Description("12.1.0")]
|
||||
Version_12_1_0,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode version 13.0.0.
|
||||
/// </summary>
|
||||
[Description("13.0.0")]
|
||||
Version_13_0_0,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// This is a port of the Python version of wcwidth() written by Jeff Quast,
|
||||
// which originally was written by Markus Kuhn.
|
||||
//
|
||||
// https://github.com/jquast/wcwidth
|
||||
// http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
//
|
||||
// In fixed-width output devices, Latin characters all occupy a single "cell" position
|
||||
// of equal width, whereas ideographic CJK characters occupy two such cells. Interoperability
|
||||
// between terminal-line applications and (teletype-style) character terminals using the
|
||||
// UTF-8 encoding requires agreement on which character should advance the cursor by how
|
||||
// many cell positions. No established formal standards exist at present on which Unicode
|
||||
// character shall occupy how many cell positions on character terminals. These routines
|
||||
// are a first attempt of defining such behavior based on simple rules applied to data
|
||||
// provided by the Unicode Consortium.
|
||||
//
|
||||
// For some graphical characters, the Unicode standard explicitly defines a character-cell
|
||||
// width via the definition of the East Asian FullWidth (F), Wide (W), Half-width (H),
|
||||
// and Narrow (Na) classes. In all these cases, there is no ambiguity about which width
|
||||
// a terminal shall use. For characters in the East Asian Ambiguous (A) class, the width
|
||||
// choice depends purely on a preference of backward compatibility with either historic
|
||||
// CJK or Western practice. Choosing single-width for these characters is easy to justify
|
||||
// as the appropriate long-term solution, as the CJK practice of displaying these characters
|
||||
// as double-width comes from historic implementation simplicity (8-bit encoded characters
|
||||
// were displayed single-width and 16-bit ones double-width, even for Greek, Cyrillic, etc.)
|
||||
// and not any typographic considerations.
|
||||
//
|
||||
// Much less clear is the choice of width for the Not East Asian (Neutral) class.
|
||||
// Existing practice does not dictate a width for any of these characters. It would
|
||||
// nevertheless make sense typographically to allocate two character cells to characters
|
||||
// such as for instance EM SPACE or VOLUME INTEGRAL, which cannot be represented adequately
|
||||
// with a single-width glyph. The following routines at present merely assign a single-cell
|
||||
// width to all neutral characters, in the interest of simplicity. This is not entirely
|
||||
// satisfactory and should be reconsidered before establishing a formal standard in this area.
|
||||
// At the moment, the decision which Not East Asian (Neutral) characters should be represented
|
||||
// by double-width glyphs cannot yet be answered by applying a simple rule from the Unicode
|
||||
// database content. Setting up a proper standard for the behavior of UTF-8 character
|
||||
// terminals will require a careful analysis not only of each Unicode character, but also
|
||||
// of each presentation form, something the author of these routines has avoided to do so far.
|
||||
//
|
||||
// http://www.unicode.org/unicode/reports/tr11/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wcwidth
|
||||
{
|
||||
/// <summary>
|
||||
/// A utility for calculating the width of Unicode characters.
|
||||
/// </summary>
|
||||
public static class Wcwidth
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the latest unicode version.
|
||||
/// </summary>
|
||||
public static Unicode Latest { get; } = Unicode.Version_13_0_0;
|
||||
|
||||
// NOTE: created by hand, there isn't anything identifiable other than
|
||||
// general Cf category code to identify these, and some characters in Cf
|
||||
// category code are of non-zero width.
|
||||
// Also includes some Cc, Mn, Zl, and Zp characters
|
||||
private static readonly HashSet<uint> ZeroWidthCf = new HashSet<uint>
|
||||
{
|
||||
0, // Null (Cc)
|
||||
0x034F, // Combining grapheme joiner (Mn)
|
||||
0x200B, // Zero width space
|
||||
0x200C, // Zero width non-joiner
|
||||
0x200D, // Zero width joiner
|
||||
0x200E, // Left-to-right mark
|
||||
0x200F, // Right-to-left mark
|
||||
0x2028, // Line separator (Zl)
|
||||
0x2029, // Paragraph separator (Zp)
|
||||
0x202A, // Left-to-right embedding
|
||||
0x202B, // Right-to-left embedding
|
||||
0x202C, // Pop directional formatting
|
||||
0x202D, // Left-to-right override
|
||||
0x202E, // Right-to-left override
|
||||
0x2060, // Word joiner
|
||||
0x2061, // Function application
|
||||
0x2062, // Invisible times
|
||||
0x2063, // Invisible separator
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width for a Unicode character.
|
||||
/// </summary>
|
||||
/// <param name="value">The charachter to calculate the width for.</param>
|
||||
/// <param name="version">The Unicode version to use.</param>
|
||||
/// <returns>The width of the character.</returns>
|
||||
public static int GetWidth(char value, Unicode? version = null)
|
||||
{
|
||||
version ??= Latest;
|
||||
|
||||
// NOTE: created by hand, there isn't anything identifiable other than
|
||||
// general Cf category code to identify these, and some characters in Cf
|
||||
// category code are of non-zero width.
|
||||
if (ZeroWidthCf.Contains(value))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// C0/C1 control characters
|
||||
if (value < 32 || (value >= 0x07F && value < 0x0A0))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Combining characters with zero width?
|
||||
if (BinarySearch(value, ZeroTable.GetTable(version.Value)) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1 + BinarySearch(value, WideTable.GetTable(version.Value));
|
||||
}
|
||||
|
||||
private static int BinarySearch(uint rune, uint[,] table)
|
||||
{
|
||||
var min = 0;
|
||||
var max = table.GetUpperBound(0);
|
||||
int mid;
|
||||
|
||||
if (rune < table[0, 0] || rune > table[max, 1])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (max >= min)
|
||||
{
|
||||
mid = (min + max) / 2;
|
||||
if (rune > table[mid, 1])
|
||||
{
|
||||
min = mid + 1;
|
||||
}
|
||||
else if (rune < table[mid, 0])
|
||||
{
|
||||
max = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"documentationRules": {
|
||||
"xmlHeader": false,
|
||||
"documentExposedElements": true,
|
||||
"documentInternalElements": false,
|
||||
"documentPrivateElements": false,
|
||||
"documentPrivateFields": false
|
||||
},
|
||||
"layoutRules": {
|
||||
"newlineAtEndOfFile": "allow",
|
||||
"allowConsecutiveUsings": true
|
||||
},
|
||||
"orderingRules": {
|
||||
"usingDirectivesPlacement": "outsideNamespace",
|
||||
"systemUsingDirectivesFirst": true,
|
||||
"elementOrder": [
|
||||
"kind",
|
||||
"accessibility",
|
||||
"constant",
|
||||
"static",
|
||||
"readonly"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче