mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Fix indentation for various nested nodes and in particular lists
Fixes #5179. Best see the updated preview snapshots
This commit is contained in:
@@ -43,6 +43,8 @@ nonisolated struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
private let mentionBuilder: MentionBuilderProtocol
|
||||
|
||||
private static let attributeMSC4286 = "msc4286-external-payment-details"
|
||||
/// Tags whose content already ends in a newline.
|
||||
private static let lineTerminatingTags: Set = ["p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "ul", "ol", "li"]
|
||||
private static let caches = Mutex<[String: LRUCache<String, AttributedString>]>([:])
|
||||
|
||||
static func invalidateCaches() {
|
||||
@@ -119,6 +121,14 @@ nonisolated struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
|
||||
for node in element.getChildNodes() {
|
||||
if let textNode = node as? TextNode {
|
||||
// Markdown generated HTML separates block elements and list items with newlines.
|
||||
// SwiftSoup normalises those whitespace only nodes into stray spaces which misindent
|
||||
// the following line, whereas HTML rendering collapses them away entirely.
|
||||
if !preserveFormatting, textNode.isBlank(),
|
||||
Self.isLineTerminating(node.previousSibling()) || Self.isLineTerminating(node.nextSibling()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If this node is plain text append the whitespace normalised version
|
||||
if node.parent() == documentBody {
|
||||
result.append(NSAttributedString(string: textNode.text()))
|
||||
@@ -278,6 +288,14 @@ nonisolated struct AttributedStringBuilder: AttributedStringBuilderProtocol {
|
||||
return result
|
||||
}
|
||||
|
||||
private static func isLineTerminating(_ node: Node?) -> Bool {
|
||||
guard let element = node as? Element else {
|
||||
return false
|
||||
}
|
||||
|
||||
return lineTerminatingTags.contains(element.tagName().lowercased())
|
||||
}
|
||||
|
||||
private static func cacheValue(_ value: AttributedString?, forKey key: String, cacheKey: String) {
|
||||
caches.withLock { caches in
|
||||
if caches[cacheKey] == nil {
|
||||
|
||||
@@ -649,7 +649,28 @@ struct AttributedStringBuilderTests {
|
||||
|
||||
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
|
||||
|
||||
#expect(String(attributedString.characters) == "like\n • this\ntest")
|
||||
#expect(String(attributedString.characters) == "like\n • this\ntest")
|
||||
}
|
||||
|
||||
/// Markdown generated HTML separates block elements with newlines which used to normalise
|
||||
/// into stray spaces, indenting the first list item deeper than the ones that followed.
|
||||
@Test
|
||||
func interBlockWhitespace() throws {
|
||||
let htmlString = "<p>intro:</p>\n<ul>\n<li>first</li>\n<li>second</li>\n</ul>\n"
|
||||
|
||||
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
|
||||
|
||||
#expect(String(attributedString.characters) == "intro:\n • first\n • second")
|
||||
}
|
||||
|
||||
/// Whitespace between inline elements is meaningful, unlike the inter block variety.
|
||||
@Test
|
||||
func interInlineWhitespace() throws {
|
||||
let htmlString = "<del>one</del> <del>two</del>"
|
||||
|
||||
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
|
||||
|
||||
#expect(String(attributedString.characters) == "one two")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -694,7 +715,7 @@ struct AttributedStringBuilderTests {
|
||||
|
||||
let attributedString = try #require(attributedStringBuilder.fromHTML(htmlString), "Could not build the attributed string")
|
||||
|
||||
#expect(String(attributedString.characters) == " 2. this is a two")
|
||||
#expect(String(attributedString.characters) == " 2. this is a two")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -725,7 +746,7 @@ struct AttributedStringBuilderTests {
|
||||
"""
|
||||
let attributedString = try #require(attributedStringBuilder.fromHTML(html), "Could not build the attributed string")
|
||||
|
||||
#expect(String(attributedString.characters) == "Stefan pushed 2 commits to main:\n • Some update \n • Some other update")
|
||||
#expect(String(attributedString.characters) == "Stefan pushed 2 commits to main:\n • Some update \n • Some other update")
|
||||
}
|
||||
|
||||
// MARK: - Phishing prevention
|
||||
|
||||
Reference in New Issue
Block a user