From 13215d05a4449b805ed5fe2ef4c62b71a50ccb8a Mon Sep 17 00:00:00 2001 From: Wesley Keetch Date: Wed, 2 Sep 2026 03:40:23 -0400 Subject: [PATCH] fix: keep the trailing "#" in headings like "# C#" parsedChecklistLine stripped heading markup with trimmingCharacters(in: CharacterSet(charactersIn: "# ")), which trims BOTH ends. Any heading ending in "#" silently lost it: "# C#" rendered as "C" "# F#" rendered as "F" "# C# vs F#" rendered as "C# vs F" DisplayLine lives in Shared/Sources, so this hit the editor and the widget on both platforms -- and "C#"/"F#" headings are ordinary content in a developer cheat sheet, which is what this app is for. Strip only the leading hashes instead, then trim whitespace. Adds two regression tests. Verified: 57 tests in 5 suites pass on macOS (was 55). The fixed parser was also compiled standalone against the four pre-existing heading/task/plain cases to confirm nothing outside the bug changed behaviour. Co-Authored-By: Claude Opus 5 --- CheatSheetTests/Sources/DisplayLineTests.swift | 16 ++++++++++++++++ Shared/Sources/DisplayLine.swift | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CheatSheetTests/Sources/DisplayLineTests.swift b/CheatSheetTests/Sources/DisplayLineTests.swift index d5ca934..7d9aaa3 100644 --- a/CheatSheetTests/Sources/DisplayLineTests.swift +++ b/CheatSheetTests/Sources/DisplayLineTests.swift @@ -10,6 +10,22 @@ struct DisplayLineTests { #expect(parsed.isComplete == false) } + @Test func headingKeepsTrailingHashInLanguageNames() { + // Regression: trimmingCharacters(in:) trimmed both ends, so "# C#" + // rendered as "C" in both the editor and the widget. + #expect("# C#".parsedChecklistLine.text == "C#") + #expect("# F#".parsedChecklistLine.text == "F#") + #expect("# C# vs F#".parsedChecklistLine.text == "C# vs F#") + } + + @Test func headingStripsOnlyLeadingHashes() { + let parsed = "## Objective-C".parsedChecklistLine + + #expect(parsed.text == "Objective-C") + #expect(parsed.isHeading) + #expect(parsed.isTask == false) + } + @Test func parsesCompleteTaskLine() { let parsed = "- [x] Run build".parsedChecklistLine diff --git a/Shared/Sources/DisplayLine.swift b/Shared/Sources/DisplayLine.swift index 2c2580f..75a8109 100644 --- a/Shared/Sources/DisplayLine.swift +++ b/Shared/Sources/DisplayLine.swift @@ -57,7 +57,10 @@ public extension String { if line.hasPrefix("#") { isHeading = true - line = line.trimmingCharacters(in: CharacterSet(charactersIn: "# ")) + // Strip only the LEADING hashes. trimmingCharacters(in:) works on both + // ends, which silently ate the trailing "#" of headings like "# C#" + // and "# F#" -- on a developer cheat sheet those are content. + line = String(line.drop(while: { $0 == "#" })).trimmingCharacters(in: .whitespaces) } let markers = ["- [x] ", "* [x] ", "[x] ", "- [X] ", "* [X] ", "[X] "]