file-folder-helper/Helpers/HelperMarkdown.cs
Mike Phares 47e6b85c21 Write index.yml.md
Helper to diff video files
Move matches from directory
Bug fix for DirectoryToISO
Find replace instead of remove
Rename Directory
Amazon
Immich Person
PersonKeyToName
PullIconsForBLM
New links
2024-06-13 08:51:00 -07:00

1406 lines
64 KiB
C#

using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.Helpers;
internal static partial class HelperMarkdown
{
private record Input(string? Destination,
string Source,
string? StartAt);
private record Record(string Directory,
string File,
string[] Lines);
private record MarkdownFile(DateTime CreationDateTime,
string Directory,
string Extension,
string File,
string FileName,
string FileNameWithoutExtension,
string H1,
bool IsKanbanIndex,
bool IsKanbanMarkdown,
DateTime LastWriteDateTime,
LineNumber LineNumber,
string Type);
private record MarkdownFileAndLines(MarkdownFile MarkdownFile,
string[] Lines);
private record MarkdownExtra(ReadOnlyCollection<string>? Assignees,
string? Effort,
ReadOnlyCollection<H2HexColor>? H2HexColorCollection,
ReadOnlyCollection<H2NoCheckboxes>? H2NoCheckboxesCollection,
ReadOnlyCollection<H2WithCheckboxes>? H2WithCheckboxesCollection,
string? RequestedDateTime);
private record MarkdownFileH1AndRelativePath(MarkdownFile? MarkdownFile, string[]? Lines, string? H1, string? RelativePath);
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext
{
}
private static void SetRecursiveLines(AppSettings appSettings, ILogger<Worker> logger, ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs, string linkTitle, MarkdownFile markdownFile, string[] lines, List<char> indentations, List<string> recursiveLines)
{
if (recursiveLines is null)
throw new Exception();
string file;
string[] segmentsA;
string[] segmentsB;
string segmentsALast;
bool fencedCodeBlock = false;
string indentation = new(indentations.ToArray());
MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath;
for (int i = 0; i < lines.Length; i++)
{
if (indentations.Count > 15)
{
recursiveLines.Add("```Error```");
break;
}
if (lines[i].Length < 1)
continue;
if (lines[i].Length > 4 && lines[i][..3] == "```")
fencedCodeBlock = !fencedCodeBlock;
if (fencedCodeBlock)
continue;
if (lines[i][0] == '#')
{
if (lines[i] == $"# {linkTitle}")
continue;
recursiveLines.Add($"{indentation}{lines[i]}");
continue;
}
segmentsA = lines[i].Split("](");
if (segmentsA.Length != 2)
continue;
segmentsALast = segmentsA[^1];
if (appSettings.ExcludeSchemes.Any(l => segmentsALast.StartsWith(l)))
continue;
segmentsB = segmentsALast.Split(")");
if (segmentsB.Length != 2)
continue;
file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsB[0]));
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, segmentsA[0].Split('[')[^1], markdownFileH1AndRelativePath.MarkdownFile, markdownFileH1AndRelativePath.Lines, indentations, recursiveLines);
}
if (indentations.Count > 0)
indentations.RemoveAt(0);
}
private static List<string> GetFrontMatterLines(string[] parsedLines)
{
List<string> results = [];
string afterTrim;
string[] segments;
StringBuilder stringBuilder = new();
for (int i = 0; i < parsedLines.Length; i++)
{
afterTrim = parsedLines[i].Trim();
if (string.IsNullOrEmpty(afterTrim) || afterTrim[0] is '{' or '}')
continue;
segments = afterTrim.Split(": ");
if (segments.Length != 2)
{
if (results[^1][^1] == '[')
{
_ = stringBuilder.Clear();
_ = stringBuilder.Append(results[^1]);
results.RemoveAt(results.Count - 1);
for (int j = i; j < parsedLines.Length; j++)
{
i = j;
afterTrim = parsedLines[j].Trim();
if (afterTrim == "],")
_ = stringBuilder.Append(afterTrim[..^1]);
else if (afterTrim[^1] == ',')
_ = stringBuilder.Append(afterTrim).Append(' ');
else
_ = stringBuilder.Append(afterTrim);
if (afterTrim is "]" or "],")
{
results.Add(stringBuilder.ToString());
break;
}
}
continue;
}
results.Clear();
break;
}
if (afterTrim[^1] != ',')
results.Add(afterTrim[1..].Replace("\": ", ": "));
else
results.Add(afterTrim[1..^1].Replace("\": ", ": "));
}
return results;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="filename">The text file to analyze.</param>
/// <returns>The detected encoding.</returns>
private static Encoding? GetEncoding(string filename)
{
Encoding? result;
byte[] bom = new byte[4];
using FileStream file = new(filename, FileMode.Open, FileAccess.Read);
_ = file.Read(bom, 0, 4);
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76)
#pragma warning disable SYSLIB0001
result = Encoding.UTF7;
#pragma warning restore SYSLIB0001
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
result = Encoding.UTF8;
if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0)
result = Encoding.UTF32; //UTF-32LE
if (bom[0] == 0xff && bom[1] == 0xfe)
result = Encoding.Unicode; //UTF-16LE
if (bom[0] == 0xfe && bom[1] == 0xff)
result = Encoding.BigEndianUnicode; //UTF-16BE
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff)
result = new UTF32Encoding(true, true); //UTF-32BE
else
result = null;
return result;
}
[GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")]
private static partial Regex HtmlColor();
private static List<MarkdownFileAndLines> GetMarkdownFileAndLines(string file, List<MarkdownFileAndLines> markdownFiles)
{
List<MarkdownFileAndLines> results = [];
List<string> distinct = [];
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 (string?, Dictionary<string, JsonElement>?, List<string>) Get(List<string> jsonLines)
{
string? result;
List<string> results;
Dictionary<string, JsonElement>? keyValuePairs;
string jsonLinesLast = jsonLines[^1];
jsonLines.RemoveAt(jsonLines.Count - 1);
jsonLines.Add(jsonLinesLast[..^1]);
jsonLines.Insert(0, "{");
jsonLines.Add("}");
result = string.Join(Environment.NewLine, jsonLines);
keyValuePairs = JsonSerializer.Deserialize(result, DictionaryStringAndJsonElementSourceGenerationContext.Default.DictionaryStringJsonElement);
if (keyValuePairs is null)
throw new NullReferenceException(nameof(keyValuePairs));
result = JsonSerializer.Serialize(keyValuePairs, DictionaryStringAndJsonElementSourceGenerationContext.Default.DictionaryStringJsonElement);
string[] parsedLines = result.Split(Environment.NewLine).ToArray();
results = GetFrontMatterLines(parsedLines);
if (results.Count == 0)
{
result = null;
keyValuePairs = null;
}
return (result, keyValuePairs, results);
}
internal static (List<string>, LineNumber) GetStatusAndFrontMatterYamlEndLineNumbers(FileInfo fileInfo)
{
string line;
int? h1LineNumber = null;
int? typeLineNumber = null;
int? statusLineNumber = null;
int? createdLineNumber = null;
int? updatedLineNumber = null;
int? frontMatterYamlEndLineNumber = null;
Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default;
string[] lines = File.ReadAllLines(fileInfo.FullName, encoding);
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;
continue;
}
if (line.Length > 6 && line[..6] == "type: ")
{
typeLineNumber = i;
continue;
}
if (line.Length > 8 && line[..8] == "status: ")
{
statusLineNumber = i;
continue;
}
if (line.Length > 9 && line[..9] == "created: ")
{
createdLineNumber = i;
continue;
}
if (line.Length > 9 && line[..9] == "updated: ")
{
updatedLineNumber = i;
continue;
}
if (h1LineNumber is null && line.Length > 2 && line[0] == '#' && line[1] == ' ')
{
h1LineNumber = i;
continue;
}
}
LineNumber lineNumber = new(createdLineNumber,
h1LineNumber,
frontMatterYamlEndLineNumber,
statusLineNumber,
typeLineNumber,
updatedLineNumber);
return (lines.ToList(), lineNumber);
}
private static MarkdownExtra GetMarkdownExtra(MarkdownFileAndLines markdownFileAndLines)
{
MarkdownExtra result;
int skip;
Match match;
string line;
int completed;
int notCompleted;
List<string> lines;
string? effort = null;
List<string> assignees = [];
string? requestedDateTime = null;
ReadOnlyCollection<Group> groups;
List<H2HexColor> h2HexColors = [];
List<H2NoCheckboxes> h2NoCheckboxes = [];
List<H2WithCheckboxes> h2WithCheckboxes = [];
if (markdownFileAndLines.MarkdownFile.LineNumber.FrontMatterYamlEnd is not null)
{
for (int i = 1; i < markdownFileAndLines.Lines.Length; i++)
{
line = markdownFileAndLines.Lines[i];
if (line.Length < 3)
continue;
if (line.Length > 8 && line[..8] == "effort: ")
{
effort = line[7..].Trim().Trim('"');
continue;
}
if (line.Length > 10 && line[..10] == "assigned: ")
{
foreach (string item in line[10..].Split(',', StringSplitOptions.RemoveEmptyEntries))
assignees.Add(item.Trim().Trim('"'));
continue;
}
if (line.Length > 11 && line[..11] == "requested: ")
{
requestedDateTime = line[10..].Trim().Trim('"');
continue;
}
if (line.Length > 3 && line[0] == '#' && line[1] == '#' && line[2] == ' ')
{
completed = 0;
notCompleted = 0;
match = HtmlColor().Match(line[3..]);
if (line.Length > 3 && match.Success)
{
groups = match.Groups.AsReadOnly();
skip = 3 + groups.Skip(1).Sum(l => l.Length);
h2HexColors.Add(new(line[skip..], $"#{groups.First(l => l.Value.Length == 6)}"));
continue;
}
lines = [];
if (i + 1 == markdownFileAndLines.Lines.Length)
continue;
for (int j = i + 1; j < markdownFileAndLines.Lines.Length; j++)
{
line = markdownFileAndLines.Lines[j];
if (line.Length == 0)
continue;
if (line.Length > 2 && line[0] == '#')
break;
lines.Add(line);
if (line.Length < 5 || line[0] != '-' || line[1] != ' ' || line[2] != '[')
continue;
if (line[3] == ' ' && line[4] == ']')
notCompleted++;
else if (line[3] is 'x' or 'X' && line[4] == ']')
completed++;
}
if (completed != 0 || notCompleted != 0)
h2WithCheckboxes.Add(new(completed,
markdownFileAndLines.Lines[i][3..],
notCompleted,
notCompleted + completed));
else if (lines.Count > 0)
h2NoCheckboxes.Add(new(markdownFileAndLines.Lines[i][3..], new(lines)));
continue;
}
}
}
result = new(new(assignees), effort, new(h2HexColors), new(h2NoCheckboxes), new(h2WithCheckboxes), requestedDateTime);
return result;
}
private static List<MarkdownFileAndLines> Distinct(IEnumerable<MarkdownFileAndLines>? markdownFileAndLinesCollection)
{
List<MarkdownFileAndLines> results = [];
if (markdownFileAndLinesCollection is not null)
{
List<string> distinct = [];
foreach (MarkdownFileAndLines markdownFileAndLines in markdownFileAndLinesCollection)
{
if (distinct.Contains(markdownFileAndLines.MarkdownFile.File))
continue;
distinct.Add(markdownFileAndLines.MarkdownFile.File);
results.Add(markdownFileAndLines);
}
}
return results;
}
private static List<MarkdownFileAndLines> GetMarkdownFileAndLines(ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs)
{
List<MarkdownFileAndLines> results = [];
foreach (KeyValuePair<string, List<MarkdownFileAndLines>> keyValuePair in keyValuePairs)
{
foreach (MarkdownFileAndLines markdownFileAndLines in keyValuePair.Value)
results.Add(markdownFileAndLines);
}
return results;
}
private static (string?, Dictionary<string, JsonElement>?, string[]) Get(int frontMatterYamlEnd, string[] lines)
{
string? result;
List<string> results;
Dictionary<string, JsonElement>? keyValuePairs;
string[] segments;
string[] segmentsB;
string segmentsLast;
string segmentsFirst;
List<string> jsonLines = [];
for (int i = 0; i < frontMatterYamlEnd; i++)
{
if (lines[i] == "---")
continue;
segments = lines[i].Split(": ");
if (segments.Length != 2)
{
jsonLines.Clear();
break;
}
segmentsLast = segments[^1].Trim();
segmentsFirst = segments[0].Trim();
if (string.IsNullOrEmpty(segmentsLast))
continue;
if (segmentsFirst[0] == '"' && segmentsFirst[^1] == '"')
jsonLines.Add($"{segmentsFirst}: ");
else if (segmentsFirst[0] == '\'' && segmentsFirst[^1] == '\'')
jsonLines.Add($"\"{segmentsFirst[1..^1]}\": ");
else
jsonLines.Add($"\"{segmentsFirst}\": ");
if (segmentsLast == "[]")
jsonLines.RemoveAt(jsonLines.Count - 1);
else if (segmentsLast.Length > 4 && segmentsLast[0] == '[' && segmentsLast[^1] == ']' && segmentsLast[1] == '"' && segmentsLast[^2] == '"')
jsonLines.Add($"{segmentsLast},");
else if (segmentsLast[0] == '"' && segmentsLast[^1] == '"')
jsonLines.Add($"{segmentsLast},");
else if (segmentsLast[0] == '"' && segmentsLast[^1] == '"')
jsonLines.Add($"\"{segmentsLast[1..^1]}\"");
else if (!segmentsLast.Contains('"') && !segmentsLast.Contains('\''))
{
if (segmentsLast is "true" or "false")
jsonLines.Add($"{segmentsLast},");
else if (DateTime.TryParse(segmentsLast, out DateTime dateTime))
jsonLines.Add($"\"{segmentsLast}\",");
else if (segmentsLast.All(char.IsNumber))
jsonLines.Add($"{segmentsLast},");
else
{
segmentsB = segmentsLast.Split('.');
if (segmentsB.Length == 2 && segmentsB[0].Length < 7 && segmentsB[^1].Length < 7 && segmentsB[0].All(char.IsNumber) && segmentsB[^1].All(char.IsNumber))
jsonLines.Add($"{segmentsLast},");
else if (!segmentsLast.Contains('[') && !segmentsLast.Contains('{'))
jsonLines.Add($"\"{segmentsLast}\",");
else
{
jsonLines.Clear();
break;
}
}
}
else
{
jsonLines.Clear();
break;
}
}
if (jsonLines.Count > 0)
(result, keyValuePairs, results) = Get(jsonLines);
else
(result, keyValuePairs, results) = (null, null, []);
return (result, keyValuePairs, results.ToArray());
}
private static ReadOnlyDictionary<string, List<MarkdownFileAndLines>> GetKeyValuePairs(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{
Dictionary<string, List<MarkdownFileAndLines>> results = [];
MarkdownFile markdownFile;
string fileNameWithoutExtension;
string fileNameWithoutExtensionB;
List<MarkdownFileAndLines>? markdownFiles;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
markdownFile = relativeTo.Value.MarkdownFile;
if (!results.TryGetValue(relativeTo.Key, out markdownFiles))
{
results.Add(relativeTo.Key, []);
if (!results.TryGetValue(relativeTo.Key, out markdownFiles))
throw new NotSupportedException();
}
markdownFiles.Add(relativeTo.Value);
}
foreach (KeyValuePair<string, MarkdownFileAndLines> 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, []);
if (!results.TryGetValue(markdownFile.FileNameWithoutExtension, out markdownFiles))
throw new NotSupportedException();
}
markdownFiles.Add(relativeTo.Value);
if (fileNameWithoutExtension == markdownFile.FileNameWithoutExtension)
continue;
if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles))
{
results.Add(fileNameWithoutExtension, []);
if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles))
throw new NotSupportedException();
}
if (fileNameWithoutExtensionB == markdownFile.FileNameWithoutExtension)
continue;
if (!results.TryGetValue(fileNameWithoutExtensionB, out markdownFiles))
{
results.Add(fileNameWithoutExtensionB, []);
if (!results.TryGetValue(fileNameWithoutExtensionB, out markdownFiles))
throw new NotSupportedException();
}
markdownFiles.Add(relativeTo.Value);
}
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
markdownFile = relativeTo.Value.MarkdownFile;
if (!results.TryGetValue(markdownFile.H1, out markdownFiles))
{
results.Add(markdownFile.H1, []);
if (!results.TryGetValue(markdownFile.H1, out markdownFiles))
throw new NotSupportedException();
}
markdownFiles.Add(relativeTo.Value);
}
return new(results);
}
private static int ConvertFileToSlugName(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
string h1;
bool gitCheck;
string h1Check;
string[] lines;
string checkName;
string checkFileName;
MarkdownFile markdownFile;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
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)
{
if (!gitCheck)
continue;
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;
if (!gitCheck)
continue;
File.Move(markdownFile.File, checkName);
result += 1;
}
return result;
}
private static string[] GetFiles(AppSettings appSettings, string directory)
{
string[] results = Directory.GetFiles(directory, "*.md", SearchOption.AllDirectories).
Where(l => !appSettings.ExcludeDirectoryNames.Any(m => l.Contains(m))).ToArray();
return results;
}
private static ReadOnlyDictionary<string, MarkdownFileAndLines> GetRelativeToCollection(AppSettings appSettings, Input input, string[] files, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles, bool force)
{
Dictionary<string, MarkdownFileAndLines> results = [];
string h1;
string key;
string type;
bool gitCheck;
FileInfo fileInfo;
bool isKanbanIndex;
List<string> lines;
bool isKanbanMarkdown;
LineNumber lineNumber;
MarkdownFile markdownFile;
string fileNameWithoutExtension;
foreach (string file in files)
{ // cSpell:disable
fileInfo = new(file);
if (fileInfo.DirectoryName is null)
continue;
key = Path.GetRelativePath(input.Source, file);
(lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
if (lines.Count > 0)
(type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber);
else
{
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(file);
if (!gitCheck)
continue;
type = appSettings.DefaultNoteType;
File.WriteAllLines(file, ["---", $"type: \"{type}\"", "---", string.Empty, $"# {h1}"]);
lines = File.ReadAllLines(file).ToList();
}
isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md");
isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase);
markdownFile = new(fileInfo.CreationTime,
fileInfo.DirectoryName,
fileInfo.Extension,
file,
fileInfo.Name,
fileNameWithoutExtension,
h1,
isKanbanIndex,
isKanbanMarkdown,
fileInfo.LastWriteTime,
lineNumber,
type);
if (force || input.StartAt is null || file.StartsWith(input.StartAt))
results.Add(key, new(markdownFile, lines.ToArray()));
else
results.Add(key, new(markdownFile, []));
} // cSpell:restore
return new(results);
}
private static List<string> GetKeys(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{
List<string> results = [];
MarkdownFile markdownFile;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanMarkdown)
continue;
results.Add(relativeTo.Key);
}
return results;
}
private static MarkdownFileAndLines? GetKanbanIndexMarkdownFileAndLines(ReadOnlyDictionary<string, MarkdownFileAndLines> keyValuePairs)
{
MarkdownFile markdownFile;
MarkdownFileAndLines? result = null;
foreach (KeyValuePair<string, MarkdownFileAndLines> keyValuePair in keyValuePairs)
{
markdownFile = keyValuePair.Value.MarkdownFile;
if (markdownFile.IsKanbanIndex)
{
if (result is not null)
{
result = null;
break;
}
result = keyValuePair.Value;
}
}
return result;
}
private static void SetCards(Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, List<Card> notLinkedKey, MarkdownFile markdownFile, string[] lines, List<Card> cards, List<string> 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<string, List<Card>> GetColumnsToCards(Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, MarkdownFileAndLines markdownFileAndLines)
{
Dictionary<string, List<Card>> results = [];
string? column;
string[] lines;
List<Card> cards = [];
List<Card> notLinkedKey = [];
lines = markdownFileAndLines.Lines;
List<string> 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);
}
private static MarkdownFileAndLines? GetMarkdownFile(ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs, MarkdownFile markdownFile, string file)
{
MarkdownFileAndLines? result;
List<MarkdownFileAndLines>? markdownFileAndLinesCollection;
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
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
{
List<MarkdownFileAndLines> matches;
matches = markdownFileAndLinesCollection is null ? [] : GetMarkdownFileAndLines(file, markdownFileAndLinesCollection);
if (matches.Count == 1)
result = matches[0];
else
{
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
{
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[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 = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.File.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)));
if (matches.Count == 1)
result = matches[0];
else
{
checkName = $"\\{checkName}";
matches = Distinct(markdownFileAndLinesCollection.Where(l => l.MarkdownFile.File.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)));
if (matches.Count == 1)
result = matches[0];
else
result = null;
}
}
}
}
}
}
return result;
}
private static Input GetInput(List<string> args)
{
Input result;
string? startAt = null;
string? destination = null;
string source = Path.GetFullPath(args[0]);
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 (startAt is not null && startAt.Length < source.Length)
throw new Exception($"Start at directory <{startAt}> must be a subdirectory!");
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(destination, source, startAt);
return result;
}
private static int ConvertFrontMatterToJsonFriendly(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
List<string> results = [];
bool write;
bool gitCheck;
string[] lines;
MarkdownFile markdownFile;
string[] frontMatterYamlLines;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
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);
if (frontMatterYamlLines.Length == 0)
continue;
results.Add("---");
results.AddRange(frontMatterYamlLines);
results.Add("---");
for (int i = markdownFile.LineNumber.FrontMatterYamlEnd.Value + 1; i < lines.Length; i++)
results.Add(lines[i]);
if (results.Count == lines.Length)
{
write = false;
for (int i = 0; i < lines.Length; i++)
{
if (results[i] == lines[i])
continue;
write = true;
break;
}
if (!write)
continue;
}
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, results);
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
result += 1;
}
return result;
}
private static int CircularReference(ILogger<Worker> logger, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
string line;
string check;
bool gitCheck;
string[] lines;
bool circularReference;
MarkdownFile markdownFile;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
circularReference = false;
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
for (int i = 0; i < lines.Length; i++)
{
check = $"[[{markdownFile.FileNameWithoutExtension}]]";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
if (!circularReference)
circularReference = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"{markdownFile.FileNameWithoutExtension}|{markdownFile.FileNameWithoutExtension}]]";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
if (!circularReference)
circularReference = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"[{markdownFile.FileNameWithoutExtension}]({markdownFile.FileName})";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
logger.LogInformation("circular reference for <{file}>", markdownFile.FileName);
if (!circularReference)
circularReference = true;
}
if (!circularReference)
continue;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, lines);
result += 1;
}
return result;
}
private static int FindReplace(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
bool found;
string line;
string check;
bool gitCheck;
string[] lines;
MarkdownFile markdownFile;
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
found = false;
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
for (int i = 0; i < lines.Length; i++)
{
check = $"[[K-A/";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, "[[.kanbn/Archive/");
if (lines[i] == line)
continue;
lines[i] = line;
if (!found)
found = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"[[K-T/";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, "[[.kanbn/Tasks/");
if (lines[i] == line)
continue;
lines[i] = line;
if (!found)
found = true;
}
if (!found)
continue;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, lines);
result += 1;
}
return result;
}
private static int ConvertToRelativePath(ILogger<Worker> logger, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
bool write;
string line;
bool gitCheck;
string[] lines;
string[] segmentsA;
string[] segmentsB;
string[] segmentsC;
MarkdownFile markdownFile;
MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath;
ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs = GetKeyValuePairs(relativeToCollection);
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
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[0].Split("[[");
if (segmentsB.Length is not 2 or 3)
continue;
segmentsC = segmentsB[^1].Split('|');
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;
}
line = $"{segmentsB[0]}[{markdownFileH1AndRelativePath.H1}]({markdownFileH1AndRelativePath.RelativePath.Replace('\\', '/')}){segmentsA[^1]}";
if (lines[i] == line)
continue;
lines[i] = line;
if (!write)
write = true;
}
if (!write)
continue;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, lines);
result += 1;
}
return result;
}
private static int ConvertFileToSlugName(AppSettings appSettings, ILogger<Worker> logger, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
bool write;
string file;
string line;
bool gitCheck;
string[] lines;
string fileName;
string checkName;
string? directory;
string[] segmentsA;
string[] segmentsB;
string[] segmentsC;
string checkFileName;
string segmentsALast;
string segmentsBFirst;
MarkdownFile markdownFile;
MarkdownFileH1AndRelativePath markdownFileH1AndRelativePath;
ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs = GetKeyValuePairs(relativeToCollection);
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanIndex)
continue;
if (!File.Exists(markdownFile.File))
continue;
write = false;
for (int i = 0; i < lines.Length; i++)
{
segmentsA = lines[i].Split("](");
if (segmentsA.Length != 2)
continue;
segmentsALast = segmentsA[^1];
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));
fileName = Path.GetFileName(file);
directory = Path.GetDirectoryName(file);
if (string.IsNullOrEmpty(directory))
continue;
checkFileName = fileName.ToLower().Replace("%20", "-").Replace(' ', '-');
checkName = Path.Combine(directory, checkFileName);
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;
}
line = $"{string.Join('[', segmentsC, 0, segmentsC.Length - 1)}[{markdownFileH1AndRelativePath.H1}]({markdownFileH1AndRelativePath.RelativePath.Replace('\\', '/')}){segmentsB[^1]}";
if (lines[i] == line)
continue;
if (fileName.Contains(' ') || fileName.Contains("%20"))
{
if (!File.Exists(file))
{
logger.LogInformation("Didn't find <{file}>", file);
continue;
}
if (File.Exists(checkName))
continue;
File.Move(file, checkName);
}
else if (!fileName.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))
{
if (file != checkName)
{
if (!File.Exists(file))
{
logger.LogInformation("Didn't find <{file}>", file);
continue;
}
File.Move(file, checkName);
}
}
lines[i] = line;
if (!write)
write = true;
}
if (!write)
continue;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, lines);
result += 1;
}
if (result == 0)
result = ConvertFileToSlugName(relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
return result;
}
private static ReadOnlyDictionary<string, MarkdownFileAndLines> GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles, bool force = false)
{
ReadOnlyDictionary<string, MarkdownFileAndLines> results;
string[] files = GetFiles(appSettings, input.Source);
results = GetRelativeToCollection(appSettings, input, files, gitOthersModifiedAndDeletedExcludingStandardFiles, force);
return new(results);
}
private static List<MarkdownFileAndLines> GetRecursiveLines(AppSettings appSettings, Input input, ILogger<Worker> logger, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{
List<MarkdownFileAndLines> results = [];
string[] lines;
List<char> indentations;
MarkdownFile markdownFile;
List<string> recursiveLines;
ReadOnlyDictionary<string, List<MarkdownFileAndLines>> keyValuePairs = GetKeyValuePairs(relativeToCollection);
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
if (input.StartAt is null || !relativeTo.Value.MarkdownFile.File.StartsWith(input.StartAt) || Path.GetFileName(relativeTo.Value.MarkdownFile.Directory) != Path.GetFileName(input.StartAt))
continue;
indentations = [];
recursiveLines = [];
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
SetRecursiveLines(appSettings, logger, keyValuePairs, markdownFile.FileNameWithoutExtension, markdownFile, lines, indentations, recursiveLines);
results.Add(new(relativeTo.Value.MarkdownFile, recursiveLines.ToArray()));
}
return results;
}
private static void Write(Input input, List<MarkdownFileAndLines> markdownFileAndLinesCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
bool gitCheck;
foreach (MarkdownFileAndLines markdownFileAndLines in markdownFileAndLinesCollection)
{
if (input.Destination is null)
continue;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFileAndLines.MarkdownFile.File);
if (!gitCheck)
continue;
File.WriteAllLines(Path.Combine(input.Destination, markdownFileAndLines.MarkdownFile.FileName), markdownFileAndLines.Lines);
}
}
private static void SaveColumnToCards(Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
{
if (string.IsNullOrEmpty(input.StartAt) || string.IsNullOrEmpty(input.Destination))
throw new NotSupportedException();
MarkdownFileAndLines? markdownFileAndLines = GetKanbanIndexMarkdownFileAndLines(relativeToCollection);
if (markdownFileAndLines is not null && File.Exists(markdownFileAndLines.MarkdownFile.File))
{
ReadOnlyDictionary<string, List<Card>> columnsToCards;
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);
File.WriteAllText(jsonFile, json);
}
}
}
private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, List<string> lines, LineNumber lineNumber)
{
string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value][5..].Trim().Trim('"');
string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..];
return (type, h1FromFile);
}
private static int SetFrontMatterAndH1(AppSettings appSettings, ILogger<Worker> logger, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
{
int result = 0;
List<string> results = [];
bool gitCheck;
string h1Line;
string[] lines;
string typeLine;
TimeSpan timeSpan;
string createdLine;
string updatedLine;
string lineDateTime;
DateTime checkDateTime;
DateTime creationDateTime;
MarkdownFile markdownFile;
string lineCreationFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
{
if (relativeTo.Value.Lines.Length == 0)
continue;
results.Clear();
lines = relativeTo.Value.Lines;
markdownFile = relativeTo.Value.MarkdownFile;
if (markdownFile.IsKanbanMarkdown)
continue;
results.AddRange(lines);
typeLine = $"type: \"{appSettings.DefaultNoteType}\"";
h1Line = $"# {markdownFile.FileNameWithoutExtension}";
creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime;
gitCheck = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(markdownFile.File);
createdLine = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
updatedLine = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
if (markdownFile.IsKanbanIndex)
HelperKanbanMetadata.SetMetadata(markdownFile.Directory, new(lines), markdownFile.LineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
{
if (markdownFile.LineNumber.H1 is not null)
results.Insert(0, string.Empty);
else
{
results.Insert(0, string.Empty);
results.Insert(0, h1Line);
results.Insert(0, string.Empty);
}
results.Insert(0, "---");
if (gitCheck)
{
results.Insert(0, updatedLine);
results.Insert(0, createdLine);
}
results.Insert(0, typeLine);
results.Insert(0, "---");
}
else
{
if (markdownFile.LineNumber.H1 is null)
{
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value + 1, string.Empty);
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value + 1, h1Line);
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value + 1, string.Empty);
}
if (markdownFile.LineNumber.Type is null)
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, typeLine);
if (gitCheck)
{
if (markdownFile.LineNumber.Updated is null)
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine);
else
{
lineDateTime = results[markdownFile.LineNumber.Updated.Value].Split(": ")[1].Trim('"');
if (!DateTime.TryParseExact(lineDateTime, lineCreationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime))
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
else
{
timeSpan = new(checkDateTime.Ticks - markdownFile.LastWriteDateTime.Ticks);
if (timeSpan.TotalDays is > 1 or < -1)
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
}
}
if (markdownFile.LineNumber.Created is null)
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, createdLine);
else
{
lineDateTime = results[markdownFile.LineNumber.Created.Value].Split(": ")[1].Trim('"');
if (!DateTime.TryParseExact(lineDateTime, lineCreationFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime))
results[markdownFile.LineNumber.Created.Value] = createdLine;
else
{
timeSpan = new(checkDateTime.Ticks - creationDateTime.Ticks);
if (timeSpan.TotalDays > 1)
results[markdownFile.LineNumber.Created.Value] = createdLine;
if (timeSpan.TotalDays < -1)
File.SetCreationTime(markdownFile.File, checkDateTime);
}
}
}
}
if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results))
continue;
if (!gitCheck)
continue;
File.WriteAllLines(markdownFile.File, results);
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
result += 1;
}
return result;
}
private static MarkdownFileH1AndRelativePath GetRelativePath(ReadOnlyDictionary<string, List<MarkdownFileAndLines>> 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)));
}
internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args, CancellationToken cancellationToken)
{
int updated;
bool usePathCombine = true;
Input input = GetInput(args);
ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles = HelperGit.GetOthersModifiedAndDeletedExcludingStandardFiles(input.Source, usePathCombine, cancellationToken);
ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
updated = SetFrontMatterAndH1(appSettings, logger, input, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
updated = ConvertFrontMatterToJsonFriendly(relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
updated = CircularReference(logger, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
updated = FindReplace(relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
updated = ConvertToRelativePath(logger, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
updated = ConvertFileToSlugName(appSettings, logger, relativeToCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
if (updated != 0)
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
}
if (!string.IsNullOrEmpty(input.StartAt) && !string.IsNullOrEmpty(input.Destination))
{
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles, force: true);
List<MarkdownFileAndLines> markdownFileAndLinesCollection = GetRecursiveLines(appSettings, input, logger, relativeToCollection);
Write(input, markdownFileAndLinesCollection, gitOthersModifiedAndDeletedExcludingStandardFiles);
}
if (!string.IsNullOrEmpty(input.StartAt) && !string.IsNullOrEmpty(input.Destination))
SaveColumnToCards(input, relativeToCollection);
}
}