diff --git a/.vscode/download-work-items.http b/.vscode/download-work-items.http index a603bda..a38368c 100644 --- a/.vscode/download-work-items.http +++ b/.vscode/download-work-items.http @@ -7,3 +7,7 @@ Accept: application/json Authorization: Basic {{pat}} ### + +GET {{host}}/tfs/FactoryIntegration/_apis/wit/workitems/{{ids}}/updates +Accept: application/json +Authorization: Basic {{pat}} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ad2787e..875c099 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -205,67 +205,6 @@ ], "problemMatcher": "$msCompile" }, - { - "label": "File-Folder-Helper AOT s J Verdaccio", - "type": "shell", - "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", - "args": [ - "s", - "J", - "L:/Verdaccio/storage", - ], - "problemMatcher": [] - }, - { - "label": "File-Folder-Helper AOT s S BaGet", - "type": "shell", - "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", - "args": [ - "s", - "S", - "L:/BaGet/packages", - ], - "problemMatcher": [] - }, - { - "label": "File-Folder-Helper AOT s X SortCodeMethods", - "type": "shell", - "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", - "args": [ - "s", - "X", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG", - "Day-Helper-2024-01-08", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/ADO", - "s", - "X", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG", - "Day-Helper-2024-01-08", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/Kanban", - "s", - "X", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG", - "Day-Helper-2024-01-08", - "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/FileHandlers/Markdown", - "s", - "X", - "L:/DevOps/Mesa_FI/File-Folder-Helper", - "Day-Helper-2024-01-08", - "L:/DevOps/Mesa_FI/File-Folder-Helper/ADO2024/PI3" - ], - "problemMatcher": [] - }, - { - "label": "File-Folder-Helper AOT s F Staging _Logs", - "type": "shell", - "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", - "args": [ - "s", - "F", - "'\\\\messv02ecc1.ec.local\\EC_EAFLog\\Staging\\_ Logs'", - ], - "problemMatcher": [] - }, { "label": "Kanbn Console", "type": "npm", diff --git a/ADO2024/PI1/Helper-2024-01-08.cs b/ADO2024/PI1/Helper-2024-01-08.cs index 8a5ed66..c46c0fa 100644 --- a/ADO2024/PI1/Helper-2024-01-08.cs +++ b/ADO2024/PI1/Helper-2024-01-08.cs @@ -1,7 +1,5 @@ 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; @@ -16,12 +14,6 @@ internal static partial class Helper20240108 int ParameterCount, int StartLine); - [JsonSourceGenerationOptions(WriteIndented = true)] - [JsonSerializable(typeof(Method[]))] - private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext - { - } - [GeneratedRegex(@"(?[A-Z]{1}[A-Za-z_0-9]*)\(")] private static partial Regex CSharpMethodName(); @@ -248,8 +240,6 @@ internal static partial class Helper20240108 { bool result; List results = []; - if (methods.Count == 0) - File.WriteAllText(".vscode/.json", JsonSerializer.Serialize(methods.ToArray(), MethodCollectionCommonSourceGenerationContext.Default.MethodArray)); ReadOnlyCollection methodLines = GetMethodLines(methods); int minMethodLines = methodLines.Min(); for (int i = 0; i < minMethodLines; i++) diff --git a/ADO2025/PI5/Helper-2025-03-15.cs b/ADO2025/PI5/Helper-2025-03-15.cs new file mode 100644 index 0000000..69b9bc7 --- /dev/null +++ b/ADO2025/PI5/Helper-2025-03-15.cs @@ -0,0 +1,55 @@ +using File_Folder_Helper.Helpers; +using Microsoft.Extensions.Logging; + +namespace File_Folder_Helper.ADO2025.PI5; + +internal static partial class Helper20250315 +{ + + internal static void Empty(ILogger logger, List args) + { + string[] searchPatterns = args[2].Split('|'); + string sourceDirectory = Path.GetFullPath(args[0]); + if (searchPatterns.Length == 1) + { + string[] files = Directory.GetFiles(sourceDirectory, searchPatterns[0], SearchOption.AllDirectories); + if (files.Length == 0) + logger.LogWarning("<{files}>(s)", files.Length); + else + { + string directoryName; + string[] directories; + foreach (string file in files) + { + directoryName = Path.GetDirectoryName(file) ?? throw new Exception(); + directories = Directory.GetDirectories(directoryName, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory); + } + } + } + else + { + string[] files; + string checkFile; + HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); + foreach (string searchPattern in searchPatterns) + { + files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); + if (files.Length == 0) + logger.LogWarning("<{files}>(s)", files.Length); + else + { + foreach (string file in files) + { + checkFile = $"{file}.json"; + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); + } + } + } + } + } + +} \ No newline at end of file diff --git a/ADO2025/PI5/Helper-2025-03-20.cs b/ADO2025/PI5/Helper-2025-03-20.cs new file mode 100644 index 0000000..0fda681 --- /dev/null +++ b/ADO2025/PI5/Helper-2025-03-20.cs @@ -0,0 +1,566 @@ +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; + +namespace File_Folder_Helper.ADO2025.PI5; + +internal static partial class Helper20250320 +{ + + private record Match(string Name, + string Parameters, + string Result, + string Scope, + string Static, + string Value, + string Async, + string Partial); + + private record Search(string Constructor, + string Delegate, + string Name, + string Not, + string Wrap); + + private record Method(int? EndLine, + ReadOnlyDictionary Parameters, + string FirstLine, + int I, + string Line, + Match Match, + ReadOnlyCollection ReferenceToLineNumbers, + int? ScopeEnum, + Search Search, + int StartLine) + { + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, MethodCollectionCommonSourceGenerationContext.Default.Method); + return result; + } + + } + + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(Method[]))] + private partial class MethodCollectionCommonSourceGenerationContext : JsonSerializerContext + { + } + + private record MethodWith(int? EndLine, + ReadOnlyDictionary Parameters, + string FirstLine, + string Line, + Match Match, + ReadOnlyCollection References, + ReadOnlyCollection ReferenceToLineNumbers, + int? ScopeEnum, + Search Search, + int StartLine) + { + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, MethodCollectionCommonSourceGenerationContext.Default.Method); + return result; + } + + } + + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(MethodWith[]))] + private partial class MethodWithCollectionCommonSourceGenerationContext : JsonSerializerContext + { + } + + private const string _Name = "name"; + private const string _Async = "async"; + private const string _Scope = "scope"; + private const string _Result = "result"; + private const string _Static = "static"; + private const string _Partial = "partial"; + private const string _Parameters = "parameters"; + + [GeneratedRegex(@"[[\]<,>?a-zA-Z0-9_()\s]*?\s[a-z_]{1}[a-zA-Z0-9_]*?,")] + private static partial Regex CSharpParameter(); + + [GeneratedRegex(@"(?public|private|internal|protected|\sI[a-zA-Z0-9_]*\.)\s?\b(?static)?\s?\b(?partial)?\s?\b(?async)?\s?\b(?[\[\]\.\?<,>a-zA-Z0-9_()\s]*?)\s?\b(?[A-Z_]{1}[a-zA-Z0-9_]*)+\((?.*)\)")] + private static partial Regex CSharpMethodLine(); + + internal static void SortCodeMethods(ILogger logger, List args, CancellationToken cancellationToken) + { + bool check; + string[] lines; + List changed = []; + bool usePathCombine = true; + long ticks = DateTime.Now.Ticks; + bool logOnly = bool.Parse(args[2]); + int scopeSpaces = int.Parse(args[3]); + logger.LogInformation("{ticks}", ticks); + string repositoryDirectory = Path.GetFullPath(args[0]); + string[] cSharpFiles = Directory.GetFiles(repositoryDirectory, "*.cs", SearchOption.AllDirectories); + ReadOnlyCollection gitOthersModifiedAndDeletedExcludingStandardFiles = logOnly ? new(cSharpFiles) : Helpers.HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(repositoryDirectory, usePathCombine, cancellationToken); + foreach (string cSharpFile in cSharpFiles) + { + if (!gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(cSharpFile)) + continue; + for (int i = 0; i < 10; i++) + { + lines = File.ReadAllLines(cSharpFile); + check = SortFile(logger, logOnly, scopeSpaces, cSharpFile, lines); + if (check) + { + Thread.Sleep(500); + changed.Add($"{i + 1:00}) {cSharpFile}"); + } + if (logOnly || !check) + break; + } + } + if (changed.Count == 0) + logger.LogInformation("No changes :)"); + else + { + changed.Reverse(); + foreach (string c in changed) + logger.LogInformation(c); + } + } + + private static bool SortFile(ILogger logger, bool logOnly, int scopeSpaces, string cSharpFile, string[] lines) + { + bool result; + ReadOnlyCollection methods = GetMethods(logger, scopeSpaces, cSharpFile, lines); + if (methods.Count == 0) + result = false; + else if (methods.Any(l => l.EndLine is null)) + result = false; + else if (logOnly) + { + foreach (Method method in methods) + logger.LogInformation("{cSharpFile} - {Name} has {lines} line(s)", cSharpFile, method.Match.Name, (method.EndLine is null ? 999999 : method.EndLine.Value - method.StartLine).ToString("000000")); + result = false; + } + else + { + ReadOnlyCollection sortedMethods = GetSortedMethods(methods); + if (Debugger.IsAttached) + File.WriteAllText(Path.Combine(".vscode", "helper", ".txt"), string.Join(Environment.NewLine, sortedMethods.Select(l => $"{l.Match.Name} => {l.Parameters.Count}"))); + ReadOnlyCollection collection = GetCollection(logger, lines, sortedMethods); + result = WriteAllLines(cSharpFile, lines, collection); + } + return result; + } + + private static ReadOnlyCollection GetMethods(ILogger logger, int scopeSpaces, string cSharpFile, string[] lines) + { + List results = []; + int check; + int blocks; + bool isLinq; + Match match; + string line; + int? endLine; + int startLine; + Method method; + Search search; + int? scopeEnum; + string firstLine; + string innerLine; + string lineSegmentFirst; + List referenceToLineNumbers; + Regex parameterRegex = CSharpParameter(); + ReadOnlyDictionary parameters; + System.Text.RegularExpressions.Match regularExpressionsMatch; + for (int i = 0; i < lines.Length; i++) + { + check = GetNumberOfStartSpaces(lines, i); + if (check != scopeSpaces) + continue; + line = lines[i].Trim(); + if (string.IsNullOrEmpty(line)) + continue; + if (line.Length < 5) + continue; + if (line.EndsWith(',')) + continue; + regularExpressionsMatch = CSharpMethodLine().Match(line); + if (!regularExpressionsMatch.Success) + continue; + match = new(Async: regularExpressionsMatch.Groups[_Async].Value, + Name: regularExpressionsMatch.Groups[_Name].Value, + Parameters: regularExpressionsMatch.Groups[_Parameters].Value, + Partial: regularExpressionsMatch.Groups[_Partial].Value, + Result: regularExpressionsMatch.Groups[_Result].Value, + Scope: regularExpressionsMatch.Groups[_Scope].Value, + Static: regularExpressionsMatch.Groups[_Static].Value, + Value: regularExpressionsMatch.Value); + scopeEnum = GetScopeEnum(match); + parameters = GetParameters(parameterRegex, match); + search = new(Constructor: $"{match.Name.ToLower()} = new(", + Delegate: $" += {match.Name};", + Name: $" {match.Name}(", + Not: $"!{match.Name}(", + Wrap: $"({match.Name}("); + logger.LogInformation("{line} {a} // {results}", line.Split(" =>")[0], "{ }", results.Count); + if (string.IsNullOrEmpty(match.Name)) + continue; + blocks = 0; + startLine = GetStartLine(lines, i); + if (!lines[startLine].StartsWith("#pragma") && !lines[startLine].StartsWith("#nullable")) + firstLine = lines[startLine].Trim(); + else + firstLine = lines[startLine + 1].Trim(); + isLinq = !lines[i + 1].StartsWith("#pragma") && !lines[i + 1].StartsWith("#nullable") && lines[i + 1].Trim() != "{"; + if (isLinq) + blocks++; + endLine = null; + for (int j = i + 1; j < lines.Length; j++) + { + innerLine = lines[j].Trim(); + if (innerLine.StartsWith("#pragma") || innerLine.StartsWith("#nullable")) + continue; + if (isLinq && string.IsNullOrEmpty(innerLine)) + { + if (line.EndsWith(';')) + blocks--; + } + blocks += GetLineBlockCount(innerLine, isLinq); + if (blocks != 0) + continue; + endLine = j; + if (lines.Length > j + 1 && string.IsNullOrEmpty(lines[j + 1].Trim())) + endLine++; + if (j > lines.Length - 2) + throw new Exception(); + break; + } + referenceToLineNumbers = GetReferenceToLineNumbers(lines: lines, start: 0, end: lines.Length, i: i, search: search, parameters: parameters); + if (referenceToLineNumbers.Count == 0) + { + lineSegmentFirst = line.Split(match.Name)[0]; + if (!lines[i - 1].Trim().StartsWith("[Obsolete")) + { + if (lineSegmentFirst.StartsWith("private")) + logger.LogWarning("// <{cSharpFileName}> {name} with {parameters} parameter(s) <{line}>", Path.GetFileName(cSharpFile), match.Name, parameters, lineSegmentFirst); + else + logger.LogInformation("// <{cSharpFileName}> {name} with {parameters} parameter(s) <{line}>", Path.GetFileName(cSharpFile), match.Name, parameters, lineSegmentFirst); + } + } + if (referenceToLineNumbers.Count == 0) + referenceToLineNumbers.Add(-1); + logger.LogInformation("{line} {a} // {results} ~~~ {startLine} => {firstUsedLine}", line.Split(" =>")[0], "{ }", results.Count, startLine, referenceToLineNumbers.First()); + method = new(EndLine: endLine, + FirstLine: firstLine, + I: i, + Line: line, + Match: match, + Parameters: parameters, + ReferenceToLineNumbers: referenceToLineNumbers.AsReadOnly(), + Search: search, + ScopeEnum: scopeEnum, + StartLine: startLine); + results.Add(method); + } + return results.AsReadOnly(); + } + + private static int GetNumberOfStartSpaces(string[] lines, int i) + { + int result = 0; + foreach (char @char in lines[i]) + { + if (@char != ' ') + break; + result += 1; + } + return result; + } + + private static int GetScopeEnum(Match match) + { + int result; + int value = match.Scope switch + { + "public" => 8000, + "internal" => 7000, + "protected" => 6000, + "private" => 5000, + _ => match.Scope.Length > 2 + && match.Scope[..2] == " I" + && match.Scope[^1] == '.' ? 9000 : throw new NotImplementedException() + }; + result = value + + (string.IsNullOrEmpty(match.Result) ? 100 : 0) + + (string.IsNullOrEmpty(match.Static) ? 0 : 10) + + (string.IsNullOrEmpty(match.Async) ? 0 : 1); + return result; + } + + private static ReadOnlyDictionary GetParameters(Regex parameterRegex, Match match) + { + Dictionary results = []; + string value; + string[] segments; + System.Text.RegularExpressions.Match[] matches = parameterRegex.Matches($"{match.Parameters},").ToArray(); + foreach (System.Text.RegularExpressions.Match m in matches) + { + if (!m.Success) + continue; + value = m.Value.Trim()[..^1]; + segments = value.Split(' '); + results.Add(segments[^1], value); + } + return new(results); + } + + 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 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 List GetReferenceToLineNumbers(string[] lines, int start, int end, int i, Search search, ReadOnlyDictionary parameters) + { + List results = []; + string[] segments; + string[] afterSegments; + string lastSegmentBeforeDot; + for (int j = start; j < end; j++) + { + if (j == i) + continue; + segments = lines[j].Split(search.Name); + if (segments.Length == 1) + { + segments = lines[j].Split(search.Not); + if (segments.Length == 1) + { + segments = lines[j].Split(search.Wrap); + if (segments.Length == 1) + { + if (!lines[j].EndsWith(search.Delegate)) + { + segments = lines[j].Split(search.Constructor); + if (segments.Length == 1) + continue; + } + } + } + } + if (lines[j].EndsWith(search.Delegate)) + results.Add(j); + else + { + lastSegmentBeforeDot = segments[^1].Split(").")[0]; + if (parameters.Count == 0) + { + if (lastSegmentBeforeDot.Contains(',')) + continue; + } + else + { + afterSegments = lastSegmentBeforeDot.Split(','); + if (afterSegments.Length != parameters.Count) + continue; + } + results.Add(j); + } + } + return results; + } + + private static ReadOnlyCollection GetSortedMethods(ReadOnlyCollection methods) => + (from l in methods orderby l.ScopeEnum descending, l.ReferenceToLineNumbers.Count descending, l.Line.Length, l.Match.Name.Length, l.Match.Name select l).ToArray().AsReadOnly(); + + private static ReadOnlyCollection GetCollection(ILogger logger, string[] lines, ReadOnlyCollection sortedMethods) + { + List results = []; + List check = sortedMethods.ToList(); + foreach (Method method in sortedMethods) + { + logger.LogInformation($"{method.Match.Name} => {method.Parameters.Count}"); + if (method.EndLine is null) + continue; + if (!check.Remove(method)) + continue; + MethodWith methodWith = GetMethodWith(lines, sortedMethods, check, method, method.EndLine.Value); + results.Add(methodWith); + } + return results.AsReadOnly(); + } + + private static MethodWith GetMethodWith(string[] lines, ReadOnlyCollection methods, List check, Method method, int methodEndLineValue) + { + MethodWith methodWith; + List referenceToLineNumbers; + MethodWith[] sortedReferences; + Dictionary references = []; + foreach (Method m in methods) + { + if (m.EndLine is null) + continue; + if (m == method) + continue; + referenceToLineNumbers = GetReferenceToLineNumbers(lines: lines, start: method.StartLine, end: methodEndLineValue, i: -1, search: m.Search, parameters: m.Parameters); + if (referenceToLineNumbers.Count > 0) + { + if (!check.Remove(m)) + continue; + foreach (int i in referenceToLineNumbers) + { + if (references.ContainsKey(i)) + continue; + methodWith = GetMethodWith(lines, methods, check, m, m.EndLine.Value); + references.Add(i, methodWith); + break; + } + } + } + if (references.Count < 2) + sortedReferences = (from l in references select l.Value).ToArray(); + else + sortedReferences = (from l in references orderby l.Key select l.Value).ToArray(); + methodWith = new(EndLine: method.EndLine, + FirstLine: method.FirstLine, + Line: method.Line, + Match: method.Match, + Parameters: method.Parameters, + References: new(sortedReferences), + ReferenceToLineNumbers: method.ReferenceToLineNumbers, + ScopeEnum: method.ScopeEnum, + Search: method.Search, + StartLine: method.StartLine); + return methodWith; + } + + private static bool WriteAllLines(string cSharpFile, string[] lines, ReadOnlyCollection collection) + { + bool result; + if (Debugger.IsAttached) + WriteDebug(collection); + List results = []; + ReadOnlyCollection methodLines = GetMethodLines(collection); + int maxMethodLines = methodLines.Max(); + for (int i = 0; i < maxMethodLines; i++) + { + if (methodLines.Contains(i)) + continue; + results.Add(lines[i]); + } + List nests = [true]; + foreach (MethodWith methodWith in collection) + { + if (methodWith.EndLine is null) + continue; + AppendLines(results, nests, lines, methodWith, methodWith.EndLine.Value); + } + for (int i = maxMethodLines + 1; i < lines.Length; i++) + 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 void WriteDebug(ReadOnlyCollection collection) + { + List results = []; + List nests = [true]; + foreach (MethodWith methodWith in collection) + AppendLines(results, nests, methodWith); + File.WriteAllText(Path.Combine(".vscode", "helper", ".md"), string.Join(Environment.NewLine, results)); + } + + private static void AppendLines(List results, List nests, MethodWith methodWith) + { + nests.Add(true); + results.Add($" - {new string('#', nests.Count)} {methodWith.Match.Name} => {methodWith.Parameters.Count}"); + foreach (MethodWith m in methodWith.References) + AppendLines(results, nests, m); + nests.RemoveAt(nests.Count - 1); + } + + private static ReadOnlyCollection GetMethodLines(ReadOnlyCollection collection) + { + List results = []; + List nests = [true]; + foreach (MethodWith methodWith in collection) + { + if (methodWith.EndLine is null) + continue; + AppendLineNumbers(results, nests, methodWith, methodWith.EndLine.Value); + } + int[] distinct = results.Distinct().ToArray(); + if (distinct.Length != results.Count) + throw new Exception(); + return new(results); + } + + private static void AppendLineNumbers(List results, List nests, MethodWith methodWith, int methodWithEndLineValue) + { + nests.Add(true); + for (int i = methodWith.StartLine; i < methodWithEndLineValue + 1; i++) + results.Add(i); + foreach (MethodWith m in methodWith.References) + { + if (m.EndLine is null) + continue; + AppendLineNumbers(results, nests, m, m.EndLine.Value); + } + nests.RemoveAt(nests.Count - 1); + } + + private static void AppendLines(List results, List nests, string[] lines, MethodWith methodWith, int methodWithEndLineValue) + { + nests.Add(true); + for (int i = methodWith.StartLine; i < methodWithEndLineValue + 1; i++) + results.Add(lines[i]); + foreach (MethodWith m in methodWith.References) + { + if (m.EndLine is null) + continue; + AppendLines(results, nests, lines, m, m.EndLine.Value); + } + nests.RemoveAt(nests.Count - 1); + } + +} \ No newline at end of file diff --git a/ADO2025/PI5/Helper-2025-03-21.cs b/ADO2025/PI5/Helper-2025-03-21.cs new file mode 100644 index 0000000..75dfbc1 --- /dev/null +++ b/ADO2025/PI5/Helper-2025-03-21.cs @@ -0,0 +1,156 @@ +using File_Folder_Helper.Helpers; +using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; + +namespace File_Folder_Helper.ADO2025.PI5; + +internal static partial class Helper20250321 +{ + + private record Record(string Directory, + string File, + ThreeDeep ThreeDeep) + { + + public static ReadOnlyCollection GetCollection(string sourceDirectory, string searchPattern, string[] files) + { + List results = []; + Record record; + string directory; + string fileNameWithoutExtension; + bool json = searchPattern.Contains(".json"); + bool check = searchPattern.Split('.').Length == 3; + ReadOnlyCollection collection = ThreeDeep.GetCollection(files); + foreach (ThreeDeep threeDeep in collection) + { + if (!json && check) + fileNameWithoutExtension = threeDeep.DirectoryName; + else if (!json && !check) + fileNameWithoutExtension = threeDeep.FileNameWithoutExtension; + else if (json) + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(threeDeep.FileNameWithoutExtension); + else + throw new NotImplementedException(); + directory = $"{fileNameWithoutExtension[^1]}{fileNameWithoutExtension[^3..][..2]}"; + if (json || (!json && !check)) + { + record = new(Directory: Path.Combine(sourceDirectory, "new-a", directory), + File: $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}", + ThreeDeep: threeDeep); + } + else if (!json && check) + { + record = new(Directory: Path.Combine(sourceDirectory, "new-b", directory, threeDeep.DirectoryName), + File: $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}", + ThreeDeep: threeDeep); + } + else + throw new NotImplementedException(); + results.Add(record); + } + return results.AsReadOnly(); + } + + } + + private record ThreeDeep(string Extension, + string FileNameWithoutExtension, + long LastModified, + long Length, + string DirectoryName, + string ParentDirectoryName, + string Root) + { + + public static ReadOnlyCollection GetCollection(string[] files) + { + List results = []; + ThreeDeep record; + FileInfo fileInfo; + string parentDirectory; + foreach (string file in files) + { + fileInfo = new(file); + parentDirectory = Path.GetDirectoryName(fileInfo.DirectoryName) ?? throw new Exception(); + record = new(Extension: Path.GetExtension(file), + FileNameWithoutExtension: Path.GetFileNameWithoutExtension(file), + LastModified: fileInfo.LastWriteTime.Ticks, + Length: fileInfo.Length, + DirectoryName: Path.GetFileName(fileInfo.DirectoryName) ?? throw new Exception(), + ParentDirectoryName: Path.GetFileName(parentDirectory), + Root: Path.GetDirectoryName(parentDirectory) ?? throw new Exception()); + results.Add(record); + } + return results.AsReadOnly(); + } + + public static string GetFullPath(ThreeDeep threeDeep) => + Path.Combine(threeDeep.Root, threeDeep.ParentDirectoryName, threeDeep.DirectoryName, $"{threeDeep.FileNameWithoutExtension}{threeDeep.Extension}"); + + } + + internal static void MoveToLast(ILogger logger, List args) + { + string[] searchPatterns = args[2].Split('|'); + string sourceDirectory = Path.GetFullPath(args[0]); + if (searchPatterns.Length == 1) + logger.LogInformation("No code for just one!"); + else + { + HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); + ReadOnlyCollection collection = GetCollection(logger, searchPatterns, sourceDirectory); + if (collection.Count != 0) + UseCollection(collection); + else + logger.LogInformation("No files!"); + if (collection.Count != 0) + HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); + } + } + + private static ReadOnlyCollection GetCollection(ILogger logger, string[] searchPatterns, string sourceDirectory) + { + string[] files; + List results = []; + foreach (string searchPattern in searchPatterns) + { + files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); + if (files.Length == 0) + logger.LogWarning("<{files}>(s)", files.Length); + else + { + ReadOnlyCollection collection = Record.GetCollection(sourceDirectory, searchPattern, files); + results.AddRange(collection); + } + } + return results.AsReadOnly(); + } + + private static void UseCollection(ReadOnlyCollection collection) + { + string fullPath; + string checkFile; + List distinct = []; + foreach (Record record in collection) + { + if (distinct.Contains(record.Directory)) + continue; + distinct.Add(record.Directory); + } + foreach (string directory in distinct) + { + if (Directory.Exists(directory)) + continue; + _ = Directory.CreateDirectory(directory); + } + foreach (Record record in collection) + { + checkFile = Path.Combine(record.Directory, record.File); + if (File.Exists(checkFile)) + continue; + fullPath = ThreeDeep.GetFullPath(record.ThreeDeep); + File.Move(fullPath, checkFile); + } + } + +} \ No newline at end of file diff --git a/Day/HelperDay.cs b/Day/HelperDay.cs index 56dd616..51419f8 100644 --- a/Day/HelperDay.cs +++ b/Day/HelperDay.cs @@ -145,6 +145,12 @@ internal static class HelperDay ADO2025.PI5.Helper20250305.WriteNginxFileSystemDelta(logger, args); else if (args[1] == "Day-Helper-2025-03-06") ADO2025.PI5.Helper20250306.ProcessDataStandardFormatToJson(logger, args); + else if (args[1] == "Day-Helper-2025-03-15") + ADO2025.PI5.Helper20250315.Empty(logger, args); + else if (args[1] == "Day-Helper-2025-03-20") + ADO2025.PI5.Helper20250320.SortCodeMethods(logger, args, cancellationToken); + else if (args[1] == "Day-Helper-2025-03-21") + ADO2025.PI5.Helper20250321.MoveToLast(logger, args); else throw new Exception(appSettings.Company); } diff --git a/File-Folder-Helper.csproj b/File-Folder-Helper.csproj index 38a5ae9..8e9133e 100644 --- a/File-Folder-Helper.csproj +++ b/File-Folder-Helper.csproj @@ -17,7 +17,7 @@ - +