From 395f5965bcf928f9f7b5d3cc25d59520f77c21fb Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 17 Dec 2023 18:54:26 -0500 Subject: [PATCH] Fix inline text/elements coming after block elements --- .../AttributedStringConverter.swift | 18 +++++++++++ .../AttributedStringConverterTests.swift | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Sources/HTMLStreamer/AttributedStringConverter.swift b/Sources/HTMLStreamer/AttributedStringConverter.swift index d2f3170..c4a326e 100644 --- a/Sources/HTMLStreamer/AttributedStringConverter.swift +++ b/Sources/HTMLStreamer/AttributedStringConverter.swift @@ -26,6 +26,7 @@ public struct AttributedStringConverter { private var actionStack: [ElementAction] = [] private var styleStack: [Style] = [] + private var previouslyFinishedBlockElement = false // The current run of text w/o styles changing private var currentRun: String = "" @@ -144,6 +145,7 @@ public struct AttributedStringConverter { private mutating func startBlockElement() { if str.length != 0 || !currentRun.isEmpty { + previouslyFinishedBlockElement = false currentRun.append("\n\n") } } @@ -170,15 +172,19 @@ public struct AttributedStringConverter { case "pre": finishRun() removeLastStyle(.monospace) + finishBlockElement() case "blockquote": finishRun() removeLastStyle(.blockquote) + finishBlockElement() case "ol": finishRun() removeLastStyle(.orderedList) + finishBlockElement() case "ul": finishRun() removeLastStyle(.unorderedList) + finishBlockElement() case "li": finishRun() default: @@ -186,6 +192,13 @@ public struct AttributedStringConverter { } } + private mutating func finishBlockElement() { + if str.length != 0 { + previouslyFinishedBlockElement = true +// currentRun.append("\n\n") + } + } + // Finds the last currently-open style of the given type. // We can't just use the last one because we need to handle mis-nested tags. private mutating func removeLastStyle(_ type: Style.StyleType) { @@ -234,6 +247,11 @@ public struct AttributedStringConverter { } else if case .replace(let replacement) = actionStack.first(where: \.isReplace) { currentRun = replacement } + + if previouslyFinishedBlockElement { + previouslyFinishedBlockElement = false + currentRun.insert(contentsOf: "\n\n", at: currentRun.startIndex) + } var attributes = [NSAttributedString.Key: Any]() var currentFontTraits: FontTrait = [] diff --git a/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift b/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift index f32c095..77f0030 100644 --- a/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift +++ b/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift @@ -170,6 +170,36 @@ final class AttributedStringConverterTests: XCTestCase { ])) } + func testTextAfterBlockquote() { + let result = NSMutableAttributedString() + result.append(NSAttributedString(string: "wee", attributes: [ + .font: italicFont, + .paragraphStyle: blockquoteParagraphStyle, + ])) + result.append(NSAttributedString(string: "\n\nafter", attributes: [ + .font: font, + .paragraphStyle: NSParagraphStyle.default, + ])) + XCTAssertEqual(convert("
wee
after"), result) + } + + func testMultipleBlockElements() { + let result = NSMutableAttributedString() + result.append(NSAttributedString(string: "a", attributes: [ + .font: italicFont, + .paragraphStyle: blockquoteParagraphStyle, + ])) + result.append(NSAttributedString(string: "\n\n", attributes: [ + .font: font, + .paragraphStyle: NSParagraphStyle.default, + ])) + result.append(NSAttributedString(string: "b", attributes: [ + .font: italicFont, + .paragraphStyle: blockquoteParagraphStyle, + ])) + XCTAssertEqual(convert("
a
b
"), result) + } + func testSelfClosing() { XCTAssertEqual(convert("asdf"), NSAttributedString(string: "asdf", attributes: [ .font: font,