<style> SonarqubleAnalyzer

This commit is contained in:
nsnail 2022-12-14 16:30:36 +08:00
parent d913e0b7cc
commit 51943833ed
16 changed files with 156 additions and 124 deletions

View File

@ -6,5 +6,6 @@
<OutputPath>$(BaseOutputPath)/$(MSBuildProjectName)/bin</OutputPath> <OutputPath>$(BaseOutputPath)/$(MSBuildProjectName)/bin</OutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</IntermediateOutputPath> <IntermediateOutputPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</IntermediateOutputPath>
<MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</MSBuildProjectExtensionsPath> <MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</MSBuildProjectExtensionsPath>
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

8
SonarqubleAnalyzer.props Normal file
View 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>

View File

@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{AD79881E-7
README.md = README.md README.md = README.md
README.zh-CN.md = README.zh-CN.md README.zh-CN.md = README.zh-CN.md
SafetyDelUnusedResx.ahk = SafetyDelUnusedResx.ahk SafetyDelUnusedResx.ahk = SafetyDelUnusedResx.ahk
SonarqubleAnalyzer.props = SonarqubleAnalyzer.props
switch-nuget.cmd = switch-nuget.cmd switch-nuget.cmd = switch-nuget.cmd
switch-project.cmd = switch-project.cmd switch-project.cmd = switch-project.cmd
switcher.json = switcher.json switcher.json = switcher.json

View File

@ -6,15 +6,11 @@ using System.Runtime.InteropServices;
namespace Dot.Color; namespace Dot.Color;
internal class MouseHook : IDisposable internal sealed class MouseHook : IDisposable
{ {
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
private struct Msllhookstruct 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(0)] public readonly int X;
[FieldOffset(4)] public readonly int Y; [FieldOffset(4)] public readonly int Y;
} }
@ -42,7 +38,9 @@ internal class MouseHook : IDisposable
private void Dispose(bool disposing) private void Dispose(bool disposing)
{ {
if (_disposed) return; if (_disposed) return;
#pragma warning disable S108
if (disposing) { } if (disposing) { }
#pragma warning restore S108
if (_hookId != default) Win32.UnhookWindowsHookEx(_hookId); if (_hookId != default) Win32.UnhookWindowsHookEx(_hookId);
_disposed = true; _disposed = true;

View File

@ -1,8 +1,4 @@
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Dot; namespace Dot;

View File

@ -1,5 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
// ReSharper disable once RedundantUsingDirective
using Panel = Spectre.Console.Panel; using Panel = Spectre.Console.Panel;
namespace Dot; namespace Dot;
@ -11,43 +12,15 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
private int _excludeCnt; //排除文件数 private int _excludeCnt; //排除文件数
// ReSharper disable once StaticMemberInGenericType // ReSharper disable once StaticMemberInGenericType
#pragma warning disable S2743
private static readonly object _lock = new(); //线程锁 private static readonly object _lock = new(); //线程锁
private int _readCnt; //读取文件数 private int _readCnt; //读取文件数
private int _totalCnt; //总文件数 private int _totalCnt; //总文件数
private int _writeCnt; //写入文件数 private int _writeCnt; //写入文件数
private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量 private readonly ConcurrentDictionary<string, int> _writeStats = new(); //写入统计:后缀,数量
private async Task CoreInternal()
// 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 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); if (!Opt.WriteMode) AnsiConsole.MarkupLine("[gray]{0}[/]", Str.ExerciseMode);
IEnumerable<string> fileList; IEnumerable<string> fileList;
await AnsiConsole.Progress() 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) protected static FileStream CreateTempFile(out string file)
{ {
file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.tmp"); file = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.tmp");
@ -105,7 +111,9 @@ internal abstract class FilesTool<TOption> : ToolBase<TOption> where TOption : D
// ignored // ignored
} }
} }
catch (IOException) { } catch (IOException) {
// ignored
}
return fsr; return fsr;
} }

View File

@ -16,61 +16,9 @@ internal class Main : ToolBase<Option>
private ConcurrentDictionary<string, TaskStatusColumn.Statues> _repoStatus; private ConcurrentDictionary<string, TaskStatusColumn.Statues> _repoStatus;
private async ValueTask DirHandle(KeyValuePair<string, ProgressTask> payload, CancellationToken _) private async Task CoreInternal()
{
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()
{ {
_gitOutputEnc = Encoding.GetEncoding(Opt.GitOutputEncoding); _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 }; var progressBar = new ProgressBarColumn { Width = 10 };
await AnsiConsole.Progress() await AnsiConsole.Progress()
.Columns(progressBar // .Columns(progressBar //
@ -124,4 +72,59 @@ internal class Main : ToolBase<Option>
AnsiConsole.Write(table); 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();
}
} }

View File

@ -16,9 +16,12 @@ internal sealed class Main : ToolBase<Option>
if (item.NetworkInterfaceType != NetworkInterfaceType.Ethernet || if (item.NetworkInterfaceType != NetworkInterfaceType.Ethernet ||
item.OperationalStatus != OperationalStatus.Up) item.OperationalStatus != OperationalStatus.Up)
continue; continue;
foreach (var ip in item.GetIPProperties().UnicastAddresses) var output = string.Join(Environment.NewLine
if (ip.Address.AddressFamily == AddressFamily.InterNetwork) , item.GetIPProperties()
Console.WriteLine(@$"{item.Name}: {ip.Address}"); .UnicastAddresses
.Where(x => x.Address.AddressFamily == AddressFamily.InterNetwork)
.Select(x => @$"{item.Name}: {x.Address}"));
Console.WriteLine(output);
} }
using var http = new HttpClient(); using var http = new HttpClient();

View File

@ -23,6 +23,22 @@ internal class Main : ToolBase<Option>
return ret; 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() private Task<string> JsonCompress()
{ {
var ret = _inputObj.Json(); var ret = _inputObj.Json();
@ -40,12 +56,12 @@ internal class Main : ToolBase<Option>
return text.Replace("\\\"", "\""); return text.Replace("\\\"", "\"");
} }
protected override async Task Core() protected override Task Core()
{ {
var inputText = Opt.InputText; var inputText = Opt.InputText;
#if NET7_0_WINDOWS #if NET7_0_WINDOWS
if (inputText.NullOrWhiteSpace()) inputText = await ClipboardService.GetTextAsync(); if (inputText.NullOrWhiteSpace()) inputText = ClipboardService.GetText();
#endif #endif
if (inputText.NullOrWhiteSpace()) throw new ArgumentException(Str.InputTextIsEmpty); if (inputText.NullOrWhiteSpace()) throw new ArgumentException(Str.InputTextIsEmpty);
@ -56,24 +72,16 @@ internal class Main : ToolBase<Option>
try { try {
inputText = UnescapeString(inputText); inputText = UnescapeString(inputText);
_inputObj = inputText.Object<object>(); _inputObj = inputText.Object<object>();
return; return Task.CompletedTask;
}
catch (JsonException) {
// ignored
} }
catch (JsonException) { }
throw new ArgumentException(Str.InvalidJsonString); throw new ArgumentException(Str.InvalidJsonString);
} }
string result = null; return CoreInternal();
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
} }
} }

View File

@ -1,4 +1,3 @@
using System.Globalization;
using System.Text; using System.Text;
using Dot; using Dot;
using Dot.Git; using Dot.Git;
@ -29,6 +28,4 @@ app.Configure(config => {
}); });
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
// CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
return app.Run(args); return app.Run(args);

View File

@ -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); ShowMessage(1, 0, 0);

View File

@ -7,7 +7,7 @@ namespace Dot.ToLf;
[Localization(typeof(Str))] [Localization(typeof(Str))]
internal sealed class Main : FilesTool<Option> 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); ShowMessage(1, 0, 0);
@ -50,7 +50,9 @@ internal sealed class Main : FilesTool<Option>
} }
#pragma warning disable S2583
if (hasWrote && !isBin) { if (hasWrote && !isBin) {
#pragma warning restore S2583
if (Opt.WriteMode) File.Copy(tmpFile, file, true); if (Opt.WriteMode) File.Copy(tmpFile, file, true);
ShowMessage(0, 1, 0); ShowMessage(0, 1, 0);
UpdateStats(Path.GetExtension(file)); UpdateStats(Path.GetExtension(file));

View 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(); Run().Wait();
return 0; return 0;
} }

View File

@ -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); ShowMessage(1, 0, 0);
int spacesCnt; int spacesCnt;

View File

@ -5,7 +5,7 @@
<UseWindowsForms Condition="'$(TargetFramework)' == 'net7.0-windows'">true</UseWindowsForms> <UseWindowsForms Condition="'$(TargetFramework)' == 'net7.0-windows'">true</UseWindowsForms>
<RootNamespace>Dot</RootNamespace> <RootNamespace>Dot</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<NoWarn>CA1416;</NoWarn> <NoWarn>CA1416;S1075</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0-windows'"> <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> <HintPath>..\..\..\..\..\ForkedGitReps\spectre.console\src\Spectre.Console\bin\Debug\net6.0\Spectre.Console.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Import Project="../SonarqubleAnalyzer.props"/>
<Import Project="../GenerateResx.targets"/> <Import Project="../GenerateResx.targets"/>
</Project> </Project>

View File

@ -5,11 +5,17 @@
<RootNamespace>Dot.Tests</RootNamespace> <RootNamespace>Dot.Tests</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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="NUnit" Version="3.13.3"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1"/>
<PackageReference Include="NUnit.Analyzers" Version="3.3.0"/> <PackageReference Include="NUnit.Analyzers" Version="3.5.0">
<PackageReference Include="coverlet.collector" Version="3.1.2"/> <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> </ItemGroup>
</Project> </Project>