From a091c0ff22c5cdbff759804db7078310d9059103 Mon Sep 17 00:00:00 2001 From: Harry Li Date: Fri, 20 Sep 2024 13:17:13 +0800 Subject: [PATCH] fix: breakline and space bug --- .../MarkupNSAttributedStringVisitor.swift | 78 ++++++++++++------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift b/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift index 04b341a..1c6991c 100644 --- a/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift +++ b/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift @@ -22,7 +22,7 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { func visit(_ markup: BreakLineMarkup) -> Result { let style = collectMarkupStyle(markup) - return makeString(in: markup, string: Self.breakLineSymbol, attributes: [.breaklinePlaceholder: NSAttributedString.Key.BreaklinePlaceholder.breaklineTag], style: style) + return makeString(in: markup, string: Self.breakLineSymbol, attributes: [.breaklinePlaceholder: BreaklineTag()], style: style) } func visit(_ markup: RawStringMarkup) -> Result { @@ -119,7 +119,16 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { func visit(_ markup: ParagraphMarkup) -> Result { let attributedString = collectAttributedString(markup) - let thisAttributedString = NSMutableAttributedString(attributedString: attributedString) + var thisAttributedString = NSMutableAttributedString(attributedString: attributedString) + + // This code replicates browser behavior by trimming leading and trailing whitespace around HTML tags. + // It removes unnecessary spaces and newlines at the beginning and end of the NSAttributedString. + // If the entire content is just whitespace, it replaces it with an empty string. + + if thisAttributedString.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + thisAttributedString = NSMutableAttributedString(string: "") + } + thisAttributedString.markPrefixTagBoundaryBreakline() thisAttributedString.markSuffixTagBoundaryBreakline() @@ -221,35 +230,37 @@ extension MarkupNSAttributedStringVisitor { let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString) let totalLength = mutableAttributedString.string.utf16.count + var rangesToDelete: [NSRange] = [] + // merge tag Boundary Breakline, e.g.

-> /n/n -> /n - var pre: (NSRange, NSAttributedString.Key.BreaklinePlaceholder)? + var pre: (NSRange, any BreaklinePlaceholder)? mutableAttributedString.enumerateAttribute(.breaklinePlaceholder, in: NSMakeRange(0, totalLength)) { value, range, _ in - if let breaklinePlaceholder = value as? NSAttributedString.Key.BreaklinePlaceholder { + if let breaklinePlaceholder = value as? any BreaklinePlaceholder { if range.location == 0 { - mutableAttributedString.deleteCharacters(in: range) + rangesToDelete.append(range) } else if let pre = pre { let preRange = pre.0 let preBreaklinePlaceholder = pre.1 switch (preBreaklinePlaceholder, breaklinePlaceholder) { - case (.breaklineTag, .tagBoundarySuffix): + case (is BreaklineTag, is TagBoundarySuffix): //
-> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) - case (.breaklineTag, .tagBoundaryPrefix): + rangesToDelete.append(preRange) + case (is BreaklineTag, is TagBoundaryPrefix): //

-> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) - case (.tagBoundarySuffix, .tagBoundarySuffix): + rangesToDelete.append(preRange) + case (is TagBoundarySuffix, is TagBoundarySuffix): // -> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) - case (.tagBoundarySuffix, .tagBoundaryPrefix): + rangesToDelete.append(preRange) + case (is TagBoundarySuffix, is TagBoundaryPrefix): //

-> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) - case (.tagBoundaryPrefix, .tagBoundaryPrefix): + rangesToDelete.append(preRange) + case (is TagBoundaryPrefix, is TagBoundaryPrefix): //

-> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) - case (.tagBoundaryPrefix, .tagBoundarySuffix): + rangesToDelete.append(preRange) + case (is TagBoundaryPrefix, is TagBoundarySuffix): //

-> /n/n -> /n - mutableAttributedString.deleteCharacters(in: preRange) + rangesToDelete.append(preRange) default: break } @@ -260,6 +271,11 @@ extension MarkupNSAttributedStringVisitor { } } + // Delete ranges in reverse order to avoid range shifting issues + for range in rangesToDelete.reversed() { + mutableAttributedString.deleteCharacters(in: range) + } + return mutableAttributedString } @@ -338,23 +354,33 @@ private extension MarkupNSAttributedStringVisitor { } } +private protocol BreaklinePlaceholder: Hashable { + +} + +private class TagBoundaryPrefix: NSObject, BreaklinePlaceholder{ + +} + +private class TagBoundarySuffix: NSObject, BreaklinePlaceholder{ + +} + +private class BreaklineTag: NSObject, BreaklinePlaceholder{ + +} + + private extension NSAttributedString.Key { static let breaklinePlaceholder: NSAttributedString.Key = .init("breaklinePlaceholder") - struct BreaklinePlaceholder: OptionSet { - let rawValue: Int - - static let tagBoundaryPrefix = BreaklinePlaceholder(rawValue: 1) - static let tagBoundarySuffix = BreaklinePlaceholder(rawValue: 2) - static let breaklineTag = BreaklinePlaceholder(rawValue: 3) - } } private extension NSMutableAttributedString { func markPrefixTagBoundaryBreakline() { - self.insert(NSAttributedString(string: MarkupNSAttributedStringVisitor.breakLineSymbol, attributes: [.breaklinePlaceholder: NSAttributedString.Key.BreaklinePlaceholder.tagBoundaryPrefix]), at: 0) + self.insert(NSAttributedString(string: MarkupNSAttributedStringVisitor.breakLineSymbol, attributes: [.breaklinePlaceholder: TagBoundaryPrefix()]), at: 0) } func markSuffixTagBoundaryBreakline() { - self.append(NSAttributedString(string: MarkupNSAttributedStringVisitor.breakLineSymbol, attributes: [.breaklinePlaceholder: NSAttributedString.Key.BreaklinePlaceholder.tagBoundarySuffix])) + self.append(NSAttributedString(string: MarkupNSAttributedStringVisitor.breakLineSymbol, attributes: [.breaklinePlaceholder: TagBoundarySuffix()])) } }