Skip to content

Commit

Permalink
Added helper function to enumerate over continuous ranges of an attri…
Browse files Browse the repository at this point in the history
…bute ignoring difference in value
  • Loading branch information
rajdeep committed Jul 19, 2024
1 parent 21a098d commit 23b8a47
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
34 changes: 34 additions & 0 deletions Proton/Sources/Swift/Helpers/NSAttributedString+Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,40 @@ public extension NSAttributedString {
}
return string.makeNSRange(from: range)
}

/// Enumerates over continuous ranges of text based on the presence or absence of a specified attribute.
/// - Parameters:
/// - attributeName: The name of the attribute to check for presence or absence.
/// - range: The range within the attributed string to enumerate. Defaults to the entire string.
/// - using: The block to apply to continuous ranges, indicating whether the attribute was present or absent for the range.
func enumerateContinuousRangesByAttribute(_ attributeName: NSAttributedString.Key, in range: NSRange? = nil, using block: (_ isPresent: Bool, _ range: NSRange) -> Void) {
let enumerationRange = range ?? NSRange(location: 0, length: self.length)
var lastRange: NSRange? = nil
var attributeWasPresentInLastRange = false

self.enumerateAttributes(in: enumerationRange, options: []) { attributes, currentRange, _ in
let attributeIsPresent = attributes[attributeName] != nil
if let lastRangeUnwrapped = lastRange {
if attributeWasPresentInLastRange != attributeIsPresent {
// Process the last range if the attribute presence state changes
block(attributeWasPresentInLastRange, lastRangeUnwrapped)
lastRange = currentRange
} else {
// Extend the last range efficiently if the state hasn't changed
lastRange = NSRange(location: lastRangeUnwrapped.location, length: NSMaxRange(currentRange) - lastRangeUnwrapped.location)
}
} else {
// Initialize with the first range
lastRange = currentRange
}
attributeWasPresentInLastRange = attributeIsPresent
}

// Process the final range after enumeration
if let lastRangeUnwrapped = lastRange {
block(attributeWasPresentInLastRange, lastRangeUnwrapped)
}
}
}

extension NSAttributedString {
Expand Down
12 changes: 12 additions & 0 deletions Proton/Tests/ExtensionTests/NSAttributedStringExtensionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,16 @@ class NSAttributedStringExtensionTests: XCTestCase {
let range = text.reverseRange(of: "isx", startingLocation: text.length - 1)
XCTAssertNil(range)
}

func testEnumeratesIgnoringValue() {
let text = NSMutableAttributedString(string: "This is a test string", attributes: [.inlineContentType: "a"])
text.append(NSAttributedString(string: " Some more text", attributes: [.inlineContentType: "b"]))
text.append(NSAttributedString(string: " And more text", attributes: [.inlineContentType: "c"]))
text.append(NSAttributedString(string: "Not with attribute", attributes: [:]))
text.append(NSAttributedString(string: "Again with attribute", attributes: [.inlineContentType: "c"]))

text.enumerateContinuousRangesByAttribute(.inlineContentType) { isPresent, range in
print("\(text.substring(from: range)) - \(isPresent)")
}
}
}

0 comments on commit 23b8a47

Please sign in to comment.