mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
..
This commit is contained in:
parent
075c8e76b2
commit
1ce8262bb3
@ -35,8 +35,10 @@ resharper_align_multline_type_parameter_constrains = true
|
||||
resharper_align_multline_type_parameter_list = true
|
||||
resharper_align_tuple_components = true
|
||||
resharper_allow_comment_after_lbrace = true
|
||||
resharper_blank_lines_before_single_line_comment = 1
|
||||
resharper_csharp_empty_block_style = together_same_line
|
||||
resharper_csharp_outdent_commas = true
|
||||
resharper_csharp_place_type_constraints_on_same_line = false
|
||||
resharper_csharp_stick_comment = false
|
||||
resharper_csharp_wrap_before_comma = true
|
||||
resharper_indent_nested_foreach_stmt = true
|
||||
@ -53,6 +55,7 @@ resharper_wrap_before_eq = true
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_wrap_switch_expression = chop_if_long
|
||||
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_indent_braces = false
|
||||
csharp_new_line_before_open_brace = local_functions, methods, types
|
238
StyleCopAnalyzers.ruleset
Normal file
238
StyleCopAnalyzers.ruleset
Normal file
@ -0,0 +1,238 @@
|
||||
<?xml version="1.0"?>
|
||||
<RuleSet Name="StyleCop.Analyzers rules with default action"
|
||||
Description="StyleCop.Analyzers with default action. Rules with IsEnabledByDefault = false are disabled."
|
||||
ToolsVersion="14.0">
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.SpecialRules">
|
||||
<Rule Id="SA0001" Action="Warning"/> <!-- XML comment analysis disabled -->
|
||||
<Rule Id="SA0002" Action="Warning"/> <!-- Invalid settings file -->
|
||||
</Rules>
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.SpacingRules">
|
||||
<Rule Id="SA1000" Action="Warning"/> <!-- Keywords should be spaced correctly -->
|
||||
<Rule Id="SA1001" Action="None"/> <!-- Commas should be spaced correctly -->
|
||||
<Rule Id="SA1002" Action="Warning"/> <!-- Semicolons should be spaced correctly -->
|
||||
<Rule Id="SA1003" Action="Warning"/> <!-- Symbols should be spaced correctly -->
|
||||
<Rule Id="SA1004" Action="Warning"/> <!-- Documentation lines should begin with single space -->
|
||||
<Rule Id="SA1005" Action="None"/> <!-- Single line comments should begin with single space -->
|
||||
<Rule Id="SA1006" Action="Warning"/> <!-- Preprocessor keywords should not be preceded by space -->
|
||||
<Rule Id="SA1007" Action="Warning"/> <!-- Operator keyword should be followed by space -->
|
||||
<Rule Id="SA1008" Action="None"/> <!-- Opening parenthesis should be spaced correctly -->
|
||||
<Rule Id="SA1009" Action="Warning"/> <!-- Closing parenthesis should be spaced correctly -->
|
||||
<Rule Id="SA1010" Action="Warning"/> <!-- Opening square brackets should be spaced correctly -->
|
||||
<Rule Id="SA1011" Action="Warning"/> <!-- Closing square brackets should be spaced correctly -->
|
||||
<Rule Id="SA1012" Action="Warning"/> <!-- Opening braces should be spaced correctly -->
|
||||
<Rule Id="SA1013" Action="Warning"/> <!-- Closing braces should be spaced correctly -->
|
||||
<Rule Id="SA1014" Action="Warning"/> <!-- Opening generic brackets should be spaced correctly -->
|
||||
<Rule Id="SA1015" Action="Warning"/> <!-- Closing generic brackets should be spaced correctly -->
|
||||
<Rule Id="SA1016" Action="Warning"/> <!-- Opening attribute brackets should be spaced correctly -->
|
||||
<Rule Id="SA1017" Action="Warning"/> <!-- Closing attribute brackets should be spaced correctly -->
|
||||
<Rule Id="SA1018" Action="Warning"/> <!-- Nullable type symbols should be spaced correctly -->
|
||||
<Rule Id="SA1019" Action="Warning"/> <!-- Member access symbols should be spaced correctly -->
|
||||
<Rule Id="SA1020" Action="Warning"/> <!-- Increment decrement symbols should be spaced correctly -->
|
||||
<Rule Id="SA1021" Action="Warning"/> <!-- Negative signs should be spaced correctly -->
|
||||
<Rule Id="SA1022" Action="Warning"/> <!-- Positive signs should be spaced correctly -->
|
||||
<Rule Id="SA1023" Action="Warning"/> <!-- Dereference and access of symbols should be spaced correctly -->
|
||||
<Rule Id="SA1024" Action="Warning"/> <!-- Colons should be spaced correctly -->
|
||||
<Rule Id="SA1025" Action="None"/> <!-- Code should not contain multiple whitespace in a row -->
|
||||
<Rule Id="SA1026" Action="Warning"/> <!-- Code should not contain space after new or stackalloc keyword in implicitly typed array allocation -->
|
||||
<Rule Id="SA1027" Action="Warning"/> <!-- Use tabs correctly -->
|
||||
<Rule Id="SA1028" Action="Warning"/> <!-- Code should not contain trailing whitespace -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.ReadabilityRules">
|
||||
<Rule Id="SA1100" Action="Warning"/> <!-- Do not prefix calls with base unless local implementation exists -->
|
||||
<Rule Id="SA1101" Action="None"/> <!-- Prefix local calls with this -->
|
||||
<Rule Id="SA1102" Action="Warning"/> <!-- Query clause should follow previous clause -->
|
||||
<Rule Id="SA1103" Action="Warning"/> <!-- Query clauses should be on separate lines or all on one line -->
|
||||
<Rule Id="SA1104" Action="Warning"/> <!-- Query clause should begin on new line when previous clause spans multiple lines -->
|
||||
<Rule Id="SA1105" Action="Warning"/> <!-- Query clauses spanning multiple lines should begin on own line -->
|
||||
<Rule Id="SA1106" Action="Warning"/> <!-- Code should not contain empty statements -->
|
||||
<Rule Id="SA1107" Action="Warning"/> <!-- Code should not contain multiple statements on one line -->
|
||||
<Rule Id="SA1108" Action="Warning"/> <!-- Block statements should not contain embedded comments -->
|
||||
<Rule Id="SA1109" Action="None"/> <!-- Block statements should not contain embedded regions -->
|
||||
<Rule Id="SA1110" Action="Warning"/> <!-- Opening parenthesis or bracket should be on declaration line -->
|
||||
<Rule Id="SA1111" Action="Warning"/> <!-- Closing parenthesis should be on line of last parameter -->
|
||||
<Rule Id="SA1112" Action="Warning"/> <!-- Closing parenthesis should be on line of opening parenthesis -->
|
||||
<Rule Id="SA1113" Action="None"/> <!-- Comma should be on the same line as previous parameter -->
|
||||
<Rule Id="SA1114" Action="Warning"/> <!-- Parameter list should follow declaration -->
|
||||
<Rule Id="SA1115" Action="Warning"/> <!-- Parameter should follow comma -->
|
||||
<Rule Id="SA1116" Action="Warning"/> <!-- Split parameters should start on line after declaration -->
|
||||
<Rule Id="SA1117" Action="None"/> <!-- Parameters should be on same line or separate lines -->
|
||||
<Rule Id="SA1118" Action="Warning"/> <!-- Parameter should not span multiple lines -->
|
||||
<Rule Id="SA1120" Action="None"/> <!-- Comments should contain text -->
|
||||
<Rule Id="SA1121" Action="Warning"/> <!-- Use built-in type alias -->
|
||||
<Rule Id="SA1122" Action="Warning"/> <!-- Use string.Empty for empty strings -->
|
||||
<Rule Id="SA1123" Action="Warning"/> <!-- Do not place regions within elements -->
|
||||
<Rule Id="SA1124" Action="Warning"/> <!-- Do not use regions -->
|
||||
<Rule Id="SA1125" Action="Warning"/> <!-- Use shorthand for nullable types -->
|
||||
<Rule Id="SA1126" Action="None"/> <!-- Prefix calls correctly -->
|
||||
<Rule Id="SA1127" Action="Warning"/> <!-- Generic type constraints should be on their own line -->
|
||||
<Rule Id="SA1128" Action="Warning"/> <!-- Put constructor initializers on their own line -->
|
||||
<Rule Id="SA1129" Action="Warning"/> <!-- Do not use default value type constructor -->
|
||||
<Rule Id="SA1130" Action="Warning"/> <!-- Use lambda syntax -->
|
||||
<Rule Id="SA1131" Action="Warning"/> <!-- Use readable conditions -->
|
||||
<Rule Id="SA1132" Action="Warning"/> <!-- Do not combine fields -->
|
||||
<Rule Id="SA1133" Action="Warning"/> <!-- Do not combine attributes -->
|
||||
<Rule Id="SA1134" Action="None"/> <!-- Attributes should not share line -->
|
||||
<Rule Id="SA1135" Action="Warning"/> <!-- Using directives should be qualified -->
|
||||
<Rule Id="SA1136" Action="Warning"/> <!-- Enum values should be on separate lines -->
|
||||
<Rule Id="SA1137" Action="Warning"/> <!-- Elements should have the same indentation -->
|
||||
<Rule Id="SA1139" Action="Warning"/> <!-- Use literal suffix notation instead of casting -->
|
||||
<Rule Id="SX1101" Action="None"/> <!-- Do not prefix local calls with 'this.' -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.OrderingRules">
|
||||
<Rule Id="SA1200" Action="None"/> <!-- Using directives should be placed correctly -->
|
||||
<Rule Id="SA1201" Action="Warning"/> <!-- Elements should appear in the correct order -->
|
||||
<Rule Id="SA1202" Action="Warning"/> <!-- Elements should be ordered by access -->
|
||||
<Rule Id="SA1203" Action="Warning"/> <!-- Constants should appear before fields -->
|
||||
<Rule Id="SA1204" Action="Warning"/> <!-- Static elements should appear before instance elements -->
|
||||
<Rule Id="SA1205" Action="Warning"/> <!-- Partial elements should declare access -->
|
||||
<Rule Id="SA1206" Action="Warning"/> <!-- Declaration keywords should follow order -->
|
||||
<Rule Id="SA1207" Action="Warning"/> <!-- Protected should come before internal -->
|
||||
<Rule Id="SA1208" Action="Warning"/> <!-- System using directives should be placed before other
|
||||
using directives -->
|
||||
<Rule Id="SA1209" Action="Warning"/> <!-- Using alias directives should be placed after other using
|
||||
directives -->
|
||||
<Rule Id="SA1210" Action="Warning"/> <!-- Using directives should be ordered alphabetically by
|
||||
namespace -->
|
||||
<Rule Id="SA1211" Action="Warning"/> <!-- Using alias directives should be ordered alphabetically by
|
||||
alias name -->
|
||||
<Rule Id="SA1212" Action="Warning"/> <!-- Property accessors should follow order -->
|
||||
<Rule Id="SA1213" Action="Warning"/> <!-- Event accessors should follow order -->
|
||||
<Rule Id="SA1214" Action="Warning"/> <!-- Readonly fields should appear before non-readonly fields -->
|
||||
<Rule Id="SA1216" Action="Warning"/> <!-- Using static directives should be placed at the correct
|
||||
location -->
|
||||
<Rule Id="SA1217" Action="Warning"/> <!-- Using static directives should be ordered alphabetically -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.NamingRules">
|
||||
<Rule Id="SA1300" Action="Warning"/> <!-- Element should begin with upper-case letter -->
|
||||
<Rule Id="SA1301" Action="None"/> <!-- Element should begin with lower-case letter -->
|
||||
<Rule Id="SA1302" Action="Warning"/> <!-- Interface names should begin with I -->
|
||||
<Rule Id="SA1303" Action="Warning"/> <!-- Const field names should begin with upper-case letter -->
|
||||
<Rule Id="SA1304" Action="Warning"/> <!-- Non-private readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1305" Action="None"/> <!-- Field names should not use Hungarian notation -->
|
||||
<Rule Id="SA1306" Action="Warning"/> <!-- Field names should begin with lower-case letter -->
|
||||
<Rule Id="SA1307" Action="Warning"/> <!-- Accessible fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1308" Action="Warning"/> <!-- Variable names should not be prefixed -->
|
||||
<Rule Id="SA1309" Action="None"/> <!-- Field names should not begin with underscore -->
|
||||
<Rule Id="SA1310" Action="None"/> <!-- Field names should not contain underscore -->
|
||||
<Rule Id="SA1311" Action="Warning"/> <!-- Static readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1312" Action="Warning"/> <!-- Variable names should begin with lower-case letter -->
|
||||
<Rule Id="SA1313" Action="None"/> <!-- Parameter names should begin with lower-case letter -->
|
||||
<Rule Id="SA1314" Action="Warning"/> <!-- Type parameter names should begin with T -->
|
||||
<Rule Id="SX1309" Action="None"/> <!-- Field names should begin with underscore -->
|
||||
<Rule Id="SX1309S" Action="None"/> <!-- Static field names should begin with underscore -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.MaintainabilityRules">
|
||||
<Rule Id="SA1119" Action="Warning"/> <!-- Statement should not use unnecessary parenthesis -->
|
||||
<Rule Id="SA1400" Action="Warning"/> <!-- Access modifier should be declared -->
|
||||
<Rule Id="SA1401" Action="Warning"/> <!-- Fields should be private -->
|
||||
<Rule Id="SA1402" Action="Warning"/> <!-- File may only contain a single type -->
|
||||
<Rule Id="SA1403" Action="Warning"/> <!-- File may only contain a single namespace -->
|
||||
<Rule Id="SA1404" Action="Warning"/> <!-- Code analysis suppression should have justification -->
|
||||
<Rule Id="SA1405" Action="Warning"/> <!-- Debug.Assert should provide message text -->
|
||||
<Rule Id="SA1406" Action="Warning"/> <!-- Debug.Fail should provide message text -->
|
||||
<Rule Id="SA1407" Action="None"/> <!-- Arithmetic expressions should declare precedence -->
|
||||
<Rule Id="SA1408" Action="Warning"/> <!-- Conditional expressions should declare precedence -->
|
||||
<Rule Id="SA1409" Action="None"/> <!-- Remove unnecessary code -->
|
||||
<Rule Id="SA1410" Action="Warning"/> <!-- Remove delegate parenthesis when possible -->
|
||||
<Rule Id="SA1411" Action="Warning"/> <!-- Attribute constructor should not use unnecessary
|
||||
parenthesis -->
|
||||
<Rule Id="SA1412" Action="None"/> <!-- Store files as UTF-8 with byte order mark -->
|
||||
<Rule Id="SA1413" Action="None"/> <!-- Use trailing comma in multi-line initializers -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.LayoutRules">
|
||||
<Rule Id="SA1500" Action="None"/> <!-- Braces for multi-line statements should not share line -->
|
||||
<Rule Id="SA1501" Action="Warning"/> <!-- Statement should not be on a single line -->
|
||||
<Rule Id="SA1502" Action="None"/> <!-- Element should not be on a single line -->
|
||||
<Rule Id="SA1503" Action="None"/> <!-- Braces should not be omitted -->
|
||||
<Rule Id="SA1504" Action="Warning"/> <!-- All accessors should be single-line or multi-line -->
|
||||
<Rule Id="SA1505" Action="Warning"/> <!-- Opening braces should not be followed by blank line -->
|
||||
<Rule Id="SA1506" Action="Warning"/> <!-- Element documentation headers should not be followed by
|
||||
blank line -->
|
||||
<Rule Id="SA1507" Action="Warning"/> <!-- Code should not contain multiple blank lines in a row -->
|
||||
<Rule Id="SA1508" Action="Warning"/> <!-- Closing braces should not be preceded by blank line -->
|
||||
<Rule Id="SA1509" Action="Warning"/> <!-- Opening braces should not be preceded by blank line -->
|
||||
<Rule Id="SA1510" Action="Warning"/> <!-- Chained statement blocks should not be preceded by blank
|
||||
line -->
|
||||
<Rule Id="SA1511" Action="Warning"/> <!-- While-do footer should not be preceded by blank line -->
|
||||
<Rule Id="SA1512" Action="Warning"/> <!-- Single-line comments should not be followed by blank line -->
|
||||
<Rule Id="SA1513" Action="Warning"/> <!-- Closing brace should be followed by blank line -->
|
||||
<Rule Id="SA1514" Action="Warning"/> <!-- Element documentation header should be preceded by blank
|
||||
line -->
|
||||
<Rule Id="SA1515" Action="Warning"/> <!-- Single-line comment should be preceded by blank line -->
|
||||
<Rule Id="SA1516" Action="Warning"/> <!-- Elements should be separated by blank line -->
|
||||
<Rule Id="SA1517" Action="Warning"/> <!-- Code should not contain blank lines at start of file -->
|
||||
<Rule Id="SA1518" Action="Warning"/> <!-- Use line endings correctly at end of file -->
|
||||
<Rule Id="SA1519" Action="Warning"/> <!-- Braces should not be omitted from multi-line child
|
||||
statement -->
|
||||
<Rule Id="SA1520" Action="Warning"/> <!-- Use braces consistently -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.DocumentationRules">
|
||||
<Rule Id="SA1600" Action="None"/> <!-- Elements should be documented -->
|
||||
<Rule Id="SA1601" Action="None"/> <!-- Partial elements should be documented -->
|
||||
<Rule Id="SA1602" Action="None"/> <!-- Enumeration items should be documented -->
|
||||
<Rule Id="SA1603" Action="None"/> <!-- Documentation should contain valid XML -->
|
||||
<Rule Id="SA1604" Action="Warning"/> <!-- Element documentation should have summary -->
|
||||
<Rule Id="SA1605" Action="Warning"/> <!-- Partial element documentation should have summary -->
|
||||
<Rule Id="SA1606" Action="Warning"/> <!-- Element documentation should have summary text -->
|
||||
<Rule Id="SA1607" Action="Warning"/> <!-- Partial element documentation should have summary text -->
|
||||
<Rule Id="SA1608" Action="Warning"/> <!-- Element documentation should not have default summary -->
|
||||
<Rule Id="SA1609" Action="None"/> <!-- Property documentation should have value -->
|
||||
<Rule Id="SA1610" Action="Warning"/> <!-- Property documentation should have value text -->
|
||||
<Rule Id="SA1611" Action="None"/> <!-- Element parameters should be documented -->
|
||||
<Rule Id="SA1612" Action="Warning"/> <!-- Element parameter documentation should match element
|
||||
parameters -->
|
||||
<Rule Id="SA1613" Action="Warning"/> <!-- Element parameter documentation should declare parameter
|
||||
name -->
|
||||
<Rule Id="SA1614" Action="Warning"/> <!-- Element parameter documentation should have text -->
|
||||
<Rule Id="SA1615" Action="None"/> <!-- Element return value should be documented -->
|
||||
<Rule Id="SA1616" Action="Warning"/> <!-- Element return value documentation should have text -->
|
||||
<Rule Id="SA1617" Action="Warning"/> <!-- Void return value should not be documented -->
|
||||
<Rule Id="SA1618" Action="None"/> <!-- Generic type parameters should be documented -->
|
||||
<Rule Id="SA1619" Action="None"/> <!-- Generic type parameters should be documented partial class -->
|
||||
<Rule Id="SA1620" Action="Warning"/> <!-- Generic type parameter documentation should match type
|
||||
parameters -->
|
||||
<Rule Id="SA1621" Action="Warning"/> <!-- Generic type parameter documentation should declare
|
||||
parameter name -->
|
||||
<Rule Id="SA1622" Action="Warning"/> <!-- Generic type parameter documentation should have text -->
|
||||
<Rule Id="SA1623" Action="Warning"/> <!-- Property summary documentation should match accessors -->
|
||||
<Rule Id="SA1624" Action="Warning"/> <!-- Property summary documentation should omit accessor with
|
||||
restricted access -->
|
||||
<Rule Id="SA1625" Action="Warning"/> <!-- Element documentation should not be copied and pasted -->
|
||||
<Rule Id="SA1626" Action="Warning"/> <!-- Single-line comments should not use documentation style
|
||||
slashes -->
|
||||
<Rule Id="SA1627" Action="Warning"/> <!-- Documentation text should not be empty -->
|
||||
<Rule Id="SA1628" Action="None"/> <!-- Documentation text should begin with a capital letter -->
|
||||
<Rule Id="SA1629" Action="Warning"/> <!-- Documentation text should end with a period -->
|
||||
<Rule Id="SA1630" Action="None"/> <!-- Documentation text should contain whitespace -->
|
||||
<Rule Id="SA1631" Action="None"/> <!-- Documentation should meet character percentage -->
|
||||
<Rule Id="SA1632" Action="None"/> <!-- Documentation text should meet minimum character length -->
|
||||
<Rule Id="SA1633" Action="None"/> <!-- File should have header -->
|
||||
<Rule Id="SA1634" Action="Warning"/> <!-- File header should show copyright -->
|
||||
<Rule Id="SA1635" Action="Warning"/> <!-- File header should have copyright text -->
|
||||
<Rule Id="SA1636" Action="Warning"/> <!-- File header copyright text should match -->
|
||||
<Rule Id="SA1637" Action="Warning"/> <!-- File header should contain file name -->
|
||||
<Rule Id="SA1638" Action="Warning"/> <!-- File header file name documentation should match file name
|
||||
-->
|
||||
<Rule Id="SA1639" Action="Warning"/> <!-- File header should have summary -->
|
||||
<Rule Id="SA1640" Action="Warning"/> <!-- File header should have valid company text -->
|
||||
<Rule Id="SA1641" Action="Warning"/> <!-- File header company name text should match -->
|
||||
<Rule Id="SA1642" Action="Warning"/> <!-- Constructor summary documentation should begin with
|
||||
standard text -->
|
||||
<Rule Id="SA1643" Action="Warning"/> <!-- Destructor summary documentation should begin with
|
||||
standard text -->
|
||||
<Rule Id="SA1644" Action="None"/> <!-- Documentation headers should not contain blank lines -->
|
||||
<Rule Id="SA1645" Action="None"/> <!-- Included documentation file does not exist -->
|
||||
<Rule Id="SA1646" Action="None"/> <!-- Included documentation XPath does not exist -->
|
||||
<Rule Id="SA1647" Action="None"/> <!-- Include node does not contain valid file and path -->
|
||||
<Rule Id="SA1648" Action="Warning"/> <!-- Inheritdoc should be used with inheriting class -->
|
||||
<Rule Id="SA1649" Action="Warning"/> <!-- File name should match first type name -->
|
||||
<Rule Id="SA1650" Action="None"/> <!-- Element documentation should be spelled correctly -->
|
||||
<Rule Id="SA1651" Action="Warning"/> <!-- Do not use placeholder elements -->
|
||||
</Rules>
|
||||
</RuleSet>
|
3
dot.sln
3
dot.sln
@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{AD79881E-7
|
||||
CodeCleanupOnSave.csx = CodeCleanupOnSave.csx
|
||||
Directory.Build.props = Directory.Build.props
|
||||
dot.sln.DotSettings = dot.sln.DotSettings
|
||||
dot.sln.DotSettings.user = dot.sln.DotSettings.user
|
||||
dotnet-tools.json = dotnet-tools.json
|
||||
GenerateResx.targets = GenerateResx.targets
|
||||
git-clean.cmd = git-clean.cmd
|
||||
@ -26,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{AD79881E-7
|
||||
README.md = README.md
|
||||
README.zh-CN.md = README.zh-CN.md
|
||||
SafetyDelUnusedResx.ahk = SafetyDelUnusedResx.ahk
|
||||
stylecop.json = stylecop.json
|
||||
StyleCopAnalyzers.ruleset = StyleCopAnalyzers.ruleset
|
||||
switch-nuget.cmd = switch-nuget.cmd
|
||||
switch-project.cmd = switch-project.cmd
|
||||
switcher.json = switcher.json
|
||||
|
@ -1,6 +1,9 @@
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64
|
||||
x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String
|
||||
x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
@ -14,32 +17,34 @@
|
||||
<Entry.SortBy>
|
||||
<Kind>
|
||||
<Kind.Order>
|
||||
<DeclarationKind>Interface</DeclarationKind>
|
||||
<DeclarationKind>Class</DeclarationKind>
|
||||
<DeclarationKind>Record</DeclarationKind>
|
||||
<DeclarationKind>Enum</DeclarationKind>
|
||||
<DeclarationKind>Struct</DeclarationKind>
|
||||
<DeclarationKind>Delegate</DeclarationKind>
|
||||
<DeclarationKind>Event</DeclarationKind>
|
||||
<DeclarationKind>Constant</DeclarationKind>
|
||||
<DeclarationKind>Field</DeclarationKind>
|
||||
<DeclarationKind>Property</DeclarationKind>
|
||||
<DeclarationKind>Constructor</DeclarationKind>
|
||||
<DeclarationKind>Destructor</DeclarationKind>
|
||||
<DeclarationKind>Delegate</DeclarationKind>
|
||||
<DeclarationKind>Event</DeclarationKind>
|
||||
<DeclarationKind>Enum</DeclarationKind>
|
||||
<DeclarationKind>Interface</DeclarationKind>
|
||||
<DeclarationKind>Property</DeclarationKind>
|
||||
<DeclarationKind>Indexer</DeclarationKind>
|
||||
<DeclarationKind>Method</DeclarationKind>
|
||||
<DeclarationKind>Struct</DeclarationKind>
|
||||
<DeclarationKind>Record</DeclarationKind>
|
||||
<DeclarationKind>Class</DeclarationKind>
|
||||
</Kind.Order>
|
||||
</Kind>
|
||||
<Access>
|
||||
<Access.Order>
|
||||
<AccessModifier>Private</AccessModifier>
|
||||
<AccessModifier>PrivateProtected</AccessModifier>
|
||||
<AccessModifier>Protected</AccessModifier>
|
||||
<AccessModifier>ProtectedInternal</AccessModifier>
|
||||
<AccessModifier>Internal</AccessModifier>
|
||||
<AccessModifier>Public</AccessModifier>
|
||||
<AccessModifier>Internal</AccessModifier>
|
||||
<AccessModifier>ProtectedInternal</AccessModifier>
|
||||
<AccessModifier>Protected</AccessModifier>
|
||||
<AccessModifier>PrivateProtected</AccessModifier>
|
||||
<AccessModifier>Private</AccessModifier>
|
||||
</Access.Order>
|
||||
</Access>
|
||||
<Static />
|
||||
<Readonly />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
|
@ -13,9 +13,8 @@ using Dot;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
public static class AssemblyInfo
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
private const string _VERSION = "1.1.6";
|
||||
public const string ASSEMBLY_COMPANY = "nsnail";
|
||||
public const string ASSEMBLY_COPYRIGHT = $"Copyright (c) 2022 {ASSEMBLY_COMPANY}";
|
||||
public const string ASSEMBLY_FILE_VERSION = _VERSION;
|
||||
@ -25,4 +24,5 @@ public static class AssemblyInfo
|
||||
public const string ASSEMBLY_PRODUCT = "dot";
|
||||
public const string ASSEMBLY_TITLE = "功能全面的实用工具 - 程序员的瑞士军刀";
|
||||
public const string ASSEMBLY_VERSION = _VERSION;
|
||||
private const string _VERSION = "1.1.6";
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
namespace Dot.Color;
|
||||
|
||||
[Description(nameof(Str.ScreenPixelTool))]
|
||||
[Localization(typeof(Str))]
|
||||
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
||||
internal sealed class Main : ToolBase<Option>
|
||||
|
||||
{
|
||||
protected override Task Core()
|
||||
{
|
||||
|
@ -8,21 +8,11 @@ namespace Dot.Color;
|
||||
|
||||
internal sealed class MouseHook : IDisposable
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private readonly struct Msllhookstruct
|
||||
{
|
||||
[FieldOffset(0)] public readonly int X;
|
||||
[FieldOffset(4)] public readonly int Y;
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable once EventNeverSubscribedTo.Global
|
||||
public event MouseEventHandler MouseEvent = delegate { };
|
||||
private const int _WH_MOUSE_LL = 14;
|
||||
private const int _WM_LBUTTONDOWN = 0x0201;
|
||||
private const int _WM_MOUSEMOVE = 0x0200;
|
||||
private bool _disposed;
|
||||
private readonly nint _hookId;
|
||||
private bool _disposed;
|
||||
|
||||
public MouseHook()
|
||||
{
|
||||
@ -34,6 +24,20 @@ internal sealed class MouseHook : IDisposable
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public event MouseEventHandler MouseEvent;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static nint SetHook(Func<int, nint, nint, nint> proc)
|
||||
{
|
||||
using var curProcess = Process.GetCurrentProcess();
|
||||
using var curModule = curProcess.MainModule!;
|
||||
return Win32.SetWindowsHookEx(_WH_MOUSE_LL, proc, Win32.GetModuleHandle(curModule.ModuleName), 0);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
@ -41,10 +45,9 @@ internal sealed class MouseHook : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable S108
|
||||
if (disposing) { }
|
||||
#pragma warning restore S108
|
||||
if (disposing) {
|
||||
//
|
||||
}
|
||||
|
||||
if (_hookId != default) {
|
||||
Win32.UnhookWindowsHookEx(_hookId);
|
||||
@ -53,15 +56,14 @@ internal sealed class MouseHook : IDisposable
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
|
||||
private nint HookCallback(int nCode, nint wParam, nint lParam)
|
||||
{
|
||||
if (nCode < 0 || (_WM_MOUSEMOVE != wParam && _WM_LBUTTONDOWN != wParam)) {
|
||||
if (nCode < 0 || (wParam != _WM_MOUSEMOVE && wParam != _WM_LBUTTONDOWN)) {
|
||||
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
var hookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct))!;
|
||||
MouseEvent(null, new MouseEventArgs( //
|
||||
MouseEvent?.Invoke(null, new MouseEventArgs( //
|
||||
wParam == _WM_MOUSEMOVE ? MouseButtons.None : MouseButtons.Left //
|
||||
, 0 //
|
||||
, hookStruct.X //
|
||||
@ -70,18 +72,11 @@ internal sealed class MouseHook : IDisposable
|
||||
return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private static nint SetHook(Win32.LowLevelMouseProc proc)
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private readonly struct Msllhookstruct
|
||||
{
|
||||
using var curProcess = Process.GetCurrentProcess();
|
||||
using var curModule = curProcess.MainModule!;
|
||||
return Win32.SetWindowsHookEx(_WH_MOUSE_LL, proc, Win32.GetModuleHandle(curModule.ModuleName), 0);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
[FieldOffset(0)] public readonly int X;
|
||||
[FieldOffset(4)] public readonly int Y;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -11,9 +11,9 @@ internal sealed class WinInfo : Form
|
||||
{
|
||||
private const int _WINDOW_SIZE = 480; //窗口大小
|
||||
private const int _ZOOM_RATE = 16; //缩放倍率
|
||||
private bool _disposed;
|
||||
private readonly Graphics _graphics;
|
||||
private readonly PictureBox _pbox;
|
||||
private bool _disposed;
|
||||
|
||||
public WinInfo()
|
||||
{
|
||||
@ -36,16 +36,36 @@ internal sealed class WinInfo : Form
|
||||
Controls.Add(_pbox);
|
||||
}
|
||||
|
||||
|
||||
~WinInfo()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void PboxOnMouseEnter(object sender, EventArgs e)
|
||||
public void UpdateImage(Bitmap img, int x, int y)
|
||||
{
|
||||
// 信息窗口避开鼠标指针指向区域
|
||||
Location = new Point(Location.X, Location.Y == 0 ? Screen.PrimaryScreen!.Bounds.Height - _WINDOW_SIZE : 0);
|
||||
// 计算复制小图的区域
|
||||
var copySize = new Size(_WINDOW_SIZE / _ZOOM_RATE, _WINDOW_SIZE / _ZOOM_RATE);
|
||||
_graphics.DrawImage(img, new Rectangle(0, 0, _WINDOW_SIZE, _WINDOW_SIZE) //
|
||||
, x - copySize.Width / 2 // 左移x,使光标位置居中
|
||||
, y - copySize.Height / 2 // 上移y,使光标位置居中
|
||||
, copySize.Width, copySize.Height, GraphicsUnit.Pixel);
|
||||
using var pen = new Pen(System.Drawing.Color.Aqua); //绘制准星
|
||||
_graphics.DrawRectangle(pen, _WINDOW_SIZE / 2 - _ZOOM_RATE / 2 //
|
||||
, _WINDOW_SIZE / 2 - _ZOOM_RATE / 2 //
|
||||
, _ZOOM_RATE, _ZOOM_RATE);
|
||||
|
||||
// 取鼠标位置颜色
|
||||
var posColor = img.GetPixel(x, y);
|
||||
|
||||
// 绘制底部文字信息
|
||||
_graphics.FillRectangle(Brushes.Black, 0, _WINDOW_SIZE - 30, _WINDOW_SIZE, 30);
|
||||
_graphics.DrawString( //
|
||||
$"{Str.ClickCopyColor} X: {x} Y: {y} RGB({posColor.R},{posColor.G},{posColor.B})"
|
||||
, new Font(FontFamily.GenericSerif, 10) //
|
||||
, Brushes.White, 0, _WINDOW_SIZE - 20);
|
||||
|
||||
// 触发重绘
|
||||
_pbox.Refresh();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@ -64,29 +84,10 @@ internal sealed class WinInfo : Form
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
|
||||
public void UpdateImage(Bitmap img, int x, int y)
|
||||
private void PboxOnMouseEnter(object sender, EventArgs e)
|
||||
{
|
||||
// 计算复制小图的区域
|
||||
var copySize = new Size(_WINDOW_SIZE / _ZOOM_RATE, _WINDOW_SIZE / _ZOOM_RATE);
|
||||
_graphics.DrawImage(img, new Rectangle(0, 0, _WINDOW_SIZE, _WINDOW_SIZE) //
|
||||
, x - copySize.Width / 2 // 左移x,使光标位置居中
|
||||
, y - copySize.Height / 2 // 上移y,使光标位置居中
|
||||
, copySize.Width, copySize.Height, GraphicsUnit.Pixel);
|
||||
using var pen = new Pen(System.Drawing.Color.Aqua); //绘制准星
|
||||
_graphics.DrawRectangle(pen, _WINDOW_SIZE / 2 - _ZOOM_RATE / 2 //
|
||||
, _WINDOW_SIZE / 2 - _ZOOM_RATE / 2 //
|
||||
, _ZOOM_RATE, _ZOOM_RATE);
|
||||
|
||||
// 取鼠标位置颜色
|
||||
var posColor = img.GetPixel(x, y);
|
||||
// 绘制底部文字信息
|
||||
_graphics.FillRectangle(Brushes.Black, 0, _WINDOW_SIZE - 30, _WINDOW_SIZE, 30);
|
||||
_graphics.DrawString($"{Str.ClickCopyColor} X: {x} Y: {y} RGB({posColor.R},{posColor.G},{posColor.B})"
|
||||
, new Font(FontFamily.GenericSerif, 10) //
|
||||
, Brushes.White, 0, _WINDOW_SIZE - 20);
|
||||
// 触发重绘
|
||||
_pbox.Refresh();
|
||||
// 信息窗口避开鼠标指针指向区域
|
||||
Location = new Point(Location.X, Location.Y == 0 ? Screen.PrimaryScreen!.Bounds.Height - _WINDOW_SIZE : 0);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -9,9 +9,9 @@ namespace Dot.Color;
|
||||
internal sealed class WinMain : Form
|
||||
{
|
||||
private readonly Bitmap _bmp;
|
||||
private bool _disposed;
|
||||
private readonly WinInfo _winInfo = new(); //小图窗口
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public WinMain()
|
||||
{
|
||||
|
@ -25,7 +25,6 @@ internal sealed class CsxEditor
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
|
||||
_ = Parallel.ForEach(files, file => {
|
||||
var startInfo = new ProcessStartInfo {
|
||||
FileName = "pngquant"
|
||||
|
@ -9,28 +9,24 @@ internal class DirOption : OptionBase
|
||||
[Localization(typeof(Str))]
|
||||
public IEnumerable<string> ExcludeRegexes { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-f|--filter")]
|
||||
[Description(nameof(Str.FileSearchPattern))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue("*")]
|
||||
public string Filter { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-d|--max-depth")]
|
||||
[Description(nameof(Str.MaxRecursionDepth))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(int.MaxValue)]
|
||||
public int MaxRecursionDepth { get; set; }
|
||||
|
||||
|
||||
[CommandArgument(0, "[path]")]
|
||||
[Description(nameof(Str.FolderPath))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(".")]
|
||||
public string Path { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-w|--write")]
|
||||
[Description(nameof(Str.WriteMode))]
|
||||
[Localization(typeof(Str))]
|
||||
|
173
src/FilesTool.cs
173
src/FilesTool.cs
@ -1,102 +1,24 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
using Panel = Spectre.Console.Panel;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : DirOption
|
||||
internal abstract class FilesTool<TOption> : ToolBase<TOption>
|
||||
where TOption : DirOption
|
||||
{
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
private static readonly object _lock = new(); //线程锁
|
||||
private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量
|
||||
private int _breakCnt; //跳过文件数
|
||||
private ProgressTask _childTask; //子任务进度
|
||||
private int _excludeCnt; //排除文件数
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
#pragma warning disable S2743
|
||||
private static readonly object _lock = new(); //线程锁
|
||||
private int _readCnt; //读取文件数
|
||||
private int _totalCnt; //总文件数
|
||||
private int _writeCnt; //写入文件数
|
||||
private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量
|
||||
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
if (!Opt.WriteMode) {
|
||||
AnsiConsole.MarkupLine(CultureInfo.InvariantCulture, "[gray]{0}[/]", Str.ExerciseMode);
|
||||
}
|
||||
|
||||
IEnumerable<string> fileList;
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(new ProgressBarColumn() //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new PercentageColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskDescriptionColumn { Alignment = Justify.Left } //
|
||||
)
|
||||
.StartAsync(async ctx => {
|
||||
var taskSearchfile = ctx.AddTask(Str.SearchingFile).IsIndeterminate();
|
||||
_childTask = ctx.AddTask("-/-", false);
|
||||
fileList = EnumerateFiles(Opt.Path, Opt.Filter, out _excludeCnt);
|
||||
_totalCnt = fileList.Count();
|
||||
taskSearchfile.StopTask();
|
||||
|
||||
_childTask.MaxValue = _totalCnt;
|
||||
_childTask.StartTask();
|
||||
await Parallel.ForEachAsync(fileList, FileHandle);
|
||||
});
|
||||
|
||||
var grid = new Grid().AddColumn(new GridColumn().NoWrap().PadRight(16))
|
||||
.AddColumn(new GridColumn().Alignment(Justify.Right));
|
||||
|
||||
foreach (var kv in _writeStats.OrderByDescending(x => x.Value).ThenBy(x => x.Key)) {
|
||||
_ = grid.AddRow(kv.Key, kv.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
AnsiConsole.Write(new Panel(grid).Header(Str.WriteFileStats));
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Local
|
||||
private string[] EnumerateFiles(string path, string searchPattern, out int excludeCnt)
|
||||
{
|
||||
var exCnt = 0;
|
||||
if (Opt.ExcludeRegexes?.FirstOrDefault() is null) //默认排除.git 、 node_modules 目录
|
||||
{
|
||||
Opt.ExcludeRegexes = new[] { @"\.git", "node_modules" };
|
||||
}
|
||||
|
||||
|
||||
var excludeRegexes = Opt.ExcludeRegexes.Select(x => new Regex(x));
|
||||
var fileList = Directory.EnumerateFiles(path, searchPattern
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip
|
||||
= FileAttributes.ReparsePoint
|
||||
, IgnoreInaccessible = true
|
||||
, MaxRecursionDepth = Opt.MaxRecursionDepth
|
||||
})
|
||||
.Where(x => {
|
||||
if (!excludeRegexes.Any(y => y.IsMatch(x))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
++exCnt;
|
||||
return false;
|
||||
})
|
||||
.ToArray();
|
||||
excludeCnt = exCnt;
|
||||
return fileList;
|
||||
}
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
return !Directory.Exists(Opt.Path)
|
||||
? throw new ArgumentException(nameof(Opt.Path)
|
||||
, string.Format(CultureInfo.InvariantCulture, Str.PathNotFound, Opt.Path))
|
||||
: CoreInternal();
|
||||
}
|
||||
|
||||
|
||||
protected static FileStream CreateTempFile(out string file)
|
||||
{
|
||||
@ -104,10 +26,6 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
return OpenFileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
}
|
||||
|
||||
|
||||
protected abstract ValueTask FileHandle(string file, CancellationToken cancelToken);
|
||||
|
||||
|
||||
protected static FileStream OpenFileStream(string file, FileMode mode, FileAccess access
|
||||
, FileShare share = FileShare.Read)
|
||||
{
|
||||
@ -131,6 +49,16 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
return fsr;
|
||||
}
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
return !Directory.Exists(Opt.Path)
|
||||
? throw new ArgumentException( //
|
||||
nameof(Opt.Path), string.Format(CultureInfo.InvariantCulture, Str.PathNotFound, Opt.Path))
|
||||
: CoreInternal();
|
||||
}
|
||||
|
||||
protected abstract ValueTask FileHandle(string file, CancellationToken cancelToken);
|
||||
|
||||
protected void ShowMessage(int readCnt, int writeCnt, int breakCnt)
|
||||
{
|
||||
lock (_lock) {
|
||||
@ -146,9 +74,76 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void UpdateStats(string key)
|
||||
{
|
||||
_ = _writeStats.AddOrUpdate(key, 1, (_, oldValue) => oldValue + 1);
|
||||
}
|
||||
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
if (!Opt.WriteMode) {
|
||||
AnsiConsole.MarkupLine(CultureInfo.InvariantCulture, "[gray]{0}[/]", Str.ExerciseMode);
|
||||
}
|
||||
|
||||
IEnumerable<string> fileList;
|
||||
await AnsiConsole.Progress()
|
||||
.Columns( //
|
||||
new ProgressBarColumn() //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new PercentageColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskDescriptionColumn { Alignment = Justify.Left }) //
|
||||
.StartAsync(async ctx => {
|
||||
var taskSearchfile = ctx.AddTask(Str.SearchingFile).IsIndeterminate();
|
||||
_childTask = ctx.AddTask("-/-", false);
|
||||
fileList = EnumerateFiles(Opt.Path, Opt.Filter, out _excludeCnt);
|
||||
_totalCnt = fileList.Count();
|
||||
taskSearchfile.StopTask();
|
||||
|
||||
_childTask.MaxValue = _totalCnt;
|
||||
_childTask.StartTask();
|
||||
await Parallel.ForEachAsync(fileList, FileHandle);
|
||||
});
|
||||
|
||||
var grid = new Grid().AddColumn(new GridColumn().NoWrap().PadRight(16))
|
||||
.AddColumn(new GridColumn().Alignment(Justify.Right));
|
||||
|
||||
foreach (var kv in _writeStats.OrderByDescending(x => x.Value).ThenBy(x => x.Key)) {
|
||||
_ = grid.AddRow(kv.Key, kv.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
AnsiConsole.Write(new Panel(grid).Header(Str.WriteFileStats));
|
||||
}
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Local
|
||||
private string[] EnumerateFiles(string path, string searchPattern, out int excludeCnt)
|
||||
{
|
||||
var exCnt = 0;
|
||||
|
||||
//默认排除.git 、 node_modules 目录
|
||||
if (Opt.ExcludeRegexes?.FirstOrDefault() is null) {
|
||||
Opt.ExcludeRegexes = new[] { @"\.git", "node_modules" };
|
||||
}
|
||||
|
||||
var excludeRegexes = Opt.ExcludeRegexes.Select(x => new Regex(x));
|
||||
var fileList = Directory.EnumerateFiles(path, searchPattern
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip
|
||||
= FileAttributes.ReparsePoint
|
||||
, IgnoreInaccessible = true
|
||||
, MaxRecursionDepth = Opt.MaxRecursionDepth
|
||||
})
|
||||
.Where(x => {
|
||||
if (!excludeRegexes.Any(y => y.IsMatch(x))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
++exCnt;
|
||||
return false;
|
||||
})
|
||||
.ToArray();
|
||||
excludeCnt = exCnt;
|
||||
return fileList;
|
||||
}
|
||||
}
|
193
src/Get/Main.cs
193
src/Get/Main.cs
@ -12,17 +12,104 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
{
|
||||
private const string _PART = "part";
|
||||
|
||||
protected override async Task Core()
|
||||
{
|
||||
using var http = new HttpClient();
|
||||
string attachment = default;
|
||||
long contentLength = default;
|
||||
var table = new Table().AddColumn(Str.DataIdentification).AddColumn(Str.DataContent).AddRow("Url", Opt.Url);
|
||||
await AnsiConsole.Status()
|
||||
.AutoRefresh(true)
|
||||
.Spinner(Spinner.Known.Default)
|
||||
.StartAsync($"{Str.RequestMetaData}: {Opt.Url}", async _ => {
|
||||
using var headRsp = await http.SendAsync(new HttpRequestMessage(HttpMethod.Head, Opt.Url));
|
||||
using var content = headRsp.Content;
|
||||
contentLength = content.Headers.ContentLength ?? 0;
|
||||
attachment = content.Headers.ContentDisposition?.FileName ??
|
||||
Opt.Url[(Opt.Url.LastIndexOf('/') + 1)..];
|
||||
foreach (var kv in content.Headers) {
|
||||
table.AddRow(kv.Key, string.Join(Environment.NewLine, kv.Value));
|
||||
}
|
||||
});
|
||||
AnsiConsole.Write(table);
|
||||
|
||||
var timer = DateTime.Now;
|
||||
var mainFilePath = BuildFilePath(Opt.Output, attachment);
|
||||
await AnsiConsole.Progress()
|
||||
.Columns( //
|
||||
new ProgressBarColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new DownloadedColumn() //
|
||||
, new TransferSpeedColumn() //
|
||||
, new PercentageColumn() //
|
||||
, new TaskDescriptionColumn() //
|
||||
, new RemainingTimeColumn()) //
|
||||
.StartAsync(async ctx => {
|
||||
var tParent = ctx.AddTask($"{Str.TotalProgress} {Str.RemainingTime}:").IsIndeterminate();
|
||||
|
||||
//未知文件长度,单线程下载;
|
||||
if (contentLength == 0) {
|
||||
await using var nets = await http.GetStreamAsync(Opt.Url);
|
||||
await using var fs
|
||||
= new FileStream(mainFilePath, FileMode.CreateNew, FileAccess.Write
|
||||
, FileShare.None);
|
||||
tParent.MaxValue = Opt.BufferSize + 1; //由于文件长度未知, 进度条终点永远至为当前长度+1
|
||||
StreamCopy(nets, fs, x => {
|
||||
tParent.MaxValue += x;
|
||||
tParent.Increment(x);
|
||||
});
|
||||
tParent.MaxValue = tParent.Value; // 写完了
|
||||
tParent.IsIndeterminate(false);
|
||||
tParent.StopTask();
|
||||
}
|
||||
|
||||
//已知文件长度,多线程下载:
|
||||
else {
|
||||
tParent.IsIndeterminate(false);
|
||||
tParent.MaxValue = contentLength;
|
||||
var chunkSize = contentLength / Opt.ChunkNumbers;
|
||||
|
||||
Parallel.For(0, Opt.ChunkNumbers
|
||||
, new ParallelOptions { MaxDegreeOfParallelism = Opt.MaxParallel } //
|
||||
, i => {
|
||||
var tChild = ctx.AddTask(
|
||||
$"{Str.Thread}{i} {Str.RemainingTime}:", maxValue: chunkSize);
|
||||
using var getReq = new HttpRequestMessage(HttpMethod.Get, Opt.Url);
|
||||
var startPos = i * chunkSize;
|
||||
var endPos = startPos + chunkSize - 1;
|
||||
if (i == Opt.ChunkNumbers - 1) {
|
||||
endPos += contentLength % chunkSize;
|
||||
}
|
||||
|
||||
getReq.Headers.Range = new RangeHeaderValue(startPos, endPos);
|
||||
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
using var getRsp
|
||||
= http.Send(getReq, HttpCompletionOption.ResponseHeadersRead);
|
||||
WritePart(getRsp, mainFilePath, i, startPos, endPos, x => {
|
||||
tChild.Increment(x);
|
||||
tParent.Increment(x);
|
||||
});
|
||||
});
|
||||
|
||||
MergeParts(mainFilePath);
|
||||
}
|
||||
});
|
||||
|
||||
AnsiConsole.MarkupLine(
|
||||
$"{Str.DownloadCompleted}, {Str.ElapsedTime}: {DateTime.Now - timer}, {Str.FileSaveLocation}: {mainFilePath}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 给定一个路径(存在的目录,或者存在的目录+存在或不存在的文件)
|
||||
/// 给定一个路径(存在的目录,或者存在的目录+存在或不存在的文件).
|
||||
/// </summary>
|
||||
/// <param name="path">存在的目录,或者存在的目录+存在或不存在的文件</param>
|
||||
/// <param name="file">要写入的文件名</param>
|
||||
/// <returns>返回一个可写的文件完整路径</returns>
|
||||
/// <param name="path">存在的目录,或者存在的目录+存在或不存在的文件.</param>
|
||||
/// <param name="file">要写入的文件名.</param>
|
||||
/// <returns>返回一个可写的文件完整路径.</returns>
|
||||
private static string BuildFilePath(string path, string file)
|
||||
{
|
||||
if (GetUseablePath(ref path))
|
||||
// path 是一个存在的文件,已追加尾标
|
||||
{
|
||||
if (GetUseablePath(ref path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -37,14 +124,15 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
private static bool GetUseablePath(ref string path)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
var ext = Path.GetExtension(path);
|
||||
var ret = false;
|
||||
#pragma warning disable SA1002
|
||||
for (var i = 1;; ++i) {
|
||||
#pragma warning restore SA1002
|
||||
if (File.Exists(path)) {
|
||||
path = Path.Combine(dir!, $"{name}({i}){ext}");
|
||||
ret = true;
|
||||
@ -57,10 +145,10 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
private static void MergeParts(string mainFilePath)
|
||||
{
|
||||
var files = Directory.GetFiles(Path.GetDirectoryName(mainFilePath)! //
|
||||
var files = Directory.GetFiles( //
|
||||
Path.GetDirectoryName(mainFilePath)! //
|
||||
, $"{Path.GetFileName(mainFilePath)}.*.{_PART}", SearchOption.TopDirectoryOnly)
|
||||
.OrderBy(x => x)
|
||||
.ToArray();
|
||||
@ -101,91 +189,4 @@ internal sealed partial class Main : ToolBase<Option>
|
||||
rateHandle(read);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task Core()
|
||||
{
|
||||
using var http = new HttpClient();
|
||||
string attachment = default;
|
||||
long contentLength = default;
|
||||
var table = new Table().AddColumn(Str.DataIdentification).AddColumn(Str.DataContent).AddRow("Url", Opt.Url);
|
||||
await AnsiConsole.Status()
|
||||
.AutoRefresh(true)
|
||||
.Spinner(Spinner.Known.Default)
|
||||
.StartAsync($"{Str.RequestMetaData}: {Opt.Url}", async _ => {
|
||||
using var headRsp = await http.SendAsync(new HttpRequestMessage(HttpMethod.Head, Opt.Url));
|
||||
using var content = headRsp.Content;
|
||||
contentLength = content.Headers.ContentLength ?? 0;
|
||||
attachment = content.Headers.ContentDisposition?.FileName ??
|
||||
Opt.Url[(Opt.Url.LastIndexOf('/') + 1)..];
|
||||
foreach (var kv in content.Headers) {
|
||||
table.AddRow(kv.Key, string.Join(Environment.NewLine, kv.Value));
|
||||
}
|
||||
});
|
||||
AnsiConsole.Write(table);
|
||||
|
||||
|
||||
var timer = DateTime.Now;
|
||||
var mainFilePath = BuildFilePath(Opt.Output, attachment);
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(new ProgressBarColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new DownloadedColumn() //
|
||||
, new TransferSpeedColumn() //
|
||||
, new PercentageColumn() //
|
||||
, new TaskDescriptionColumn() //
|
||||
, new RemainingTimeColumn() //
|
||||
)
|
||||
.StartAsync(async ctx => {
|
||||
var tParent = ctx.AddTask($"{Str.TotalProgress} {Str.RemainingTime}:").IsIndeterminate();
|
||||
if (contentLength == 0) //未知文件长度,单线程下载;
|
||||
{
|
||||
await using var nets = await http.GetStreamAsync(Opt.Url);
|
||||
await using var fs
|
||||
= new FileStream(mainFilePath, FileMode.CreateNew, FileAccess.Write
|
||||
, FileShare.None);
|
||||
tParent.MaxValue = Opt.BufferSize + 1; //由于文件长度未知, 进度条终点永远至为当前长度+1
|
||||
StreamCopy(nets, fs, x => {
|
||||
tParent.MaxValue += x;
|
||||
tParent.Increment(x);
|
||||
});
|
||||
tParent.MaxValue = tParent.Value; // 写完了
|
||||
tParent.IsIndeterminate(false);
|
||||
tParent.StopTask();
|
||||
}
|
||||
else //已知文件长度,多线程下载:
|
||||
{
|
||||
tParent.IsIndeterminate(false);
|
||||
tParent.MaxValue = contentLength;
|
||||
var chunkSize = contentLength / Opt.ChunkNumbers;
|
||||
|
||||
|
||||
Parallel.For(0, Opt.ChunkNumbers
|
||||
, new ParallelOptions { MaxDegreeOfParallelism = Opt.MaxParallel } //
|
||||
, i => {
|
||||
var tChild = ctx.AddTask(
|
||||
$"{Str.Thread}{i} {Str.RemainingTime}:", maxValue: chunkSize);
|
||||
using var getReq = new HttpRequestMessage(HttpMethod.Get, Opt.Url);
|
||||
var startPos = i * chunkSize;
|
||||
var endPos = startPos + chunkSize - 1;
|
||||
if (i == Opt.ChunkNumbers - 1) {
|
||||
endPos += contentLength % chunkSize;
|
||||
}
|
||||
|
||||
getReq.Headers.Range = new RangeHeaderValue(startPos, endPos);
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
using var getRsp
|
||||
= http.Send(getReq, HttpCompletionOption.ResponseHeadersRead);
|
||||
WritePart(getRsp, mainFilePath, i, startPos, endPos, x => {
|
||||
tChild.Increment(x);
|
||||
tParent.Increment(x);
|
||||
});
|
||||
});
|
||||
|
||||
MergeParts(mainFilePath);
|
||||
}
|
||||
});
|
||||
|
||||
AnsiConsole.MarkupLine(
|
||||
$"{Str.DownloadCompleted}, {Str.ElapsedTime}: {DateTime.Now - timer}, {Str.FileSaveLocation}: {mainFilePath}");
|
||||
}
|
||||
}
|
@ -17,21 +17,18 @@ internal sealed class Option : OptionBase
|
||||
[DefaultValue(5)]
|
||||
public int ChunkNumbers { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-m|--max-parallel")]
|
||||
[Description(nameof(Str.MaxParallel))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(5)]
|
||||
public int MaxParallel { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-o|--output")]
|
||||
[Description(nameof(Str.OutputPath))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(".")]
|
||||
public string Output { get; set; }
|
||||
|
||||
|
||||
[CommandArgument(0, "<url>")]
|
||||
[Description(nameof(Str.Url))]
|
||||
[Localization(typeof(Str))]
|
||||
|
@ -16,18 +16,26 @@ internal sealed class Main : ToolBase<Option>
|
||||
private ConcurrentDictionary<string, StringBuilder> _repoRsp; //仓库信息容器
|
||||
private ConcurrentDictionary<string, TaskStatusColumn.Statues> _repoStatus;
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
return !Directory.Exists(Opt.Path)
|
||||
? throw new ArgumentException( //
|
||||
nameof(Opt.Path) //
|
||||
, string.Format(CultureInfo.InvariantCulture, Str.PathNotFound, Opt.Path))
|
||||
: CoreInternal();
|
||||
}
|
||||
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
_gitOutputEnc = Encoding.GetEncoding(Opt.GitOutputEncoding);
|
||||
var progressBar = new ProgressBarColumn { Width = 10 };
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(progressBar //
|
||||
.Columns( //
|
||||
progressBar //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskStatusColumn() //
|
||||
, new TaskDescriptionColumn { Alignment = Justify.Left } //
|
||||
)
|
||||
, new TaskDescriptionColumn { Alignment = Justify.Left }) //
|
||||
.StartAsync(async ctx => {
|
||||
var taskFinder = ctx
|
||||
.AddTask(string.Format(CultureInfo.InvariantCulture, Str.FindGitReps
|
||||
@ -39,8 +47,7 @@ internal sealed class Main : ToolBase<Option>
|
||||
MaxRecursionDepth = Opt.MaxRecursionDepth
|
||||
, RecurseSubdirectories = true
|
||||
, IgnoreInaccessible = true
|
||||
, AttributesToSkip
|
||||
= FileAttributes.ReparsePoint
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
})
|
||||
.Select(x => Directory.GetParent(x)!.FullName);
|
||||
|
||||
@ -67,7 +74,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
$"{Str.ZeroCode}: [green]{_repoStatus.Count(x => x.Value == TaskStatusColumn.Statues
|
||||
.Succeed)}[/]/{_repoStatus.Count}");
|
||||
|
||||
|
||||
foreach (var repo in _repoRsp) {
|
||||
var status = _repoStatus[repo.Key].Desc();
|
||||
table.AddRow(status.Replace(_repoStatus[repo.Key].ToString(), new DirectoryInfo(repo.Key).Name), Opt.Args
|
||||
@ -77,7 +83,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
AnsiConsole.Write(table);
|
||||
}
|
||||
|
||||
|
||||
private async ValueTask DirHandle(KeyValuePair<string, ProgressTask> payload, CancellationToken _)
|
||||
{
|
||||
payload.Value.StartTask();
|
||||
@ -95,7 +100,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
}
|
||||
|
||||
// 启动git进程
|
||||
|
||||
var startInfo = new ProcessStartInfo {
|
||||
CreateNoWindow = true
|
||||
, WorkingDirectory = payload.Key
|
||||
@ -126,12 +130,4 @@ internal sealed class Main : ToolBase<Option>
|
||||
|
||||
payload.Value.StopTask();
|
||||
}
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
return !Directory.Exists(Opt.Path)
|
||||
? throw new ArgumentException(nameof(Opt.Path) //
|
||||
, string.Format(CultureInfo.InvariantCulture, Str.PathNotFound, Opt.Path))
|
||||
: CoreInternal();
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ internal sealed class Option : OptionBase
|
||||
[DefaultValue(int.MaxValue)]
|
||||
public int MaxRecursionDepth { get; set; }
|
||||
|
||||
|
||||
[CommandArgument(0, "[path]")]
|
||||
[Description(nameof(Str.FolderPath))]
|
||||
[Localization(typeof(Str))]
|
||||
|
@ -11,6 +11,7 @@ internal sealed class TaskStatusColumn : ProgressColumn
|
||||
public enum Statues : byte
|
||||
{
|
||||
[Description($"[gray]{nameof(Ready)}[/]")]
|
||||
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
Ready
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
global using System.ComponentModel;
|
||||
global using Dot.Lang;
|
||||
global using Spectre.Console;
|
||||
global using Spectre.Console.Cli;
|
||||
global using Dot.Lang;
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
using System.Globalization;
|
||||
#if NET7_0_WINDOWS
|
||||
using TextCopy;
|
||||
|
@ -1,3 +1,3 @@
|
||||
namespace Dot;
|
||||
|
||||
public interface IOption { }
|
||||
internal interface IOption { }
|
@ -8,7 +8,6 @@ namespace Dot.IP;
|
||||
[Description(nameof(Str.Ip))]
|
||||
[Localization(typeof(Str))]
|
||||
internal sealed class Main : ToolBase<Option>
|
||||
|
||||
{
|
||||
protected override async Task Core()
|
||||
{
|
||||
@ -18,10 +17,10 @@ internal sealed class Main : ToolBase<Option>
|
||||
continue;
|
||||
}
|
||||
|
||||
var output = string.Join(Environment.NewLine
|
||||
var output = string.Join( //
|
||||
Environment.NewLine
|
||||
, item.GetIPProperties()
|
||||
.UnicastAddresses
|
||||
.Where(x => x.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
.UnicastAddresses.Where(x => x.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
.Select(x => @$"{item.Name}: {x.Address}"));
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using NSExt.Extensions;
|
||||
using TextCopy;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Dot.Json;
|
||||
|
||||
[Description(nameof(Str.Json))]
|
||||
@ -15,6 +14,42 @@ internal sealed class Main : ToolBase<Option>
|
||||
{
|
||||
private object _inputObj;
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
var inputText = Opt.InputText;
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
if (inputText.NullOrWhiteSpace()) {
|
||||
inputText = ClipboardService.GetText();
|
||||
}
|
||||
#endif
|
||||
if (inputText.NullOrWhiteSpace()) {
|
||||
throw new ArgumentException(Str.InputTextIsEmpty);
|
||||
}
|
||||
|
||||
try {
|
||||
_inputObj = inputText.Object<object>();
|
||||
}
|
||||
catch (JsonException) {
|
||||
try {
|
||||
inputText = UnescapeString(inputText);
|
||||
_inputObj = inputText.Object<object>();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (JsonException) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
throw new ArgumentException(Str.InvalidJsonString);
|
||||
}
|
||||
|
||||
return CoreInternal();
|
||||
}
|
||||
|
||||
private static string UnescapeString(string text)
|
||||
{
|
||||
return text.Replace("\\\"", "\"");
|
||||
}
|
||||
|
||||
private async Task<string> ConvertToString()
|
||||
{
|
||||
@ -54,42 +89,4 @@ internal sealed class Main : ToolBase<Option>
|
||||
var ret = _inputObj.Json(true);
|
||||
return Task.FromResult(ret);
|
||||
}
|
||||
|
||||
private static string UnescapeString(string text)
|
||||
{
|
||||
return text.Replace("\\\"", "\"");
|
||||
}
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
var inputText = Opt.InputText;
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
if (inputText.NullOrWhiteSpace()) {
|
||||
inputText = ClipboardService.GetText();
|
||||
}
|
||||
#endif
|
||||
if (inputText.NullOrWhiteSpace()) {
|
||||
throw new ArgumentException(Str.InputTextIsEmpty);
|
||||
}
|
||||
|
||||
try {
|
||||
_inputObj = inputText.Object<object>();
|
||||
}
|
||||
catch (JsonException) {
|
||||
try {
|
||||
inputText = UnescapeString(inputText);
|
||||
_inputObj = inputText.Object<object>();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (JsonException) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
throw new ArgumentException(Str.InvalidJsonString);
|
||||
}
|
||||
|
||||
|
||||
return CoreInternal();
|
||||
}
|
||||
}
|
@ -11,21 +11,18 @@ internal sealed class Option : OptionBase
|
||||
[DefaultValue(false)]
|
||||
public bool Compress { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-s|--convert-to-string")]
|
||||
[Description(nameof(Str.JsonToString))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(false)]
|
||||
public bool ConvertToString { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-f|--format")]
|
||||
[Description(nameof(Str.FormatJson))]
|
||||
[Localization(typeof(Str))]
|
||||
[DefaultValue(true)]
|
||||
public bool Format { get; set; }
|
||||
|
||||
|
||||
[CommandArgument(0, "[input text]")]
|
||||
[Description(nameof(Str.TextTobeProcessed))]
|
||||
[Localization(typeof(Str))]
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Dot.Git;
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace Dot;
|
||||
|
||||
@ -11,7 +13,6 @@ internal sealed class Program
|
||||
{
|
||||
var app = new CommandApp();
|
||||
|
||||
|
||||
app.Configure(config => {
|
||||
config.SetApplicationName(AssemblyInfo.ASSEMBLY_PRODUCT);
|
||||
config.SetApplicationVersion(AssemblyInfo.ASSEMBLY_VERSION);
|
||||
@ -33,7 +34,6 @@ internal sealed class Program
|
||||
config.AddCommand<ToLf.Main>(nameof(ToLf).ToLower(CultureInfo.InvariantCulture));
|
||||
config.AddCommand<Get.Main>(nameof(Get).ToLower(CultureInfo.InvariantCulture));
|
||||
|
||||
|
||||
config.ValidateExamples();
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,6 @@ using NSExt.Extensions;
|
||||
using TextCopy;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Dot.Pwd;
|
||||
|
||||
[Description(nameof(Str.RandomPasswordGenerator))]
|
||||
@ -19,7 +18,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".ToCharArray()
|
||||
};
|
||||
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
unsafe {
|
||||
@ -33,7 +31,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.LowerCaseLetter)) {
|
||||
foreach (var c in _charTable[1]) {
|
||||
*(pSource + sourceLen++) = c;
|
||||
@ -46,17 +43,14 @@ internal sealed class Main : ToolBase<Option>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Opt.Type.HasFlag(Option.GenerateTypes.SpecialCharacter)) {
|
||||
foreach (var c in _charTable[3]) {
|
||||
*(pSource + sourceLen++) = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var randScope = new[] { 0, sourceLen };
|
||||
for (var i = 0; i != Opt.Length; ++i) //
|
||||
{
|
||||
for (var i = 0; i != Opt.Length; ++i) {
|
||||
*(pDest + i) = *(pSource + randScope.Rand());
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,11 @@ internal sealed class Option : OptionBase
|
||||
, SpecialCharacter = 0b1000
|
||||
}
|
||||
|
||||
|
||||
[CommandArgument(1, "<password length>")]
|
||||
[Description(nameof(Str.PwdLength))]
|
||||
[Localization(typeof(Str))]
|
||||
public int Length { get; set; }
|
||||
|
||||
|
||||
[CommandArgument(0, "<generate type>")]
|
||||
[Description(nameof(Str.PwdGenerateTypes))]
|
||||
[Localization(typeof(Str))]
|
||||
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.Rbom;
|
||||
|
||||
[Description(nameof(Str.TrimUtf8Bom))]
|
||||
@ -9,27 +8,6 @@ internal sealed class Main : FilesTool<Option>
|
||||
{
|
||||
private readonly byte[] _utf8Bom = { 0xef, 0xbb, 0xbf };
|
||||
|
||||
|
||||
private bool CloneFileWithoutBom(Stream fsr, ref string tempFile)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[_utf8Bom.Length];
|
||||
var readLen = fsr.Read(buffer);
|
||||
if (readLen != _utf8Bom.Length || !buffer.SequenceEqual(_utf8Bom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using var fsw = CreateTempFile(out tempFile);
|
||||
int data;
|
||||
|
||||
|
||||
while ((data = fsr.ReadByte()) != -1) {
|
||||
fsw.WriteByte((byte)data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
@ -58,4 +36,22 @@ internal sealed class Main : FilesTool<Option>
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CloneFileWithoutBom(Stream fsr, ref string tempFile)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[_utf8Bom.Length];
|
||||
var readLen = fsr.Read(buffer);
|
||||
if (readLen != _utf8Bom.Length || !buffer.SequenceEqual(_utf8Bom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using var fsw = CreateTempFile(out tempFile);
|
||||
int data;
|
||||
|
||||
while ((data = fsr.ReadByte()) != -1) {
|
||||
fsw.WriteByte((byte)data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.Rbom;
|
||||
|
||||
internal sealed class Option : DirOption { }
|
22
src/Text/Main.Output.cs
Normal file
22
src/Text/Main.Output.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Dot.Text;
|
||||
|
||||
internal sealed partial class Main
|
||||
{
|
||||
private ref struct Output
|
||||
{
|
||||
public ReadOnlySpan<char> Base64;
|
||||
public ReadOnlySpan<char> Base64DeCode;
|
||||
public ReadOnlySpan<char> Base64DeCodeHex;
|
||||
public ReadOnlySpan<char> EncodingName;
|
||||
public ReadOnlySpan<char> Hex;
|
||||
public ReadOnlySpan<char> HtmlDecode;
|
||||
public ReadOnlySpan<char> HtmlEncode;
|
||||
public ReadOnlySpan<char> Md5;
|
||||
public ReadOnlySpan<char> OriginText;
|
||||
public ReadOnlySpan<char> Sha1;
|
||||
public ReadOnlySpan<char> Sha256;
|
||||
public ReadOnlySpan<char> Sha512;
|
||||
public ReadOnlySpan<char> UrlDecode;
|
||||
public ReadOnlySpan<char> UrlEncode;
|
||||
}
|
||||
}
|
@ -1,38 +1,39 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using NSExt.Extensions;
|
||||
#if NET7_0_WINDOWS
|
||||
using TextCopy;
|
||||
using System.Diagnostics;
|
||||
using TextCopy;
|
||||
#endif
|
||||
|
||||
namespace Dot.Text;
|
||||
|
||||
[Description(nameof(Str.TextTool))]
|
||||
[Localization(typeof(Str))]
|
||||
internal sealed class Main : ToolBase<Option>
|
||||
internal sealed partial class Main : ToolBase<Option>
|
||||
{
|
||||
private ref struct Output
|
||||
#if NET7_0_WINDOWS
|
||||
protected override async Task Core()
|
||||
#else
|
||||
protected override Task Core()
|
||||
#endif
|
||||
{
|
||||
public ReadOnlySpan<char> Base64;
|
||||
public ReadOnlySpan<char> Base64DeCode;
|
||||
public ReadOnlySpan<char> Base64DeCodeHex;
|
||||
public ReadOnlySpan<char> EncodingName;
|
||||
public ReadOnlySpan<char> Hex;
|
||||
public ReadOnlySpan<char> HtmlDecode;
|
||||
public ReadOnlySpan<char> HtmlEncode;
|
||||
public ReadOnlySpan<char> Md5;
|
||||
public ReadOnlySpan<char> OriginText;
|
||||
public ReadOnlySpan<char> Sha1;
|
||||
public ReadOnlySpan<char> Sha256;
|
||||
public ReadOnlySpan<char> Sha512;
|
||||
public ReadOnlySpan<char> UrlDecode;
|
||||
public ReadOnlySpan<char> UrlEncode;
|
||||
#if NET7_0_WINDOWS
|
||||
if (Opt.Text.NullOrEmpty()) {
|
||||
Opt.Text = await ClipboardService.GetTextAsync();
|
||||
}
|
||||
#endif
|
||||
if (Opt.Text.NullOrEmpty()) {
|
||||
throw new ArgumentException(Str.InputTextIsEmpty);
|
||||
}
|
||||
|
||||
ParseAndShow(Opt.Text);
|
||||
#if !NET7_0_WINDOWS
|
||||
return Task.CompletedTask;
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Output BuildOutput(string text, Encoding enc)
|
||||
{
|
||||
@ -53,7 +54,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
ret.HtmlDecode = text.HtmlDe();
|
||||
ret.HtmlEncode = text.Html();
|
||||
|
||||
|
||||
if (!text.IsBase64String()) {
|
||||
return ret;
|
||||
}
|
||||
@ -73,7 +73,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
ret.Base64DeCodeHex = base64DeHex.String();
|
||||
ret.Base64DeCode = enc.GetString(base64DeHex);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -84,7 +83,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
var unicodeLittleEndian = BuildOutput(text, Encoding.Unicode);
|
||||
var unicodeBigEndian = BuildOutput(text, Encoding.BigEndianUnicode);
|
||||
|
||||
|
||||
PrintOutput(ansi);
|
||||
PrintOutput(utf8);
|
||||
PrintOutput(unicodeLittleEndian);
|
||||
@ -116,26 +114,4 @@ html-decode: {o.HtmlDecode}
|
||||
Process.Start("explorer", file);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
protected override async Task Core()
|
||||
#else
|
||||
protected override Task Core()
|
||||
#endif
|
||||
{
|
||||
#if NET7_0_WINDOWS
|
||||
if (Opt.Text.NullOrEmpty()) {
|
||||
Opt.Text = await ClipboardService.GetTextAsync();
|
||||
}
|
||||
#endif
|
||||
if (Opt.Text.NullOrEmpty()) {
|
||||
throw new ArgumentException(Str.InputTextIsEmpty);
|
||||
}
|
||||
|
||||
|
||||
ParseAndShow(Opt.Text);
|
||||
#if !NET7_0_WINDOWS
|
||||
return Task.CompletedTask;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.Text;
|
||||
|
||||
internal sealed class Option : OptionBase
|
||||
|
155
src/Time/Main.cs
155
src/Time/Main.cs
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net.Sockets;
|
||||
|
||||
@ -33,9 +32,82 @@ internal sealed class Main : ToolBase<Option>
|
||||
|
||||
private double _offsetAvg;
|
||||
|
||||
|
||||
private int _successCnt;
|
||||
|
||||
protected override async Task Core()
|
||||
{
|
||||
await AnsiConsole.Progress()
|
||||
.Columns( //
|
||||
new TaskDescriptionColumn() //
|
||||
, new ProgressBarColumn() //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskStatusColumn() //
|
||||
, new TaskResultColumn())
|
||||
.StartAsync(async ctx => {
|
||||
var tasks = _ntpServers.ToDictionary( //
|
||||
server => server, server => ctx.AddTask(server, false).IsIndeterminate());
|
||||
|
||||
await Parallel.ForEachAsync(
|
||||
tasks, new ParallelOptions { MaxDegreeOfParallelism = _MAX_DEGREE_OF_PARALLELISM }
|
||||
, ServerHandle);
|
||||
|
||||
_offsetAvg = tasks.Where(x => x.Value.State.Status() == TaskStatusColumn.Statues.Succeed)
|
||||
.Average(x => x.Value.State.Result().TotalMilliseconds);
|
||||
});
|
||||
|
||||
AnsiConsole.MarkupLine(CultureInfo.InvariantCulture, Str.NtpReceiveDone, $"[green]{_successCnt}[/]"
|
||||
, _ntpServers.Length, $"[yellow]{_offsetAvg:f2}[/]");
|
||||
|
||||
if (Opt.Sync) {
|
||||
SetSysteTime(DateTime.Now.AddMilliseconds(-_offsetAvg));
|
||||
AnsiConsole.MarkupLine($"[green]{Str.LocalTimeSyncDone}[/]");
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task Run()
|
||||
{
|
||||
await Core();
|
||||
if (Opt.KeepSession) {
|
||||
var table = new Table().HideHeaders()
|
||||
.AddColumn(new TableColumn(string.Empty))
|
||||
.AddColumn(new TableColumn(string.Empty))
|
||||
.Caption(Str.PressAnyKey)
|
||||
.AddRow(Str.NtpClock, DateTime.Now.AddMilliseconds(-_offsetAvg).ToString("O"))
|
||||
.AddRow(Str.LocalClock, DateTime.Now.ToString("O"));
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var task = AnsiConsole.Live(table)
|
||||
.StartAsync(async ctx => {
|
||||
while (!cts.IsCancellationRequested) {
|
||||
ctx.UpdateTarget(
|
||||
table.UpdateCell(
|
||||
0, 1, DateTime.Now.AddMilliseconds(-_offsetAvg).ToString("O"))
|
||||
.UpdateCell(1, 1, DateTime.Now.ToString("O")));
|
||||
await Task.Delay(100, CancellationToken.None);
|
||||
}
|
||||
});
|
||||
|
||||
_ = await AnsiConsole.Console.Input.ReadKeyAsync(true, cts.Token);
|
||||
cts.Cancel();
|
||||
await task;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetSysteTime(DateTime time)
|
||||
{
|
||||
var timeToSet = new Win32.Systemtime {
|
||||
wDay = (ushort)time.Day
|
||||
, wDayOfWeek = (ushort)time.DayOfWeek
|
||||
, wHour = (ushort)time.Hour
|
||||
, wMilliseconds = (ushort)time.Millisecond
|
||||
, wMinute = (ushort)time.Minute
|
||||
, wMonth = (ushort)time.Month
|
||||
, wSecond = (ushort)time.Second
|
||||
, wYear = (ushort)time.Year
|
||||
};
|
||||
Win32.SetLocalTime(timeToSet);
|
||||
}
|
||||
|
||||
private TimeSpan GetNtpOffset(string server)
|
||||
{
|
||||
@ -46,7 +118,6 @@ internal sealed class Main : ToolBase<Option>
|
||||
ReceiveTimeout = Opt.Timeout
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
socket.Connect(server, _NTP_PORT);
|
||||
_ = socket.Send(ntpData);
|
||||
@ -91,82 +162,4 @@ internal sealed class Main : ToolBase<Option>
|
||||
payload.Value.StopTask();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
private static void SetSysteTime(DateTime time)
|
||||
{
|
||||
var timeToSet = new Win32.Systemtime {
|
||||
wDay = (ushort)time.Day
|
||||
, wDayOfWeek = (ushort)time.DayOfWeek
|
||||
, wHour = (ushort)time.Hour
|
||||
, wMilliseconds = (ushort)time.Millisecond
|
||||
, wMinute = (ushort)time.Minute
|
||||
, wMonth = (ushort)time.Month
|
||||
, wSecond = (ushort)time.Second
|
||||
, wYear = (ushort)time.Year
|
||||
};
|
||||
Win32.SetLocalTime(timeToSet);
|
||||
}
|
||||
|
||||
|
||||
protected override async Task Core()
|
||||
{
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(new TaskDescriptionColumn() //
|
||||
, new ProgressBarColumn() //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskStatusColumn() //
|
||||
, new TaskResultColumn())
|
||||
.StartAsync(async ctx => {
|
||||
var tasks = _ntpServers.ToDictionary(server => server
|
||||
, server => ctx.AddTask(server, false)
|
||||
.IsIndeterminate());
|
||||
|
||||
await Parallel.ForEachAsync(
|
||||
tasks, new ParallelOptions { MaxDegreeOfParallelism = _MAX_DEGREE_OF_PARALLELISM }
|
||||
, ServerHandle);
|
||||
|
||||
_offsetAvg = tasks.Where(x => x.Value.State.Status() == TaskStatusColumn.Statues.Succeed)
|
||||
.Average(x => x.Value.State.Result().TotalMilliseconds);
|
||||
});
|
||||
|
||||
AnsiConsole.MarkupLine(CultureInfo.InvariantCulture, Str.NtpReceiveDone, $"[green]{_successCnt}[/]"
|
||||
, _ntpServers.Length, $"[yellow]{_offsetAvg:f2}[/]");
|
||||
|
||||
|
||||
if (Opt.Sync) {
|
||||
SetSysteTime(DateTime.Now.AddMilliseconds(-_offsetAvg));
|
||||
AnsiConsole.MarkupLine($"[green]{Str.LocalTimeSyncDone}[/]");
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task Run()
|
||||
{
|
||||
await Core();
|
||||
if (Opt.KeepSession) {
|
||||
var table = new Table().HideHeaders()
|
||||
.AddColumn(new TableColumn(string.Empty))
|
||||
.AddColumn(new TableColumn(string.Empty))
|
||||
.Caption(Str.PressAnyKey)
|
||||
.AddRow(Str.NtpClock, DateTime.Now.AddMilliseconds(-_offsetAvg).ToString("O"))
|
||||
.AddRow(Str.LocalClock, DateTime.Now.ToString("O"));
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var task = AnsiConsole.Live(table)
|
||||
.StartAsync(async ctx => {
|
||||
while (!cts.IsCancellationRequested) {
|
||||
ctx.UpdateTarget(
|
||||
table.UpdateCell(
|
||||
0, 1, DateTime.Now.AddMilliseconds(-_offsetAvg).ToString("O"))
|
||||
.UpdateCell(1, 1, DateTime.Now.ToString("O")));
|
||||
await Task.Delay(100, CancellationToken.None);
|
||||
}
|
||||
});
|
||||
|
||||
_ = await AnsiConsole.Console.Input.ReadKeyAsync(true, cts.Token);
|
||||
cts.Cancel();
|
||||
await task;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ internal sealed class Option : OptionBase
|
||||
[DefaultValue(false)]
|
||||
public bool Sync { get; set; }
|
||||
|
||||
|
||||
[CommandOption("-t|--timeout")]
|
||||
[Description(nameof(Str.TimeoutMillSecs))]
|
||||
[Localization(typeof(Str))]
|
||||
|
@ -1,7 +1,6 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
|
||||
using NSExt.Extensions;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
@ -11,8 +10,8 @@ internal sealed class TaskStatusColumn : ProgressColumn
|
||||
{
|
||||
public enum Statues : byte
|
||||
{
|
||||
[Description($"[gray]{nameof(Ready)}[/]")]
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
[Description($"[gray]{nameof(Ready)}[/]")]
|
||||
Ready
|
||||
|
||||
, [Description($"[yellow]{nameof(Connecting)}[/]")]
|
||||
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.ToLf;
|
||||
|
||||
[Description(nameof(Str.ConvertEndOfLineToLF))]
|
||||
@ -14,6 +13,7 @@ internal sealed class Main : FilesTool<Option>
|
||||
var hasWrote = false;
|
||||
var isBin = false;
|
||||
string tmpFile;
|
||||
|
||||
// ReSharper disable once TooWideLocalVariableScope
|
||||
int data;
|
||||
|
||||
@ -23,7 +23,6 @@ internal sealed class Main : FilesTool<Option>
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await using var fsw = CreateTempFile(out tmpFile);
|
||||
|
||||
while ((data = fsr.ReadByte()) != -1) {
|
||||
@ -49,10 +48,7 @@ internal sealed class Main : FilesTool<Option>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable S2583
|
||||
if (hasWrote && !isBin) {
|
||||
#pragma warning restore S2583
|
||||
if (Opt.WriteMode) {
|
||||
File.Copy(tmpFile, file, true);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.ToLf;
|
||||
|
||||
internal sealed class Option : DirOption { }
|
@ -1,8 +1,17 @@
|
||||
namespace Dot;
|
||||
|
||||
internal abstract class ToolBase<TOption> : Command<TOption> where TOption : OptionBase
|
||||
internal abstract class ToolBase<TOption> : Command<TOption>
|
||||
where TOption : OptionBase
|
||||
{
|
||||
protected TOption Opt { get; private set; }
|
||||
|
||||
public override int Execute(CommandContext context, TOption settings)
|
||||
{
|
||||
Opt = settings;
|
||||
Run().Wait();
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected abstract Task Core();
|
||||
|
||||
protected virtual async Task Run()
|
||||
@ -13,11 +22,4 @@ internal abstract class ToolBase<TOption> : Command<TOption> where TOption : Opt
|
||||
_ = AnsiConsole.Console.Input.ReadKey(true);
|
||||
}
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, TOption settings)
|
||||
{
|
||||
Opt = settings;
|
||||
Run().Wait();
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
using NSExt.Extensions;
|
||||
|
||||
namespace Dot.Trim;
|
||||
@ -9,6 +8,32 @@ namespace Dot.Trim;
|
||||
[Localization(typeof(Str))]
|
||||
internal sealed class Main : FilesTool<Option>
|
||||
{
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
int spacesCnt;
|
||||
|
||||
await using var fsrw = OpenFileStream(file, FileMode.Open, FileAccess.ReadWrite);
|
||||
|
||||
if (fsrw is null || fsrw.Length == 0 || (spacesCnt = GetSpacesCnt(fsrw)) == 0) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = fsrw.Seek(0, SeekOrigin.Begin);
|
||||
if (!fsrw.IsTextStream()) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Opt.WriteMode) {
|
||||
fsrw.SetLength(fsrw.Length - spacesCnt);
|
||||
}
|
||||
|
||||
ShowMessage(0, 1, 0);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
}
|
||||
|
||||
private static int GetSpacesCnt(Stream fsr)
|
||||
{
|
||||
var trimLen = 0;
|
||||
@ -30,33 +55,4 @@ internal sealed class Main : FilesTool<Option>
|
||||
|
||||
return trimLen;
|
||||
}
|
||||
|
||||
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
int spacesCnt;
|
||||
|
||||
await using var fsrw = OpenFileStream(file, FileMode.Open, FileAccess.ReadWrite);
|
||||
|
||||
|
||||
if (fsrw is null || fsrw.Length == 0 || (spacesCnt = GetSpacesCnt(fsrw)) == 0) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = fsrw.Seek(0, SeekOrigin.Begin);
|
||||
if (!fsrw.IsTextStream()) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Opt.WriteMode) {
|
||||
fsrw.SetLength(fsrw.Length - spacesCnt);
|
||||
}
|
||||
|
||||
ShowMessage(0, 1, 0);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
||||
namespace Dot.Trim;
|
||||
|
||||
internal sealed class Option : DirOption { }
|
40
src/Win32.cs
40
src/Win32.cs
@ -5,28 +5,13 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
public static partial class Win32
|
||||
internal static partial class Win32
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal ref struct Systemtime
|
||||
{
|
||||
[FieldOffset(6)] public ushort wDay;
|
||||
[FieldOffset(4)] public ushort wDayOfWeek;
|
||||
[FieldOffset(8)] public ushort wHour;
|
||||
[FieldOffset(14)] public ushort wMilliseconds;
|
||||
[FieldOffset(10)] public ushort wMinute;
|
||||
[FieldOffset(2)] public ushort wMonth;
|
||||
[FieldOffset(12)] public ushort wSecond;
|
||||
[FieldOffset(0)] public ushort wYear;
|
||||
}
|
||||
|
||||
public delegate nint LowLevelMouseProc(int nCode, nint wParam, nint lParam);
|
||||
public const int SW_HIDE = 0;
|
||||
|
||||
private const string _GDI32_DLL = "gdi32.dll";
|
||||
private const string _KERNEL32_DLL = "kernel32.dll";
|
||||
private const string _USER32_DLL = "user32.dll";
|
||||
public const int SW_HIDE = 0;
|
||||
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam);
|
||||
@ -34,11 +19,9 @@ public static partial class Win32
|
||||
[LibraryImport(_KERNEL32_DLL)]
|
||||
internal static partial nint GetConsoleWindow();
|
||||
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint GetDesktopWindow();
|
||||
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL, StringMarshalling = StringMarshalling.Utf16)]
|
||||
internal static partial nint GetModuleHandle(string lpModuleName);
|
||||
|
||||
@ -51,12 +34,12 @@ public static partial class Win32
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int ReleaseDC(nint hWnd, nint dc);
|
||||
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL)]
|
||||
internal static partial void SetLocalTime(Systemtime st);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, nint hMod, uint dwThreadId);
|
||||
internal static partial nint SetWindowsHookEx(int idHook, Func<int, nint, nint, nint> lpfn, nint hMod
|
||||
, uint dwThreadId);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@ -65,4 +48,19 @@ public static partial class Win32
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool UnhookWindowsHookEx(nint hhk);
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal ref struct Systemtime
|
||||
{
|
||||
#pragma warning disable SA1307
|
||||
[FieldOffset(6)] public ushort wDay;
|
||||
[FieldOffset(4)] public ushort wDayOfWeek;
|
||||
[FieldOffset(8)] public ushort wHour;
|
||||
[FieldOffset(14)] public ushort wMilliseconds;
|
||||
[FieldOffset(10)] public ushort wMinute;
|
||||
[FieldOffset(2)] public ushort wMonth;
|
||||
[FieldOffset(12)] public ushort wSecond;
|
||||
[FieldOffset(0)] public ushort wYear;
|
||||
#pragma warning restore SA1307
|
||||
}
|
||||
}
|
@ -5,14 +5,15 @@
|
||||
<UseWindowsForms Condition="'$(TargetFramework)' == 'net7.0-windows'">true</UseWindowsForms>
|
||||
<RootNamespace>Dot</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AnalysisLevel>preview-all</AnalysisLevel>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||
<MSBuildWarningsAsErrors>true</MSBuildWarningsAsErrors>
|
||||
<WarningsAsErrors>true</WarningsAsErrors>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<CodeAnalysisRuleSet>../StyleCopAnalyzers.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0-windows'">
|
||||
<DefineConstants>$(DefineConstants);NET7_0_WINDOWS</DefineConstants>
|
||||
@ -21,6 +22,10 @@
|
||||
<PackageReference Include="NSExt" Version="1.0.9-alpha.0.1"/>
|
||||
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.48"/>
|
||||
<PackageReference Include="Spectre.Console.NS" Version="0.45.1-preview.0.48"/>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net7.0-windows'" Include="TextCopy" Version="6.2.0"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
9
stylecop.json
Normal file
9
stylecop.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"indentation": {
|
||||
"useTabs": false,
|
||||
"indentationSize": 4
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,6 @@ namespace Dot.Tests;
|
||||
|
||||
public class TestGet
|
||||
{
|
||||
private static string GetFileSha1(string file)
|
||||
{
|
||||
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return BitConverter.ToString(SHA1.HashData(fs));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DownloadFile()
|
||||
{
|
||||
@ -35,4 +29,10 @@ public class TestGet
|
||||
|
||||
[SetUp]
|
||||
public void Setup() { }
|
||||
|
||||
private static string GetFileSha1(string file)
|
||||
{
|
||||
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return BitConverter.ToString(SHA1.HashData(fs));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user