file-folder-helper/ADO2024/PI1/Helper-2024-01-08.cs
2024-11-22 17:51:40 -07:00

326 lines
12 KiB
C#

using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240108
{
private record Method(int EndLine,
string FirstLine,
int FirstUsedLine,
string Name,
int ParameterCount,
int StartLine);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Method[]))]
private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext
{
}
[GeneratedRegex(@"(?<method>[A-Z]{1}[A-Za-z_0-9]*)\(")]
private static partial Regex CSharpMethodName();
[GeneratedRegex(@"\s[a-zA-Z_]*,")]
private static partial Regex CSharpParameter();
[GeneratedRegex(@"\b(public|private|internal|protected)\s\b(static)?\s?\b(partial)?\s?\b(async)?\s?[[\]<,>?a-zA-Z()\s]*\s[A-Z]{1}[a-zA-Z_]+\(.*\)")]
private static partial Regex CSharpMethodLine();
private static string? GetName(string line)
{
string? result;
Match match = CSharpMethodName().Match(line);
if (!match.Success)
result = null;
else
result = match.Groups["method"].Value;
return result;
}
private static int GetStartLine(string[] lines, int i)
{
int result = i;
string line;
for (int j = i - 1; j > -1; j--)
{
line = lines[j].Trim();
if (!line.StartsWith('[') && !line.StartsWith('#') && !line.StartsWith("/// "))
break;
result--;
}
return result;
}
private static int GetParameterCount(string line, string search)
{
int result;
string after = line.Split(search)[^1];
if (after.StartsWith(')'))
result = 0;
else
{
string[] segments = CSharpParameter().Split(after);
result = segments.Length;
}
return result;
}
private static int GetLineBlockCount(string line, bool isLinq)
{
int result = 0;
bool ignore = false;
for (int i = 0; i < line.Length; i++)
{
if (line[i] == '\'')
i++;
else if (!isLinq && !ignore && line[i] == '{')
result++;
else if (!isLinq && !ignore && line[i] == '}')
result--;
else if (isLinq && !ignore && line[i] == ';')
result--;
else if (i > 0 && line[i] == '"' && line[i - 1] != '\\')
ignore = !ignore;
}
return result;
}
private static int? GetFirstUsedLine(string[] lines, int i, string search, string searchNot, string searchWrap, string searchDelegate, string searchConstructor, int parameterCount)
{
int? result = null;
string[] segments;
string[] afterSegments;
string lastSegmentBeforeDot;
for (int j = 0; j < lines.Length; j++)
{
if (j == i)
continue;
segments = lines[j].Split(search);
if (segments.Length == 1)
{
segments = lines[j].Split(searchNot);
if (segments.Length == 1)
{
segments = lines[j].Split(searchWrap);
if (segments.Length == 1)
{
if (!lines[j].EndsWith(searchDelegate))
{
segments = lines[j].Split(searchConstructor);
if (segments.Length == 1)
continue;
}
}
}
}
if (lines[j].EndsWith(searchDelegate))
{
result = j;
break;
}
else
{
lastSegmentBeforeDot = segments[^1].Split(").")[0];
if (parameterCount == 0)
{
if (lastSegmentBeforeDot.Contains(','))
continue;
}
else
{
afterSegments = lastSegmentBeforeDot.Split(',');
if (afterSegments.Length != parameterCount)
continue;
}
result = j;
break;
}
}
return result;
}
private static ReadOnlyCollection<int> GetMethodLines(ReadOnlyCollection<Method> methods)
{
List<int> results = [];
foreach (Method method in methods)
{
for (int i = method.StartLine; i < method.EndLine + 1; i++)
results.Add(i);
}
return new(results);
}
private static ReadOnlyCollection<Method> GetMethods(string cSharpFile, ILogger<Worker> logger, string[] lines)
{
List<Method> results = [];
int blocks;
bool isLinq;
int endLine;
string line;
string? name;
int startLine;
Method method;
string search;
string firstLine;
string innerLine;
string searchNot;
string searchWrap;
int parameterCount;
int? firstUsedLine;
string searchDelegate;
string lineSegmentFirst;
string searchConstructor;
for (int i = 0; i < lines.Length; i++)
{
line = lines[i].Trim();
if (string.IsNullOrEmpty(line))
continue;
if (line.Length < 5)
continue;
if (line.EndsWith(','))
continue;
if (!CSharpMethodLine().Match(line).Success)
continue;
name = GetName(line);
search = $" {name}(";
searchNot = $"!{name}(";
searchWrap = $"({name}(";
searchDelegate = $" += {name};";
if (string.IsNullOrEmpty(name))
continue;
blocks = 0;
startLine = GetStartLine(lines, i);
searchConstructor = $"{name.ToLower()} = new(";
parameterCount = GetParameterCount(line, search);
if (!lines[startLine].StartsWith("#pragma"))
firstLine = lines[startLine].Trim();
else
firstLine = lines[startLine + 1].Trim();
isLinq = !lines[i + 1].StartsWith("#pragma") && lines[i + 1].Trim() != "{";
if (isLinq)
blocks++;
for (int j = i + 1; j < lines.Length; j++)
{
innerLine = lines[j].Trim();
if (innerLine.StartsWith("#pragma"))
continue;
if (isLinq && string.IsNullOrEmpty(innerLine))
{
if (line.EndsWith(';'))
blocks--;
}
blocks += GetLineBlockCount(innerLine, isLinq);
if (blocks == 0)
{
endLine = j;
if (lines.Length > j + 1 && string.IsNullOrEmpty(lines[j + 1].Trim()))
endLine++;
firstUsedLine = GetFirstUsedLine(lines, i, search, searchNot, searchWrap, searchDelegate, searchConstructor, parameterCount);
if (firstUsedLine is null)
{
lineSegmentFirst = line.Split(search)[0];
if (!lines[i - 1].Trim().StartsWith("[Obsolete"))
{
if (lineSegmentFirst.StartsWith("private"))
logger.LogWarning("<{cSharpFileName}> {name} with {parameterCount} parameter(s) <{line}>", Path.GetFileName(cSharpFile), name, parameterCount, lineSegmentFirst);
else
logger.LogInformation("<{cSharpFileName}> {name} with {parameterCount} parameter(s) <{line}>", Path.GetFileName(cSharpFile), name, parameterCount, lineSegmentFirst);
}
break;
}
if (j > lines.Length - 2)
throw new Exception();
method = new(endLine, firstLine, firstUsedLine.Value, name, parameterCount, startLine);
results.Add(method);
break;
}
}
}
return new(results.OrderBy(l => l.FirstUsedLine).ToArray());
}
private static bool WriteAllLines(string cSharpFile, string[] lines, ReadOnlyCollection<Method> methods)
{
bool result;
List<string> results = [];
if (methods.Count == 0)
File.WriteAllText(".vscode/.json", JsonSerializer.Serialize(methods.ToArray(), MethodCollectionCommonSourceGenerationContext.Default.MethodArray));
ReadOnlyCollection<int> methodLines = GetMethodLines(methods);
int minMethodLines = methodLines.Min();
for (int i = 0; i < minMethodLines; i++)
results.Add(lines[i]);
foreach (Method method in methods)
{
for (int i = method.StartLine; i < method.EndLine + 1; i++)
results.Add(lines[i]);
}
for (int i = minMethodLines; i < lines.Length; i++)
{
if (methodLines.Contains(i))
continue;
results.Add(lines[i]);
}
string text = File.ReadAllText(cSharpFile);
string join = string.Join(Environment.NewLine, results);
if (join == text)
result = false;
else
{
result = true;
File.WriteAllText(cSharpFile, join);
}
return result;
}
private static bool SortFile(ILogger<Worker> logger, bool logOnly, string cSharpFile, string[] lines)
{
bool result;
ReadOnlyCollection<Method> methods = GetMethods(cSharpFile, logger, lines);
if (methods.Count == 0)
result = false;
else if (logOnly)
{
foreach (Method method in methods.OrderBy(l => l.Name))
logger.LogInformation("{cSharpFile} - {Name} has {lines} line(s)", cSharpFile, method.Name, (method.EndLine - method.StartLine).ToString("000000"));
result = false;
}
else
result = WriteAllLines(cSharpFile, lines, methods);
return result;
}
internal static void SortCodeMethods(ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken)
{
bool result = false;
bool check;
string[] lines;
bool usePathCombine = true;
long ticks = DateTime.Now.Ticks;
bool logOnly = bool.Parse(args[3]);
logger.LogInformation("{ticks}", ticks);
string directory = Path.GetFullPath(args[2]);
string repositoryDirectory = Path.GetFullPath(args[0]);
string[] cSharpFiles = Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories);
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = logOnly ? new(cSharpFiles) : Helpers.HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(repositoryDirectory, usePathCombine, cancellationToken);
for (int i = 0; i < 10; i++)
{
foreach (string cSharpFile in cSharpFiles)
{
if (!gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(cSharpFile))
continue;
lines = File.ReadAllLines(cSharpFile);
check = SortFile(logger, logOnly, cSharpFile, lines);
if (check && !result)
result = true;
}
if (logOnly || !result)
break;
}
}
}