mirror of
https://github.com/nsnail/dot.git
synced 2025-06-19 13:58:16 +08:00
<refactor> ui美化...
This commit is contained in:
132
src/Git/Main.cs
132
src/Git/Main.cs
@ -1,3 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using NSExt.Extensions;
|
||||
@ -6,13 +7,9 @@ namespace Dot.Git;
|
||||
|
||||
public class Main : ToolBase<Option>
|
||||
{
|
||||
private const int _POS_Y_MSG = 74; //git command rsp 显示的位置 y
|
||||
private const int _POST_Y_LOADING = 70; //loading 动画显示的位置 y
|
||||
private const int _REP_PATH_LENGTH_LIMIT = 32; //仓库路径长度显示截断阈值
|
||||
private (int x, int y) _cursorPosBackup; //光标位置备份
|
||||
private readonly Encoding _gitOutputEnc; //git command rsp 编码
|
||||
private List<string> _repoPathList; //仓库目录列表
|
||||
|
||||
private readonly Encoding _gitOutputEnc; //git command rsp 编码
|
||||
private ConcurrentDictionary<string, StringBuilder> _repoRsp; //仓库信息容器
|
||||
private ConcurrentDictionary<string, TaskStatusColumn.Statues> _repoStatus;
|
||||
|
||||
public Main(Option opt) : base(opt)
|
||||
{
|
||||
@ -23,25 +20,24 @@ public class Main : ToolBase<Option>
|
||||
}
|
||||
|
||||
|
||||
private async ValueTask DirHandle(string dir, CancellationToken cancelToken)
|
||||
private async ValueTask DirHandle(KeyValuePair<string, ProgressTask> payload, CancellationToken _)
|
||||
{
|
||||
var row = _repoPathList.FindIndex(x => x == dir); // 行号
|
||||
var tAnimate = LoadingAnimate(_POST_Y_LOADING, _cursorPosBackup.y + row, out var cts);
|
||||
payload.Value.StartTask();
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Executing);
|
||||
|
||||
// 打印 git command rsp
|
||||
void ExecRspReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (e.Data is null) return;
|
||||
var msg = Encoding.UTF8.GetString(_gitOutputEnc.GetBytes(e.Data));
|
||||
ConcurrentWrite(_POS_Y_MSG, _cursorPosBackup.y + row, new string(' ', Console.WindowWidth - _POS_Y_MSG));
|
||||
ConcurrentWrite(_POS_Y_MSG, _cursorPosBackup.y + row, msg);
|
||||
_repoRsp[payload.Key].Append(msg.EscapeMarkup());
|
||||
}
|
||||
|
||||
// 启动git进程
|
||||
{
|
||||
var startInfo = new ProcessStartInfo {
|
||||
CreateNoWindow = true
|
||||
, WorkingDirectory = dir
|
||||
, WorkingDirectory = payload.Key
|
||||
, FileName = "git"
|
||||
, Arguments = Opt.Args
|
||||
, UseShellExecute = false
|
||||
@ -54,58 +50,78 @@ public class Main : ToolBase<Option>
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
await p.WaitForExitAsync();
|
||||
|
||||
payload.Value.IsIndeterminate(false);
|
||||
if (p.ExitCode == 0) {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Succeed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Succeed
|
||||
, (_, _) => TaskStatusColumn.Statues.Succeed);
|
||||
payload.Value.Increment(100);
|
||||
}
|
||||
else {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Failed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Failed
|
||||
, (_, _) => TaskStatusColumn.Statues.Failed);
|
||||
payload.Value.Increment(0);
|
||||
}
|
||||
}
|
||||
|
||||
cts.Cancel();
|
||||
await tAnimate;
|
||||
cts.Dispose();
|
||||
}
|
||||
|
||||
private void StashCurorPos()
|
||||
{
|
||||
_cursorPosBackup = Console.GetCursorPosition();
|
||||
}
|
||||
|
||||
|
||||
public override async Task Run()
|
||||
protected override async Task Core()
|
||||
{
|
||||
// 查找git仓库目录
|
||||
{
|
||||
Console.Write(Str.FindGitReps, Opt.Path);
|
||||
StashCurorPos();
|
||||
var progressBar = new ProgressBarColumn { Width = 10 };
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(progressBar //
|
||||
, new ElapsedTimeColumn() //
|
||||
, new SpinnerColumn() //
|
||||
, new TaskStatusColumn() //
|
||||
, new TaskDescriptionColumn { Alignment = Justify.Left } //
|
||||
)
|
||||
.StartAsync(async ctx => {
|
||||
var taskFinder = ctx.AddTask(string.Format(Str.FindGitReps, Opt.Path)).IsIndeterminate();
|
||||
var paths = Directory.GetDirectories(Opt.Path, ".git" //
|
||||
, new EnumerationOptions //
|
||||
{
|
||||
MaxRecursionDepth = Opt.MaxRecursionDepth
|
||||
, RecurseSubdirectories = true
|
||||
, IgnoreInaccessible = true
|
||||
, AttributesToSkip
|
||||
= FileAttributes.ReparsePoint
|
||||
})
|
||||
.Select(x => Directory.GetParent(x)!.FullName);
|
||||
|
||||
var tAnimate = LoadingAnimate(_cursorPosBackup.x, _cursorPosBackup.y, out var cts);
|
||||
_repoPathList = Directory.GetDirectories(Opt.Path, ".git" //
|
||||
, new EnumerationOptions //
|
||||
{
|
||||
MaxRecursionDepth = Opt.MaxRecursionDepth
|
||||
, RecurseSubdirectories = true
|
||||
, IgnoreInaccessible = true
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
})
|
||||
.Select(x => Directory.GetParent(x)!.FullName)
|
||||
.ToList();
|
||||
cts.Cancel();
|
||||
await tAnimate;
|
||||
cts.Dispose();
|
||||
_repoRsp = new ConcurrentDictionary<string, StringBuilder>();
|
||||
_repoStatus = new ConcurrentDictionary<string, TaskStatusColumn.Statues>();
|
||||
var tasks = new Dictionary<string, ProgressTask>();
|
||||
foreach (var path in paths) {
|
||||
_repoRsp.TryAdd(path, new StringBuilder());
|
||||
_repoStatus.TryAdd(path, default);
|
||||
var task = ctx.AddTask(new DirectoryInfo(path).Name, false).IsIndeterminate();
|
||||
tasks.Add(path, task);
|
||||
}
|
||||
|
||||
taskFinder.IsIndeterminate(false);
|
||||
taskFinder.Increment(100);
|
||||
taskFinder.State.Status(TaskStatusColumn.Statues.Succeed);
|
||||
|
||||
await Parallel.ForEachAsync(tasks, DirHandle);
|
||||
});
|
||||
|
||||
var table = new Table().AddColumn(new TableColumn(Str.Repository) { Width = 50 })
|
||||
.AddColumn(new TableColumn(Str.Command))
|
||||
.AddColumn(new TableColumn(Str.Response) { Width = 50 })
|
||||
.Caption(
|
||||
$"{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
|
||||
, status.Replace(_repoStatus[repo.Key].ToString(), repo.Value.ToString()));
|
||||
}
|
||||
|
||||
// 打印git仓库目录
|
||||
{
|
||||
Console.WriteLine(Str.Ok);
|
||||
StashCurorPos();
|
||||
|
||||
var i = 0;
|
||||
Console.WriteLine( //
|
||||
string.Join(Environment.NewLine
|
||||
, _repoPathList.Select(
|
||||
x => $"{++i}: {new DirectoryInfo(x).Name.Sub(0, _REP_PATH_LENGTH_LIMIT)}"))
|
||||
//
|
||||
);
|
||||
}
|
||||
|
||||
// 并行执行git命令
|
||||
await Parallel.ForEachAsync(_repoPathList, DirHandle);
|
||||
Console.SetCursorPosition(_cursorPosBackup.x, _cursorPosBackup.y + _repoPathList.Count);
|
||||
AnsiConsole.Write(table);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user