@@ -14,6 +14,7 @@ import CodeEditTextView
1414import CodeEditLanguages
1515import Combine
1616import OSLog
17+ import TextStory
1718
1819enum CodeFileError : Error {
1920 case failedToDecode
@@ -161,18 +162,38 @@ final class CodeFileDocument: NSDocument, ObservableObject {
161162 convertedString: & nsString,
162163 usedLossyConversion: nil
163164 )
164- if let validEncoding = FileEncoding ( rawEncoding) , let nsString {
165- self . sourceEncoding = validEncoding
166- if let content {
167- content. mutableString. setString ( nsString as String )
168- } else {
169- self . content = NSTextStorage ( string: nsString as String )
170- }
171- } else {
165+ guard let validEncoding = FileEncoding ( rawEncoding) , let nsString else {
172166 Self . logger. error ( " Failed to read file from data using encoding: \( rawEncoding) " )
167+ return
168+ }
169+ self . sourceEncoding = validEncoding
170+ if let content {
171+ registerContentChangeUndo ( fileURL: fileURL, nsString: nsString, content: content)
172+ content. mutableString. setString ( nsString as String )
173+ } else {
174+ self . content = NSTextStorage ( string: nsString as String )
173175 }
174176 NotificationCenter . default. post ( name: Self . didOpenNotification, object: self )
175177 }
178+
179+ /// If this file is already open and being tracked by an undo manager, we register an undo mutation
180+ /// of the entire contents. This allows the user to undo changes that occurred outside of CodeEdit
181+ /// while the file was displayed in CodeEdit.
182+ ///
183+ /// - Note: This is inefficient memory-wise. We could do a diff of the file and only register the
184+ /// mutations that would recreate the diff. However, that would instead be CPU intensive.
185+ /// Tradeoffs.
186+ private func registerContentChangeUndo( fileURL: URL ? , nsString: NSString , content: NSTextStorage ) {
187+ guard let fileURL else { return }
188+ // If there's an undo manager, register a mutation replacing the entire contents.
189+ let mutation = TextMutation (
190+ string: nsString as String ,
191+ range: NSRange ( location: 0 , length: content. length) ,
192+ limit: content. length
193+ )
194+ let undoManager = self . findWorkspace ( ) ? . undoRegistration. managerIfExists ( forFile: fileURL)
195+ undoManager? . registerMutation ( mutation)
196+ }
176197
177198 // MARK: - Autosave
178199
0 commit comments