From 7566ce13065dda02ac189ac09ab25d37b3e8f86b Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Fri, 20 Jun 2025 18:07:40 -0700 Subject: [PATCH] Update Markdown Helper to write json files for tables and yaml --- .gitignore | 1 + .vscode/launch.json | 277 +++++++++++++----------- .vscode/mklink.md | 4 + Helpers/HelperMarkdown.cs | 428 +++++++++++++++++++++++++++++++++----- Scripts/markdown.js | 7 + 5 files changed, 543 insertions(+), 174 deletions(-) create mode 100644 Scripts/markdown.js diff --git a/.gitignore b/.gitignore index d13d98d..254e5d1 100644 --- a/.gitignore +++ b/.gitignore @@ -336,3 +336,4 @@ ASALocalRun/ .extensions-vscode-oss .extensions-vscode-insiders .vscode/.UserSecrets/secrets.json +.vscode/.helper diff --git a/.vscode/launch.json b/.vscode/launch.json index 1959b02..2ffab90 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,128 +1,153 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": ".NET Core Launch (console)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", - "args": [ - "s", - "X", - "D:/5-Other-Small/Proxmox/DiskInfo", - "Day-Helper-2025-06-18", - "*.json", - "D:/5-Other-Small/Proxmox/Disk-Info-Old", - "-2025-", - "1", - "s", - "X", - "D:/Tmp", - "Day-Helper-2025-06-02", - "infineon\\MESPhares", - "BACKLOG~BIORAD2~BIORAD3~BIORAD4~BIORAD5~CDE4~CDE5~CDE6~DEP08CEPIEPSILON~DEP08SIASM~DEP08SIHTRPLC~EC~HGCV1~HGCV2~HGCV3~MESAFIBACKLOG~MET06AWCT~MET08ANLYSDIFAAST230~MET08AWCT~MET08DDUPSFS6420~MET08DDUPSP1TBI~MET08RESIHGCV~MET08RESIMAPCDE~MET08RESISRP2100~MET08THFTIRQS408M~MET08THFTIRSTRATUS~METCLIMATEC~R29~R32~R36~R47~R55~R57~R61~R62~R65~R70~R72~R73~R74~R75~R77~SP101~SPV01~SRP~TENCOR1~TENCOR2~TENCOR3~TRENDLOG~WC6INCH1~WC6INCH2~WC6INCH3~WC6INCH4~WC8INCH1~WC8INCH2~WC8INCH3", - "s", - "X", - "D:/5-Other-Small/Proxmox/ffnm", - "Day-Helper-2025-05-21", - "*.pdf", - "*.md", - "2", - "MM-dd-yy", - "Trans Date~Effective Date~Description~Withdrawal Deposit~Balance", - "s", - "X", - "D:/Tmp/phares/VisualStudioCode", - "Day-Helper-2025-05-19", - "D:/Tmp/phares/VisualStudioCode/.vscode/input.json", - "s", - "X", - "D:/Tmp/phares/VisualStudioCode", - "Day-Helper-2025-05-19", - "D:/Tmp/phares/VisualStudioCodeLeft", - "z-include-patterns.nsv", - "z-exclude-patterns.nsv", - "http://localhost:5004", - "/api/SyncV1/?", - ",L", - ".G", - "+~G~~L~+~Custom-Default", - "", - "+~G~~G~-~Mirror", - "+~G~~~~Update", - "+~G~~L~+~Custom-Default", - "-~G~~G~+~Custom-A", - "-~L~~L~+~Custom-B", - "+~L~~L~-~Custom-C", - "s", - "X", - "\\\\mesfs.infineon.com\\EC_Characterization_Si\\Archive\\BIORAD4\\2025_Week_16\\2025-04-17", - "Day-Helper-2025-02-19", - "csv-*.pdsf", - "*.pdsf", - "Time,HeaderUniqueId,UniqueId,Date,Wafer,Position,BIORAD4", - ",BIORAD4", - ",BIORAD4", - "Test|EventId,Date|DateTime,Position|Slot,DeltaThicknessSlotsOneAndTwentyFive|Actual Delta Thick Pts 1 and 25,PercentDeltaThicknessSlotsOneAndTwentyFive|% Delta Thick Pts 1 and 25,MID|Cassette,Lot|Batch,Title|Batch,Wafer|Text,Thickness|Site,MeanThickness|GradeMean,|BIORAD4", - "Time,A_LOGISTICS,B_LOGISTICS,Test,Count,Index,MesEntity,MID,Date,Employee,Lot,PSN,Reactor,Recipe,Cassette,GradeStdDev,HeaderUniqueId,Layer,MeanThickness,PassFail,RDS,Slot,Title,UniqueId,Wafer,Zone,Mean,Position,StdDev,Thickness,ThicknessSlotOne,ThicknessSlotTwentyFive,DeltaThicknessSlotsOneAndTwentyFive,PercentDeltaThicknessSlotsOneAndTwentyFive", - "Time,A_LOGISTICS,B_LOGISTICS,Count,Sequence,MesEntity,Index,Batch,Cassette,DateTime,Destination,Mean,PassFail,Recipe,Reference,Site,Slot,Source,StdDev,Text,GradeMean,GradeStdDev,RDS,PSN,Reactor,Layer,Zone,Employee,InferredLot,Thickness First Slot,Thickness Last Slot,Actual Delta Thick Pts 1 and 25,% Delta Thick Pts 1 and 25,EventId", - "0,1,2,31,3,6,5,8,9,27,7,23,24,13,8,21,-1,25,20,12,22,16,7,-1,19,26,11,16,18,15,-1,-1,29,30", - "s", - "X", - "C:/Users/phares/AppData/Roaming/FreeFileSync", - "Day-Helper-2025-04-21", - "GlobalSettings.xml", - "LastSync|Config", - "s", - "X", - "L:/Tmp/MET08ANLYSDIFAAST230", - "Day-Helper-2025-03-06", - "*.pdsf", - "s", - "X", - "D:/ProgramData/VisualStudioCode|D:/6-Other-Large-Z/Linux-Ubuntu-Phares/home/lphares/dorico", - "Day-Helper-2025-04-07", - "z-include-patterns.nsv", - "z-exclude-patterns.nsv", - "https://isccvm57294f1ed/VisualStudioCode|hxttps://dorico.phares.duckdns.org|hxttps://mestsa006.infineon.com/VisualStudioCode", - "+|G|G|G|-", - "||||", - "666", - "777", - "888", - "999", - "s", - "X", - "C:/Users/PHARES/AppData/Local/IFXApps/gatus", - "Day-Helper-2025-04-04", - "*.json", - ".metrics", - "https://messa010ec.infineon.com/metrics", - "gatus_results_endpoint_success", - "666", - "777", - "888", - "999", - "" - ], - "cwd": "${workspaceFolder}", - "console": "integratedTerminal", - "stopAtEntry": false - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - }, - { - "type": "node", - "request": "launch", - "name": "node Launch Current Opened File", - "program": "${file}" - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", + "args": [ + "s", + "M", + "D:/5-Other-Small/Notes/Infineon", + "-d", + "D:/5-Other-Small/Notes/Infineon/.vscode/helper", + "s", + "X", + "D:/5-Other-Small/Proxmox/DiskInfo", + "Day-Helper-2025-06-18", + "*.json", + "D:/5-Other-Small/Proxmox/Disk-Info-Old", + "-2025-", + "1", + "s", + "X", + "D:/Tmp", + "Day-Helper-2025-06-02", + "infineon\\MESPhares", + "BACKLOG~BIORAD2~BIORAD3~BIORAD4~BIORAD5~CDE4~CDE5~CDE6~DEP08CEPIEPSILON~DEP08SIASM~DEP08SIHTRPLC~EC~HGCV1~HGCV2~HGCV3~MESAFIBACKLOG~MET06AWCT~MET08ANLYSDIFAAST230~MET08AWCT~MET08DDUPSFS6420~MET08DDUPSP1TBI~MET08RESIHGCV~MET08RESIMAPCDE~MET08RESISRP2100~MET08THFTIRQS408M~MET08THFTIRSTRATUS~METCLIMATEC~R29~R32~R36~R47~R55~R57~R61~R62~R65~R70~R72~R73~R74~R75~R77~SP101~SPV01~SRP~TENCOR1~TENCOR2~TENCOR3~TRENDLOG~WC6INCH1~WC6INCH2~WC6INCH3~WC6INCH4~WC8INCH1~WC8INCH2~WC8INCH3", + "s", + "X", + "D:/5-Other-Small/Proxmox/ffnm", + "Day-Helper-2025-05-21", + "*.pdf", + "*.md", + "2", + "MM-dd-yy", + "Trans Date~Effective Date~Description~Withdrawal Deposit~Balance", + "s", + "X", + "D:/Tmp/phares/VisualStudioCode", + "Day-Helper-2025-05-19", + "D:/Tmp/phares/VisualStudioCode/.vscode/input.json", + "s", + "X", + "D:/Tmp/phares/VisualStudioCode", + "Day-Helper-2025-05-19", + "D:/Tmp/phares/VisualStudioCodeLeft", + "z-include-patterns.nsv", + "z-exclude-patterns.nsv", + "http://localhost:5004", + "/api/SyncV1/?", + ",L", + ".G", + "+~G~~L~+~Custom-Default", + "", + "+~G~~G~-~Mirror", + "+~G~~~~Update", + "+~G~~L~+~Custom-Default", + "-~G~~G~+~Custom-A", + "-~L~~L~+~Custom-B", + "+~L~~L~-~Custom-C", + "s", + "X", + "\\\\mesfs.infineon.com\\EC_Characterization_Si\\Archive\\BIORAD4\\2025_Week_16\\2025-04-17", + "Day-Helper-2025-02-19", + "csv-*.pdsf", + "*.pdsf", + "Time,HeaderUniqueId,UniqueId,Date,Wafer,Position,BIORAD4", + ",BIORAD4", + ",BIORAD4", + "Test|EventId,Date|DateTime,Position|Slot,DeltaThicknessSlotsOneAndTwentyFive|Actual Delta Thick Pts 1 and 25,PercentDeltaThicknessSlotsOneAndTwentyFive|% Delta Thick Pts 1 and 25,MID|Cassette,Lot|Batch,Title|Batch,Wafer|Text,Thickness|Site,MeanThickness|GradeMean,|BIORAD4", + "Time,A_LOGISTICS,B_LOGISTICS,Test,Count,Index,MesEntity,MID,Date,Employee,Lot,PSN,Reactor,Recipe,Cassette,GradeStdDev,HeaderUniqueId,Layer,MeanThickness,PassFail,RDS,Slot,Title,UniqueId,Wafer,Zone,Mean,Position,StdDev,Thickness,ThicknessSlotOne,ThicknessSlotTwentyFive,DeltaThicknessSlotsOneAndTwentyFive,PercentDeltaThicknessSlotsOneAndTwentyFive", + "Time,A_LOGISTICS,B_LOGISTICS,Count,Sequence,MesEntity,Index,Batch,Cassette,DateTime,Destination,Mean,PassFail,Recipe,Reference,Site,Slot,Source,StdDev,Text,GradeMean,GradeStdDev,RDS,PSN,Reactor,Layer,Zone,Employee,InferredLot,Thickness First Slot,Thickness Last Slot,Actual Delta Thick Pts 1 and 25,% Delta Thick Pts 1 and 25,EventId", + "0,1,2,31,3,6,5,8,9,27,7,23,24,13,8,21,-1,25,20,12,22,16,7,-1,19,26,11,16,18,15,-1,-1,29,30", + "s", + "X", + "C:/Users/phares/AppData/Roaming/FreeFileSync", + "Day-Helper-2025-04-21", + "GlobalSettings.xml", + "LastSync|Config", + "s", + "X", + "L:/Tmp/MET08ANLYSDIFAAST230", + "Day-Helper-2025-03-06", + "*.pdsf", + "s", + "X", + "D:/ProgramData/VisualStudioCode|D:/6-Other-Large-Z/Linux-Ubuntu-Phares/home/lphares/dorico", + "Day-Helper-2025-04-07", + "z-include-patterns.nsv", + "z-exclude-patterns.nsv", + "https://isccvm57294f1ed/VisualStudioCode|hxttps://dorico.phares.duckdns.org|hxttps://mestsa006.infineon.com/VisualStudioCode", + "+|G|G|G|-", + "||||", + "666", + "777", + "888", + "999", + "s", + "X", + "C:/Users/PHARES/AppData/Local/IFXApps/gatus", + "Day-Helper-2025-04-04", + "*.json", + ".metrics", + "https://messa010ec.infineon.com/metrics", + "gatus_results_endpoint_success", + "666", + "777", + "888", + "999", + "" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + }, + { + "type": "node", + "request": "launch", + "name": "node Launch Current Opened File", + "program": "${file}" + }, + { + "type": "bun", + "internalConsoleOptions": "neverOpen", + "request": "launch", + "name": "Debug File", + "program": "${file}", + "cwd": "${workspaceFolder}", + "stopOnEntry": false, + "watchMode": false + }, + { + "type": "bun", + "internalConsoleOptions": "neverOpen", + "request": "launch", + "name": "Run File", + "program": "${file}", + "cwd": "${workspaceFolder}", + "noDebug": true, + "watchMode": false + } + ] } \ No newline at end of file diff --git a/.vscode/mklink.md b/.vscode/mklink.md index 75bdf43..8c5ec42 100644 --- a/.vscode/mklink.md +++ b/.vscode/mklink.md @@ -35,3 +35,7 @@ mklink /J "C:\Users\phares\.vscode-insiders\extensions\infineon-technologies-ag- mkdir "L:\DevOps\MESA_FI\file-folder-helper\bin\Release\net8.0\win-x64" mklink /J "L:\DevOps\MESA_FI\file-folder-helper\bin\Release\net8.0\win-x64\publish" "D:\5-Other-Small\Proxmox\publish" ``` + +```bash 1750459968132 = 638860567681320000 = 2025-3.Summer = Fri Jun 20 2025 15:52:47 GMT-0700 (Mountain Standard Time) +mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.vscode\.helper" "D:\5-Other-Small\Notes\Infineon\.vscode\helper" +``` diff --git a/Helpers/HelperMarkdown.cs b/Helpers/HelperMarkdown.cs index 9dd3895..5c880df 100644 --- a/Helpers/HelperMarkdown.cs +++ b/Helpers/HelperMarkdown.cs @@ -16,8 +16,20 @@ internal static partial class HelperMarkdown private record Input(string? Destination, string Source, + string? ReplaceWithTitle, bool UseProcessStart); + private record Table(ReadOnlyCollection Columns, + int ColumnsLine, + ReadOnlyCollection> Rows, + string? Title); + + private record Block(int Line, + string? Json, + int RowCount, + ReadOnlyCollection? Rows, + string? Title); + private record Record(string Directory, string File, string[] Lines); @@ -30,7 +42,7 @@ internal static partial class HelperMarkdown string FileNameWithoutExtension, ReadOnlyDictionary FrontMatterYaml, string H1, - bool IsGitOthersModifiedAndDeletedExcludingStandard, + bool? IsGitOthersModifiedAndDeletedExcludingStandard, bool IsKanbanIndex, bool IsKanbanMarkdown, DateTime LastWriteDateTime, @@ -49,15 +61,27 @@ internal static partial class HelperMarkdown private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath); + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(Dictionary))] + internal partial class DictionaryStringAndObjectSourceGenerationContext : JsonSerializerContext + { + } + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSerializable(typeof(Dictionary))] internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext { } + [GeneratedRegex("([A-Z]+(.))")] + private static partial Regex UpperCase(); + [GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")] private static partial Regex HtmlColor(); + [GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")] + private static partial Regex InvalidCharacter(); + private static MarkdownExtra GetMarkdownExtra(MarkdownFileAndLines markdownFileAndLines) { MarkdownExtra result; @@ -301,10 +325,10 @@ internal static partial class HelperMarkdown if (!directoryInfo.Exists || (!string.IsNullOrEmpty(directoryInfo.LinkTarget) && !Directory.Exists(directoryInfo.LinkTarget))) continue; collection.AddRange(GetFiles(appSettings, directoryInfo, SearchOption.TopDirectoryOnly)); - foreach (FileInfo file in collection) - results.Add(file); + foreach (FileInfo fileInfo in collection) + results.Add(fileInfo); } - return new(results); + return (from l in results orderby l.FullName select l).ToArray().AsReadOnly(); } internal static LineNumber GetLineNumbers(FileInfo fileInfo) @@ -525,7 +549,7 @@ internal static partial class HelperMarkdown h1Check = $"# {h1[2..]}"; if (h1Check.Length == h1.Length && h1Check != h1) { - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; lines[markdownFile.LineNumber.H1.Value] = h1Check; File.WriteAllLines(markdownFile.File, lines); @@ -541,7 +565,7 @@ internal static partial class HelperMarkdown checkName = Path.Combine(markdownFile.Directory, checkFileName); if (checkName == markdownFile.File) continue; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; File.Move(markdownFile.File, checkName); result += 1; @@ -658,6 +682,7 @@ internal static partial class HelperMarkdown Input result; string? destination = null; bool useProcessStart = false; + string? replaceWithTitle = null; string source = Path.GetFullPath(args[0]); for (int i = 1; i < args.Count; i++) { @@ -667,6 +692,8 @@ internal static partial class HelperMarkdown useProcessStart = args[i + 1] == "true"; else if (args[i][1] == 'd') destination = Path.GetFullPath(args[i + 1]); + else if (args[i][1] == 't') + replaceWithTitle = args[i + 1]; i++; } } @@ -678,11 +705,11 @@ internal static partial class HelperMarkdown if (!Directory.Exists(destination)) _ = Directory.CreateDirectory(destination); } - result = new(destination, source, useProcessStart); + result = new(Destination: destination, Source: source, ReplaceWithTitle: replaceWithTitle, UseProcessStart: useProcessStart); return result; } - private static ReadOnlyDictionary GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection gitOthersModifiedAndDeletedExcludingStandardFiles) + private static ReadOnlyDictionary GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection? gitOthersModifiedAndDeletedExcludingStandardFiles) { Dictionary results = []; string h1; @@ -696,17 +723,22 @@ internal static partial class HelperMarkdown string fileNameWithoutExtension; ReadOnlyCollection lines; ReadOnlyDictionary frontMatterYaml; - bool isGitOthersModifiedAndDeletedExcludingStandard; + bool? isGitOthersModifiedAndDeletedExcludingStandard; ReadOnlyCollection files = GetFiles(appSettings, input); foreach (FileInfo fileInfo in files) { // cSpell:disable if (fileInfo.DirectoryName is null) continue; key = Path.GetRelativePath(input.Source, fileInfo.FullName); - isWithinSource = fileInfo.FullName.Contains(input.Source); - isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName); - if (!isWithinSource && results.ContainsKey(key)) - continue; + if (gitOthersModifiedAndDeletedExcludingStandardFiles is null) + isGitOthersModifiedAndDeletedExcludingStandard = null; + else + { + isWithinSource = fileInfo.FullName.Contains(input.Source); + isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName); + if (!isWithinSource && results.ContainsKey(key)) + continue; + } lineNumber = GetLineNumbers(fileInfo); lines = lineNumber.Lines; fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName); @@ -716,7 +748,7 @@ internal static partial class HelperMarkdown (type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber); else { - if (!isGitOthersModifiedAndDeletedExcludingStandard) + if (isGitOthersModifiedAndDeletedExcludingStandard is not null && !isGitOthersModifiedAndDeletedExcludingStandard.Value) continue; type = appSettings.DefaultNoteType; File.WriteAllLines(fileInfo.FullName, ["---", $"type: {type}\"", "---", string.Empty, $"# {h1}"]); @@ -724,20 +756,20 @@ internal static partial class HelperMarkdown } isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md"); isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase); - markdownFile = new(fileInfo.CreationTime, - fileInfo.DirectoryName, - fileInfo.Extension, - fileInfo.FullName, - fileInfo.Name, - fileNameWithoutExtension, - frontMatterYaml, - h1, - isGitOthersModifiedAndDeletedExcludingStandard, - isKanbanIndex, - isKanbanMarkdown, - fileInfo.LastWriteTime, - lineNumber, - type); + markdownFile = new(CreationDateTime: fileInfo.CreationTime, + Directory: fileInfo.DirectoryName, + Extension: fileInfo.Extension, + File: fileInfo.FullName, + FileName: fileInfo.Name, + FileNameWithoutExtension: fileNameWithoutExtension, + FrontMatterYaml: frontMatterYaml, + H1: h1, + IsGitOthersModifiedAndDeletedExcludingStandard: isGitOthersModifiedAndDeletedExcludingStandard, + IsKanbanIndex: isKanbanIndex, + IsKanbanMarkdown: isKanbanMarkdown, + LastWriteDateTime: fileInfo.LastWriteTime, + LineNumber: lineNumber, + Type: type); results.Add(key, new(markdownFile, lines.ToArray())); } // cSpell:restore return new(results); @@ -763,7 +795,7 @@ internal static partial class HelperMarkdown markdownFile = relativeTo.Value.MarkdownFile; if (markdownFile.IsKanbanMarkdown) continue; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; if (markdownFile.LineNumber.FrontMatterYamlEnd is null) continue; @@ -797,7 +829,7 @@ internal static partial class HelperMarkdown circularReference = false; lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; for (int i = 0; i < lines.Length; i++) { @@ -859,7 +891,7 @@ internal static partial class HelperMarkdown found = false; lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; for (int i = 0; i < lines.Length; i++) { @@ -912,7 +944,7 @@ internal static partial class HelperMarkdown write = false; lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; - if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; for (int i = 0; i < lines.Length; i++) { @@ -940,7 +972,7 @@ internal static partial class HelperMarkdown } if (!write) continue; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; File.WriteAllLines(markdownFile.File, lines); result += 1; @@ -975,7 +1007,7 @@ internal static partial class HelperMarkdown markdownFile = relativeTo.Value.MarkdownFile; if (markdownFile.IsKanbanIndex) continue; - if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (!input.UseProcessStart && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; if (!File.Exists(markdownFile.File)) continue; @@ -1040,7 +1072,7 @@ internal static partial class HelperMarkdown } if (!write) continue; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; File.WriteAllLines(markdownFile.File, lines); result += 1; @@ -1057,18 +1089,12 @@ internal static partial class HelperMarkdown MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection); if (markdownFileAndLines is not null && File.Exists(markdownFileAndLines.MarkdownFile.File)) { - ReadOnlyDictionary> columnsToCards; + ReadOnlyDictionary> columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines); string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json"); - if (File.Exists(jsonFile)) - File.Delete(jsonFile); - columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines); - if (columnsToCards.Count == 0) - File.WriteAllText(jsonFile, "{}"); - else - { - string json = JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard); + string old = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile); + string json = columnsToCards.Count == 0 ? "{}" : JsonSerializer.Serialize(columnsToCards, ColumnsAndCardsSourceGenerationContext.Default.ReadOnlyDictionaryStringListCard); + if (json != old) File.WriteAllText(jsonFile, json); - } } } @@ -1122,7 +1148,7 @@ internal static partial class HelperMarkdown results.Insert(0, string.Empty); } results.Insert(0, "---"); - if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) { results.Insert(0, updatedLine); results.Insert(0, createdLine); @@ -1140,7 +1166,7 @@ internal static partial class HelperMarkdown } if (markdownFile.LineNumber.Type is null) results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine); - if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) { if (markdownFile.LineNumber.Updated is null) results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine); @@ -1176,7 +1202,7 @@ internal static partial class HelperMarkdown } if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results)) continue; - if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard) + if (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard is not null && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard.Value) continue; File.WriteAllLines(markdownFile.File, results); File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime); @@ -1235,7 +1261,313 @@ internal static partial class HelperMarkdown logger.LogInformation("{updated} Markdown file(s) were updated", updated); } if (!string.IsNullOrEmpty(input.Destination)) + { SaveColumnToCards(input, relativeToCollection); + SaveTablesToJson(appSettings, logger, input); + } + } + + private static void SaveTablesToJson(AppSettings appSettings, ILogger logger, Input input) + { + if (string.IsNullOrEmpty(input.Destination)) + throw new NotSupportedException(); + string[] lines; + string fileName; + List blocks; + MarkdownFile markdownFile; + List fileNames = []; + ReadOnlyCollection tables; + ReadOnlyCollection javaScriptObjectNotationBlocks; + ReadOnlyCollection yetAnotherMarkupLanguageBlocks; + ReadOnlyDictionary relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles: null); + foreach (KeyValuePair relativeTo in relativeToCollection) + { + if (relativeTo.Value.Lines.Length == 0) + continue; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; + if (markdownFile.IsKanbanIndex) + continue; + if (markdownFile.IsKanbanMarkdown) + continue; + if (markdownFile.FileName.StartsWith("index.yml.md")) + continue; + if (!File.Exists(markdownFile.File)) + continue; + tables = GetTables(lines); + javaScriptObjectNotationBlocks = GetJavaScriptObjectNotationBlocks(lines); + yetAnotherMarkupLanguageBlocks = GetYetAnotherMarkupLanguageBlocks(lines); + if (tables.Count == 0 && javaScriptObjectNotationBlocks.Count == 0 && yetAnotherMarkupLanguageBlocks.Count == 0) + continue; + fileName = Path.Combine(input.Destination, $"{markdownFile.FileNameWithoutExtension}.json"); + if (fileNames.Contains(fileName)) + continue; + fileNames.Add(fileName); + blocks = []; + blocks.AddRange(GetBlocks(tables)); + blocks.AddRange(javaScriptObjectNotationBlocks); + blocks.AddRange(GetBlocks(logger, markdownFile, yetAnotherMarkupLanguageBlocks)); + if (blocks.Count == 0) + continue; + WriteBlocks(logger, input, markdownFile, fileName, blocks.AsReadOnly()); + } + } + + private static ReadOnlyCollection
GetTables(string[] lines) + { + List
results = []; + Table table; + string? title; + string[] segments; + int? columnsLine = null; + List> rows = []; + string[]? columns = null; + for (int i = 0; i < lines.Length; i++) + { + if (columns is null && (!lines[i].StartsWith('|') || !lines[i].EndsWith('|') || i + 3 >= lines.Length)) + continue; + if (columns is null && (lines[i + 1].Length < 3 || lines[i + 1][0] != '|' || (lines[i + 1][1] is not ':' and not '-'))) + continue; + if (columns is null) + { + columnsLine = i; + segments = lines[i][1..^1].Split('|'); + columns = (from l in segments select l.Trim()).ToArray(); + i++; + continue; + } + segments = lines[i].Length < 3 ? [] : lines[i][1..^1].Split('|'); + rows.Add((from l in segments select l.Trim()).ToArray().AsReadOnly()); + if (columns is not null && columnsLine is not null && string.IsNullOrEmpty(lines[i])) + { + if (columnsLine.Value - 2 > 0 && lines[columnsLine.Value - 2].StartsWith('#')) + title = lines[columnsLine.Value - 2]; + else if (columnsLine.Value - 4 > 0 && lines[columnsLine.Value - 4].StartsWith('#')) + title = lines[columnsLine.Value - 4]; + else + title = null; + rows.RemoveAt(rows.Count - 1); + table = new(columns.AsReadOnly(), columnsLine.Value, rows.AsReadOnly(), title); + results.Add(table); + columnsLine = null; + columns = null; + rows = []; + } + } + return results.AsReadOnly(); + } + + private static string GetParamCase(string value) + { + string result; + StringBuilder stringBuilder = new(value); + Match[] matches = UpperCase().Matches(value).ToArray(); + for (int i = matches.Length - 1; i > -1; i--) + _ = stringBuilder.Insert(matches[i].Index, '-'); + string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower()); + result = string.Join('-', segments).Trim('-'); + return result; + } + + private static ReadOnlyCollection GetBlocks(ReadOnlyCollection
tables) + { + List results = []; + Block block; + string json; + foreach (Table table in tables) + { + if (table.Title is null) + continue; + if (table.Rows.Count == 0) + continue; + json = GetJson(table); + block = new(Line: table.ColumnsLine, Json: json, RowCount: table.Rows.Count, Rows: null, Title: table.Title); + results.Add(block); + } + return results.AsReadOnly(); + } + + private static string GetJson(Table table) + { + string result; + string line; + string value; + string[] segments; + List lines = []; + for (int i = 0; i < table.Rows.Count; i++) + { + line = "{"; + segments = table.Rows[i].ToArray(); + if (segments.Length != table.Columns.Count) + break; + for (int c = 0; c < segments.Length; c++) + { + value = segments[c].Replace("\\", "\\\\").Replace("\"", "\\\""); + line += string.Concat('"', table.Columns[c].Trim('"'), '"', ':', '"', value, '"', ','); + } + line = string.Concat(line[..^1], '}'); + lines.Add(line); + } + result = string.Concat('[', Environment.NewLine, string.Join($",{Environment.NewLine}", lines), Environment.NewLine, ']'); + return result; + } + + private static ReadOnlyCollection GetJavaScriptObjectNotationBlocks(string[] lines) + { + List results = []; + string json; + string? title; + int? blockLine = null; + List rows = []; + Block javaScriptObjectNotationBlock; + for (int i = 0; i < lines.Length; i++) + { + if (blockLine is null && !lines[i].StartsWith("```json")) + continue; + if (blockLine is null) + { + blockLine = i; + continue; + } + rows.Add(lines[i]); + if (blockLine is not null && lines[i] == "```") + { + rows.RemoveAt(rows.Count - 1); + if (blockLine.Value - 2 > 0 && lines[blockLine.Value - 2].StartsWith('#')) + title = lines[blockLine.Value - 2]; + else if (blockLine.Value - 4 > 0 && lines[blockLine.Value - 4].StartsWith('#')) + title = lines[blockLine.Value - 4]; + else + title = null; + if (string.IsNullOrEmpty(title)) + continue; + json = string.Join(Environment.NewLine, rows); + javaScriptObjectNotationBlock = new(Line: blockLine.Value, Json: json, RowCount: rows.Count, Rows: null, Title: title); + results.Add(javaScriptObjectNotationBlock); + blockLine = null; + rows = []; + } + } + return results.AsReadOnly(); + } + + private static ReadOnlyCollection GetYetAnotherMarkupLanguageBlocks(string[] lines) + { + List results = []; + string? title = null; + int? blockLine = null; + List rows = []; + Block yetAnotherMarkupLanguageBlock; + if (lines[0] == "---") + { + for (int i = 1; i < lines.Length; i++) + { + rows.Add(lines[i]); + if (lines[i] == "---") + { + rows.RemoveAt(rows.Count - 1); + yetAnotherMarkupLanguageBlock = new(Line: 0, Json: null, RowCount: rows.Count, Rows: rows.AsReadOnly(), Title: title); + results.Add(yetAnotherMarkupLanguageBlock); + rows = []; + } + } + } + rows = []; + for (int i = 0; i < lines.Length; i++) + { + if (blockLine is null && !lines[i].StartsWith("```yml") && !lines[i].StartsWith("```yaml")) + continue; + if (blockLine is null) + { + blockLine = i; + continue; + } + rows.Add(lines[i]); + if (blockLine is not null && lines[i] == "```") + { + rows.RemoveAt(rows.Count - 1); + if (blockLine.Value - 2 > 0 && lines[blockLine.Value - 2].StartsWith('#')) + title = lines[blockLine.Value - 2]; + else if (blockLine.Value - 4 > 0 && lines[blockLine.Value - 4].StartsWith('#')) + title = lines[blockLine.Value - 4]; + else + title = null; + yetAnotherMarkupLanguageBlock = new(Line: blockLine.Value, Json: null, RowCount: rows.Count, Rows: rows.AsReadOnly(), Title: title); + results.Add(yetAnotherMarkupLanguageBlock); + blockLine = null; + rows = []; + } + } + return results.AsReadOnly(); + } + + private static ReadOnlyCollection GetBlocks(ILogger logger, MarkdownFile markdownFile, ReadOnlyCollection yetAnotherMarkupLanguageBlocks) + { + List results = []; + string key; + Block block; + string json; + string text; + Dictionary? keyValuePairs; + Dictionary keyValuePairsB = []; +#pragma warning disable IL3050 + IDeserializer deserializer = new DeserializerBuilder().Build(); +#pragma warning restore IL3050 + foreach (Block yaml in yetAnotherMarkupLanguageBlocks) + { + if (yaml.Rows is null || yaml.RowCount == 0) + continue; + if (yaml.Title is null && yaml.Line != 0) + continue; + text = string.Join(Environment.NewLine, yaml.Rows); + try + { keyValuePairs = deserializer.Deserialize>(text); } + catch (Exception) + { + keyValuePairs = null; + logger.LogWarning("Invalid yaml file for <{file}>", markdownFile.FileName); + } + if (keyValuePairs is null) + continue; + keyValuePairsB.Clear(); + foreach (KeyValuePair keyValuePair in keyValuePairs) + { + key = GetParamCase(keyValuePair.Key); + if (keyValuePairsB.ContainsKey(key)) + continue; + keyValuePairsB.Add(key, keyValuePair.Value); + } + try + { json = JsonSerializer.Serialize(keyValuePairsB, DictionaryStringAndObjectSourceGenerationContext.Default.DictionaryStringObject); } + catch (Exception) + { + logger.LogWarning("yaml contains special values not allowed in AOT <{file}>", markdownFile.FileName); + continue; + } + block = new(Line: yaml.Line, Json: json, RowCount: yaml.Rows.Count, Rows: null, Title: yaml.Title); + results.Add(block); + } + return results.AsReadOnly(); + } + + private static void WriteBlocks(ILogger logger, Input input, MarkdownFile markdownFile, string fileName, ReadOnlyCollection blocks) + { + string paramCase; + List lines = []; + foreach (Block block in blocks.OrderBy(l => l.Line)) + { + paramCase = block.Title is null ? $"Line-{block.Line}" : GetParamCase(block.Title); + if (!string.IsNullOrEmpty(input.ReplaceWithTitle) && paramCase == input.ReplaceWithTitle) + paramCase = GetParamCase(markdownFile.FileNameWithoutExtension); + lines.Add($"\"{paramCase}\": {block.Json}"); + } + string old = !File.Exists(fileName) ? string.Empty : File.ReadAllText(fileName); + string json = string.Concat('{', Environment.NewLine, string.Join($",{Environment.NewLine}", lines), Environment.NewLine, '}'); + if (json != old) + { + File.WriteAllText(fileName, json); + logger.LogInformation("Updated json file for <{fileName}>", fileName); + } } } \ No newline at end of file diff --git a/Scripts/markdown.js b/Scripts/markdown.js new file mode 100644 index 0000000..d2c75bc --- /dev/null +++ b/Scripts/markdown.js @@ -0,0 +1,7 @@ +// import data from './oi-metrology-viewer-0-Line-yaml.json' with { type: 'json' }; +// import data from '../.vscode/oi-metrology-viewer-0-Line-yaml.json' with { type: 'json' }; +import data from '../.vscode/.helper/hosts-legend-table.json' with { type: 'json' }; + +data.forEach(element => { + console.log(element.Concat); +}); \ No newline at end of file