From ba5427b04986044ee53241e28a75a06527c0c93a Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Thu, 10 Aug 2023 19:13:31 -0700 Subject: [PATCH] GetRecursiveLines --- Helpers/HelperKanbanMetadata.cs | 2 - Helpers/HelperMarkdown.cs | 743 +++++++++++++++++--------------- 2 files changed, 385 insertions(+), 360 deletions(-) diff --git a/Helpers/HelperKanbanMetadata.cs b/Helpers/HelperKanbanMetadata.cs index 2fea9a9..2d81cd5 100644 --- a/Helpers/HelperKanbanMetadata.cs +++ b/Helpers/HelperKanbanMetadata.cs @@ -48,8 +48,6 @@ internal static class HelperKanbanMetadata string fullPath = Path.GetFullPath(sourceDirectory); if (!Directory.Exists(fullPath)) _ = Directory.CreateDirectory(fullPath); - List<(MarkdownFile, string[])> collection; - collection = HelperMarkdown.GetCollection(appSettings, HelperMarkdown.GetFiles(appSettings, fullPath)); string indexFile = Path.Combine(fullPath, "index.md"); if (File.Exists(indexFile)) { diff --git a/Helpers/HelperMarkdown.cs b/Helpers/HelperMarkdown.cs index 8924253..525047c 100644 --- a/Helpers/HelperMarkdown.cs +++ b/Helpers/HelperMarkdown.cs @@ -1,5 +1,6 @@ using File_Folder_Helper.Models; using Microsoft.Extensions.Logging; +using System.Collections.ObjectModel; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -9,9 +10,11 @@ namespace File_Folder_Helper.Helpers; internal static partial class HelperMarkdown { - private record Record(string Source, - string? StartAt, - string? Destination); + private record Record(string Source, string? StartAt, string? Destination); + + private record MarkdownFileAndLines(MarkdownFile MarkdownFile, string[] Lines); + + private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath); [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSerializable(typeof(Dictionary))] @@ -19,6 +22,36 @@ internal static partial class HelperMarkdown { } + private static Record GetRecord(List args) + { + Record result; + string? startAt = null; + string? destination = null; + for (int i = 1; i < args.Count; i++) + { + if (args[i].Length == 2 && i + 1 < args.Count) + { + if (args[i][1] == 's') + startAt = Path.GetFullPath(args[i + 1]); + else if (args[i][1] == 'd') + destination = Path.GetFullPath(args[i + 1]); + i++; + } + } + if (startAt is not null && !Directory.Exists(startAt)) + throw new Exception($"Start at directory <{startAt}> doesn't exist!"); + if (destination is not null) + { + string? root = Path.GetPathRoot(destination); + if (root is null || !Directory.Exists(root)) + throw new NotSupportedException($"This method requires frontMatterYamlLines valid -d path <{root}>!"); + if (!Directory.Exists(destination)) + _ = Directory.CreateDirectory(destination); + } + result = new(Path.GetFullPath(args[0]), startAt, destination); + return result; + } + /// /// Determines files text file's encoding by analyzing its byte order mark (BOM). /// Defaults to ASCII when detection of the text file's endianness fails. @@ -57,12 +90,6 @@ internal static partial class HelperMarkdown return results; } - private static string[] GetFiles(AppSettings appSettings, Record record) - { - string[] results = record.StartAt is null ? GetFiles(appSettings, record.Source) : GetFiles(appSettings, record.StartAt); - return results; - } - private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, List lines, LineNumber lineNumber) { string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value].Replace("type: ", string.Empty); @@ -126,10 +153,11 @@ internal static partial class HelperMarkdown return (lines.ToList(), lineNumber); } - internal static List<(MarkdownFile, string[])> GetCollection(AppSettings appSettings, string[] files) + private static ReadOnlyDictionary GetRelativeToCollection(AppSettings appSettings, Record record, string[] files, bool force) { - List<(MarkdownFile, string[])> results = new(); + Dictionary results = new(); string h1; + string key; string type; FileInfo fileInfo; List lines; @@ -141,6 +169,7 @@ internal static partial class HelperMarkdown fileInfo = new(file); if (fileInfo.DirectoryName is null) continue; + key = Path.GetRelativePath(record.Source, file); (lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo); fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName); h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-'); @@ -153,35 +182,42 @@ internal static partial class HelperMarkdown lines = File.ReadAllLines(file).ToList(); } markdownFile = new(file, fileInfo.DirectoryName, fileInfo.Name, fileNameWithoutExtension, fileInfo.Extension, fileInfo.CreationTime, fileInfo.LastWriteTime, lineNumber, type, h1); - results.Add(new(markdownFile, lines.ToArray())); + if (force || record.StartAt is null || file.StartsWith(record.StartAt)) + results.Add(key, new(markdownFile, lines.ToArray())); + else + results.Add(key, new(markdownFile, Array.Empty())); } - return results; + return new(results); } - private static List<(MarkdownFile, string[])> GetCollection(AppSettings appSettings, Record record) + private static ReadOnlyDictionary GetRelativeToCollection(AppSettings appSettings, Record record, bool force = false) { - List<(MarkdownFile, string[])> results; + ReadOnlyDictionary results; string[] files = GetFiles(appSettings, record.Source); - results = GetCollection(appSettings, files); - return results; + results = GetRelativeToCollection(appSettings, record, files, force); + return new(results); } - private static int SetFrontMatterAndH1(AppSettings appSettings, List<(MarkdownFile, string[])> collection) + private static int SetFrontMatterAndH1(AppSettings appSettings, ReadOnlyDictionary relativeToCollection) { int result = 0; + List results = new(); string h1Line; + string[] lines; string typeLine; string createdLine; string updatedLine; DateTime creationDateTime; + MarkdownFile markdownFile; string createdLineCompare; string updatedLineCompare; - List results = new(); - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + foreach (KeyValuePair relativeTo in relativeToCollection) { - if (!lines.Any()) + if (!relativeTo.Value.Lines.Any()) continue; results.Clear(); + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; results.AddRange(lines); creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime; typeLine = $"type: \"{appSettings.DefaultNoteType}\""; @@ -245,7 +281,7 @@ internal static partial class HelperMarkdown for (int i = 0; i < parsedLines.Length; i++) { afterTrim = parsedLines[i].Trim(); - if (string.IsNullOrEmpty(afterTrim) || afterTrim.First() is '{' or '}') + if (string.IsNullOrEmpty(afterTrim) || afterTrim[0] is '{' or '}') continue; segments = afterTrim.Split(": "); if (segments.Length != 2) @@ -330,22 +366,22 @@ internal static partial class HelperMarkdown break; } segmentsLast = segments.Last().Trim(); - segmentsFirst = segments.First().Trim(); + segmentsFirst = segments[0].Trim(); if (string.IsNullOrEmpty(segmentsLast)) continue; - if (segmentsFirst.First() == '"' && segmentsFirst.Last() == '"') + if (segmentsFirst[0] == '"' && segmentsFirst.Last() == '"') jsonLines.Add($"{segmentsFirst}: "); - else if (segmentsFirst.First() == '\'' && segmentsFirst.Last() == '\'') + else if (segmentsFirst[0] == '\'' && segmentsFirst.Last() == '\'') jsonLines.Add($"\"{segmentsFirst[1..^1]}\": "); else jsonLines.Add($"\"{segmentsFirst}\": "); if (segmentsLast == "[]") jsonLines.RemoveAt(jsonLines.Count - 1); - else if (segmentsLast.Length > 4 && segmentsLast.First() == '[' && segmentsLast.Last() == ']' && segmentsLast[1] == '"' && segmentsLast[^2] == '"') + else if (segmentsLast.Length > 4 && segmentsLast[0] == '[' && segmentsLast.Last() == ']' && segmentsLast[1] == '"' && segmentsLast[^2] == '"') jsonLines.Add($"{segmentsLast},"); - else if (segmentsLast.First() == '"' && segmentsLast.Last() == '"') + else if (segmentsLast[0] == '"' && segmentsLast.Last() == '"') jsonLines.Add($"{segmentsLast},"); - else if (segmentsLast.First() == '"' && segmentsLast.Last() == '"') + else if (segmentsLast[0] == '"' && segmentsLast.Last() == '"') jsonLines.Add($"\"{segmentsLast[1..^1]}\""); else if (!segmentsLast.Contains('"') && !segmentsLast.Contains('\'')) { @@ -358,7 +394,7 @@ internal static partial class HelperMarkdown else { segmentsB = segmentsLast.Split('.'); - if (segmentsB.Length == 2 && segmentsB.First().Length < 7 && segmentsB.Last().Length < 7 && segmentsB.First().All(l => char.IsNumber(l)) && segmentsB.Last().All(l => char.IsNumber(l))) + if (segmentsB.Length == 2 && segmentsB[0].Length < 7 && segmentsB.Last().Length < 7 && segmentsB[0].All(l => char.IsNumber(l)) && segmentsB.Last().All(l => char.IsNumber(l))) jsonLines.Add($"{segmentsLast},"); else if (!segmentsLast.Contains('[') && !segmentsLast.Contains('{')) jsonLines.Add($"\"{segmentsLast}\","); @@ -382,17 +418,21 @@ internal static partial class HelperMarkdown return (result, keyValuePairs, results.ToArray()); } - private static int ConvertFrontMatterToJsonFriendly(List<(MarkdownFile, string[])> collection) + private static int ConvertFrontMatterToJsonFriendly(ReadOnlyDictionary relativeToCollection) { int result = 0; - bool write; List results = new(); + bool write; + string[] lines; + MarkdownFile markdownFile; string[] frontMatterYamlLines; - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + foreach (KeyValuePair relativeTo in relativeToCollection) { - if (!lines.Any()) + if (!relativeTo.Value.Lines.Any()) continue; results.Clear(); + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; if (markdownFile.LineNumber.FrontMatterYamlEnd is null) continue; (_, _, frontMatterYamlLines) = Get(markdownFile.LineNumber.FrontMatterYamlEnd.Value, lines); @@ -423,17 +463,21 @@ internal static partial class HelperMarkdown return result; } - private static int CircularReference(ILogger logger, List<(MarkdownFile, string[])> collection) + private static int CircularReference(ILogger logger, ReadOnlyDictionary relativeToCollection) { int result = 0; string line; string check; + string[] lines; bool circularReference; - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + MarkdownFile markdownFile; + foreach (KeyValuePair relativeTo in relativeToCollection) { - if (!lines.Any()) + if (!relativeTo.Value.Lines.Any()) continue; circularReference = false; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; for (int i = 0; i < lines.Length; i++) { check = $"[[{markdownFile.FileNameWithoutExtension}]]"; @@ -480,17 +524,21 @@ internal static partial class HelperMarkdown return result; } - private static int FindReplace(List<(MarkdownFile, string[])> collection) + private static int FindReplace(ReadOnlyDictionary relativeToCollection) { int result = 0; bool found; string line; string check; - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + string[] lines; + MarkdownFile markdownFile; + foreach (KeyValuePair relativeTo in relativeToCollection) { - if (!lines.Any()) + if (!relativeTo.Value.Lines.Any()) continue; found = false; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; for (int i = 0; i < lines.Length; i++) { check = $"[[K-A/"; @@ -524,23 +572,36 @@ internal static partial class HelperMarkdown return result; } - private static Dictionary> GetKeyValuePairs(List<(MarkdownFile MarkdownFile, string[] Lines)> collection) + private static ReadOnlyDictionary> GetKeyValuePairs(ReadOnlyDictionary relativeToCollection) { - Dictionary> results = new(); - List? markdownFiles; + Dictionary> results = new(); + MarkdownFile markdownFile; string fileNameWithoutExtension; - foreach ((MarkdownFile markdownFile, _) in collection) + string fileNameWithoutExtensionB; + List? markdownFiles; + foreach (KeyValuePair relativeTo in relativeToCollection) { + markdownFile = relativeTo.Value.MarkdownFile; + if (!results.TryGetValue(relativeTo.Key, out markdownFiles)) + { + results.Add(relativeTo.Key, new()); + if (!results.TryGetValue(relativeTo.Key, out markdownFiles)) + throw new NotSupportedException(); + } + markdownFiles.Add(relativeTo.Value); + } + foreach (KeyValuePair relativeTo in relativeToCollection) + { + markdownFile = relativeTo.Value.MarkdownFile; fileNameWithoutExtension = markdownFile.FileNameWithoutExtension.ToLower(); + fileNameWithoutExtensionB = fileNameWithoutExtension.Replace("%20", "-").Replace(' ', '-'); if (!results.TryGetValue(markdownFile.FileNameWithoutExtension, out markdownFiles)) { results.Add(markdownFile.FileNameWithoutExtension, new()); if (!results.TryGetValue(markdownFile.FileNameWithoutExtension, out markdownFiles)) throw new NotSupportedException(); } - if (markdownFiles.Contains(markdownFile)) - continue; - markdownFiles.Add(markdownFile); + markdownFiles.Add(relativeTo.Value); if (fileNameWithoutExtension == markdownFile.FileNameWithoutExtension) continue; if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles)) @@ -549,177 +610,175 @@ internal static partial class HelperMarkdown if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles)) throw new NotSupportedException(); } - if (markdownFiles.Contains(markdownFile)) + if (fileNameWithoutExtensionB == markdownFile.FileNameWithoutExtension) continue; - markdownFiles.Add(markdownFile); + if (!results.TryGetValue(fileNameWithoutExtensionB, out markdownFiles)) + { + results.Add(fileNameWithoutExtensionB, new()); + if (!results.TryGetValue(fileNameWithoutExtensionB, out markdownFiles)) + throw new NotSupportedException(); + } + markdownFiles.Add(relativeTo.Value); } - foreach ((MarkdownFile markdownFile, _) in collection) + foreach (KeyValuePair relativeTo in relativeToCollection) { + markdownFile = relativeTo.Value.MarkdownFile; if (!results.TryGetValue(markdownFile.H1, out markdownFiles)) { results.Add(markdownFile.H1, new()); if (!results.TryGetValue(markdownFile.H1, out markdownFiles)) throw new NotSupportedException(); } - if (markdownFiles.Contains(markdownFile)) - continue; - markdownFiles.Add(markdownFile); + markdownFiles.Add(relativeTo.Value); } - return results; + return new(results); } - private static Dictionary> GetKeyValuePairs(AppSettings appSettings, Record record) + private static List GetMarkdownFileAndLines(string file, List markdownFiles) { - Dictionary> results; - List<(MarkdownFile MarkdownFile, string[] Lines)> collection = GetCollection(appSettings, record); - results = GetKeyValuePairs(collection); - return results; - } - - private static (string?, string?) GetMatchAndTitle(string? directory, List markdownFiles) - { - int check = 0; - string? match = null; - string? title = null; - string? directoryName = Path.GetFileName(directory); - foreach (MarkdownFile markdownFile in markdownFiles) - { - if (directory is null || directoryName is null) - continue; - if (markdownFiles.Count == 1) - { - check++; - match = markdownFile.File; - title = markdownFile.H1; - } - else - { - if (Path.GetFileName(markdownFile.Directory) == directoryName) - { - check++; - match = markdownFile.File; - title = markdownFile.H1; - } - } - } - if (check != 1) - { - match = null; - title = null; - } - return (match, title); - } - - private static (string?, string?) GetRelativePath(Dictionary> keyValuePairs, MarkdownFile markdownFile, string file) - { - string? result; - string? match; - string? title; - List? markdownFiles; + List results = new(); + List distinct = new(); string? directory = Path.GetDirectoryName(file); + foreach (MarkdownFileAndLines markdownFileAndLines in markdownFiles) + { + if (string.IsNullOrEmpty(directory) || markdownFileAndLines.MarkdownFile.Directory != directory) + continue; + if (distinct.Contains(markdownFileAndLines.MarkdownFile.File)) + continue; + distinct.Add(markdownFileAndLines.MarkdownFile.File); + results.Add(markdownFileAndLines); + } + return results; + } + + private static List GetMarkdownFileAndLines(ReadOnlyDictionary> keyValuePairs) + { + List results = new(); + foreach (KeyValuePair> keyValuePair in keyValuePairs) + { + foreach (MarkdownFileAndLines markdownFileAndLines in keyValuePair.Value) + results.Add(markdownFileAndLines); + } + return results; + } + + private static List Distinct(IEnumerable? markdownFileAndLinesCollection) + { + List results = new(); + if (markdownFileAndLinesCollection is not null) + { + List distinct = new(); + foreach (MarkdownFileAndLines markdownFileAndLines in markdownFileAndLinesCollection) + { + if (distinct.Contains(markdownFileAndLines.MarkdownFile.File)) + continue; + distinct.Add(markdownFileAndLines.MarkdownFile.File); + results.Add(markdownFileAndLines); + } + } + return results; + } + + private static MarkdownFileAndLines? GetMarkdownFile(ReadOnlyDictionary> keyValuePairs, MarkdownFile markdownFile, string file) + { + MarkdownFileAndLines? result; + List? markdownFileAndLinesCollection; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (keyValuePairs.TryGetValue(fileNameWithoutExtension, out markdownFiles)) - (match, title) = GetMatchAndTitle(directory, markdownFiles); + if (!keyValuePairs.TryGetValue(fileNameWithoutExtension, out markdownFileAndLinesCollection)) + _ = keyValuePairs.TryGetValue(fileNameWithoutExtension.ToLower(), out markdownFileAndLinesCollection); + markdownFileAndLinesCollection = Distinct(markdownFileAndLinesCollection); + if (markdownFileAndLinesCollection is not null && markdownFileAndLinesCollection.Count == 1) + result = markdownFileAndLinesCollection[0]; else { - if (keyValuePairs.TryGetValue(fileNameWithoutExtension.ToLower(), out markdownFiles)) - (match, title) = GetMatchAndTitle(directory, markdownFiles); - else - (match, title) = (null, null); - } - if (match is null) - { - List files = new(); - List fileNames = new(); - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - foreach (MarkdownFile keyValue in keyValuePair.Value) - { - files.Add(keyValue.File); - fileNames.Add(keyValue.FileNameWithoutExtension); - } - } - string[] matches = fileNames.Where(l => l.Length == fileNameWithoutExtension.Length && l.Contains(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (matches.Length == 1) - match = matches.First(); + List matches; + matches = markdownFileAndLinesCollection is null ? new() : GetMarkdownFileAndLines(file, markdownFileAndLinesCollection); + if (matches.Count == 1) + result = matches[0]; else { - string checkName = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-'); - matches = fileNames.Where(l => l.Length == checkName.Length && l.Contains(checkName, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (matches.Length == 1) - match = matches.First(); + markdownFileAndLinesCollection = GetMarkdownFileAndLines(keyValuePairs); + matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.FileNameWithoutExtension.Length == fileNameWithoutExtension.Length && l.MarkdownFile.FileNameWithoutExtension.Contains(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))); + if (matches.Count == 1) + result = matches[0]; else { - if (!matches.Any()) - match = null; + string checkName = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-'); + matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.FileNameWithoutExtension.Length == checkName.Length && l.MarkdownFile.FileNameWithoutExtension.Contains(checkName, StringComparison.OrdinalIgnoreCase))); + if (matches.Count == 1) + result = matches[0]; + else if (matches.Count == 0) + result = null; else { - checkName = matches.First(); - matches = files.Where(l => l.Contains(checkName, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (matches.Length == 1) - match = matches.First(); + checkName = matches[0].MarkdownFile.FileNameWithoutExtension; + matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.File.Contains(checkName, StringComparison.OrdinalIgnoreCase))); + if (matches.Count == 1) + result = matches[0]; else { checkName = $"{checkName}{markdownFile.Extension}"; - matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (matches.Length == 1) - match = matches.First(); + matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.File.EndsWith(checkName, StringComparison.OrdinalIgnoreCase))); + if (matches.Count == 1) + result = matches[0]; else { checkName = $"\\{checkName}"; - matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray(); - if (matches.Length == 1) - match = matches.First(); + matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.File.EndsWith(checkName, StringComparison.OrdinalIgnoreCase))); + if (matches.Count == 1) + result = matches[0]; else - match = null; + result = null; } } } } } } - result = match is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(match)); - return (result, title); + return result; } - private static int ConvertToRelativePath(AppSettings appSettings, ILogger logger, Record record, List<(MarkdownFile MarkdownFile, string[] Lines)> collection) + private static MarkdownFileH1AndRelativePath GetRelativePath(ReadOnlyDictionary> keyValuePairs, MarkdownFile markdownFile, string file) + { + MarkdownFileAndLines? result = GetMarkdownFile(keyValuePairs, markdownFile, file); + return new(result?.MarkdownFile, result?.Lines, result?.MarkdownFile.H1, result is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(result.MarkdownFile.File))); + } + + private static int ConvertToRelativePath(ILogger logger, ReadOnlyDictionary relativeToCollection) { int result = 0; bool write; string line; - string? title; + string[] lines; string[] segmentsA; string[] segmentsB; string[] segmentsC; - string? relativePath; - Dictionary> keyValuePairs = record.StartAt is null ? GetKeyValuePairs(collection) : GetKeyValuePairs(appSettings, record); - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + MarkdownFile markdownFile; + MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath; + ReadOnlyDictionary> keyValuePairs = GetKeyValuePairs(relativeToCollection); + foreach (KeyValuePair relativeTo in relativeToCollection) { - if (!lines.Any()) + if (!relativeTo.Value.Lines.Any()) continue; write = false; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; for (int i = 0; i < lines.Length; i++) { segmentsA = lines[i].Split("]]"); if (segmentsA.Length is not 2 or 3) continue; - segmentsB = segmentsA.First().Split("[["); + segmentsB = segmentsA[0].Split("[["); if (segmentsB.Length is not 2 or 3) continue; segmentsC = segmentsB.Last().Split('|'); - (relativePath, title) = GetRelativePath(keyValuePairs, markdownFile, segmentsC.First()); - if (relativePath is null) + markdownFileH1AndRelativePath = GetRelativePath(keyValuePairs, markdownFile, segmentsC[0]); + if (markdownFileH1AndRelativePath.MarkdownFile is null || markdownFileH1AndRelativePath.H1 is null || markdownFileH1AndRelativePath.RelativePath is null) { logger.LogInformation("Didn't find {line} in <{file}>", lines[i], markdownFile.FileNameWithoutExtension); continue; } - if (title is null) - { - title = segmentsC.Last(); - if (title.Length != segmentsC.Last().Length) - title = segmentsC.Last(); - } - line = $"{segmentsB.First()}[{title}]({relativePath.Replace('\\', '/')}){segmentsA.Last()}"; + line = $"{segmentsB[0]}[{markdownFileH1AndRelativePath.H1}]({markdownFileH1AndRelativePath.RelativePath.Replace('\\', '/')}){segmentsA.Last()}"; if (lines[i] == line) continue; lines[i] = line; @@ -735,128 +794,56 @@ internal static partial class HelperMarkdown return result; } - private static (string?, string?) GetRelativePath(string[] allSourceFiles, List<(MarkdownFile MarkdownFile, string[] Lines)> collection, MarkdownFile markdownFile, string file) - { - string? title; - string? relativePath; - List<(string RelativePath, string Title)> results = new(); - foreach ((MarkdownFile MarkdownFile, string[] Lines) item in collection) - { - if (item.MarkdownFile.File != file) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(item.MarkdownFile.File)), item.MarkdownFile.H1)); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - foreach (string allSourceFile in allSourceFiles) - { - if (allSourceFile.EndsWith(".md")) - continue; - if (allSourceFile != file) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(allSourceFile)), Path.GetFileNameWithoutExtension(allSourceFile))); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - string fileLowered = file.ToLower(); - foreach ((MarkdownFile MarkdownFile, string[] Lines) item in collection) - { - if (item.MarkdownFile.File.ToLower() != fileLowered) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(item.MarkdownFile.File)), item.MarkdownFile.H1)); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - foreach (string allSourceFile in allSourceFiles) - { - if (allSourceFile.EndsWith(".md")) - continue; - if (allSourceFile.ToLower() != fileLowered) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(allSourceFile)), Path.GetFileNameWithoutExtension(allSourceFile))); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - string fileFullPath = Path.GetFullPath(fileLowered); - foreach ((MarkdownFile MarkdownFile, string[] Lines) item in collection) - { - if (Path.GetFullPath(item.MarkdownFile.File).ToLower() != fileFullPath) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(item.MarkdownFile.File)), item.MarkdownFile.H1)); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - foreach (string allSourceFile in allSourceFiles) - { - if (allSourceFile.EndsWith(".md")) - continue; - if (allSourceFile.ToLower() != fileFullPath) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(allSourceFile)), Path.GetFileNameWithoutExtension(allSourceFile))); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - string spaceNaming = fileFullPath.Replace(" ", "%20"); - foreach ((MarkdownFile MarkdownFile, string[] Lines) item in collection) - { - if (Path.GetFullPath(item.MarkdownFile.File).ToLower().Replace(" ", "%20") != spaceNaming) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(item.MarkdownFile.File)), item.MarkdownFile.H1)); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - { - results.Clear(); - foreach (string allSourceFile in allSourceFiles) - { - if (allSourceFile.EndsWith(".md")) - continue; - if (Path.GetFullPath(allSourceFile).ToLower().Replace(" ", "%20") != spaceNaming) - continue; - results.Add((Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(allSourceFile)), Path.GetFileNameWithoutExtension(allSourceFile))); - } - if (results.Count == 1) - (relativePath, title) = (results.First().RelativePath.Replace(" ", "%20"), results.First().Title); - else - (relativePath, title) = (null, null); - } - } - } - } - } - } - } - return (relativePath, title); - } - - private static int ConvertFileToSlugName(AppSettings appSettings, ILogger logger, Record record, List<(MarkdownFile MarkdownFile, string[] Lines)> collection) + private static int ConvertFileToSlugName(ReadOnlyDictionary relativeToCollection) { int result = 0; string h1; + string h1Check; + string[] lines; + string checkName; + string checkFileName; + MarkdownFile markdownFile; + foreach (KeyValuePair relativeTo in relativeToCollection) + { + if (!relativeTo.Value.Lines.Any()) + continue; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; + if (markdownFile.LineNumber.H1 is not null) + { + h1 = lines[markdownFile.LineNumber.H1.Value]; + if (h1.Length > 2) + { + h1Check = $"# {h1[2..]}"; + if (h1Check.Length == h1.Length && h1Check != h1) + { + lines[markdownFile.LineNumber.H1.Value] = h1Check; + File.WriteAllLines(markdownFile.File, lines); + result += 1; + } + } + } + checkFileName = markdownFile.FileName.ToLower().Replace("%20", "-").Replace(' ', '-'); + if (checkFileName == markdownFile.FileName) + continue; + if (!File.Exists(markdownFile.File)) + continue; + checkName = Path.Combine(markdownFile.Directory, checkFileName); + if (checkName == markdownFile.File) + continue; + File.Move(markdownFile.File, checkName); + result += 1; + } + return result; + } + + private static int ConvertFileToSlugName(AppSettings appSettings, ILogger logger, ReadOnlyDictionary relativeToCollection) + { + int result = 0; bool write; string file; string line; - string? title; - string h1Check; + string[] lines; string fileName; string checkName; string? directory; @@ -865,12 +852,16 @@ internal static partial class HelperMarkdown string[] segmentsC; string checkFileName; string segmentsALast; - string? relativePath; string segmentsBFirst; - string[] allSourceFiles = Directory.GetFiles(record.Source, "*", SearchOption.AllDirectories); - List<(MarkdownFile MarkdownFile, string[] Lines)> sourceCollection = record.StartAt is null ? collection : GetCollection(appSettings, record); - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + MarkdownFile markdownFile; + MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath; + ReadOnlyDictionary> keyValuePairs = GetKeyValuePairs(relativeToCollection); + foreach (KeyValuePair relativeTo in relativeToCollection) { + if (!relativeTo.Value.Lines.Any()) + continue; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; if (markdownFile.FileNameWithoutExtension == "index" && markdownFile.Directory.EndsWith(".kanbn")) continue; if (!File.Exists(markdownFile.File)) @@ -887,7 +878,7 @@ internal static partial class HelperMarkdown segmentsB = segmentsALast.Split(")"); if (segmentsB.Length != 2) continue; - segmentsBFirst = segmentsB.First(); + segmentsBFirst = segmentsB[0]; file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst)); fileName = Path.GetFileName(file); directory = Path.GetDirectoryName(file); @@ -895,20 +886,14 @@ internal static partial class HelperMarkdown continue; checkFileName = fileName.ToLower().Replace("%20", "-").Replace(' ', '-'); checkName = Path.Combine(directory, checkFileName); - segmentsC = segmentsA.First().Split('['); - (relativePath, title) = GetRelativePath(allSourceFiles, sourceCollection, markdownFile, file); - if (relativePath is null) + segmentsC = segmentsA[0].Split('['); + markdownFileH1AndRelativePath = GetRelativePath(keyValuePairs, markdownFile, file); + if (markdownFileH1AndRelativePath.MarkdownFile is null || markdownFileH1AndRelativePath.H1 is null || markdownFileH1AndRelativePath.RelativePath is null) { logger.LogInformation("Didn't find {line} in <{file}>", lines[i], markdownFile.FileNameWithoutExtension); continue; } - if (title is null) - { - title = segmentsC.Last(); - if (title.Length != segmentsC.Last().Length) - title = segmentsC.Last(); - } - line = $"{segmentsC.First()}[{title}]({relativePath.Replace('\\', '/')}){segmentsB.Last()}"; + line = $"{string.Join('[', segmentsC, 0, segmentsC.Length - 1)}[{markdownFileH1AndRelativePath.H1}]({markdownFileH1AndRelativePath.RelativePath.Replace('\\', '/')}){segmentsB.Last()}"; if (lines[i] == line) continue; if (fileName.Contains(' ') || fileName.Contains("%20")) @@ -945,119 +930,155 @@ internal static partial class HelperMarkdown } } if (result == 0) - { - foreach ((MarkdownFile markdownFile, string[] lines) in collection) - { - if (markdownFile.LineNumber.H1 is not null) - { - h1 = lines[markdownFile.LineNumber.H1.Value]; - if (h1.Length > 2) - { - h1Check = $"# {h1[2..]}"; - if (h1Check.Length == h1.Length && h1Check != h1) - { - lines[markdownFile.LineNumber.H1.Value] = h1Check; - File.WriteAllLines(markdownFile.File, lines); - result += 1; - } - } - } - checkFileName = markdownFile.FileName.ToLower().Replace("%20", "-").Replace(' ', '-'); - if (checkFileName == markdownFile.FileName) - continue; - if (!File.Exists(markdownFile.File)) - continue; - checkName = Path.Combine(markdownFile.Directory, checkFileName); - if (checkName == markdownFile.File) - continue; - File.Move(markdownFile.File, checkName); - result += 1; - } - } + result = ConvertFileToSlugName(relativeToCollection); return result; } - private static Record GetRecord(List args) + private static void SetRecursiveLines(AppSettings appSettings, ILogger logger, ReadOnlyDictionary> keyValuePairs, MarkdownFile markdownFile, string[] lines, List indentations, List recursiveLines) { - Record result; - string? startAt = null; - string? destination = null; - for (int i = 1; i < args.Count; i++) + if (recursiveLines is null) + throw new Exception(); + string file; + string[] segmentsA; + string[] segmentsB; + string segmentsALast; + string segmentsBFirst; + string indentation = new(indentations.ToArray()); + MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath; + for (int i = 0; i < lines.Length; i++) { - if (args[i].Length == 2 && i + 1 < args.Count) + if (indentations.Count > 15) { - if (args[i][1] == 's') - startAt = Path.GetFullPath(args[i + 1]); - else if (args[i][1] == 'd') - destination = Path.GetFullPath(args[i + 1]); - i++; + recursiveLines.Add("```Error```"); + break; } + if (lines[i].Length < 1) + continue; + if (lines[i][0] == '#') + { + recursiveLines.Add($"{indentation}{lines[i]}"); + continue; + } + segmentsA = lines[i].Split("]("); + if (segmentsA.Length != 2) + continue; + segmentsALast = segmentsA.Last(); + if (appSettings.ExcludeSchemes.Any(l => segmentsALast.StartsWith(l))) + continue; + segmentsB = segmentsALast.Split(")"); + if (segmentsB.Length != 2) + continue; + segmentsBFirst = segmentsB[0]; + file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst)); + markdownFileH1AndRelativePath = GetRelativePath(keyValuePairs, markdownFile, file); + if (markdownFileH1AndRelativePath.MarkdownFile is null || markdownFileH1AndRelativePath.H1 is null || markdownFileH1AndRelativePath.RelativePath is null) + { + recursiveLines.Add($"???{indentation}{lines[i]}"); + logger.LogInformation("Didn't find {line} in <{file}>", lines[i], markdownFile.FileNameWithoutExtension); + continue; + } + if (markdownFileH1AndRelativePath.Lines is null) + continue; + indentations.Add('\t'); + recursiveLines.Add($"{indentation}{lines[i]}"); + SetRecursiveLines(appSettings, logger, keyValuePairs, markdownFileH1AndRelativePath.MarkdownFile, markdownFileH1AndRelativePath.Lines, indentations, recursiveLines); } - if (startAt is not null && !Directory.Exists(startAt)) - throw new Exception($"Start at directory <{startAt}> doesn't exist!"); - if (destination is not null) + if (indentations.Count > 0) + indentations.RemoveAt(0); + } + + private static List GetRecursiveLines(AppSettings appSettings, Record record, ILogger logger, ReadOnlyDictionary relativeToCollection) + { + List results = new(); + string[] lines; + List indentations; + MarkdownFile markdownFile; + List recursiveLines; + ReadOnlyDictionary> keyValuePairs = GetKeyValuePairs(relativeToCollection); + foreach (KeyValuePair relativeTo in relativeToCollection) { - string? root = Path.GetPathRoot(destination); - if (root is null || !Directory.Exists(root)) - throw new NotSupportedException($"This method requires frontMatterYamlLines valid -d path <{root}>!"); - if (!Directory.Exists(destination)) - _ = Directory.CreateDirectory(destination); + if (!relativeTo.Value.Lines.Any()) + continue; + if (record.StartAt is null || !relativeTo.Value.MarkdownFile.File.StartsWith(record.StartAt) || Path.GetFileName(relativeTo.Value.MarkdownFile.Directory) != Path.GetFileName(record.StartAt)) + continue; + indentations = new(); + recursiveLines = new(); + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; + SetRecursiveLines(appSettings, logger, keyValuePairs, markdownFile, lines, indentations, recursiveLines); + results.Add(new(relativeTo.Value.MarkdownFile, recursiveLines.ToArray())); + } + return results; + } + + private static void Write(Record record, List markdownFileAndLinesCollection) + { + foreach (MarkdownFileAndLines markdownFileAndLines in markdownFileAndLinesCollection) + { + if (record.Destination is null) + continue; + File.WriteAllLines(Path.Combine(record.Destination, markdownFileAndLines.MarkdownFile.FileName), markdownFileAndLines.Lines); } - result = new(Path.GetFullPath(args.First()), startAt, destination); - return result; } internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger logger, List args) { int updated; Record record = GetRecord(args); - List<(MarkdownFile MarkdownFile, string[] Lines)> collection; - collection = GetCollection(appSettings, GetFiles(appSettings, record)); - updated = SetFrontMatterAndH1(appSettings, collection); + ReadOnlyDictionary relativeToCollection; + relativeToCollection = GetRelativeToCollection(appSettings, record); + updated = SetFrontMatterAndH1(appSettings, relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } - updated = ConvertFrontMatterToJsonFriendly(collection); + updated = ConvertFrontMatterToJsonFriendly(relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } - updated = CircularReference(logger, collection); + updated = CircularReference(logger, relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } - updated = FindReplace(collection); + updated = FindReplace(relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } - updated = ConvertToRelativePath(appSettings, logger, record, collection); + updated = ConvertToRelativePath(logger, relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } - updated = ConvertFileToSlugName(appSettings, logger, record, collection); + updated = ConvertFileToSlugName(appSettings, logger, relativeToCollection); if (updated != 0) { - collection = GetCollection(appSettings, GetFiles(appSettings, record)); + relativeToCollection = GetRelativeToCollection(appSettings, record); logger.LogInformation("{updated} Markdown file(s) were updated", updated); } + if (!string.IsNullOrEmpty(record.StartAt)) + { + relativeToCollection = GetRelativeToCollection(appSettings, record, force: true); + List markdownFileAndLinesCollection = GetRecursiveLines(appSettings, record, logger, relativeToCollection); + if (!string.IsNullOrEmpty(record.Destination)) + Write(record, markdownFileAndLinesCollection); + } string directory = Path.Combine(Environment.CurrentDirectory, ".vscode"); if (!Directory.Exists(directory)) { string json; - MarkdownFile markdownFile = collection.First().MarkdownFile; + MarkdownFile markdownFile = relativeToCollection.ElementAt(0).Value.MarkdownFile; json = JsonSerializer.Serialize(markdownFile, MarkdownFileSourceGenerationContext.Default.MarkdownFile); if (json != "{}") { - json = JsonSerializer.Serialize(collection.Select(l => l.MarkdownFile).ToArray(), MarkdownFileCollectionSourceGenerationContext.Default.MarkdownFileArray); + json = JsonSerializer.Serialize(relativeToCollection.Select(l => l.Value.MarkdownFile).ToArray(), MarkdownFileCollectionSourceGenerationContext.Default.MarkdownFileArray); File.WriteAllText($"{DateTime.Now.Ticks}.json", json); } } @@ -1068,6 +1089,7 @@ internal static partial class HelperMarkdown List<(string, string, string[])> results = new(); string file; string line; + string[] lines; string fileName; string? directory; string[] segmentsA; @@ -1075,10 +1097,15 @@ internal static partial class HelperMarkdown string relativeFile; string segmentsALast; string segmentsBFirst; + MarkdownFile markdownFile; int sourceDirectoryLength = record.Source.Length; - List<(MarkdownFile MarkdownFile, string[] Lines)> collection = GetCollection(appSettings, GetFiles(appSettings, record)); - foreach ((MarkdownFile markdownFile, string[] lines) in collection) + ReadOnlyDictionary relativeToCollection = GetRelativeToCollection(appSettings, record); + foreach (KeyValuePair relativeTo in relativeToCollection) { + if (!relativeTo.Value.Lines.Any()) + continue; + lines = relativeTo.Value.Lines; + markdownFile = relativeTo.Value.MarkdownFile; if (record.Destination is null) continue; if (markdownFile.File.Length < sourceDirectoryLength) @@ -1100,13 +1127,13 @@ internal static partial class HelperMarkdown segmentsB = segmentsALast.Split(")"); if (segmentsB.Length != 2) continue; - segmentsBFirst = segmentsB.First(); + segmentsBFirst = segmentsB[0]; if (!segmentsBFirst.EndsWith(".md")) file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst)); else file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst[..^3])); relativeFile = Path.GetRelativePath(record.Source, file).Replace('\\', '/'); - line = $"{segmentsA.First()}]({relativeFile}){segmentsB.Last()}"; + line = $"{segmentsA[0]}]({relativeFile}){segmentsB.Last()}"; if (lines[i] == line) throw new NotSupportedException($"Line {i} shouldn't match with {line}"); lines[i] = line;