mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
<style> SonarqubleAnalyzer
This commit is contained in:
parent
d913e0b7cc
commit
51943833ed
@ -6,5 +6,6 @@
|
||||
<OutputPath>$(BaseOutputPath)/$(MSBuildProjectName)/bin</OutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</IntermediateOutputPath>
|
||||
<MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</MSBuildProjectExtensionsPath>
|
||||
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
8
SonarqubleAnalyzer.props
Normal file
8
SonarqubleAnalyzer.props
Normal file
@ -0,0 +1,8 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.50.0.58025">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
1
dot.sln
1
dot.sln
@ -27,6 +27,7 @@ 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
|
||||
SonarqubleAnalyzer.props = SonarqubleAnalyzer.props
|
||||
switch-nuget.cmd = switch-nuget.cmd
|
||||
switch-project.cmd = switch-project.cmd
|
||||
switcher.json = switcher.json
|
||||
|
@ -6,15 +6,11 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot.Color;
|
||||
|
||||
internal class MouseHook : IDisposable
|
||||
internal sealed class MouseHook : IDisposable
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct Msllhookstruct
|
||||
{
|
||||
// [FieldOffset(20)] private readonly nint dwExtraInfo;
|
||||
// [FieldOffset(12)] private readonly uint flags;
|
||||
// [FieldOffset(8)] private readonly uint mouseData;
|
||||
// [FieldOffset(16)] private readonly uint time;
|
||||
[FieldOffset(0)] public readonly int X;
|
||||
[FieldOffset(4)] public readonly int Y;
|
||||
}
|
||||
@ -42,7 +38,9 @@ internal class MouseHook : IDisposable
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
#pragma warning disable S108
|
||||
if (disposing) { }
|
||||
#pragma warning restore S108
|
||||
|
||||
if (_hookId != default) Win32.UnhookWindowsHookEx(_hookId);
|
||||
_disposed = true;
|
||||
|
@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.RegularExpressions;
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
using Panel = Spectre.Console.Panel;
|
||||
|
||||
namespace Dot;
|
||||
@ -11,43 +12,15 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
private int _excludeCnt; //排除文件数
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
#pragma warning disable S2743
|
||||
private static readonly object _lock = new(); //线程锁
|
||||
private int _readCnt; //读取文件数
|
||||
private int _totalCnt; //总文件数
|
||||
private int _writeCnt; //写入文件数
|
||||
private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量
|
||||
|
||||
|
||||
// ReSharper disable once ReturnTypeCanBeEnumerable.Local
|
||||
private string[] EnumerateFiles(string path, string searchPattern, out int excludeCnt)
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
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 async Task Core()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
|
||||
if (!Opt.WriteMode) AnsiConsole.MarkupLine("[gray]{0}[/]", Str.ExerciseMode);
|
||||
IEnumerable<string> fileList;
|
||||
await AnsiConsole.Progress()
|
||||
@ -79,6 +52,39 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
}
|
||||
|
||||
|
||||
// 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()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path), string.Format(Str.PathNotFound, Opt.Path));
|
||||
return CoreInternal();
|
||||
}
|
||||
|
||||
|
||||
protected static FileStream CreateTempFile(out string file)
|
||||
{
|
||||
file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.tmp");
|
||||
@ -105,7 +111,9 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
catch (IOException) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
return fsr;
|
||||
}
|
||||
|
109
src/Git/Main.cs
109
src/Git/Main.cs
@ -16,61 +16,9 @@ internal class Main : ToolBase<Option>
|
||||
private ConcurrentDictionary<string, TaskStatusColumn.Statues> _repoStatus;
|
||||
|
||||
|
||||
private async ValueTask DirHandle(KeyValuePair<string, ProgressTask> payload, CancellationToken _)
|
||||
{
|
||||
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));
|
||||
_repoRsp[payload.Key].Append(msg.EscapeMarkup());
|
||||
}
|
||||
|
||||
// 启动git进程
|
||||
{
|
||||
var startInfo = new ProcessStartInfo {
|
||||
CreateNoWindow = true
|
||||
, WorkingDirectory = payload.Key
|
||||
, FileName = "git"
|
||||
, Arguments = Opt.Args
|
||||
, UseShellExecute = false
|
||||
, RedirectStandardOutput = true
|
||||
, RedirectStandardError = true
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p!.OutputDataReceived += ExecRspReceived;
|
||||
p.ErrorDataReceived += ExecRspReceived;
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
await p.WaitForExitAsync(CancellationToken.None);
|
||||
|
||||
if (p.ExitCode == 0) {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Succeed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Succeed
|
||||
, (_, _) => TaskStatusColumn.Statues.Succeed);
|
||||
payload.Value.StopTask();
|
||||
}
|
||||
else {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Failed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Failed
|
||||
, (_, _) => TaskStatusColumn.Statues.Failed);
|
||||
}
|
||||
|
||||
payload.Value.StopTask();
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task Core()
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
_gitOutputEnc = Encoding.GetEncoding(Opt.GitOutputEncoding);
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path) //
|
||||
, string.Format(Str.PathNotFound, Opt.Path));
|
||||
|
||||
|
||||
var progressBar = new ProgressBarColumn { Width = 10 };
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(progressBar //
|
||||
@ -124,4 +72,59 @@ internal class Main : ToolBase<Option>
|
||||
|
||||
AnsiConsole.Write(table);
|
||||
}
|
||||
|
||||
|
||||
private async ValueTask DirHandle(KeyValuePair<string, ProgressTask> payload, CancellationToken _)
|
||||
{
|
||||
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));
|
||||
_repoRsp[payload.Key].Append(msg.EscapeMarkup());
|
||||
}
|
||||
|
||||
// 启动git进程
|
||||
|
||||
var startInfo = new ProcessStartInfo {
|
||||
CreateNoWindow = true
|
||||
, WorkingDirectory = payload.Key
|
||||
, FileName = "git"
|
||||
, Arguments = Opt.Args
|
||||
, UseShellExecute = false
|
||||
, RedirectStandardOutput = true
|
||||
, RedirectStandardError = true
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p!.OutputDataReceived += ExecRspReceived;
|
||||
p.ErrorDataReceived += ExecRspReceived;
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
await p.WaitForExitAsync(CancellationToken.None);
|
||||
|
||||
if (p.ExitCode == 0) {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Succeed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Succeed
|
||||
, (_, _) => TaskStatusColumn.Statues.Succeed);
|
||||
payload.Value.StopTask();
|
||||
}
|
||||
else {
|
||||
payload.Value.State.Status(TaskStatusColumn.Statues.Failed);
|
||||
_repoStatus.AddOrUpdate(payload.Key, _ => TaskStatusColumn.Statues.Failed
|
||||
, (_, _) => TaskStatusColumn.Statues.Failed);
|
||||
}
|
||||
|
||||
payload.Value.StopTask();
|
||||
}
|
||||
|
||||
protected override Task Core()
|
||||
{
|
||||
if (!Directory.Exists(Opt.Path))
|
||||
throw new ArgumentException(nameof(Opt.Path) //
|
||||
, string.Format(Str.PathNotFound, Opt.Path));
|
||||
return CoreInternal();
|
||||
}
|
||||
}
|
@ -16,9 +16,12 @@ internal sealed class Main : ToolBase<Option>
|
||||
if (item.NetworkInterfaceType != NetworkInterfaceType.Ethernet ||
|
||||
item.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
foreach (var ip in item.GetIPProperties().UnicastAddresses)
|
||||
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
Console.WriteLine(@$"{item.Name}: {ip.Address}");
|
||||
var output = string.Join(Environment.NewLine
|
||||
, item.GetIPProperties()
|
||||
.UnicastAddresses
|
||||
.Where(x => x.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
.Select(x => @$"{item.Name}: {x.Address}"));
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
|
||||
using var http = new HttpClient();
|
||||
|
@ -23,6 +23,22 @@ internal class Main : ToolBase<Option>
|
||||
return ret;
|
||||
}
|
||||
|
||||
private async Task CoreInternal()
|
||||
{
|
||||
string result = null;
|
||||
if (Opt.Compress)
|
||||
result = await JsonCompress();
|
||||
else if (Opt.ConvertToString)
|
||||
result = await ConvertToString();
|
||||
else if (Opt.Format) result = await JsonFormat();
|
||||
|
||||
if (!result.NullOrWhiteSpace()) {
|
||||
#if NET7_0_WINDOWS
|
||||
await ClipboardService.SetTextAsync(result!);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private Task<string> JsonCompress()
|
||||
{
|
||||
var ret = _inputObj.Json();
|
||||
@ -40,12 +56,12 @@ internal class Main : ToolBase<Option>
|
||||
return text.Replace("\\\"", "\"");
|
||||
}
|
||||
|
||||
protected override async Task Core()
|
||||
protected override Task Core()
|
||||
{
|
||||
var inputText = Opt.InputText;
|
||||
|
||||
#if NET7_0_WINDOWS
|
||||
if (inputText.NullOrWhiteSpace()) inputText = await ClipboardService.GetTextAsync();
|
||||
if (inputText.NullOrWhiteSpace()) inputText = ClipboardService.GetText();
|
||||
#endif
|
||||
if (inputText.NullOrWhiteSpace()) throw new ArgumentException(Str.InputTextIsEmpty);
|
||||
|
||||
@ -56,24 +72,16 @@ internal class Main : ToolBase<Option>
|
||||
try {
|
||||
inputText = UnescapeString(inputText);
|
||||
_inputObj = inputText.Object<object>();
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (JsonException) {
|
||||
// ignored
|
||||
}
|
||||
catch (JsonException) { }
|
||||
|
||||
throw new ArgumentException(Str.InvalidJsonString);
|
||||
}
|
||||
|
||||
|
||||
string result = null;
|
||||
if (Opt.Compress)
|
||||
result = await JsonCompress();
|
||||
else if (Opt.ConvertToString)
|
||||
result = await ConvertToString();
|
||||
else if (Opt.Format) result = await JsonFormat();
|
||||
|
||||
if (result.NullOrWhiteSpace()) return;
|
||||
#if NET7_0_WINDOWS
|
||||
await ClipboardService.SetTextAsync(result!);
|
||||
#endif
|
||||
return CoreInternal();
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Dot;
|
||||
using Dot.Git;
|
||||
@ -29,6 +28,4 @@ app.Configure(config => {
|
||||
});
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
// CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
|
||||
// CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
|
||||
return app.Run(args);
|
@ -23,7 +23,7 @@ internal sealed class Main : FilesTool<Option>
|
||||
}
|
||||
|
||||
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace Dot.ToLf;
|
||||
[Localization(typeof(Str))]
|
||||
internal sealed class Main : FilesTool<Option>
|
||||
{
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
|
||||
@ -50,7 +50,9 @@ internal sealed class Main : FilesTool<Option>
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable S2583
|
||||
if (hasWrote && !isBin) {
|
||||
#pragma warning restore S2583
|
||||
if (Opt.WriteMode) File.Copy(tmpFile, file, true);
|
||||
ShowMessage(0, 1, 0);
|
||||
UpdateStats(Path.GetExtension(file));
|
||||
|
@ -14,9 +14,9 @@ internal abstract class ToolBase<TOption> : Command<TOption> where TOption : Opt
|
||||
}
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, TOption option)
|
||||
public override int Execute(CommandContext context, TOption settings)
|
||||
{
|
||||
Opt = option;
|
||||
Opt = settings;
|
||||
Run().Wait();
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ internal sealed class Main : FilesTool<Option>
|
||||
}
|
||||
|
||||
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken _)
|
||||
protected override async ValueTask FileHandle(string file, CancellationToken cancelToken)
|
||||
{
|
||||
ShowMessage(1, 0, 0);
|
||||
int spacesCnt;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<UseWindowsForms Condition="'$(TargetFramework)' == 'net7.0-windows'">true</UseWindowsForms>
|
||||
<RootNamespace>Dot</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<NoWarn>CA1416;</NoWarn>
|
||||
<NoWarn>CA1416;S1075</NoWarn>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0-windows'">
|
||||
@ -28,5 +28,6 @@
|
||||
<HintPath>..\..\..\..\..\ForkedGitReps\spectre.console\src\Spectre.Console\bin\Debug\net6.0\Spectre.Console.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<Import Project="../SonarqubleAnalyzer.props"/>
|
||||
<Import Project="../GenerateResx.targets"/>
|
||||
</Project>
|
@ -5,11 +5,17 @@
|
||||
<RootNamespace>Dot.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0-preview-20221003-04"/>
|
||||
<PackageReference Include="NUnit" Version="3.13.3"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.5.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user