From 6e8064c053c017096efc7e3d46e717977e419884 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 02:24:34 +0000 Subject: [PATCH 1/2] Trim blank lines after the header in CR-only documents [patch] TrimBody only treated '\n' as a line end, so a CR-only document kept its blank lines after the header: ExtractBody returned "\r\rBody" where LF and CRLF give "Body", and RemoveFrontmatter and ReplaceFrontmatter carried the same stray lines into their output. Treat '\r' as a line end as well; CRLF still lands after its '\n'. Fixes #184 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ho7QngpAmRRAxjmT2qp1sn --- .gitignore | 18 +++++++++++++++ Frontmatter.Test/LineEndingTests.cs | 35 +++++++++++++++++++++++++++++ Frontmatter/Frontmatter.cs | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dc0470a..e043c9f 100644 --- a/.gitignore +++ b/.gitignore @@ -203,6 +203,11 @@ PublishScripts/ **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ +# and except a Unity project's Packages/, which is source: Unity's package manifest and its +# resolved lock file are both meant to be committed, and a NuGet restore folder never contains +# a file by either name. +!**/[Pp]ackages/manifest.json +!**/[Pp]ackages/packages-lock.json # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files @@ -651,3 +656,16 @@ Temporary Items # ImGui.ini files imgui.ini + +# Game engine projects +# +# Godot: the import cache, and the mono/temp bin+obj a C# build writes. +.godot/ + +# Unity: .meta files are source, not the Visual Studio C++ build artifact that the `*.meta` rule +# further up targets. Unity generates one per asset and it carries the GUID that scenes, prefabs +# and serialized references point at, so ignoring them gives every clone fresh GUIDs and silently +# breaks those references - including for a plug-in whose .dll is itself a build output. This +# negation has to come after that rule to win, and is scoped to the asset tree so the Visual +# Studio artifact stays ignored everywhere else. +!**/[Aa]ssets/**/*.meta diff --git a/Frontmatter.Test/LineEndingTests.cs b/Frontmatter.Test/LineEndingTests.cs index 8f6f77c..063287f 100644 --- a/Frontmatter.Test/LineEndingTests.cs +++ b/Frontmatter.Test/LineEndingTests.cs @@ -83,4 +83,39 @@ public void RemoveFrontmatter_KeepsTheInputLineEnding(string nl) Assert.AreEqual(Lines(nl, "line1", "line2"), result); } + + [TestMethod] + [DataRow("\r\n", DisplayName = "CRLF")] + [DataRow("\n", DisplayName = "LF")] + [DataRow("\r", DisplayName = "CR")] + public void ExtractBody_DropsBlankLinesAfterTheHeader_ForEveryLineEnding(string nl) + { + string input = Lines(nl, "---", "title: x", "---", string.Empty, string.Empty, "Body"); + + Assert.AreEqual("Body", Frontmatter.ExtractBody(input)); + } + + [TestMethod] + [DataRow("\r\n", DisplayName = "CRLF")] + [DataRow("\n", DisplayName = "LF")] + [DataRow("\r", DisplayName = "CR")] + public void RemoveFrontmatter_DropsBlankLinesAfterTheHeader_ForEveryLineEnding(string nl) + { + string input = Lines(nl, "---", "title: x", "---", string.Empty, string.Empty, "Body"); + + Assert.AreEqual(Lines(nl, "Body"), Frontmatter.RemoveFrontmatter(input)); + } + + [TestMethod] + [DataRow("\r\n", DisplayName = "CRLF")] + [DataRow("\n", DisplayName = "LF")] + [DataRow("\r", DisplayName = "CR")] + public void ReplaceFrontmatter_DropsBlankLinesAfterTheHeader_ForEveryLineEnding(string nl) + { + string input = Lines(nl, "---", "title: x", "---", string.Empty, string.Empty, "Body"); + + string result = Frontmatter.ReplaceFrontmatter(input, new Dictionary { ["title"] = "y" }); + + Assert.AreEqual(Lines(nl, "---", "title: y", "---", "Body"), result); + } } diff --git a/Frontmatter/Frontmatter.cs b/Frontmatter/Frontmatter.cs index 2f9b62a..df26925 100644 --- a/Frontmatter/Frontmatter.cs +++ b/Frontmatter/Frontmatter.cs @@ -524,7 +524,7 @@ private static string TrimBody(string body) for (int i = start; i < body.Length; i++) { char c = body[i]; - if (c == '\n') + if (c is '\n' or '\r') { start = i + 1; } From 02928f0ac00e818875e0ec1d3367070825f0e33d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 02:27:32 +0000 Subject: [PATCH 2/2] Leave .gitignore as it is on main The build's ktsu.Sdk sync rewrote it, and it was committed by mistake. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ho7QngpAmRRAxjmT2qp1sn --- .gitignore | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.gitignore b/.gitignore index e043c9f..dc0470a 100644 --- a/.gitignore +++ b/.gitignore @@ -203,11 +203,6 @@ PublishScripts/ **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ -# and except a Unity project's Packages/, which is source: Unity's package manifest and its -# resolved lock file are both meant to be committed, and a NuGet restore folder never contains -# a file by either name. -!**/[Pp]ackages/manifest.json -!**/[Pp]ackages/packages-lock.json # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files @@ -656,16 +651,3 @@ Temporary Items # ImGui.ini files imgui.ini - -# Game engine projects -# -# Godot: the import cache, and the mono/temp bin+obj a C# build writes. -.godot/ - -# Unity: .meta files are source, not the Visual Studio C++ build artifact that the `*.meta` rule -# further up targets. Unity generates one per asset and it carries the GUID that scenes, prefabs -# and serialized references point at, so ignoring them gives every clone fresh GUIDs and silently -# breaks those references - including for a plug-in whose .dll is itself a build output. This -# negation has to come after that rule to win, and is scoped to the asset tree so the Visual -# Studio artifact stays ignored everywhere else. -!**/[Aa]ssets/**/*.meta