From 299aa19d538bcfd5e06e4743a5a05f6d62960a80 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Wed, 1 May 2024 17:08:21 -0700 Subject: [PATCH] Kanban Index for Typescript Helper --- .vscode/mklink.md | 6 +- Helpers/HelperKanbanMetadata.cs | 84 +++++++------ Helpers/HelperMarkdown.cs | 213 ++++++++++++++++---------------- Worker.cs | 4 +- 4 files changed, 159 insertions(+), 148 deletions(-) diff --git a/.vscode/mklink.md b/.vscode/mklink.md index 808cbd5..3b47e79 100644 --- a/.vscode/mklink.md +++ b/.vscode/mklink.md @@ -15,7 +15,7 @@ mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.kanbn" "D:\5-Other-Small\Kanban ``` ```bash -mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode" "C:\Users\phares\.vscode\extensions\ifx.type-script-helper-1.5.2" -mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss" "C:\Users\phares\.vscode-oss\extensions\ifx.type-script-helper-1.5.2" -mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders" "C:\Users\phares\.vscode-insiders\extensions\ifx.type-script-helper-1.5.2" +mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode" "C:\Users\phares\.vscode\extensions\ifx.type-script-helper-1.6.0" +mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-oss" "C:\Users\phares\.vscode-oss\extensions\ifx.type-script-helper-1.6.0" +mklink /J "L:\DevOps\Mesa_FI\File-Folder-Helper\.extensions-vscode-insiders" "C:\Users\phares\.vscode-insiders\extensions\ifx.type-script-helper-1.6.0" ``` diff --git a/Helpers/HelperKanbanMetadata.cs b/Helpers/HelperKanbanMetadata.cs index fb79fbc..5aff400 100644 --- a/Helpers/HelperKanbanMetadata.cs +++ b/Helpers/HelperKanbanMetadata.cs @@ -102,10 +102,10 @@ internal static partial class HelperKanbanMetadata return results; } - private static void WriteKanbanBoardFile(string fullPath, List records, string h1) + private static void WriteKanbanBoardFile(string directory, List records, string h1) { string? last = null; - List lines = [h1, string.Empty]; + List lines = [h1]; foreach (Record record in records) { if (last is null || record.Group != last) @@ -117,56 +117,64 @@ internal static partial class HelperKanbanMetadata lines.Add($"- [ ] {Path.GetFileNameWithoutExtension(record.FileInfo.Name)}"); last = record.Group; } - File.WriteAllLines(Path.Combine(fullPath, "index.knb.md"), lines); + File.WriteAllLines(Path.Combine(directory, "index.knb.md"), lines); } - internal static void SetMetadata(ILogger log, AppSettings appSettings, string sourceDirectory) + internal static void SetMetadata(string sourceDirectory, string indexFile) { bool? match; string? paramCase; string statusLine; List lines; LineNumber lineNumber; - if (log is null) - throw new NullReferenceException(); + FileInfo fileInfo = new(indexFile); + string? directory = Path.GetDirectoryName(sourceDirectory); + (lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo); + ReadOnlyCollection indexFileLines = new(lines); + List records = GetCollectionFromIndex(sourceDirectory, indexFileLines); + if (directory is not null && lineNumber.H1 is not null) + { + string checkDirectory = Path.Combine(directory, ".vscode", "helper"); + if (Directory.Exists(checkDirectory)) + WriteKanbanBoardFile(checkDirectory, records, indexFileLines[lineNumber.H1.Value]); + } + foreach (Record record in records) + { + if (record.ItemLineNumber == 0) + throw new NotSupportedException(); + (lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo); + if (lines.Count == 0) + continue; + statusLine = $"status: \"{record.GroupCount}-{record.Group}\""; + paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]); + match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase; + if (lineNumber.FrontMatterYamlEnd is null) + throw new NotSupportedException($"{nameof(SetMetadata)} must be executed first!"); + if (lineNumber.H1 is not null && paramCase is not null && match is not null && !match.Value) + lines[lineNumber.H1.Value] = $"# {paramCase}"; + if (lineNumber.Status is null) + lines.Insert(lineNumber.FrontMatterYamlEnd.Value, statusLine); + else + { + if ((match is null || match.Value) && lines[lineNumber.Status.Value] == statusLine) + continue; + lines[lineNumber.Status.Value] = statusLine; + } + File.WriteAllLines(record.FileInfo.FullName, lines); + } + } + + internal static void SetMetadata(ILogger logger, string sourceDirectory) + { TestParamCases(); string fullPath = Path.GetFullPath(sourceDirectory); if (!Directory.Exists(fullPath)) _ = Directory.CreateDirectory(fullPath); string indexFile = Path.Combine(fullPath, "index.md"); if (File.Exists(indexFile)) - { - FileInfo fileInfo = new(indexFile); - (lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo); - ReadOnlyCollection indexFileLines = new(lines); - List records = GetCollectionFromIndex(sourceDirectory, indexFileLines); - if (lineNumber.H1 is not null) - WriteKanbanBoardFile(fullPath, records, indexFileLines[lineNumber.H1.Value]); - foreach (Record record in records) - { - if (record.ItemLineNumber == 0) - throw new NotSupportedException(); - (lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo); - if (lines.Count == 0) - continue; - statusLine = $"status: \"{record.GroupCount}-{record.Group}\""; - paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]); - match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase; - if (lineNumber.FrontMatterYamlEnd is null) - throw new NotSupportedException($"{nameof(SetMetadata)} must be executed first!"); - if (lineNumber.H1 is not null && paramCase is not null && match is not null && !match.Value) - lines[lineNumber.H1.Value] = $"# {paramCase}"; - if (lineNumber.Status is null) - lines.Insert(lineNumber.FrontMatterYamlEnd.Value, statusLine); - else - { - if ((match is null || match.Value) && lines[lineNumber.Status.Value] == statusLine) - continue; - lines[lineNumber.Status.Value] = statusLine; - } - File.WriteAllLines(record.FileInfo.FullName, lines); - } - } + SetMetadata(fullPath, indexFile); + else + logger.LogInformation("<{indexFile}> doesn't exist!", indexFile); } } \ No newline at end of file diff --git a/Helpers/HelperMarkdown.cs b/Helpers/HelperMarkdown.cs index c067e0c..714a958 100644 --- a/Helpers/HelperMarkdown.cs +++ b/Helpers/HelperMarkdown.cs @@ -28,6 +28,7 @@ internal static partial class HelperMarkdown string FileNameWithoutExtension, string H1, bool IsKanbanIndex, + bool IsKanbanMarkdown, DateTime LastWriteDateTime, LineNumber LineNumber, string Type); @@ -243,11 +244,13 @@ internal static partial class HelperMarkdown int? frontMatterYamlEndLineNumber = null; Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default; string[] lines = File.ReadAllLines(fileInfo.FullName, encoding); - for (int i = 1; i < lines.Length; i++) + for (int i = 0; i < lines.Length; i++) { line = lines[i]; if (line.Length < 3) continue; + if (i == 0 && line[..3] == "---") + continue; if (h1LineNumber is null && line[..3] == "---") { frontMatterYamlEndLineNumber = i; @@ -594,6 +597,7 @@ internal static partial class HelperMarkdown FileInfo fileInfo; bool isKanbanIndex; List lines; + bool isKanbanMarkdown; LineNumber lineNumber; MarkdownFile markdownFile; string fileNameWithoutExtension; @@ -614,7 +618,8 @@ internal static partial class HelperMarkdown File.WriteAllLines(file, ["---", $"type: \"{type}\"", "---", string.Empty, $"# {h1}"]); lines = File.ReadAllLines(file).ToList(); } - isKanbanIndex = fileNameWithoutExtension == "index" && type == "Kanban"; + isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md"); + isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase); markdownFile = new(fileInfo.CreationTime, fileInfo.DirectoryName, fileInfo.Extension, @@ -623,6 +628,7 @@ internal static partial class HelperMarkdown fileNameWithoutExtension, h1, isKanbanIndex, + isKanbanMarkdown, fileInfo.LastWriteTime, lineNumber, type); @@ -634,112 +640,105 @@ internal static partial class HelperMarkdown return new(results); } - private static ReadOnlyDictionary> GetColumnsToCards(Input input, ReadOnlyDictionary relativeToCollection) + private static List GetKeys(ReadOnlyDictionary relativeToCollection) { - Dictionary> results = []; - Card card; - string key; - string[] lines; - string[] segmentsA; - string? column = null; - List cards = []; - List allKeys = []; + List results = []; MarkdownFile markdownFile; - MarkdownExtra markdownExtra; - MarkdownFileAndLines? markdownFileAndLines; - foreach (KeyValuePair relativeTo in relativeToCollection) - allKeys.Add(relativeTo.Key); foreach (KeyValuePair relativeTo in relativeToCollection) { - if (relativeTo.Value.Lines.Length == 0) - continue; - lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; + if (markdownFile.IsKanbanMarkdown) + continue; + results.Add(relativeTo.Key); + } + return results; + } + + private static MarkdownFileAndLines? GetKanbanIndexMarkdownFileAndLines(ReadOnlyDictionary keyValuePairs) + { + MarkdownFile markdownFile; + MarkdownFileAndLines? result = null; + foreach (KeyValuePair keyValuePair in keyValuePairs) + { + markdownFile = keyValuePair.Value.MarkdownFile; if (markdownFile.IsKanbanIndex) - continue; - if (!File.Exists(markdownFile.File)) - continue; - for (int i = 0; i < lines.Length; i++) { - if (lines[i].Length < 4 || lines[i][0] != '#' || lines[i][1] != '#' || lines[i][2] != ' ') - continue; - if (cards.Count > 1) + if (result is not null) { - if (column is null) - throw new NullReferenceException(nameof(column)); - results.Add(column, cards); - cards = []; + result = null; + break; } - column = lines[i][3..].TrimEnd(); - if (lines.Length == i + 1) - continue; - for (int j = i + 1; j < lines.Length; j++) - { - if (lines[j].Length < 5) - continue; - if (lines[j].Length >= 4 && lines[j][0] == '#' && lines[j][1] == '#' && lines[j][2] == ' ') - break; - segmentsA = lines[j].Split("]("); - if (segmentsA.Length != 2 || segmentsA[1][^1] != ')') - continue; - key = Path.GetRelativePath(input.Source, Path.Combine(markdownFile.Directory, segmentsA[1][..^1])); - if (!allKeys.Remove(key)) - continue; - if (!relativeToCollection.TryGetValue(key, out markdownFileAndLines)) - continue; - markdownExtra = GetMarkdownExtra(markdownFileAndLines); - card = new(markdownExtra.Assignees, - markdownFileAndLines.MarkdownFile.CreationDateTime, - markdownFileAndLines.MarkdownFile.Directory, - markdownExtra.Effort, - markdownFileAndLines.MarkdownFile.Extension, - markdownFileAndLines.MarkdownFile.File, - markdownFileAndLines.MarkdownFile.FileName, - markdownFileAndLines.MarkdownFile.FileNameWithoutExtension, - markdownFileAndLines.MarkdownFile.H1, - markdownExtra.H2HexColorCollection, - markdownExtra.H2NoCheckboxesCollection, - markdownExtra.H2WithCheckboxesCollection, - markdownFileAndLines.MarkdownFile.LastWriteDateTime, - markdownFileAndLines.MarkdownFile.LineNumber, - markdownExtra.RequestedDateTime, - markdownFileAndLines.MarkdownFile.Type); - cards.Add(card); - } - } - foreach (string notLinkedKey in allKeys) - { - key = notLinkedKey; - if (!relativeToCollection.TryGetValue(key, out markdownFileAndLines)) - continue; - if (markdownFileAndLines.MarkdownFile.LineNumber.FrontMatterYamlEnd is null) - continue; - markdownExtra = GetMarkdownExtra(markdownFileAndLines); - card = new(markdownExtra.Assignees, - markdownFileAndLines.MarkdownFile.CreationDateTime, - markdownFileAndLines.MarkdownFile.Directory, - markdownExtra.Effort, - markdownFileAndLines.MarkdownFile.Extension, - markdownFileAndLines.MarkdownFile.File, - markdownFileAndLines.MarkdownFile.FileName, - markdownFileAndLines.MarkdownFile.FileNameWithoutExtension, - markdownFileAndLines.MarkdownFile.H1, - markdownExtra.H2HexColorCollection, - markdownExtra.H2NoCheckboxesCollection, - markdownExtra.H2WithCheckboxesCollection, - markdownFileAndLines.MarkdownFile.LastWriteDateTime, - markdownFileAndLines.MarkdownFile.LineNumber, - markdownExtra.RequestedDateTime, - markdownFileAndLines.MarkdownFile.Type); - cards.Add(card); - } - if (cards.Count > 1) - { - column = "Not Linked"; - results.Add(column, cards); - cards = []; + result = keyValuePair.Value; } } + return result; + } + + private static void SetCards(Input input, ReadOnlyDictionary relativeToCollection, List notLinkedKey, MarkdownFile markdownFile, string[] lines, List cards, List allKeys, int i) + { + Card card; + string key; + string[] segmentsA; + MarkdownExtra markdownExtra; + MarkdownFileAndLines? markdownFileAndLines; + for (int j = i + 1; j < lines.Length; j++) + { + if (lines[j].Length < 5) + continue; + if (lines[j].Length >= 4 && lines[j][0] == '#' && lines[j][1] == '#' && lines[j][2] == ' ') + break; + segmentsA = lines[j].Split("]("); + if (segmentsA.Length != 2 || segmentsA[1][^1] != ')') + continue; + key = Path.GetRelativePath(input.Source, Path.Combine(markdownFile.Directory, segmentsA[1][..^1])); + if (!relativeToCollection.TryGetValue(key, out markdownFileAndLines)) + continue; + markdownExtra = GetMarkdownExtra(markdownFileAndLines); + card = new(markdownExtra.Assignees, + markdownFileAndLines.MarkdownFile.CreationDateTime, + markdownFileAndLines.MarkdownFile.Directory, + markdownExtra.Effort, + markdownFileAndLines.MarkdownFile.Extension, + markdownFileAndLines.MarkdownFile.File, + markdownFileAndLines.MarkdownFile.FileName, + markdownFileAndLines.MarkdownFile.FileNameWithoutExtension, + markdownFileAndLines.MarkdownFile.H1, + markdownExtra.H2HexColorCollection, + markdownExtra.H2NoCheckboxesCollection, + markdownExtra.H2WithCheckboxesCollection, + markdownFileAndLines.MarkdownFile.LastWriteDateTime, + markdownFileAndLines.MarkdownFile.LineNumber, + markdownExtra.RequestedDateTime, + markdownFileAndLines.MarkdownFile.Type); + if (allKeys.Remove(key)) + cards.Add(card); + else + notLinkedKey.Add(card); + } + } + + private static ReadOnlyDictionary> GetColumnsToCards(Input input, ReadOnlyDictionary relativeToCollection, MarkdownFileAndLines markdownFileAndLines) + { + Dictionary> results = []; + string? column; + string[] lines; + List cards = []; + List notLinkedKey = []; + lines = markdownFileAndLines.Lines; + List allKeys = GetKeys(relativeToCollection); + for (int i = 0; i < lines.Length; i++) + { + if (lines[i].Length < 4 || lines[i][0] != '#' || lines[i][1] != '#' || lines[i][2] != ' ') + continue; + column = lines[i][3..].TrimEnd(); + if (lines.Length == i + 1) + continue; + SetCards(input, relativeToCollection, notLinkedKey, markdownFileAndLines.MarkdownFile, lines, cards, allKeys, i); + results.Add(column, cards); + cards = []; + } + if (notLinkedKey.Count > 1) + results.Add("Not Linked", notLinkedKey); return new(results); } @@ -1182,14 +1181,14 @@ internal static partial class HelperMarkdown { if (string.IsNullOrEmpty(input.StartAt) || string.IsNullOrEmpty(input.Destination)) throw new NotSupportedException(); - ReadOnlyDictionary> columnsToCards; - int kanbanIndexFiles = (from l in relativeToCollection where l.Value.MarkdownFile.IsKanbanIndex select 1).Sum(); - if (kanbanIndexFiles == 1) + MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection); + if (markdownFileAndLines is not null && File.Exists(markdownFileAndLines.MarkdownFile.File)) { + ReadOnlyDictionary> columnsToCards; string jsonFile = Path.Combine(input.Destination, $"{nameof(columnsToCards)}.json"); if (File.Exists(jsonFile)) File.Delete(jsonFile); - columnsToCards = GetColumnsToCards(input, relativeToCollection); + columnsToCards = GetColumnsToCards(input, relativeToCollection, markdownFileAndLines); if (columnsToCards.Count == 0) File.WriteAllText(jsonFile, "{}"); else @@ -1207,7 +1206,7 @@ internal static partial class HelperMarkdown return (type, h1FromFile); } - private static int SetFrontMatterAndH1(AppSettings appSettings, Input input, ReadOnlyDictionary relativeToCollection, ReadOnlyCollection gitOthersModifiedAndDeletedExcludingStandardFiles) + private static int SetFrontMatterAndH1(AppSettings appSettings, ILogger logger, Input input, ReadOnlyDictionary relativeToCollection, ReadOnlyCollection gitOthersModifiedAndDeletedExcludingStandardFiles) { int result = 0; List results = []; @@ -1230,6 +1229,10 @@ internal static partial class HelperMarkdown results.Clear(); lines = relativeTo.Value.Lines; markdownFile = relativeTo.Value.MarkdownFile; + if (markdownFile.IsKanbanMarkdown) + continue; + if (markdownFile.IsKanbanIndex) + HelperKanbanMetadata.SetMetadata(markdownFile.Directory, markdownFile.File); results.AddRange(lines); typeLine = $"type: \"{appSettings.DefaultNoteType}\""; h1Line = $"# {markdownFile.FileNameWithoutExtension}"; @@ -1239,7 +1242,9 @@ internal static partial class HelperMarkdown updatedLine = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\""; if (markdownFile.LineNumber.FrontMatterYamlEnd is null) { - if (markdownFile.LineNumber.H1 is null) + if (markdownFile.LineNumber.H1 is not null) + results.Insert(0, string.Empty); + else { results.Insert(0, string.Empty); results.Insert(0, h1Line); @@ -1320,7 +1325,7 @@ internal static partial class HelperMarkdown Input input = GetInput(args); ReadOnlyDictionary relativeToCollection = GetRelativeToCollection(appSettings, input); ReadOnlyCollection gitOthersModifiedAndDeletedExcludingStandardFiles = HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(input.Source, usePathCombine, cancellationToken); - updated = SetFrontMatterAndH1(appSettings, input, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles); + updated = SetFrontMatterAndH1(appSettings, logger, input, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles); if (updated != 0) { relativeToCollection = GetRelativeToCollection(appSettings, input); diff --git a/Worker.cs b/Worker.cs index cc68497..80dc21e 100644 --- a/Worker.cs +++ b/Worker.cs @@ -176,7 +176,7 @@ public class Worker : BackgroundService Helpers.HelperPackageFilesByDate.SetDateFromJsonEntry(_Logger, _Args[0]); break; case ConsoleKey.K: - Helpers.HelperKanbanMetadata.SetMetadata(_Logger, _AppSettings, _Args[0]); + Helpers.HelperKanbanMetadata.SetMetadata(_Logger, _Args[0]); break; case ConsoleKey.L: Helpers.HelperLogMerge.LogMerge(_Args[0]); @@ -185,8 +185,6 @@ public class Worker : BackgroundService Helpers.HelperCreateNoteFiles.CreateNoteFiles(_Logger, _Args[0]); break; case ConsoleKey.M: - if (_Args[0].EndsWith(".kanbn") && Directory.Exists(_Args[0])) - Helpers.HelperKanbanMetadata.SetMetadata(_Logger, _AppSettings, _Args[0]); Helpers.HelperMarkdown.MarkdownWikiLinkVerification(_AppSettings, _Logger, _Args, cancellationToken); break; case ConsoleKey.O: