Skip to content

Commit 25b7072

Browse files
committed
Update Text Selection On Undo/Redo
1 parent a8a0e9c commit 25b7072

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

Sources/CodeEditTextView/Utils/CEUndoManager.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ public class CEUndoManager: UndoManager {
8383
textView.replaceCharacters(in: mutation.inverse.range, with: mutation.inverse.string)
8484
}
8585
textView.textStorage.endEditing()
86+
87+
updateSelectionsForMutations(mutations: item.mutations.map { $0.mutation })
88+
8689
NotificationCenter.default.post(name: .NSUndoManagerDidUndoChange, object: self)
8790
redoStack.append(item)
8891
_isUndoing = false
@@ -101,16 +104,36 @@ public class CEUndoManager: UndoManager {
101104

102105
_isRedoing = true
103106
NotificationCenter.default.post(name: .NSUndoManagerWillRedoChange, object: self)
107+
textView.selectionManager.removeCursors()
104108
textView.textStorage.beginEditing()
105109
for mutation in item.mutations {
106110
textView.replaceCharacters(in: mutation.mutation.range, with: mutation.mutation.string)
107111
}
108112
textView.textStorage.endEditing()
113+
114+
updateSelectionsForMutations(mutations: item.mutations.map { $0.inverse })
115+
109116
NotificationCenter.default.post(name: .NSUndoManagerDidRedoChange, object: self)
110117
undoStack.append(item)
111118
_isRedoing = false
112119
}
113120

121+
/// We often undo/redo a group of mutations that contain updated ranges that are next to each other but for a user
122+
/// should be one continuous range. This merges those ranges into a set of disjoint ranges before updating the
123+
/// selection manager.
124+
private func updateSelectionsForMutations(mutations: [TextMutation]) {
125+
if mutations.reduce(0, { $0 + $1.range.length }) == 0, let last = mutations.last {
126+
// If the mutations are only deleting text (no replacement), we just place the cursor at the last range,
127+
// since all the ranges are the same but the other method will return no ranges (empty range).
128+
textView?.selectionManager.setSelectedRange(last.range)
129+
} else {
130+
let mergedRanges = mutations.reduce(into: IndexSet(), { set, mutation in
131+
set.insert(range: mutation.range)
132+
})
133+
textView?.selectionManager.setSelectedRanges(mergedRanges.rangeView.map { NSRange($0) })
134+
}
135+
}
136+
114137
/// Clears the undo/redo stacks.
115138
public func clearStack() {
116139
undoStack.removeAll()

0 commit comments

Comments
 (0)