@@ -31,22 +31,26 @@ public final class TextAttachmentManager {
31
31
layoutManager? . setNeedsLayout ( )
32
32
}
33
33
34
- public func remove( atOffset offset: Int ) {
34
+ /// Removes an attachment and invalidates layout for the removed range.
35
+ /// - Parameter offset: The offset the attachment begins at.
36
+ /// - Returns: The removed attachment, if it exists.
37
+ @discardableResult
38
+ public func remove( atOffset offset: Int ) -> AnyTextAttachment ? {
35
39
let index = findInsertionIndex ( for: offset)
36
40
37
41
guard index < orderedAttachments. count && orderedAttachments [ index] . range. location == offset else {
38
- assertionFailure ( " No attachment found at offset \( offset) " )
39
- return
42
+ return nil
40
43
}
41
44
42
45
let attachment = orderedAttachments. remove ( at: index)
43
46
layoutManager? . invalidateLayoutForRange ( attachment. range)
47
+ return attachment
44
48
}
45
49
46
50
/// Finds attachments starting in the given line range, and returns them as an array.
47
51
/// Returned attachment's ranges will be relative to the _document_, not the line.
48
52
/// - Complexity: `O(n log(n))`, ideally `O(log(n))`
49
- public func get ( startingIn range: NSRange ) -> [ AnyTextAttachment ] {
53
+ public func getAttachmentsStartingIn ( _ range: NSRange ) -> [ AnyTextAttachment ] {
50
54
var results : [ AnyTextAttachment ] = [ ]
51
55
var idx = findInsertionIndex ( for: range. location)
52
56
while idx < orderedAttachments. count {
@@ -69,11 +73,11 @@ public final class TextAttachmentManager {
69
73
70
74
/// Returns all attachments whose ranges overlap the given query range.
71
75
///
72
- /// - Parameter query : The `NSRange` to test for overlap.
76
+ /// - Parameter range : The `NSRange` to test for overlap.
73
77
/// - Returns: An array of `AnyTextAttachment` instances whose ranges intersect `query`.
74
- public func get ( overlapping query : NSRange ) -> [ AnyTextAttachment ] {
78
+ public func getAttachmentsOverlapping ( _ range : NSRange ) -> [ AnyTextAttachment ] {
75
79
// Find the first attachment whose end is beyond the start of the query.
76
- guard let startIdx = firstIndex ( where: { $0. range. upperBound > query . location } ) else {
80
+ guard let startIdx = firstIndex ( where: { $0. range. upperBound > range . location } ) else {
77
81
return [ ]
78
82
}
79
83
@@ -83,10 +87,10 @@ public final class TextAttachmentManager {
83
87
// Collect every subsequent attachment that truly overlaps the query.
84
88
while idx < orderedAttachments. count {
85
89
let attachment = orderedAttachments [ idx]
86
- if attachment. range. location >= query . upperBound {
90
+ if attachment. range. location >= range . upperBound {
87
91
break
88
92
}
89
- if NSIntersectionRange ( attachment. range, query ) . length > 0 ,
93
+ if attachment. range. intersection ( range ) ? . length ?? 0 > 0 ,
90
94
results. last? . range != attachment. range {
91
95
results. append ( attachment)
92
96
}
@@ -96,6 +100,11 @@ public final class TextAttachmentManager {
96
100
return results
97
101
}
98
102
103
+ /// Updates the text attachments to stay in the same relative spot after the edit, and removes any attachments that
104
+ /// were in the updated range.
105
+ /// - Parameters:
106
+ /// - atOffset: The offset text was updated at.
107
+ /// - delta: The change delta, positive is an insertion.
99
108
package func textUpdated( atOffset: Int , delta: Int ) {
100
109
for (idx, attachment) in orderedAttachments. enumerated ( ) . reversed ( ) {
101
110
if attachment. range. contains ( atOffset) {
0 commit comments