mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
<refactor> * 去除ShellProgressBar组件
This commit is contained in:
parent
a77ba1966b
commit
de2e156fa5
@ -4,6 +4,19 @@ namespace Dot.Color;
|
||||
|
||||
public 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);
|
||||
|
||||
private const string _GDI32_DLL = "gdi32.dll";
|
||||
@ -35,6 +48,10 @@ 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);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace Dot.Color;
|
||||
|
||||
|
128
src/FilesTool.cs
Normal file
128
src/FilesTool.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Panel = Spectre.Console.Panel;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
public abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : DirOption
|
||||
{
|
||||
private int _breakCnt; //跳过文件数
|
||||
private ProgressTask _childTask; //子任务进度
|
||||
private static readonly object _lock = new(); //线程锁
|
||||
private int _readCnt; //读取文件数
|
||||
private int _totalCnt; //总文件数
|
||||
private int _writeCnt; //写入文件数
|
||||
private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量
|
||||
|
||||
protected FilesTool(TOption opt) : base(opt)
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
}
|
||||
|
||||
private static string[] EnumerateFiles(string path, string searchPattern)
|
||||
{
|
||||
var fileList = Directory
|
||||
.EnumerateFiles(path, searchPattern
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
})
|
||||
.Where(x => !new[] { ".git", "node_modules" }.Any(
|
||||
y => x.Contains(y, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToArray();
|
||||
return fileList;
|
||||
}
|
||||
|
||||
|
||||
protected static void CopyFile(string source, string dest)
|
||||
{
|
||||
try {
|
||||
File.Copy(source, dest, true);
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
File.SetAttributes(dest, new FileInfo(dest).Attributes & ~FileAttributes.ReadOnly);
|
||||
File.Copy(source, dest, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected FileStream CreateTempFile(out string file)
|
||||
{
|
||||
file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.tmp");
|
||||
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)
|
||||
{
|
||||
FileStream fsr = null;
|
||||
try {
|
||||
fsr = new FileStream(file, mode, access, share);
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
try {
|
||||
File.SetAttributes(file, new FileInfo(file).Attributes & ~FileAttributes.ReadOnly);
|
||||
fsr = new FileStream(file, mode, access, share);
|
||||
}
|
||||
catch (Exception) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
|
||||
return fsr;
|
||||
}
|
||||
|
||||
protected void ShowMessage(int readCnt, int writeCnt, int breakCnt)
|
||||
{
|
||||
lock (_lock) {
|
||||
_readCnt += readCnt;
|
||||
_writeCnt += writeCnt;
|
||||
_breakCnt += breakCnt;
|
||||
if (readCnt > 0) _childTask.Increment(1);
|
||||
_childTask.Description
|
||||
= $"{Str.Read}: [green]{_readCnt}[/]/{_totalCnt}, {Str.Write}: [red]{_writeCnt}[/], {Str.Break}: [gray]{_breakCnt}[/]";
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateStats(string key)
|
||||
{
|
||||
_writeStats.AddOrUpdate(key, 1, (_, oldValue) => oldValue + 1);
|
||||
}
|
||||
|
||||
public override async Task Run()
|
||||
{
|
||||
if (!Opt.WriteMode) AnsiConsole.MarkupLine("[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);
|
||||
_totalCnt = fileList.Count();
|
||||
taskSearchfile.IsIndeterminate(false);
|
||||
taskSearchfile.Increment(100);
|
||||
|
||||
_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) grid.AddRow(kv.Key, kv.Value.ToString());
|
||||
|
||||
AnsiConsole.Write(new Panel(grid).Header(Str.WriteFileStats));
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
global using ShellProgressBar;
|
||||
global using Spectre.Console;
|
||||
global using CommandLine;
|
||||
global using Dot.Lang;
|
@ -1,7 +1,6 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Dot;
|
||||
using Spectre.Console;
|
||||
|
||||
Type[] LoadVerbs()
|
||||
{
|
||||
|
@ -1,57 +1,11 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NSExt.Extensions;
|
||||
|
||||
namespace Dot.RmBlank;
|
||||
|
||||
public sealed class Main : ToolBase<Option>, IDisposable
|
||||
public sealed class Main : FilesTool<Option>
|
||||
{
|
||||
private int _breakCnt;
|
||||
private bool _disposed;
|
||||
private static readonly object _lockObj = new();
|
||||
private int _procedCnt;
|
||||
private int _replaceCnt;
|
||||
private ChildProgressBar _step2Bar;
|
||||
private int _totalCnt;
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
|
||||
~Main()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing) _step2Bar?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private async ValueTask FileHandle(string file, CancellationToken _)
|
||||
{
|
||||
_step2Bar.Tick();
|
||||
ShowMessage(1, 0, 0);
|
||||
int spacesCnt;
|
||||
|
||||
await using var fsrw = OpenFileStream(file, FileMode.Open, FileAccess.ReadWrite);
|
||||
|
||||
if (!Opt.WriteMode) { //测试,只读模式
|
||||
ShowMessage(0, 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fsrw is null || fsrw.Length == 0 || (spacesCnt = GetSpacesCnt(fsrw)) == 0) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
fsrw.Seek(0, SeekOrigin.Begin);
|
||||
if (!fsrw.IsTextStream()) return;
|
||||
ShowMessage(0, 1, 0);
|
||||
fsrw.SetLength(fsrw.Length - spacesCnt);
|
||||
}
|
||||
|
||||
private static int GetSpacesCnt(Stream fsr)
|
||||
{
|
||||
var trimLen = 0;
|
||||
@ -71,43 +25,28 @@ public sealed class Main : ToolBase<Option>, IDisposable
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int removeCnt, int breakCnt)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_replaceCnt += removeCnt;
|
||||
_breakCnt += breakCnt;
|
||||
_step2Bar.Message = string.Format(Str.ShowMessageTemp, _procedCnt, _totalCnt, _replaceCnt, _breakCnt);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override async Task Run()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
fsrw.Seek(0, SeekOrigin.Begin);
|
||||
if (!fsrw.IsTextStream()) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
using var step1Bar = new IndeterminateProgressBar(Str.SearchingFile, DefaultProgressBarOptions);
|
||||
|
||||
|
||||
var fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
|
||||
step1Bar.Message = string.Format(Str.SearchingFileOK, _totalCnt);
|
||||
step1Bar.Finished();
|
||||
if (_totalCnt == 0) return;
|
||||
|
||||
|
||||
_step2Bar = step1Bar.Spawn(_totalCnt, string.Empty, DefaultProgressBarOptions);
|
||||
|
||||
await Parallel.ForEachAsync(fileList, FileHandle);
|
||||
if (Opt.WriteMode) fsrw.SetLength(fsrw.Length - spacesCnt);
|
||||
ShowMessage(0, 1, 0);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
}
|
||||
}
|
@ -1,110 +1,47 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dot.RmBom;
|
||||
|
||||
public sealed class Main : ToolBase<Option>, IDisposable
|
||||
public sealed class Main : FilesTool<Option>
|
||||
{
|
||||
private int _breakCnt;
|
||||
private bool _disposed;
|
||||
private static readonly object _lockObj = new();
|
||||
private int _procedCnt;
|
||||
private ChildProgressBar _step2Bar;
|
||||
private int _totalCnt;
|
||||
private int _trimCnt;
|
||||
private readonly byte[] _utf8Bom = { 0xef, 0xbb, 0xbf };
|
||||
private readonly byte[] _utf8Bom = { 0xef, 0xbb, 0xbf };
|
||||
|
||||
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
|
||||
~Main()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private bool CreateTempFile(Stream fsr, string 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 = OpenFileStream(tmpFile, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
|
||||
using var fsw = CreateTempFile(out tempFile);
|
||||
int data;
|
||||
while ((data = fsr.ReadByte()) != -1) fsw.WriteByte((byte)data);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing) _step2Bar?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private async ValueTask FileHandle(string file, CancellationToken _)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
{
|
||||
_step2Bar.Tick();
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
var tmpFile = $"{file}.tmp";
|
||||
bool isReplaced;
|
||||
string tmpFile = default;
|
||||
await using (var fsr = OpenFileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
|
||||
if (!Opt.WriteMode) { //测试,只读模式
|
||||
ShowMessage(0, 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fsr is null) {
|
||||
ShowMessage(0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
isReplaced = CreateTempFile(fsr, tmpFile);
|
||||
if (CloneFileWithoutBom(fsr, ref tmpFile)) {
|
||||
if (Opt.WriteMode) CopyFile(tmpFile, file);
|
||||
ShowMessage(0, 1, 0);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
}
|
||||
else {
|
||||
ShowMessage(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isReplaced) {
|
||||
MoveFile(tmpFile, file);
|
||||
ShowMessage(0, 1, 0);
|
||||
}
|
||||
else {
|
||||
ShowMessage(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int replaceCnt, int breakCnt)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_trimCnt += replaceCnt;
|
||||
_breakCnt += breakCnt;
|
||||
_step2Bar.Message = string.Format(Str.ShowMessageTemp, _procedCnt, _totalCnt, _trimCnt, _breakCnt);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override async Task Run()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
|
||||
|
||||
using var step1Bar = new IndeterminateProgressBar(Str.SearchingFile, DefaultProgressBarOptions);
|
||||
|
||||
|
||||
var fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
|
||||
step1Bar.Message = string.Format(Str.SearchingFileOK, _totalCnt);
|
||||
step1Bar.Finished();
|
||||
if (_totalCnt == 0) return;
|
||||
|
||||
_step2Bar = step1Bar.Spawn(_totalCnt, string.Empty, DefaultProgressBarOptions);
|
||||
|
||||
await Parallel.ForEachAsync(fileList, FileHandle);
|
||||
if (tmpFile != default) File.Delete(tmpFile);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dot.Color;
|
||||
|
||||
namespace Dot.Time;
|
||||
|
||||
@ -22,23 +22,11 @@ public sealed class Main : ToolBase<Option>
|
||||
, Failed
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private 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;
|
||||
}
|
||||
|
||||
private const int _MAX_DEGREE_OF_PARALLELISM = 10;
|
||||
private const int _NTP_PORT = 123;
|
||||
private const string _OUTPUT_TEMP = "{0,-30} {1,20} {2,20}";
|
||||
private static readonly object _lockObj = new();
|
||||
private static readonly object _lock = new();
|
||||
private int _procedCnt;
|
||||
private readonly int _serverCnt;
|
||||
|
||||
@ -101,7 +89,7 @@ public sealed class Main : ToolBase<Option>
|
||||
|
||||
private static void DrawTextInConsole(int left, int top, string text)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
lock (_lock) {
|
||||
Console.SetCursorPosition(left, top);
|
||||
Console.Write(text);
|
||||
}
|
||||
@ -177,22 +165,19 @@ public sealed class Main : ToolBase<Option>
|
||||
}
|
||||
|
||||
|
||||
[DllImport("Kernel32.dll")]
|
||||
private static extern void SetLocalTime(Systemtime st);
|
||||
|
||||
private static void SetSysteTime(DateTime time)
|
||||
{
|
||||
var timeToSet = new 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
|
||||
};
|
||||
SetLocalTime(timeToSet);
|
||||
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);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "AccessToDisposedClosure")]
|
||||
|
@ -1,34 +1,17 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Spectre.Console;
|
||||
using Panel = Spectre.Console.Panel;
|
||||
|
||||
namespace Dot.ToLf;
|
||||
|
||||
public sealed class Main : ToolBase<Option>
|
||||
public sealed class Main : FilesTool<Option>
|
||||
{
|
||||
private int _breakCnt;
|
||||
private ProgressTask _fileTask;
|
||||
private static readonly object _lockObj = new();
|
||||
private long _procedCnt;
|
||||
private int _replaceCnt;
|
||||
private readonly ConcurrentDictionary<string, int> _replacedStats = new();
|
||||
private int _totalCnt;
|
||||
public Main(Option opt) : base(opt) { }
|
||||
|
||||
public Main(Option opt) : base(opt)
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
}
|
||||
|
||||
private async ValueTask FileHandle(string file, CancellationToken _)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
var tmpFile = $"{file}.tmp";
|
||||
var isReplaced = false;
|
||||
var isBin = false;
|
||||
int data;
|
||||
var hasWrote = false;
|
||||
var isBin = false;
|
||||
string tmpFile;
|
||||
int data;
|
||||
|
||||
await using (var fsr = OpenFileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
|
||||
if (fsr is null) {
|
||||
@ -37,18 +20,18 @@ public sealed class Main : ToolBase<Option>
|
||||
}
|
||||
|
||||
|
||||
await using var fsw = OpenFileStream(tmpFile, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
await using var fsw = CreateTempFile(out tmpFile);
|
||||
|
||||
while ((data = fsr.ReadByte()) != -1) {
|
||||
switch (data) {
|
||||
case 0x0d when fsr.ReadByte() == 0x0a: // crlf windows
|
||||
fsw.WriteByte(0x0a);
|
||||
isReplaced = true;
|
||||
hasWrote = true;
|
||||
continue;
|
||||
case 0x0d: //cr macos
|
||||
fsw.WriteByte(0x0a);
|
||||
fsr.Seek(-1, SeekOrigin.Current);
|
||||
isReplaced = true;
|
||||
hasWrote = true;
|
||||
continue;
|
||||
case 0x00 or 0xff: //非文本文件
|
||||
isBin = true;
|
||||
@ -63,11 +46,10 @@ public sealed class Main : ToolBase<Option>
|
||||
}
|
||||
|
||||
|
||||
if (isReplaced && !isBin) {
|
||||
if (Opt.WriteMode) //测试,只读模式
|
||||
MoveFile(tmpFile, file);
|
||||
if (hasWrote && !isBin) {
|
||||
if (Opt.WriteMode) CopyFile(tmpFile, file);
|
||||
ShowMessage(0, 1, 0);
|
||||
_replacedStats.AddOrUpdate(Path.GetExtension(file), 1, (_, oldValue) => oldValue + 1);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
}
|
||||
else {
|
||||
ShowMessage(0, 0, 1);
|
||||
@ -75,51 +57,4 @@ public sealed class Main : ToolBase<Option>
|
||||
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
|
||||
|
||||
private void ShowMessage(int procedCnt, int replaceCnt, int breakCnt)
|
||||
{
|
||||
lock (_lockObj) {
|
||||
_procedCnt += procedCnt;
|
||||
_replaceCnt += replaceCnt;
|
||||
_breakCnt += breakCnt;
|
||||
if (procedCnt > 0) _fileTask.Increment(1);
|
||||
_fileTask.Description
|
||||
= $"{Str.Read}: [green]{_procedCnt}[/]/{_totalCnt}, {Str.Write}: [red]{_replaceCnt}[/], {Str.Break}: [gray]{_breakCnt}[/]";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
public override async Task Run()
|
||||
{
|
||||
if (!Opt.WriteMode) AnsiConsole.MarkupLine("[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();
|
||||
_fileTask = ctx.AddTask("-/-", false);
|
||||
fileList = EnumerateFiles(Opt.Path, Opt.Filter);
|
||||
_totalCnt = fileList.Count();
|
||||
taskSearchfile.IsIndeterminate(false);
|
||||
taskSearchfile.Increment(100);
|
||||
|
||||
_fileTask.MaxValue = _totalCnt;
|
||||
_fileTask.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 _replacedStats) grid.AddRow(kv.Key, kv.Value.ToString());
|
||||
|
||||
AnsiConsole.Write(new Panel(grid).Header(Str.WriteFileStats));
|
||||
}
|
||||
}
|
@ -5,16 +5,6 @@ public abstract class ToolBase<TOption> : ITool where TOption : OptionBase
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
private static SpinLock _spinlock;
|
||||
|
||||
protected readonly ProgressBarOptions //
|
||||
DefaultProgressBarOptions = new() {
|
||||
MessageEncodingName = "utf-8"
|
||||
, ProgressBarOnBottom = true
|
||||
, ForegroundColor = ConsoleColor.Yellow
|
||||
, ForegroundColorDone = ConsoleColor.DarkGreen
|
||||
, BackgroundColor = ConsoleColor.DarkGray
|
||||
, BackgroundCharacter = '\u2500'
|
||||
, ProgressCharacter = '\u2500'
|
||||
};
|
||||
|
||||
protected TOption Opt { get; }
|
||||
|
||||
@ -37,18 +27,6 @@ public abstract class ToolBase<TOption> : ITool where TOption : OptionBase
|
||||
}
|
||||
}
|
||||
|
||||
protected static IEnumerable<string> EnumerateFiles(string path, string searchPattern)
|
||||
{
|
||||
var fileList = Directory
|
||||
.EnumerateFiles(path, searchPattern
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
})
|
||||
.Where(x => !new[] { ".git", "node_modules" }.Any(
|
||||
y => x.Contains(y, StringComparison.OrdinalIgnoreCase)));
|
||||
return fileList;
|
||||
}
|
||||
|
||||
protected static Task LoadingAnimate(int x, int y, out CancellationTokenSource cts)
|
||||
{
|
||||
@ -72,37 +50,6 @@ public abstract class ToolBase<TOption> : ITool where TOption : OptionBase
|
||||
});
|
||||
}
|
||||
|
||||
protected static void MoveFile(string source, string dest)
|
||||
{
|
||||
try {
|
||||
File.Move(source, dest, true);
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
File.SetAttributes(dest, new FileInfo(dest).Attributes & ~FileAttributes.ReadOnly);
|
||||
File.Move(source, dest, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected static FileStream OpenFileStream(string file, FileMode mode, FileAccess access
|
||||
, FileShare share = FileShare.Read)
|
||||
{
|
||||
FileStream fsr = null;
|
||||
try {
|
||||
fsr = new FileStream(file, mode, access, share);
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
try {
|
||||
File.SetAttributes(file, new FileInfo(file).Attributes & ~FileAttributes.ReadOnly);
|
||||
fsr = new FileStream(file, mode, access, share);
|
||||
}
|
||||
catch (Exception) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
|
||||
return fsr;
|
||||
}
|
||||
|
||||
public abstract Task Run();
|
||||
}
|
@ -26,7 +26,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1"/>
|
||||
<PackageReference Include="NSExt" Version="1.0.6"/>
|
||||
<PackageReference Include="ShellProgressBar" Version="5.2.0"/>
|
||||
<PackageReference Include="Spectre.Console" Version="0.45.1-preview.0.46"/>
|
||||
<PackageReference Include="TextCopy" Version="6.2.0"/>
|
||||
</ItemGroup>
|
||||
|
Loading…
x
Reference in New Issue
Block a user