Skip to content

Commit fb30f7a

Browse files
committed
Escape line-start list markers in HTML markdown
1 parent bb7839c commit fb30f7a

File tree

2 files changed

+17
-0
lines changed

2 files changed

+17
-0
lines changed

Packages/Models/Sources/Models/Alias/HTMLString.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
279279
var txt = node.description
280280

281281
txt = (try? Entities.unescape(txt)) ?? txt
282+
let isLineStart = asMarkdown.isEmpty || asMarkdown.hasSuffix("\n")
282283

283284
if let underscore_regex, let main_regex {
284285
// This is the markdown escaper
@@ -289,6 +290,11 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
289290
in: txt, options: [], range: NSRange(location: 0, length: txt.count),
290291
withTemplate: "\\\\$1")
291292
}
293+
if isLineStart,
294+
txt.hasPrefix("- ") || txt.hasPrefix("* ") || txt.hasPrefix("+ ")
295+
{
296+
txt = "\\" + txt
297+
}
292298
// Strip newlines and line separators - they should be being sent as <br>s
293299
asMarkdown += txt.replacingOccurrences(of: "\n", with: "").replacingOccurrences(
294300
of: "\u{2028}", with: "")

Packages/Models/Tests/ModelsTests/HTMLStringTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ func testHTMLStringInit_markdownEscaping() throws {
9898
#expect("This \\~is\\~ a \\`test\\`" == htmlString.asMarkdown)
9999
}
100100

101+
@Test
102+
func testHTMLStringInit_lineStartListMarkers() throws {
103+
let decoder = JSONDecoder()
104+
105+
let listLikeContent = "\"<p>2025 year review:<br />- 11 accepted and merged pull requests</p>\""
106+
let htmlString = try decoder.decode(HTMLString.self, from: Data(listLikeContent.utf8))
107+
#expect("2025 year review:\n- 11 accepted and merged pull requests" == htmlString.asRawText)
108+
#expect(
109+
"2025 year review:\n\\- 11 accepted and merged pull requests" == htmlString.asMarkdown)
110+
}
111+
101112
@Test
102113
func testHTMLStringInit_quoteInlineRemoval() throws {
103114
let decoder = JSONDecoder()

0 commit comments

Comments
 (0)