Skip to content

Commit

Permalink
Make CEUndoManager public (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
avinizhanov authored Oct 15, 2023
1 parent 7f130bd commit 6a04ca7
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 19 deletions.
9 changes: 7 additions & 2 deletions Sources/CodeEditTextView/CodeEditTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
/// character's width between characters, etc. Defaults to `1.0`
/// - bracketPairHighlight: The type of highlight to use to highlight bracket pairs.
/// See `BracketPairHighlight` for more information. Defaults to `nil`
/// - undoManager: The undo manager for the text view. Defaults to `nil`, which will create a new CEUndoManager
public init(
_ text: Binding<String>,
language: CodeLanguage,
Expand All @@ -51,7 +52,8 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
contentInsets: NSEdgeInsets? = nil,
isEditable: Bool = true,
letterSpacing: Double = 1.0,
bracketPairHighlight: BracketPairHighlight? = nil
bracketPairHighlight: BracketPairHighlight? = nil,
undoManager: CEUndoManager? = nil
) {
self._text = text
self.language = language
Expand All @@ -69,6 +71,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
self.isEditable = isEditable
self.letterSpacing = letterSpacing
self.bracketPairHighlight = bracketPairHighlight
self.undoManager = undoManager ?? CEUndoManager()
}

@Binding private var text: String
Expand All @@ -87,6 +90,7 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
private var isEditable: Bool
private var letterSpacing: Double
private var bracketPairHighlight: BracketPairHighlight?
private var undoManager: CEUndoManager

public typealias NSViewControllerType = STTextViewController

Expand All @@ -107,7 +111,8 @@ public struct CodeEditTextView: NSViewControllerRepresentable {
contentInsets: contentInsets,
isEditable: isEditable,
letterSpacing: letterSpacing,
bracketPairHighlight: bracketPairHighlight
bracketPairHighlight: bracketPairHighlight,
undoManager: undoManager
)
return controller
}
Expand Down
33 changes: 20 additions & 13 deletions Sources/CodeEditTextView/Controller/CEUndoManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,30 @@ import TextStory
/// - Grouping pasted text
///
/// If needed, the automatic undo grouping can be overridden using the `beginGrouping()` and `endGrouping()` methods.
class CEUndoManager {
public class CEUndoManager {
/// An `UndoManager` subclass that forwards relevant actions to a `CEUndoManager`.
/// Allows for objects like `STTextView` to use the `UndoManager` API
/// while CETV manages the undo/redo actions.
class DelegatedUndoManager: UndoManager {
public class DelegatedUndoManager: UndoManager {
weak var parent: CEUndoManager?

override var canUndo: Bool { parent?.canUndo ?? false }
override var canRedo: Bool { parent?.canRedo ?? false }
public override var canUndo: Bool { parent?.canUndo ?? false }
public override var canRedo: Bool { parent?.canRedo ?? false }

func registerMutation(_ mutation: TextMutation) {
public func registerMutation(_ mutation: TextMutation) {
parent?.registerMutation(mutation)
removeAllActions()
}

override func undo() {
public override func undo() {
parent?.undo()
}

override func redo() {
public override func redo() {
parent?.redo()
}

override func registerUndo(withTarget target: Any, selector: Selector, object anObject: Any?) {
public override func registerUndo(withTarget target: Any, selector: Selector, object anObject: Any?) {
// no-op, but just in case to save resources:
removeAllActions()
}
Expand Down Expand Up @@ -71,36 +71,41 @@ class CEUndoManager {
/// A stack of operations that can be redone.
private var redoStack: [UndoGroup] = []

private unowned let textView: STTextView
internal weak var textView: STTextView?
private(set) var isGrouping: Bool = false

public init(textView: STTextView) {
self.textView = textView
public init() {
self.manager = DelegatedUndoManager()
manager.parent = self
}

/// Performs an undo operation if there is one available.
public func undo() {
guard let item = undoStack.popLast() else {
guard let item = undoStack.popLast(),
let textView else {
return
}
isUndoing = true
for mutation in item.mutations.reversed() {
NotificationCenter.default.post(name: .NSUndoManagerWillUndoChange, object: self.manager)
textView.applyMutationNoUndo(mutation.inverse)
NotificationCenter.default.post(name: .NSUndoManagerDidUndoChange, object: self.manager)
}
redoStack.append(item)
isUndoing = false
}

/// Performs a redo operation if there is one available.
public func redo() {
guard let item = redoStack.popLast() else {
guard let item = redoStack.popLast(),
let textView else {
return
}
isRedoing = true
for mutation in item.mutations {
NotificationCenter.default.post(name: .NSUndoManagerWillRedoChange, object: self.manager)
textView.applyMutationNoUndo(mutation.mutation)
NotificationCenter.default.post(name: .NSUndoManagerDidRedoChange, object: self.manager)
}
undoStack.append(item)
isRedoing = false
Expand All @@ -117,6 +122,8 @@ class CEUndoManager {
/// Calling this method while the manager is in an undo/redo operation will result in a no-op.
/// - Parameter mutation: The mutation to register for undo/redo
public func registerMutation(_ mutation: TextMutation) {
guard let textView else { return }

if (mutation.range.length == 0 && mutation.string.isEmpty) || isUndoing || isRedoing { return }
let newMutation = UndoGroup.Mutation(mutation: mutation, inverse: textView.inverseMutation(for: mutation))
if !undoStack.isEmpty, let lastMutation = undoStack.last?.mutations.last {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension STTextViewController {
return event
}

textViewUndoManager = CEUndoManager(textView: textView)
textViewUndoManager.textView = textView
reloadUI()
setUpHighlighter()
setHighlightProvider(self.highlightProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
/// for every new selection.
internal var lastTextSelections: [NSTextRange] = []

internal var textViewUndoManager: CEUndoManager!
internal var textViewUndoManager: CEUndoManager

/// Binding for the `textView`s string
public var text: Binding<String>
Expand Down Expand Up @@ -133,7 +133,8 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
contentInsets: NSEdgeInsets? = nil,
isEditable: Bool,
letterSpacing: Double,
bracketPairHighlight: BracketPairHighlight? = nil
bracketPairHighlight: BracketPairHighlight? = nil,
undoManager: CEUndoManager
) {
self.text = text
self.language = language
Expand All @@ -150,6 +151,7 @@ public class STTextViewController: NSViewController, STTextViewDelegate, ThemeAt
self.contentInsets = contentInsets
self.isEditable = isEditable
self.bracketPairHighlight = bracketPairHighlight
self.textViewUndoManager = undoManager
super.init(nibName: nil, bundle: nil)
}

Expand Down
3 changes: 2 additions & 1 deletion Tests/CodeEditTextViewTests/STTextViewControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ final class STTextViewControllerTests: XCTestCase {
editorOverscroll: 0.5,
useThemeBackground: true,
isEditable: true,
letterSpacing: 1.0
letterSpacing: 1.0,
undoManager: CEUndoManager()
)

controller.loadView()
Expand Down

0 comments on commit 6a04ca7

Please sign in to comment.