From 0af60c4b1eac2e0163e3288cbb9138ca2dc8512a Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sun, 5 Feb 2023 21:23:47 +0100 Subject: [PATCH] Faster HTML String disk cache --- .../Sources/Models/Alias/HTMLString.swift | 82 ++++++++++++------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/Packages/Models/Sources/Models/Alias/HTMLString.swift b/Packages/Models/Sources/Models/Alias/HTMLString.swift index be3c5f91..eba0f350 100644 --- a/Packages/Models/Sources/Models/Alias/HTMLString.swift +++ b/Packages/Models/Sources/Models/Alias/HTMLString.swift @@ -2,53 +2,70 @@ import Foundation import SwiftSoup import SwiftUI +fileprivate enum CodingKeys: CodingKey { + case htmlValue, asMarkdown, asRawText, statusesURLs +} + public struct HTMLString: Codable, Equatable, Hashable { public var htmlValue: String = "" public var asMarkdown: String = "" public var asRawText: String = "" public var statusesURLs = [URL]() + public var asSafeMarkdownAttributedString: AttributedString = .init() private var regex: NSRegularExpression? - + public init(from decoder: Decoder) { + var alreadyDecoded: Bool = false do { let container = try decoder.singleValueContainer() htmlValue = try container.decode(String.self) } catch { - htmlValue = "" + do { + alreadyDecoded = true + let container = try decoder.container(keyedBy: CodingKeys.self) + htmlValue = try container.decode(String.self, forKey: .htmlValue) + asMarkdown = try container.decode(String.self, forKey: .asMarkdown) + asRawText = try container.decode(String.self, forKey: .asRawText) + statusesURLs = try container.decode([URL].self, forKey: .statusesURLs) + } catch { + htmlValue = "" + } } - // https://daringfireball.net/projects/markdown/syntax - // Pre-escape \ ` _ * and [ as these are the only - // characters the markdown parser used picks up - // when it renders to attributed text - regex = try? NSRegularExpression(pattern: "([\\_\\*\\`\\[\\\\])", options: .caseInsensitive) + if !alreadyDecoded { + // https://daringfireball.net/projects/markdown/syntax + // Pre-escape \ ` _ * and [ as these are the only + // characters the markdown parser used picks up + // when it renders to attributed text + regex = try? NSRegularExpression(pattern: "([\\_\\*\\`\\[\\\\])", options: .caseInsensitive) - asMarkdown = "" - do { - let document: Document = try SwiftSoup.parse(htmlValue) - handleNode(node: document) + asMarkdown = "" + do { + let document: Document = try SwiftSoup.parse(htmlValue) + handleNode(node: document) - document.outputSettings(OutputSettings().prettyPrint(pretty: false)) - try document.select("br").after("\n") - try document.select("p").after("\n\n") - let html = try document.html() - var text = try SwiftSoup.clean(html, "", Whitelist.none(), OutputSettings().prettyPrint(pretty: false)) ?? "" - // Remove the two last line break added after the last paragraph. - if text.hasSuffix("\n\n") { - _ = text.removeLast() - _ = text.removeLast() + document.outputSettings(OutputSettings().prettyPrint(pretty: false)) + try document.select("br").after("\n") + try document.select("p").after("\n\n") + let html = try document.html() + var text = try SwiftSoup.clean(html, "", Whitelist.none(), OutputSettings().prettyPrint(pretty: false)) ?? "" + // Remove the two last line break added after the last paragraph. + if text.hasSuffix("\n\n") { + _ = text.removeLast() + _ = text.removeLast() + } + asRawText = text + + if asMarkdown.hasPrefix("\n") { + _ = asMarkdown.removeFirst() + } + + } catch { + asRawText = htmlValue } - asRawText = text - - if asMarkdown.hasPrefix("\n") { - _ = asMarkdown.removeFirst() - } - - } catch { - asRawText = htmlValue } - + do { let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true, interpretedSyntax: .inlineOnlyPreservingWhitespace) @@ -67,8 +84,11 @@ public struct HTMLString: Codable, Equatable, Hashable { } public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(htmlValue) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(htmlValue, forKey: .htmlValue) + try container.encode(asMarkdown, forKey: .asMarkdown) + try container.encode(asRawText, forKey: .asRawText) + try container.encode(statusesURLs, forKey: .statusesURLs) } private mutating func handleNode(node: SwiftSoup.Node) {