diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift index efc436e97..d6b49e89a 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift @@ -31,6 +31,7 @@ extension NSAttributedString.Key { static let MatrixEventOnRoomID: NSAttributedString.Key = .init(rawValue: EventOnRoomIDAttribute.name) static let MatrixEventOnRoomAlias: NSAttributedString.Key = .init(rawValue: EventOnRoomAliasAttribute.name) static let MatrixAllUsersMention: NSAttributedString.Key = .init(rawValue: AllUsersMentionAttribute.name) + static let CodeBlock: NSAttributedString.Key = .init(rawValue: CodeBlockAttribute.name) } struct AttributedStringBuilder: AttributedStringBuilderProtocol { diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderV2.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderV2.swift index 0a4f93b59..853aa9b2a 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderV2.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilderV2.swift @@ -85,6 +85,7 @@ struct AttributedStringBuilderV2: AttributedStringBuilderProtocol { // MARK: - Private + // swiftlint:disable:next function_body_length func attributedString(from element: Element, preserveFormatting: Bool, listTag: String?, listIndex: inout Int, @@ -162,15 +163,17 @@ struct AttributedStringBuilderV2: AttributedStringBuilderProtocol { let preserveFormatting = preserveFormatting || tag == "pre" content = attributedString(from: childElement, preserveFormatting: preserveFormatting, listTag: listTag, listIndex: &childIndex, indentLevel: indentLevel) content.setFontPreservingSymbolicTraits(UIFont.monospacedSystemFont(ofSize: UIFont.systemFontSize, weight: .regular)) + content.addAttribute(.CodeBlock, value: true, range: NSRange(location: 0, length: content.length)) content.addAttribute(.backgroundColor, value: UIColor.compound._bgCodeBlock as Any, range: NSRange(location: 0, length: content.length)) - // Don't allow identifiers in code blocks + // Don't allow identifiers or links in code blocks content.removeAttribute(.MatrixRoomID, range: NSRange(location: 0, length: content.length)) content.removeAttribute(.MatrixRoomAlias, range: NSRange(location: 0, length: content.length)) content.removeAttribute(.MatrixUserID, range: NSRange(location: 0, length: content.length)) content.removeAttribute(.MatrixEventOnRoomID, range: NSRange(location: 0, length: content.length)) content.removeAttribute(.MatrixEventOnRoomAlias, range: NSRange(location: 0, length: content.length)) content.removeAttribute(.MatrixAllUsersMention, range: NSRange(location: 0, length: content.length)) + content.removeAttribute(.link, range: NSRange(location: 0, length: content.length)) case "hr": content = NSMutableAttributedString(string: "\n") @@ -295,9 +298,15 @@ struct AttributedStringBuilderV2: AttributedStringBuilderProtocol { // Sort the links by length so the longest one always takes priority matches.sorted { $0.range.length > $1.range.length }.forEach { [attributedString] match in + // Don't highlight links within codeblocks + let isInCodeBlock = attributedString.attribute(.CodeBlock, at: match.range.location, effectiveRange: nil) != nil + if isInCodeBlock { + return + } + var hasLink = false attributedString.enumerateAttribute(.link, in: match.range, options: []) { value, _, stop in - if value != nil { + if value != nil, !isInCodeBlock { hasLink = true stop.pointee = true } diff --git a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift index a4943c96c..02d76c0bd 100644 --- a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift +++ b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift @@ -62,6 +62,11 @@ enum AllUsersMentionAttribute: AttributedStringKey { static let name = "MXAllUsersMentionAttribute" } +enum CodeBlockAttribute: AttributedStringKey { + typealias Value = Bool + static let name = "MXCodeBlockAttribute" +} + // periphery: ignore - required to make NSAttributedString to AttributedString conversion even if not used directly extension AttributeScopes { struct ElementXAttributes: AttributeScope { @@ -77,6 +82,8 @@ extension AttributeScopes { let allUsersMention: AllUsersMentionAttribute + let codeBlock: CodeBlockAttribute + let swiftUI: SwiftUIAttributes let uiKit: UIKitAttributes } diff --git a/ElementX/Sources/Other/HTMLParsing/HTMLFixtures.swift b/ElementX/Sources/Other/HTMLParsing/HTMLFixtures.swift index d79e20207..277b6ee15 100644 --- a/ElementX/Sources/Other/HTMLParsing/HTMLFixtures.swift +++ b/ElementX/Sources/Other/HTMLParsing/HTMLFixtures.swift @@ -89,7 +89,8 @@ enum HTMLFixtures: String, CaseIterable { Followed by some plain code blocks
Hello, world! Hello, world! - Hello, world! + This link should not be interpreted as such + And this https://www.matrix.org should be not highlighted """ case .unorderedList: """ diff --git a/UnitTests/Sources/AttributedStringBuilderTests.swift b/UnitTests/Sources/AttributedStringBuilderTests.swift index 20034d8cc..e09637772 100644 --- a/UnitTests/Sources/AttributedStringBuilderTests.swift +++ b/UnitTests/Sources/AttributedStringBuilderTests.swift @@ -590,10 +590,6 @@ class AttributedStringBuilderV1Tests: XCTestCase { } func testURLsAreIgnoredInCode() { - if AttributedStringBuilder.useNextGenHTMLParser { - return - } - var htmlString = "
test https://matrix.org test
" var attributedStringFromHTML = attributedStringBuilder.fromHTML(htmlString) XCTAssert(attributedStringFromHTML?.runs.count == 1) @@ -606,10 +602,6 @@ class AttributedStringBuilderV1Tests: XCTestCase { } func testHyperlinksAreIgnoredInCode() { - if AttributedStringBuilder.useNextGenHTMLParser { - return - } - let htmlString = "
test matrix test
" let attributedStringFromHTML = attributedStringBuilder.fromHTML(htmlString) XCTAssertNil(attributedStringFromHTML?.link) @@ -619,11 +611,7 @@ class AttributedStringBuilderV1Tests: XCTestCase { let htmlString = "
test https://matrix.org/#/@test:matrix.org test
" let attributedString = attributedStringBuilder.fromHTML(htmlString) - if AttributedStringBuilder.useNextGenHTMLParser { - XCTAssert(attributedString?.runs.count == 3) - } else { - XCTAssert(attributedString?.runs.count == 1) - } + XCTAssert(attributedString?.runs.count == 1) XCTAssertNil(attributedString?.attachment) } @@ -632,11 +620,7 @@ class AttributedStringBuilderV1Tests: XCTestCase { let htmlString = "
Hey @some.user.ceriu:matrix.org
" let attributedString = attributedStringBuilder.fromHTML(htmlString) - if AttributedStringBuilder.useNextGenHTMLParser { - XCTAssert(attributedString?.runs.count == 2) - } else { - XCTAssert(attributedString?.runs.count == 1) - } + XCTAssert(attributedString?.runs.count == 1) XCTAssertNil(attributedString?.attachment) } @@ -645,11 +629,7 @@ class AttributedStringBuilderV1Tests: XCTestCase { let htmlString = "
test @room test
" let attributedString = attributedStringBuilder.fromHTML(htmlString) - if AttributedStringBuilder.useNextGenHTMLParser { - XCTAssert(attributedString?.runs.count == 3) - } else { - XCTAssert(attributedString?.runs.count == 1) - } + XCTAssert(attributedString?.runs.count == 1) XCTAssertNil(attributedString?.attachment) }