From 7e624c0a4a19baefbcb4d03677158765aeca7103 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 4 Feb 2024 15:09:08 -0500 Subject: [PATCH] Fix .skip action skipping too much if a run was in-progress when the skip element was encountered --- .../HTMLStreamer/AttributedStringConverter.swift | 3 +++ Sources/HTMLStreamer/TextConverter.swift | 3 +++ .../AttributedStringConverterTests.swift | 16 ++++++++++++++++ Tests/HTMLStreamerTests/TextConverterTests.swift | 9 +++++++++ 4 files changed, 31 insertions(+) diff --git a/Sources/HTMLStreamer/AttributedStringConverter.swift b/Sources/HTMLStreamer/AttributedStringConverter.swift index 4f8d193..7dd826f 100644 --- a/Sources/HTMLStreamer/AttributedStringConverter.swift +++ b/Sources/HTMLStreamer/AttributedStringConverter.swift @@ -67,6 +67,9 @@ public struct AttributedStringConverter { case .startTag(let name, let selfClosing, let attributes): currentElementIsEmpty = true let action = Callbacks.elementAction(name: name, attributes: attributes) + if action != .default { + finishRun() + } actionStack.append(action) handleStartTag(name, selfClosing: selfClosing, attributes: attributes) case .endTag(let name): diff --git a/Sources/HTMLStreamer/TextConverter.swift b/Sources/HTMLStreamer/TextConverter.swift index 78d398b..b302582 100644 --- a/Sources/HTMLStreamer/TextConverter.swift +++ b/Sources/HTMLStreamer/TextConverter.swift @@ -48,6 +48,9 @@ public struct TextConverter { case .startTag(let name, let selfClosing, let attributes): currentElementIsEmpty = true let action = Callbacks.elementAction(name: name, attributes: attributes) + if action != .default { + finishRun() + } actionStack.append(action) handleStartTag(name, selfClosing: selfClosing, attributes: attributes) case .endTag(let name): diff --git a/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift b/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift index 8a8f5b7..e574a31 100644 --- a/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift +++ b/Tests/HTMLStreamerTests/AttributedStringConverterTests.swift @@ -336,4 +336,20 @@ final class AttributedStringConverterTests: XCTestCase { XCTAssertEqual(convert("a
  1. b
  2. c
"), result) } + func testSkipElementActionFollowingUnfinishedRun() { + struct Callbacks: HTMLConversionCallbacks { + static func elementAction(name: String, attributes: [Attribute]) -> ElementAction { + attributes.attributeValue(for: "class") == "invisible" ? .skip : .default + } + } + let result = NSMutableAttributedString() + result.append(NSAttributedString(string: "example.com", attributes: [ + .font: font, + .paragraphStyle: NSParagraphStyle.default, + .foregroundColor: color, + .link: URL(string: "https://example.com")!, + ])) + XCTAssertEqual(convert(#"example.com"#, callbacks: Callbacks.self), result) + } + } diff --git a/Tests/HTMLStreamerTests/TextConverterTests.swift b/Tests/HTMLStreamerTests/TextConverterTests.swift index 482bd44..09858f9 100644 --- a/Tests/HTMLStreamerTests/TextConverterTests.swift +++ b/Tests/HTMLStreamerTests/TextConverterTests.swift @@ -74,4 +74,13 @@ final class TextConverterTests: XCTestCase { XCTAssertEqual(convert("

inside
quote
after

"), "inside\nquote\n\nafter") } + func testSkipElementActionFollowingUnfinishedRun() { + struct Callbacks: HTMLConversionCallbacks { + static func elementAction(name: String, attributes: [Attribute]) -> ElementAction { + attributes.attributeValue(for: "class") == "invisible" ? .skip : .default + } + } + XCTAssertEqual(convert(#"example.com"#, callbacks: Callbacks.self), "example.com") + } + }