Download SSL Certificates Sort Subtasks of Markdown files Test BioRad EAF CopyDirectories json to Markdown Sort Day 2024 Q2 GitRemoteRemove Handle directoryInfo.LinkTarget better Remove StartAt Handle directoryInfo.LinkTarget
1249 lines
57 KiB
C#
1249 lines
57 KiB
C#
using File_Folder_Helper.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Text.RegularExpressions;
|
|
using YamlDotNet.Serialization;
|
|
|
|
namespace File_Folder_Helper.Helpers;
|
|
|
|
internal static partial class HelperMarkdown
|
|
{
|
|
|
|
private record Input(string? Destination,
|
|
string Source,
|
|
bool UseProcessStart);
|
|
|
|
private record Record(string Directory,
|
|
string File,
|
|
string[] Lines);
|
|
|
|
private record MarkdownFile(DateTime CreationDateTime,
|
|
string Directory,
|
|
string Extension,
|
|
string File,
|
|
string FileName,
|
|
string FileNameWithoutExtension,
|
|
ReadOnlyDictionary<string, object> FrontMatterYaml,
|
|
string H1,
|
|
bool IsGitOthersModifiedAndDeletedExcludingStandard,
|
|
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
|
|
{
|
|
}
|
|
|
|
[GeneratedRegex("(~~)?(#)([a-zA-Z0-9]{6})(~~)?( )")]
|
|
private static partial Regex HtmlColor();
|
|
|
|
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> 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[] GetFiles(AppSettings appSettings, string directory, SearchOption searchOption)
|
|
{
|
|
string[] results = Directory.GetFiles(directory, "*.md", searchOption).
|
|
Where(l => !appSettings.ExcludeDirectoryNames.Any(m => l.Contains(m))).ToArray();
|
|
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;
|
|
}
|
|
|
|
private static ReadOnlyCollection<string> GetFromMatterYamlLines(List<string> lines, LineNumber lineNumber)
|
|
{
|
|
List<string> results = [];
|
|
if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
|
|
{
|
|
for (int i = 1; i < lineNumber.FrontMatterYamlEnd.Value; i++)
|
|
results.Add(lines[i]);
|
|
}
|
|
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 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 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 ReadOnlyCollection<string> GetFiles(AppSettings appSettings, Input input)
|
|
{
|
|
List<string> results = [];
|
|
List<string> files = [];
|
|
string[] directories = Directory.GetDirectories(input.Source, "*", SearchOption.AllDirectories);
|
|
foreach (string directory in directories)
|
|
{
|
|
files.Clear();
|
|
files.AddRange(GetFiles(appSettings, directory, SearchOption.TopDirectoryOnly));
|
|
foreach (string file in files)
|
|
results.Add(file);
|
|
}
|
|
return new(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 Dictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> frontMatterYamlLines)
|
|
{
|
|
Dictionary<string, object> results = [];
|
|
string[] segments;
|
|
foreach (string line in frontMatterYamlLines.OrderBy(l => l))
|
|
{
|
|
segments = line.Split(": ");
|
|
if (segments.Length != 2)
|
|
{
|
|
results.Clear();
|
|
break;
|
|
}
|
|
if (segments[1] is "''" or "\"\"")
|
|
results.Add(segments[0], string.Empty);
|
|
else if (segments[1] == "[]")
|
|
results.Add(segments[0], Array.Empty<string>());
|
|
else
|
|
results.Add(segments[0], segments[1].Trim());
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static ReadOnlyDictionary<string, object> GetFromMatterYaml(List<string> lines, LineNumber lineNumber)
|
|
{
|
|
Dictionary<string, object> results = [];
|
|
#pragma warning disable IL3050
|
|
IDeserializer deserializer = new DeserializerBuilder().Build();
|
|
#pragma warning restore IL3050
|
|
ReadOnlyCollection<string> frontMatterYamlLines = GetFromMatterYamlLines(lines, lineNumber);
|
|
if (!frontMatterYamlLines.Any(l => l.StartsWith(' ')))
|
|
results = GetFromMatterYaml(frontMatterYamlLines);
|
|
else
|
|
{
|
|
string frontMatterYaml = string.Join(Environment.NewLine, frontMatterYamlLines);
|
|
Dictionary<string, object>? keyValuePairs = deserializer.Deserialize<Dictionary<string, object>>(frontMatterYaml);
|
|
if (keyValuePairs is null)
|
|
results = GetFromMatterYaml(frontMatterYamlLines);
|
|
else
|
|
{
|
|
foreach (string key in keyValuePairs.Keys.OrderBy(l => l))
|
|
results.Add(key, keyValuePairs[key]);
|
|
}
|
|
}
|
|
return new(results);
|
|
}
|
|
|
|
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)
|
|
{
|
|
int result = 0;
|
|
string h1;
|
|
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;
|
|
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 (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
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 (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
File.Move(markdownFile.File, checkName);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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 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? destination = null;
|
|
bool useProcessStart = false;
|
|
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] == 'u')
|
|
useProcessStart = args[i + 1] == "true";
|
|
else if (args[i][1] == 'd')
|
|
destination = Path.GetFullPath(args[i + 1]);
|
|
i++;
|
|
}
|
|
}
|
|
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, useProcessStart);
|
|
return result;
|
|
}
|
|
|
|
private static ReadOnlyDictionary<string, MarkdownFileAndLines> GetRelativeToCollection(AppSettings appSettings, Input input, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
|
|
{
|
|
Dictionary<string, MarkdownFileAndLines> results = [];
|
|
string h1;
|
|
string key;
|
|
string type;
|
|
FileInfo fileInfo;
|
|
bool isKanbanIndex;
|
|
List<string> lines;
|
|
bool isWithinSource;
|
|
bool isKanbanMarkdown;
|
|
LineNumber lineNumber;
|
|
MarkdownFile markdownFile;
|
|
string fileNameWithoutExtension;
|
|
ReadOnlyDictionary<string, object> frontMatterYaml;
|
|
bool isGitOthersModifiedAndDeletedExcludingStandard;
|
|
ReadOnlyCollection<string> files = GetFiles(appSettings, input);
|
|
foreach (string file in files)
|
|
{ // cSpell:disable
|
|
fileInfo = new(file);
|
|
if (fileInfo.DirectoryName is null)
|
|
continue;
|
|
key = Path.GetRelativePath(input.Source, file);
|
|
isWithinSource = file.Contains(input.Source);
|
|
isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(file);
|
|
if (!isWithinSource && results.ContainsKey(key))
|
|
continue;
|
|
(lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
|
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
|
|
h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
|
|
frontMatterYaml = GetFromMatterYaml(lines, lineNumber);
|
|
if (lines.Count > 0)
|
|
(type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber);
|
|
else
|
|
{
|
|
if (!isGitOthersModifiedAndDeletedExcludingStandard)
|
|
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,
|
|
frontMatterYaml,
|
|
h1,
|
|
isGitOthersModifiedAndDeletedExcludingStandard,
|
|
isKanbanIndex,
|
|
isKanbanMarkdown,
|
|
fileInfo.LastWriteTime,
|
|
lineNumber,
|
|
type);
|
|
results.Add(key, new(markdownFile, lines.ToArray()));
|
|
} // cSpell:restore
|
|
return new(results);
|
|
}
|
|
|
|
private static int SortFrontMatter(AppSettings appSettings, ILogger<Worker> logger, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
int result = 0;
|
|
List<string> results = [];
|
|
string[] lines;
|
|
string frontMatterYaml;
|
|
MarkdownFile markdownFile;
|
|
string[] frontMatterYamlLines;
|
|
#pragma warning disable IL3050
|
|
ISerializer serializer = new SerializerBuilder().WithIndentedSequences().Build();
|
|
#pragma warning restore IL3050
|
|
foreach (KeyValuePair<string, MarkdownFileAndLines> relativeTo in relativeToCollection)
|
|
{
|
|
results.Clear();
|
|
if (relativeTo.Value.Lines.Length < 2)
|
|
continue;
|
|
lines = relativeTo.Value.Lines;
|
|
markdownFile = relativeTo.Value.MarkdownFile;
|
|
if (markdownFile.IsKanbanMarkdown)
|
|
continue;
|
|
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
|
|
continue;
|
|
frontMatterYaml = serializer.Serialize(markdownFile.FrontMatterYaml).Trim();
|
|
frontMatterYamlLines = frontMatterYaml.Split(Environment.NewLine);
|
|
results.Add("---");
|
|
results.AddRange(frontMatterYamlLines);
|
|
for (int i = markdownFile.LineNumber.FrontMatterYamlEnd.Value; i < lines.Length; i++)
|
|
results.Add(lines[i]);
|
|
if (results.Count == lines.Length && string.Join('\r', lines) == string.Join('\r', results))
|
|
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)
|
|
{
|
|
int result = 0;
|
|
string line;
|
|
string check;
|
|
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;
|
|
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
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;
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int FindReplace(ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
int result = 0;
|
|
bool found;
|
|
string line;
|
|
string check;
|
|
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;
|
|
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
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;
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int ConvertToRelativePath(ILogger<Worker> logger, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
int result = 0;
|
|
bool write;
|
|
string line;
|
|
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;
|
|
if (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
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.FileName);
|
|
if (input.UseProcessStart && File.Exists(markdownFile.File))
|
|
_ = Process.Start(new ProcessStartInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "VSCodium", "VSCodium.exe"), markdownFile.File) { WorkingDirectory = input.Source });
|
|
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;
|
|
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int ConvertFileToSlugName(AppSettings appSettings, ILogger<Worker> logger, Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
int result = 0;
|
|
bool write;
|
|
string file;
|
|
string line;
|
|
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 (!input.UseProcessStart && !markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
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.FileName);
|
|
if (input.UseProcessStart && File.Exists(markdownFile.File))
|
|
_ = Process.Start(new ProcessStartInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "VSCodium", "VSCodium.exe"), markdownFile.File) { WorkingDirectory = input.Source });
|
|
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;
|
|
if (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
continue;
|
|
File.WriteAllLines(markdownFile.File, lines);
|
|
result += 1;
|
|
}
|
|
if (result == 0)
|
|
result = ConvertFileToSlugName(relativeToCollection);
|
|
return result;
|
|
}
|
|
|
|
private static void SaveColumnToCards(Input input, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
if (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, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles, ReadOnlyDictionary<string, MarkdownFileAndLines> relativeToCollection)
|
|
{
|
|
int result = 0;
|
|
List<string> results = [];
|
|
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;
|
|
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 (markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
{
|
|
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.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
{
|
|
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 (!markdownFile.IsGitOthersModifiedAndDeletedExcludingStandard)
|
|
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)));
|
|
}
|
|
|
|
private static List<string> GetTaskArgumentsForDayHelper20240623(Input input)
|
|
{
|
|
if (string.IsNullOrEmpty(input.Destination))
|
|
throw new NotSupportedException();
|
|
List<string> results = [];
|
|
string? vscodeDirectory = Path.GetDirectoryName(input.Destination);
|
|
string? taskFile = vscodeDirectory is null ? null : Path.Combine(vscodeDirectory, "tasks.json");
|
|
if (!string.IsNullOrEmpty(taskFile) && File.Exists(taskFile))
|
|
{
|
|
string json = File.ReadAllText(taskFile);
|
|
VSCodeTasks? vsCodeTasks = JsonSerializer.Deserialize(json, VSCodeTasksSourceGenerationContext.Default.VSCodeTasks);
|
|
if (vsCodeTasks is not null && vsCodeTasks.TaskCollection is not null)
|
|
{
|
|
foreach (VSCodeTask vsCodeTask in vsCodeTasks.TaskCollection)
|
|
{
|
|
if (vsCodeTask.Arguments is null || vsCodeTask.Arguments.Count < 4 || vsCodeTask.Arguments[3] != "Day-Helper-2024-06-23")
|
|
continue;
|
|
if (results.Count > 0)
|
|
{
|
|
results.Clear();
|
|
break;
|
|
}
|
|
results.AddRange(vsCodeTask.Arguments.Skip(2));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
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, gitOthersModifiedAndDeletedExcludingStandardFiles, relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = SortFrontMatter(appSettings, logger, input, relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = CircularReference(logger, relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = FindReplace(relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = ConvertToRelativePath(logger, input, relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
updated = ConvertFileToSlugName(appSettings, logger, input, relativeToCollection);
|
|
if (updated != 0)
|
|
{
|
|
relativeToCollection = GetRelativeToCollection(appSettings, input, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
|
logger.LogInformation("{updated} Markdown file(s) were updated", updated);
|
|
}
|
|
if (!string.IsNullOrEmpty(input.Destination))
|
|
{
|
|
SaveColumnToCards(input, relativeToCollection);
|
|
List<string> taskArgumentsForDayHelper20240623 = GetTaskArgumentsForDayHelper20240623(input);
|
|
if (taskArgumentsForDayHelper20240623.Count > 0)
|
|
HelperDay.Select(appSettings, logger, taskArgumentsForDayHelper20240623, cancellationToken: CancellationToken.None);
|
|
}
|
|
}
|
|
|
|
} |