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