Skip to content

Commit

Permalink
Fix #1453: App crashes when unsaved file alert presented (#1454)
Browse files Browse the repository at this point in the history
* Fix #1453

* fix unit tests
  • Loading branch information
avinizhanov authored Oct 18, 2023
1 parent f20f28e commit 2cf0170
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ final class CEWorkspaceFileManager {
// Event returns file/folder that was changed, but in tree we need to update it's parent
let parent = "/" + event.path.split(separator: "/").dropLast().joined(separator: "/")
guard let parentItem = self.getFile(parent) else {
return
continue
}

switch event.eventType {
Expand Down
70 changes: 51 additions & 19 deletions CodeEdit/Features/Editor/Models/Editor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation
import OrderedCollections
import DequeModule
import AppKit

final class Editor: ObservableObject, Identifiable {
typealias Tab = CEWorkspaceFile
Expand Down Expand Up @@ -86,8 +87,15 @@ final class Editor: ObservableObject, Identifiable {
/// This will also write any changes to the file on disk and will add the tab to the tab history.
/// - Parameter item: the tab to close.
func closeTab(item: Tab) {
guard canCloseTab(item: item) else { return }

if temporaryTab == item {
temporaryTab = nil
} else {
// When tab actually closed (not changed from temporary to normal)
// we need to set fileDocument to nil, otherwise it will keep file in memory
// and not reload content on next openTabFile with same id
item.fileDocument = nil
}

historyOffset = 0
Expand All @@ -98,25 +106,6 @@ final class Editor: ObservableObject, Identifiable {
if let selectedTab {
history.prepend(selectedTab)
}

guard let file = item.fileDocument else { return }

if file.isDocumentEdited {
let shouldClose = UnsafeMutablePointer<Bool>.allocate(capacity: 1)
shouldClose.initialize(to: true)
defer {
_ = shouldClose.move()
shouldClose.deallocate()
}
file.canClose(
withDelegate: self,
shouldClose: #selector(WorkspaceDocument.document(_:shouldClose:contextInfo:)),
contextInfo: shouldClose
)
guard shouldClose.pointee else {
return
}
}
}

/// Closes the currently opened tab in the tab group.
Expand Down Expand Up @@ -234,6 +223,49 @@ final class Editor: ObservableObject, Identifiable {
var canGoForwardInHistory: Bool {
historyOffset != 0
}

/// Check if tab can be closed
/// If document edited it will show dialog where user can save document before closing or cancel.
private func canCloseTab(item: Tab) -> Bool {
guard let file = item.fileDocument else { return true }

if file.isDocumentEdited {
let shouldClose = UnsafeMutablePointer<Bool>.allocate(capacity: 1)
shouldClose.initialize(to: true)
defer {
_ = shouldClose.move()
shouldClose.deallocate()
}
file.canClose(
withDelegate: self,
shouldClose: #selector(document(_:shouldClose:contextInfo:)),
contextInfo: shouldClose
)

return shouldClose.pointee
}

return true
}

/// Receives result of `canClose` and then, set `shouldClose` to `contextInfo`'s `pointee`.
///
/// - Parameters:
/// - document: The document may be closed.
/// - shouldClose: The result of user selection.
/// `shouldClose` becomes false if the user selects cancel, otherwise true.
/// - contextInfo: The additional info which will be set `shouldClose`.
/// `contextInfo` must be `UnsafeMutablePointer<Bool>`.
@objc
func document(
_ document: NSDocument,
shouldClose: Bool,
contextInfo: UnsafeMutableRawPointer
) {
let opaquePtr = OpaquePointer(contextInfo)
let mutablePointer = UnsafeMutablePointer<Bool>(opaquePtr)
mutablePointer.pointee = shouldClose
}
}

extension Editor: Equatable, Hashable {
Expand Down

0 comments on commit 2cf0170

Please sign in to comment.