@@ -13,15 +13,23 @@ extension TextView {
13
13
/// - Parameters:
14
14
/// - ranges: The ranges to replace
15
15
/// - string: The string to insert in the ranges.
16
- public func replaceCharacters( in ranges: [ NSRange ] , with string: String ) {
16
+ /// - skipUpdateSelection: Skips the selection update step
17
+ public func replaceCharacters(
18
+ in ranges: [ NSRange ] ,
19
+ with string: String ,
20
+ skipUpdateSelection: Bool = false
21
+ ) {
17
22
guard isEditable else { return }
18
23
NotificationCenter . default. post ( name: Self . textWillChangeNotification, object: self )
19
24
textStorage. beginEditing ( )
20
25
26
+ func valid( range: NSRange , string: String ) -> Bool {
27
+ ( !range. isEmpty || !string. isEmpty) &&
28
+ ( delegate? . textView ( self , shouldReplaceContentsIn: range, with: string) ?? true )
29
+ }
30
+
21
31
// Can't insert an empty string into an empty range. One must be not empty
22
- for range in ranges. sorted ( by: { $0. location > $1. location } ) where
23
- ( !range. isEmpty || !string. isEmpty) &&
24
- ( delegate? . textView ( self , shouldReplaceContentsIn: range, with: string) ?? true ) {
32
+ for range in ranges. sorted ( by: { $0. location > $1. location } ) where valid ( range: range, string: string) {
25
33
delegate? . textView ( self , willReplaceContentsIn: range, with: string)
26
34
27
35
_undoManager? . registerMutation (
@@ -31,13 +39,17 @@ extension TextView {
31
39
in: range,
32
40
with: NSAttributedString ( string: string, attributes: typingAttributes)
33
41
)
34
- selectionManager. didReplaceCharacters ( in: range, replacementLength: ( string as NSString ) . length)
42
+ if !skipUpdateSelection {
43
+ selectionManager. didReplaceCharacters ( in: range, replacementLength: ( string as NSString ) . length)
44
+ }
35
45
36
46
delegate? . textView ( self , didReplaceContentsIn: range, with: string)
37
47
}
38
48
39
49
textStorage. endEditing ( )
40
- selectionManager. notifyAfterEdit ( )
50
+ if !skipUpdateSelection {
51
+ selectionManager. notifyAfterEdit ( )
52
+ }
41
53
NotificationCenter . default. post ( name: Self . textDidChangeNotification, object: self )
42
54
43
55
// `scrollSelectionToVisible` is a little expensive to call every time. Instead we just check if the first
@@ -51,7 +63,33 @@ extension TextView {
51
63
/// - Parameters:
52
64
/// - range: The range to replace.
53
65
/// - string: The string to insert in the range.
54
- public func replaceCharacters( in range: NSRange , with string: String ) {
55
- replaceCharacters ( in: [ range] , with: string)
66
+ /// - skipUpdateSelection: Skips the selection update step
67
+ public func replaceCharacters(
68
+ in range: NSRange ,
69
+ with string: String ,
70
+ skipUpdateSelection: Bool = false
71
+ ) {
72
+ replaceCharacters ( in: [ range] , with: string, skipUpdateSelection: skipUpdateSelection)
73
+ }
74
+
75
+ /// Iterates over all text selections in the `TextView` and applies the provided callback.
76
+ ///
77
+ /// This method is typically used when you need to perform an operation on each text selection in the editor,
78
+ /// such as adjusting indentation, or other selection-based operations. The callback
79
+ /// is executed for each selection, and you can modify the selection or perform related tasks.
80
+ ///
81
+ /// - Parameters:
82
+ /// - callback: A closure that will be executed for each selection in the `TextView`. It takes two parameters:
83
+ /// a `TextView` instance, allowing access to the view's properties and methods and a
84
+ /// `TextSelectionManager.TextSelection` representing the current selection to operate on.
85
+ ///
86
+ /// - Note: The selections are iterated in reverse order, so modifications to earlier selections won't affect later
87
+ /// ones. The method automatically calls `notifyAfterEdit()` on the `selectionManager` after all
88
+ /// selections are processed.
89
+ public func editSelections( callback: ( TextView , TextSelectionManager . TextSelection ) -> Void ) {
90
+ for textSelection in selectionManager. textSelections. reversed ( ) {
91
+ callback ( self , textSelection)
92
+ }
93
+ selectionManager. notifyAfterEdit ( force: true )
56
94
}
57
95
}
0 commit comments