Preserve the blank lines typed after a list

The newline to break replacement only matched paragraph to paragraph, so a blank
line after a list was dropped and the text following it glued to the last item.
Only a following paragraph is matched, because without a blank line its text
would have continued the last item instead of starting a paragraph of its own.
This commit is contained in:
Stefan Ceriu
2026-08-21 17:18:19 +03:00
parent 8ead25eaa3
commit 925e61e40c
3 changed files with 51 additions and 7 deletions
@@ -684,6 +684,41 @@ struct AttributedStringBuilderTests {
}
}
/// A blank line the user typed after a list must survive, just like one between two paragraphs.
@Test
func newLineAfterList() throws {
let htmlString = "<p>Line 1</p>\n<ul>\n<li>Line 2</li>\n</ul>\n<p>Line 4</p>\n"
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
#expect(String(attributedString.characters) == "Line 1\n • Line 2\n\nLine 4")
}
/// Without a blank line the trailing text is a lazy continuation of the item, so it stays on its line.
@Test
func noNewLineAfterList() throws {
let htmlString = "<ul><li>Line 1</li></ul><p>Line 2</p>"
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
#expect(String(attributedString.characters) == " • Line 1\nLine 2")
}
/// A blank line is preserved wherever it can be told apart from mere block separation.
@Test
func newLinesBetweenBlocks() throws {
let expectations = ["<p>a</p>\n<p>b</p>": "a\n\nb",
"<p>a</p>\n\n<p>b</p>": "a\n\n\nb",
"<ol start=\"2\"><li>a</li></ol>\n<p>b</p>": " 2. a\n\nb",
// A list can interrupt a paragraph, so this newline is block separation.
"<p>a</p>\n<ul><li>b</li></ul>": "a\n • b"]
for (htmlString, expectation) in expectations {
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
#expect(String(attributedString.characters) == expectation, "Wrong rendering for \(htmlString)")
}
}
@Test
func unorderedList() throws {
let htmlString = "<ul><li>1</li><li>2</li><li>3</li></ul>"