From 1ce8262bb3338b75b355ba4fe373bae025cf00c9 Mon Sep 17 00:00:00 2001 From: nsnail Date: Thu, 15 Dec 2022 08:53:42 +0800 Subject: [PATCH] .. --- .editorconfig | 3 + StyleCopAnalyzers.ruleset | 238 +++++++++++++++++++++++++++++++ dot.sln | 3 + dot.sln.DotSettings | 33 +++-- src/AssemblyInfo.cs | 4 +- src/Color/Main.cs | 5 +- src/Color/MouseHook.cs | 71 +++++---- src/Color/WinInfo.cs | 55 +++---- src/Color/WinMain.cs | 2 +- src/CsxEditor.cs | 1 - src/DirOption.cs | 4 - src/FilesTool.cs | 177 +++++++++++------------ src/Get/Main.cs | 197 ++++++++++++------------- src/Get/Option.cs | 3 - src/Git/Main.cs | 38 +++-- src/Git/Option.cs | 1 - src/Git/TaskStatusColumn.cs | 1 + src/GlobalUsings.cs | 4 +- src/Guid/Main.cs | 1 - src/IOption.cs | 2 +- src/IP/Main.cs | 11 +- src/Json/Main.cs | 75 +++++----- src/Json/Option.cs | 3 - src/{Option.cs => OptionBase.cs} | 0 src/Program.cs | 6 +- src/Pwd/Main.cs | 8 +- src/Pwd/Option.cs | 2 - src/Rbom/Main.cs | 40 +++--- src/Rbom/Option.cs | 1 - src/Text/Main.Output.cs | 22 +++ src/Text/Main.cs | 64 +++------ src/Text/Option.cs | 1 - src/Time/Main.cs | 155 ++++++++++---------- src/Time/Option.cs | 1 - src/Time/TaskStatusColumn.cs | 3 +- src/ToLf/Main.cs | 6 +- src/ToLf/Option.cs | 1 - src/ToolBase.cs | 22 +-- src/Trim/Main.cs | 56 ++++---- src/Trim/Option.cs | 1 - src/Win32.cs | 40 +++--- src/dot.csproj | 7 +- stylecop.json | 9 ++ tests/TestGet.cs | 12 +- 44 files changed, 793 insertions(+), 596 deletions(-) create mode 100644 StyleCopAnalyzers.ruleset rename src/{Option.cs => OptionBase.cs} (100%) create mode 100644 src/Text/Main.Output.cs create mode 100644 stylecop.json diff --git a/.editorconfig b/.editorconfig index 0808675..960af82 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 \ No newline at end of file diff --git a/StyleCopAnalyzers.ruleset b/StyleCopAnalyzers.ruleset new file mode 100644 index 0000000..33887e1 --- /dev/null +++ b/StyleCopAnalyzers.ruleset @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dot.sln b/dot.sln index 1a36ef8..47c43e2 100644 --- a/dot.sln +++ b/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 diff --git a/dot.sln.DotSettings b/dot.sln.DotSettings index fdb713b..a095ae7 100644 --- a/dot.sln.DotSettings +++ b/dot.sln.DotSettings @@ -1,7 +1,10 @@ - Required + 1 + 1 + Required Required - { protected override Task Core() { diff --git a/src/Color/MouseHook.cs b/src/Color/MouseHook.cs index 13ecd29..0d410f5 100644 --- a/src/Color/MouseHook.cs +++ b/src/Color/MouseHook.cs @@ -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 const int _WH_MOUSE_LL = 14; + private const int _WM_LBUTTONDOWN = 0x0201; + private const int _WM_MOUSEMOVE = 0x0200; + 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 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,35 +56,27 @@ 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( // - wParam == _WM_MOUSEMOVE ? MouseButtons.None : MouseButtons.Left // - , 0 // - , hookStruct.X // - , hookStruct.Y // - , 0)); + MouseEvent?.Invoke(null, new MouseEventArgs( // + wParam == _WM_MOUSEMOVE ? MouseButtons.None : MouseButtons.Left // + , 0 // + , hookStruct.X // + , hookStruct.Y // + , 0)); 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 \ No newline at end of file diff --git a/src/Color/WinInfo.cs b/src/Color/WinInfo.cs index f4da95c..fd157bc 100644 --- a/src/Color/WinInfo.cs +++ b/src/Color/WinInfo.cs @@ -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 \ No newline at end of file diff --git a/src/Color/WinMain.cs b/src/Color/WinMain.cs index 34bec60..78dfe70 100644 --- a/src/Color/WinMain.cs +++ b/src/Color/WinMain.cs @@ -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() { diff --git a/src/CsxEditor.cs b/src/CsxEditor.cs index e72c596..073ea71 100644 --- a/src/CsxEditor.cs +++ b/src/CsxEditor.cs @@ -25,7 +25,6 @@ internal sealed class CsxEditor }) .ToArray(); - _ = Parallel.ForEach(files, file => { var startInfo = new ProcessStartInfo { FileName = "pngquant" diff --git a/src/DirOption.cs b/src/DirOption.cs index cbb4648..ffac2fc 100644 --- a/src/DirOption.cs +++ b/src/DirOption.cs @@ -9,28 +9,24 @@ internal class DirOption : OptionBase [Localization(typeof(Str))] public IEnumerable 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))] diff --git a/src/FilesTool.cs b/src/FilesTool.cs index 295a08d..cf9317d 100644 --- a/src/FilesTool.cs +++ b/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 : ToolBase where TOption : DirOption +internal abstract class FilesTool : ToolBase + where TOption : DirOption { - private int _breakCnt; //跳过文件数 - private ProgressTask _childTask; //子任务进度 - private int _excludeCnt; //排除文件数 - // ReSharper disable once StaticMemberInGenericType - #pragma warning disable S2743 - private static readonly object _lock = new(); //线程锁 + private static readonly object _lock = new(); //线程锁 + private readonly ConcurrentDictionary _writeStats = new(); //写入统计:后缀,数量 + private int _breakCnt; //跳过文件数 + private ProgressTask _childTask; //子任务进度 + private int _excludeCnt; //排除文件数 private int _readCnt; //读取文件数 private int _totalCnt; //总文件数 private int _writeCnt; //写入文件数 - private readonly ConcurrentDictionary _writeStats = new(); //写入统计:后缀,数量 - - private async Task CoreInternal() - { - if (!Opt.WriteMode) { - AnsiConsole.MarkupLine(CultureInfo.InvariantCulture, "[gray]{0}[/]", Str.ExerciseMode); - } - - IEnumerable 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 : ToolBase 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 : ToolBase 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 : ToolBase 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 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; + } } \ No newline at end of file diff --git a/src/Get/Main.cs b/src/Get/Main.cs index 9a13425..07f4d07 100644 --- a/src/Get/Main.cs +++ b/src/Get/Main.cs @@ -12,17 +12,104 @@ internal sealed partial class Main : ToolBase