1053 lines
44 KiB
C#
1053 lines
44 KiB
C#
using File_Folder_Helper.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace File_Folder_Helper.Helpers;
|
|
|
|
internal static partial class HelperMarkdown
|
|
{
|
|
|
|
private record Record(string Source,
|
|
string? StartAt,
|
|
string? Destination);
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
|
[JsonSerializable(typeof(Dictionary<string, JsonElement>))]
|
|
internal partial class DictionaryStringAndJsonElementSourceGenerationContext : JsonSerializerContext
|
|
{
|
|
}
|
|
|
|
/// <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>
|
|
internal 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;
|
|
}
|
|
|
|
internal 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 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<string> lines, LineNumber lineNumber)
|
|
{
|
|
string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value].Replace("type: ", string.Empty);
|
|
string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..];
|
|
return (type, h1FromFile);
|
|
}
|
|
|
|
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 = 1; i < lines.Length; i++)
|
|
{
|
|
line = lines[i];
|
|
if (line.Length < 3)
|
|
continue;
|
|
if (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[..2] == "# ")
|
|
{
|
|
h1LineNumber = i;
|
|
continue;
|
|
}
|
|
}
|
|
LineNumber lineNumber = new(createdLineNumber,
|
|
h1LineNumber,
|
|
frontMatterYamlEndLineNumber,
|
|
statusLineNumber,
|
|
typeLineNumber,
|
|
updatedLineNumber);
|
|
return (lines.ToList(), lineNumber);
|
|
}
|
|
|
|
internal static List<(MarkdownFile, string[])> GetCollection(AppSettings appSettings, string[] files)
|
|
{
|
|
List<(MarkdownFile, string[])> results = new();
|
|
string h1;
|
|
string type;
|
|
FileInfo fileInfo;
|
|
List<string> lines;
|
|
LineNumber lineNumber;
|
|
MarkdownFile markdownFile;
|
|
string fileNameWithoutExtension;
|
|
foreach (string file in files)
|
|
{
|
|
fileInfo = new(file);
|
|
if (fileInfo.DirectoryName is null)
|
|
continue;
|
|
(lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
|
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
|
|
h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
|
|
if (lines.Any())
|
|
(type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber);
|
|
else
|
|
{
|
|
type = appSettings.DefaultNoteType;
|
|
File.WriteAllLines(file, new string[] { "---", $"type: \"{type}\"", "---", string.Empty, $"# {h1}" });
|
|
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()));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<(MarkdownFile, string[])> GetCollection(AppSettings appSettings, Record record)
|
|
{
|
|
List<(MarkdownFile, string[])> results;
|
|
string[] files = GetFiles(appSettings, record.Source);
|
|
results = GetCollection(appSettings, files);
|
|
return results;
|
|
}
|
|
|
|
private static int SetFrontMatterAndH1(AppSettings appSettings, List<(MarkdownFile, string[])> collection)
|
|
{
|
|
int result = 0;
|
|
string h1Line;
|
|
string typeLine;
|
|
string createdLine;
|
|
string updatedLine;
|
|
DateTime creationDateTime;
|
|
string createdLineCompare;
|
|
string updatedLineCompare;
|
|
List<string> results = new();
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (!lines.Any())
|
|
continue;
|
|
results.Clear();
|
|
results.AddRange(lines);
|
|
creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime;
|
|
typeLine = $"type: \"{appSettings.DefaultNoteType}\"";
|
|
h1Line = $"# {markdownFile.FileNameWithoutExtension}";
|
|
createdLineCompare = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-dd}T";
|
|
createdLine = $"created: \"{creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
|
|
updatedLineCompare = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-dd}T";
|
|
updatedLine = $"updated: \"{markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}\"";
|
|
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
|
|
{
|
|
if (markdownFile.LineNumber.H1 is null)
|
|
{
|
|
results.Insert(0, string.Empty);
|
|
results.Insert(0, h1Line);
|
|
results.Insert(0, string.Empty);
|
|
}
|
|
results.Insert(0, "---");
|
|
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 (markdownFile.LineNumber.Updated is null)
|
|
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, updatedLine);
|
|
else
|
|
{
|
|
if (results[markdownFile.LineNumber.Updated.Value].Contains('$'))
|
|
continue;
|
|
if (results[markdownFile.LineNumber.Updated.Value][..updatedLineCompare.Length] == updatedLineCompare)
|
|
continue;
|
|
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
|
|
}
|
|
if (markdownFile.LineNumber.Created is null)
|
|
results.Insert(markdownFile.LineNumber.FrontMatterYamlEnd.Value, createdLine);
|
|
else if (results[markdownFile.LineNumber.Created.Value][..createdLineCompare.Length] != createdLineCompare)
|
|
results[markdownFile.LineNumber.Created.Value] = createdLine;
|
|
}
|
|
File.WriteAllLines(markdownFile.File, results);
|
|
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static (string?, Dictionary<string, JsonElement>?, string[]) Get(int frontMatterYamlEnd, string[] lines)
|
|
{
|
|
string? result;
|
|
List<string> results = new();
|
|
Dictionary<string, JsonElement>? keyValuePairs;
|
|
string[] segments;
|
|
string[] segmentsB;
|
|
string segmentsLast;
|
|
string segmentsFirst;
|
|
List<string> jsonLines = new();
|
|
for (int i = 0; i < frontMatterYamlEnd; i++)
|
|
{
|
|
if (lines[i] == "---")
|
|
continue;
|
|
segments = lines[i].Split(": ");
|
|
if (segments.Length != 2)
|
|
{
|
|
jsonLines.Clear();
|
|
break;
|
|
}
|
|
segmentsLast = segments.Last().Trim();
|
|
segmentsFirst = segments.First().Trim();
|
|
if (string.IsNullOrEmpty(segmentsLast))
|
|
continue;
|
|
if (segmentsFirst.First() == '"' && segmentsFirst.Last() == '"')
|
|
jsonLines.Add($"{segmentsFirst}: ");
|
|
else if (segmentsFirst.First() == '\'' && segmentsFirst.Last() == '\'')
|
|
jsonLines.Add($"\"{segmentsFirst[1..^1]}\": ");
|
|
else
|
|
jsonLines.Add($"\"{segmentsFirst}\": ");
|
|
if (segmentsLast == "[]")
|
|
jsonLines.RemoveAt(jsonLines.Count - 1);
|
|
else if (segmentsLast.First() == '"' && segmentsLast.Last() == '"')
|
|
jsonLines.Add($"{segmentsLast},");
|
|
else if (segmentsLast.First() == '"' && segmentsLast.Last() == '"')
|
|
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(l => char.IsNumber(l)))
|
|
jsonLines.Add($"{segmentsLast},");
|
|
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)))
|
|
jsonLines.Add($"{segmentsLast},");
|
|
else if (!segmentsLast.Contains('[') && !segmentsLast.Contains('{'))
|
|
jsonLines.Add($"\"{segmentsLast}\",");
|
|
else
|
|
{
|
|
jsonLines.Clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jsonLines.Clear();
|
|
break;
|
|
}
|
|
}
|
|
if (!jsonLines.Any())
|
|
(result, keyValuePairs) = (null, null);
|
|
else
|
|
{
|
|
string afterTrim;
|
|
string jsonLinesLast = jsonLines.Last();
|
|
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);
|
|
jsonLines = result.Split(Environment.NewLine).ToList();
|
|
foreach (string jsonLine in jsonLines)
|
|
{
|
|
afterTrim = jsonLine.Trim();
|
|
if (string.IsNullOrEmpty(afterTrim) || afterTrim.First() is '{' or '}')
|
|
continue;
|
|
segments = afterTrim.Split(": ");
|
|
if (segments.Length != 2)
|
|
{
|
|
result = null;
|
|
results.Clear();
|
|
keyValuePairs = null;
|
|
break;
|
|
}
|
|
if (afterTrim[^1] != ',')
|
|
results.Add(afterTrim[1..].Replace("\": ", ": "));
|
|
else
|
|
results.Add(afterTrim[1..^1].Replace("\": ", ": "));
|
|
}
|
|
}
|
|
return (result, keyValuePairs, results.ToArray());
|
|
}
|
|
|
|
private static int ConvertFrontMatterToJsonFriendly(List<(MarkdownFile, string[])> collection)
|
|
{
|
|
int result = 0;
|
|
bool write;
|
|
List<string> results = new();
|
|
string[] frontMatterYamlLines;
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (!lines.Any())
|
|
continue;
|
|
results.Clear();
|
|
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
|
|
continue;
|
|
(_, _, frontMatterYamlLines) = Get(markdownFile.LineNumber.FrontMatterYamlEnd.Value, lines);
|
|
if (!frontMatterYamlLines.Any())
|
|
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;
|
|
}
|
|
File.WriteAllLines(markdownFile.File, results);
|
|
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int CircularReference(ILogger<Worker> logger, List<(MarkdownFile, string[])> collection)
|
|
{
|
|
int result = 0;
|
|
string line;
|
|
string check;
|
|
bool circularReference;
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (!lines.Any())
|
|
continue;
|
|
circularReference = false;
|
|
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)
|
|
{
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int FindReplace(List<(MarkdownFile, string[])> collection)
|
|
{
|
|
int result = 0;
|
|
bool found;
|
|
string line;
|
|
string check;
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (!lines.Any())
|
|
continue;
|
|
found = false;
|
|
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)
|
|
{
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static Dictionary<string, List<MarkdownFile>> GetKeyValuePairs(List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
|
|
{
|
|
Dictionary<string, List<MarkdownFile>> results = new();
|
|
List<MarkdownFile>? markdownFiles;
|
|
string fileNameWithoutExtension;
|
|
foreach ((MarkdownFile markdownFile, _) in collection)
|
|
{
|
|
fileNameWithoutExtension = markdownFile.FileNameWithoutExtension.ToLower();
|
|
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);
|
|
if (fileNameWithoutExtension == markdownFile.FileNameWithoutExtension)
|
|
continue;
|
|
if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles))
|
|
{
|
|
results.Add(fileNameWithoutExtension, new());
|
|
if (!results.TryGetValue(fileNameWithoutExtension, out markdownFiles))
|
|
throw new NotSupportedException();
|
|
}
|
|
if (markdownFiles.Contains(markdownFile))
|
|
continue;
|
|
markdownFiles.Add(markdownFile);
|
|
}
|
|
foreach ((MarkdownFile markdownFile, _) in collection)
|
|
{
|
|
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);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static Dictionary<string, List<MarkdownFile>> GetKeyValuePairs(AppSettings appSettings, Record record)
|
|
{
|
|
Dictionary<string, List<MarkdownFile>> results;
|
|
List<(MarkdownFile MarkdownFile, string[] Lines)> collection = GetCollection(appSettings, record);
|
|
results = GetKeyValuePairs(collection);
|
|
return results;
|
|
}
|
|
|
|
private static (string?, string?) GetMatchAndTitle(string? directory, List<MarkdownFile> 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<string, List<MarkdownFile>> keyValuePairs, MarkdownFile markdownFile, string file)
|
|
{
|
|
string? result;
|
|
string? match;
|
|
string? title;
|
|
List<MarkdownFile>? markdownFiles;
|
|
string? directory = Path.GetDirectoryName(file);
|
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
|
if (keyValuePairs.TryGetValue(fileNameWithoutExtension, out markdownFiles))
|
|
(match, title) = GetMatchAndTitle(directory, markdownFiles);
|
|
else
|
|
{
|
|
if (keyValuePairs.TryGetValue(fileNameWithoutExtension.ToLower(), out markdownFiles))
|
|
(match, title) = GetMatchAndTitle(directory, markdownFiles);
|
|
else
|
|
(match, title) = (null, null);
|
|
}
|
|
if (match is null)
|
|
{
|
|
List<string> files = new();
|
|
List<string> fileNames = new();
|
|
foreach (KeyValuePair<string, List<MarkdownFile>> 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();
|
|
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();
|
|
else
|
|
{
|
|
if (!matches.Any())
|
|
match = null;
|
|
else
|
|
{
|
|
checkName = matches.First();
|
|
matches = files.Where(l => l.Contains(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
|
|
if (matches.Length == 1)
|
|
match = matches.First();
|
|
else
|
|
{
|
|
checkName = $"{checkName}{markdownFile.Extension}";
|
|
matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
|
|
if (matches.Length == 1)
|
|
match = matches.First();
|
|
else
|
|
{
|
|
checkName = $"\\{checkName}";
|
|
matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
|
|
if (matches.Length == 1)
|
|
match = matches.First();
|
|
else
|
|
match = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result = match is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(match));
|
|
return (result, title);
|
|
}
|
|
|
|
private static int ConvertToRelativePath(AppSettings appSettings, ILogger<Worker> logger, Record record, List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
|
|
{
|
|
int result = 0;
|
|
bool write;
|
|
string line;
|
|
string? title;
|
|
string[] segmentsA;
|
|
string[] segmentsB;
|
|
string[] segmentsC;
|
|
string? relativePath;
|
|
Dictionary<string, List<MarkdownFile>> keyValuePairs = record.StartAt is null ? GetKeyValuePairs(collection) : GetKeyValuePairs(appSettings, record);
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (!lines.Any())
|
|
continue;
|
|
write = false;
|
|
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("[[");
|
|
if (segmentsB.Length is not 2 or 3)
|
|
continue;
|
|
segmentsC = segmentsB.Last().Split('|');
|
|
(relativePath, title) = GetRelativePath(keyValuePairs, markdownFile, segmentsC.First());
|
|
if (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()}";
|
|
if (lines[i] == line)
|
|
continue;
|
|
lines[i] = line;
|
|
if (!write)
|
|
write = true;
|
|
}
|
|
if (write)
|
|
{
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static (string?, string?) GetRelativePath(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();
|
|
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();
|
|
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();
|
|
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
|
|
(relativePath, title) = (null, null);
|
|
}
|
|
}
|
|
}
|
|
return (relativePath, title);
|
|
}
|
|
|
|
private static int ConvertFileToSlugName(AppSettings appSettings, ILogger<Worker> logger, Record record, List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
|
|
{
|
|
int result = 0;
|
|
string h1;
|
|
bool write;
|
|
string file;
|
|
string line;
|
|
string? title;
|
|
string h1Check;
|
|
string fileName;
|
|
string checkName;
|
|
string? directory;
|
|
string[] segmentsA;
|
|
string[] segmentsB;
|
|
string[] segmentsC;
|
|
string checkFileName;
|
|
string segmentsALast;
|
|
string? relativePath;
|
|
string segmentsBFirst;
|
|
List<(MarkdownFile MarkdownFile, string[] Lines)> sourceCollection = record.StartAt is null ? collection : GetCollection(appSettings, record);
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (markdownFile.FileNameWithoutExtension == "index")
|
|
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.Last();
|
|
if (appSettings.ExcludeSchemes.Any(l => segmentsALast.StartsWith(l)))
|
|
continue;
|
|
segmentsB = segmentsALast.Split(")");
|
|
if (segmentsB.Length != 2)
|
|
continue;
|
|
segmentsBFirst = segmentsB.First();
|
|
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.First().Split('[');
|
|
(relativePath, title) = GetRelativePath(sourceCollection, markdownFile, file);
|
|
if (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()}";
|
|
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 != fileName.ToLower())
|
|
{
|
|
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)
|
|
{
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static Record GetRecord(List<string> 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.First()), startAt, destination);
|
|
return result;
|
|
}
|
|
|
|
internal static void MarkdownWikiLinkVerification(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
|
|
{
|
|
int updated;
|
|
Record record = GetRecord(args);
|
|
List<(MarkdownFile MarkdownFile, string[] Lines)> collection;
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
updated = SetFrontMatterAndH1(appSettings, collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = ConvertFrontMatterToJsonFriendly(collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = CircularReference(logger, collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = FindReplace(collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = ConvertToRelativePath(appSettings, logger, record, collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = ConvertFileToSlugName(appSettings, logger, record, collection);
|
|
if (updated != 0)
|
|
{
|
|
collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode");
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
string json;
|
|
MarkdownFile markdownFile = collection.First().MarkdownFile;
|
|
json = JsonSerializer.Serialize(markdownFile, MarkdownFileSourceGenerationContext.Default.MarkdownFile);
|
|
if (json != "{}")
|
|
{
|
|
json = JsonSerializer.Serialize(collection.Select(l => l.MarkdownFile).ToArray(), MarkdownFileCollectionSourceGenerationContext.Default.MarkdownFileArray);
|
|
File.WriteAllText($"{DateTime.Now.Ticks}.json", json);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static List<(string, string, string[])> GetWithLinksForHugo(AppSettings appSettings, Record record)
|
|
{
|
|
List<(string, string, string[])> results = new();
|
|
string file;
|
|
string line;
|
|
string fileName;
|
|
string? directory;
|
|
string[] segmentsA;
|
|
string[] segmentsB;
|
|
string relativeFile;
|
|
string segmentsALast;
|
|
string segmentsBFirst;
|
|
int sourceDirectoryLength = record.Source.Length;
|
|
List<(MarkdownFile MarkdownFile, string[] Lines)> collection = GetCollection(appSettings, GetFiles(appSettings, record));
|
|
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
|
|
{
|
|
if (record.Destination is null)
|
|
continue;
|
|
if (markdownFile.File.Length < sourceDirectoryLength)
|
|
continue;
|
|
if (!File.Exists(markdownFile.File))
|
|
continue;
|
|
fileName = $"{record.Destination}{markdownFile.File[sourceDirectoryLength..]}";
|
|
directory = Path.GetDirectoryName(fileName);
|
|
if (string.IsNullOrEmpty(directory))
|
|
continue;
|
|
for (int i = 0; i < lines.Length; i++)
|
|
{
|
|
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.First();
|
|
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()}";
|
|
if (lines[i] == line)
|
|
throw new NotSupportedException($"Line {i} shouldn't match with {line}");
|
|
lines[i] = line;
|
|
}
|
|
results.Add((directory, fileName, lines));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static List<string> GetDistinct(List<(string, string, string[])> collection)
|
|
{
|
|
List<string> results = new();
|
|
foreach ((string directory, _, _) in collection)
|
|
{
|
|
if (results.Contains(directory))
|
|
continue;
|
|
results.Add(directory);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static void CreateMissingDirectories(List<string> directories)
|
|
{
|
|
foreach (string directory in directories)
|
|
{
|
|
if (!Directory.Exists(directory))
|
|
_ = Directory.CreateDirectory(directory);
|
|
}
|
|
}
|
|
|
|
internal static void MarkdownConvertLinksForHugo(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
|
|
{
|
|
Record record = GetRecord(args);
|
|
if (string.IsNullOrEmpty(record.Destination))
|
|
throw new NotSupportedException("This method requires frontMatterYamlLines -d path!");
|
|
List<(string, string, string[])> collection = GetWithLinksForHugo(appSettings, record);
|
|
if (!collection.Any())
|
|
logger.LogInformation("No files?");
|
|
List<string> distinct = GetDistinct(collection);
|
|
CreateMissingDirectories(distinct);
|
|
foreach ((_, string file, string[] lines) in collection)
|
|
File.WriteAllLines(file, lines);
|
|
}
|
|
|
|
} |