<feat> + git 输出编码参数

This commit is contained in:
nsnail 2022-12-06 21:26:17 +08:00
parent 6bd37be483
commit db83230391
9 changed files with 105 additions and 78 deletions

View File

@ -2,48 +2,47 @@ using System.Runtime.InteropServices;
namespace Dot.Color; namespace Dot.Color;
public static class Win32 public static partial class Win32
{ {
public delegate nint LowLevelMouseProc(int nCode, nint wParam, nint lParam); public delegate nint LowLevelMouseProc(int nCode, nint wParam, nint lParam);
private const string _GDI32_DLL = "gdi32.dll"; private const string _GDI32_DLL = "gdi32.dll";
private const string _KERNEL32_DLL = "kernel32.dll"; private const string _KERNEL32_DLL = "kernel32.dll";
private const string _USER32_DLL = "user32.dll"; private const string _USER32_DLL = "user32.dll";
public const int SW_HIDE = 0; public const int SW_HIDE = 0;
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam); internal static partial nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam);
[DllImport(_KERNEL32_DLL)] [LibraryImport(_KERNEL32_DLL)]
public static extern nint GetConsoleWindow(); internal static partial nint GetConsoleWindow();
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern nint GetDesktopWindow(); internal static partial nint GetDesktopWindow();
[DllImport(_KERNEL32_DLL, CharSet = CharSet.Unicode)] [LibraryImport(_KERNEL32_DLL, StringMarshalling = StringMarshalling.Utf16)]
public static extern nint GetModuleHandle(string lpModuleName); internal static partial nint GetModuleHandle(string lpModuleName);
[DllImport(_GDI32_DLL)] [LibraryImport(_GDI32_DLL)]
public static extern uint GetPixel(nint dc, int x, int y); internal static partial uint GetPixel(nint dc, int x, int y);
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern nint GetWindowDC(nint hWnd); internal static partial nint GetWindowDC(nint hWnd);
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern int ReleaseDC(nint hWnd, nint dc); internal static partial int ReleaseDC(nint hWnd, nint dc);
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern nint SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, nint hMod, uint dwThreadId); internal static partial nint SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, nint hMod, uint dwThreadId);
[DllImport(_USER32_DLL)] [LibraryImport(_USER32_DLL)]
public static extern bool ShowWindow(nint hWnd, int nCmdShow);
[DllImport(_USER32_DLL)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(nint hhk); internal static partial bool ShowWindow(nint hWnd, int nCmdShow);
[LibraryImport(_USER32_DLL)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool UnhookWindowsHookEx(nint hhk);
} }

View File

@ -6,32 +6,37 @@ namespace Dot.Git;
public class Main : ToolBase<Option> public class Main : ToolBase<Option>
{ {
private const int _POS_Y_MSG = 74; private const int _POS_Y_MSG = 74; //git command rsp 显示的位置 y
private const int _POST_Y_LOADING = 70; private const int _POST_Y_LOADING = 70; //loading 动画显示的位置 y
private const int _REP_MAX_LENGTH = 32; private const int _REP_PATH_LENGTH_LIMIT = 32; //仓库路径长度显示截断阈值
private (int x, int y) _cursorInitPos; private (int x, int y) _cursorPosBackup; //光标位置备份
private List<string> _dirList; private readonly Encoding _gitOutputEnc; //git command rsp 编码
private Encoding _encGbk; private List<string> _repoPathList; //仓库目录列表
public Main(Option opt) : base(opt) { } public Main(Option opt) : base(opt)
{
_gitOutputEnc = Encoding.GetEncoding(Opt.GitOutputEncoding);
if (!Directory.Exists(Opt.Path))
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
}
private async ValueTask DirHandle(string dir, CancellationToken cancelToken) private async ValueTask DirHandle(string dir, CancellationToken cancelToken)
{ {
var index = _dirList.FindIndex(x => x == dir); var index = _repoPathList.FindIndex(x => x == dir);
var tAnimate = LoadingAnimate(_POST_Y_LOADING, _cursorInitPos.y + index, out var cts); var tAnimate = LoadingAnimate(_POST_Y_LOADING, _cursorPosBackup.y + index, out var cts);
void Write(object sender, DataReceivedEventArgs e) void ExecRspReceived(object sender, DataReceivedEventArgs e)
{ {
if (e.Data is null) return; if (e.Data is null) return;
var msg = Encoding.UTF8.GetString(_encGbk.GetBytes(e.Data)); var msg = Encoding.UTF8.GetString(_gitOutputEnc.GetBytes(e.Data));
ConcurrentWrite(_POS_Y_MSG, _cursorInitPos.y + index, new string(' ', Console.WindowWidth - _POS_Y_MSG)); ConcurrentWrite(_POS_Y_MSG, _cursorPosBackup.y + index, new string(' ', Console.WindowWidth - _POS_Y_MSG));
ConcurrentWrite(_POS_Y_MSG, _cursorInitPos.y + index, msg); ConcurrentWrite(_POS_Y_MSG, _cursorPosBackup.y + index, msg);
} }
var gitStartInfo = new ProcessStartInfo { var startInfo = new ProcessStartInfo {
CreateNoWindow = true CreateNoWindow = true
, WorkingDirectory = dir , WorkingDirectory = dir
, FileName = "git" , FileName = "git"
@ -40,9 +45,9 @@ public class Main : ToolBase<Option>
, RedirectStandardOutput = true , RedirectStandardOutput = true
, RedirectStandardError = true , RedirectStandardError = true
}; };
using var p = Process.Start(gitStartInfo); using var p = Process.Start(startInfo);
p.OutputDataReceived += Write; p!.OutputDataReceived += ExecRspReceived;
p.ErrorDataReceived += Write; p.ErrorDataReceived += ExecRspReceived;
p.BeginOutputReadLine(); p.BeginOutputReadLine();
p.BeginErrorReadLine(); p.BeginErrorReadLine();
await p.WaitForExitAsync(); await p.WaitForExitAsync();
@ -53,37 +58,45 @@ public class Main : ToolBase<Option>
cts.Dispose(); cts.Dispose();
} }
private void StashCurorPos()
{
_cursorPosBackup = Console.GetCursorPosition();
}
public override async Task Run() public override async Task Run()
{ {
if (!Directory.Exists(Opt.Path))
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
_encGbk = Encoding.GetEncoding("gbk");
Console.Write(Str.FindGitReps, Opt.Path); Console.Write(Str.FindGitReps, Opt.Path);
_cursorInitPos = Console.GetCursorPosition(); StashCurorPos();
var tAnimate = LoadingAnimate(_cursorInitPos.x, _cursorInitPos.y, out var cts);
_dirList = Directory.GetDirectories(Opt.Path, ".git", SearchOption.AllDirectories) var tAnimate = LoadingAnimate(_cursorPosBackup.x, _cursorPosBackup.y, out var cts);
_repoPathList = Directory.GetDirectories(Opt.Path, ".git", SearchOption.AllDirectories)
.Select(x => Directory.GetParent(x)!.FullName) .Select(x => Directory.GetParent(x)!.FullName)
.ToList(); .ToList();
cts.Cancel(); cts.Cancel();
await tAnimate; await tAnimate;
cts.Dispose(); cts.Dispose();
Console.WriteLine(Str.Ok); Console.WriteLine(Str.Ok);
_cursorInitPos = Console.GetCursorPosition(); StashCurorPos();
{
var i = 0; var i = 0;
Console.WriteLine(string.Join(Environment.NewLine Console.WriteLine( //
, _dirList.Select( string.Join(Environment.NewLine
x => $"{++i}: {new DirectoryInfo(x).Name.Sub(0, _REP_MAX_LENGTH)}"))); , _repoPathList.Select(
x => $"{++i}: {new DirectoryInfo(x).Name.Sub(0, _REP_PATH_LENGTH_LIMIT)}"))
//
);
}
await Parallel.ForEachAsync(_dirList, DirHandle); await Parallel.ForEachAsync(_repoPathList, DirHandle);
Console.SetCursorPosition(_cursorInitPos.x, _cursorInitPos.y + _dirList.Count); Console.SetCursorPosition(_cursorPosBackup.x, _cursorPosBackup.y + _repoPathList.Count);
} }
} }

View File

@ -6,6 +6,10 @@ public class Option : OptionBase
[Option('a', "args", HelpText = nameof(Str.GitArgs), Default = "status", ResourceType = typeof(Str))] [Option('a', "args", HelpText = nameof(Str.GitArgs), Default = "status", ResourceType = typeof(Str))]
public string Args { get; set; } public string Args { get; set; }
[Option('e', "git-output-encoding", HelpText = nameof(Str.GitOutputEncoding), Default = "utf-8"
, ResourceType = typeof(Str))]
public string GitOutputEncoding { get; set; }
[Value(0, HelpText = nameof(Str.FolderPath), Default = ".", ResourceType = typeof(Str))] [Value(0, HelpText = nameof(Str.FolderPath), Default = ".", ResourceType = typeof(Str))]
public string Path { get; set; } public string Path { get; set; }
} }

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
id="root" xmlns=""> id="root" xmlns="">
@ -149,4 +149,7 @@
<data name="FindGitReps" xml:space="preserve"> <data name="FindGitReps" xml:space="preserve">
<value>Find all git repository directories under "{0}"...</value> <value>Find all git repository directories under "{0}"...</value>
</data> </data>
<data name="GitOutputEncoding" xml:space="preserve">
<value>Git output encoding</value>
</data>
</root> </root>

View File

@ -57,6 +57,9 @@
<data name="Copied" xml:space="preserve"> <data name="Copied" xml:space="preserve">
<value>{0}(已复制到剪贴板)</value> <value>{0}(已复制到剪贴板)</value>
</data> </data>
<data name="GitOutputEncoding" xml:space="preserve">
<value>Git输出编码</value>
</data>
<data name="FileSearchPattern" xml:space="preserve"> <data name="FileSearchPattern" xml:space="preserve">
<value>文件通配符</value> <value>文件通配符</value>
</data> </data>

View File

@ -1,4 +1,5 @@
using System.Reflection; using System.Reflection;
using System.Text;
using Dot; using Dot;
Type[] LoadVerbs() Type[] LoadVerbs()
@ -24,8 +25,8 @@ async Task Run(object args)
} }
//Entry Point // Entry Point
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var types = LoadVerbs(); var types = LoadVerbs();
try { try {

View File

@ -66,8 +66,6 @@ public sealed class Main : ToolBase<Option>
private static void ParseAndShow(string text) private static void ParseAndShow(string text)
{ {
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var ansi = BuildOutput(text, Encoding.GetEncoding("gbk")); var ansi = BuildOutput(text, Encoding.GetEncoding("gbk"));
var utf8 = BuildOutput(text, Encoding.UTF8); var utf8 = BuildOutput(text, Encoding.UTF8);
var unicodeLittleEndian = BuildOutput(text, Encoding.Unicode); var unicodeLittleEndian = BuildOutput(text, Encoding.Unicode);

View File

@ -2,7 +2,8 @@ namespace Dot;
public abstract class ToolBase<TOption> : ITool where TOption : OptionBase public abstract class ToolBase<TOption> : ITool where TOption : OptionBase
{ {
private static readonly object _lockObj = new(); // ReSharper disable once StaticMemberInGenericType
private static SpinLock _spinlock;
protected readonly ProgressBarOptions // protected readonly ProgressBarOptions //
DefaultProgressBarOptions = new() { DefaultProgressBarOptions = new() {
@ -25,10 +26,15 @@ public abstract class ToolBase<TOption> : ITool where TOption : OptionBase
protected static void ConcurrentWrite(int x, int y, string text) protected static void ConcurrentWrite(int x, int y, string text)
{ {
lock (_lockObj) { var lockTaken = false;
try {
_spinlock.Enter(ref lockTaken);
Console.SetCursorPosition(x, y); Console.SetCursorPosition(x, y);
Console.Write(text); Console.Write(text);
} }
finally {
if (lockTaken) _spinlock.Exit(false);
}
} }
protected static IEnumerable<string> EnumerateFiles(string path, string searchPattern) protected static IEnumerable<string> EnumerateFiles(string path, string searchPattern)

View File

@ -50,8 +50,8 @@
<ItemGroup Condition="!Exists('Lang\Str.Designer.cs')"> <ItemGroup Condition="!Exists('Lang\Str.Designer.cs')">
<Compile Include="Lang\Str.Designer.cs"/> <Compile Include="Lang\Str.Designer.cs"/>
</ItemGroup> </ItemGroup>
<Exec Command="dotnet tool restore"/> <Exec Command="dotnet tool restore" StdOutEncoding="utf-8"/>
<Exec WorkingDirectory="$(ProjectDir)\Lang" Command="dotnet t4 Str.tt"/> <Exec WorkingDirectory="$(ProjectDir)\Lang" Command="dotnet t4 Str.tt" StdOutEncoding="utf-8"/>
</Target> </Target>
</Project> </Project>