Skip to content

Commit 05704cb

Browse files
Correctly Set Up EditorManager on New Workspaces (#2114)
### Description Adjusts the setup of EditorManager to always assign the `workspace` property on all editors after restoring it. This creates duplicate work when we have editors that successfully restore from a saved state, but ensures we *always* set up the editor manager correctly even when an error is thrown or there is no restoration data. Adjusted UI tests to catch this. ### Related Issues * Issue on discord. ### Checklist - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots N/A
1 parent 7e51213 commit 05704cb

File tree

6 files changed

+69
-16
lines changed

6 files changed

+69
-16
lines changed

CodeEdit/Features/Editor/Models/Editor/Editor.swift

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ import Foundation
99
import OrderedCollections
1010
import DequeModule
1111
import AppKit
12+
import OSLog
1213

1314
final class Editor: ObservableObject, Identifiable {
15+
enum EditorError: Error {
16+
case noWorkspaceAttached
17+
}
18+
1419
typealias Tab = EditorInstance
1520

1621
/// Set of open tabs.
@@ -57,6 +62,8 @@ final class Editor: ObservableObject, Identifiable {
5762
weak var parent: SplitViewData?
5863
weak var workspace: WorkspaceDocument?
5964

65+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "Editor")
66+
6067
init() {
6168
self.tabs = []
6269
self.temporaryTab = nil
@@ -185,29 +192,45 @@ final class Editor: ObservableObject, Identifiable {
185192

186193
switch (temporaryTab, asTemporary) {
187194
case (.some(let tab), true):
188-
if let index = tabs.firstIndex(of: tab) {
189-
clearFuture()
190-
addToHistory(item)
191-
tabs.remove(tab)
192-
tabs.insert(item, at: index)
193-
self.selectedTab = item
194-
temporaryTab = item
195-
}
196-
195+
replaceTemporaryTab(tab: tab, with: item)
197196
case (.some(let tab), false) where tab == item:
198197
temporaryTab = nil
199198
case (.some(let tab), false) where tab != item:
200199
openTab(file: item.file)
201-
200+
case (.some, false):
201+
// A temporary tab exists, but we don't want to open this one as temporary.
202+
// Clear the temp tab and open the new one.
203+
openTab(file: item.file)
202204
case (.none, true):
203205
openTab(file: item.file)
204206
temporaryTab = item
205-
206207
case (.none, false):
207208
openTab(file: item.file)
209+
}
210+
}
211+
212+
/// Replaces the given temporary tab with a new tab item.
213+
/// - Parameters:
214+
/// - tab: The temporary tab to replace.
215+
/// - newItem: The new tab to replace it with and open as a temporary tab.
216+
private func replaceTemporaryTab(tab: Tab, with newItem: Tab) {
217+
if let index = tabs.firstIndex(of: tab) {
218+
do {
219+
try openFile(item: newItem)
220+
} catch {
221+
logger.error("Error opening file: \(error)")
222+
}
208223

209-
default:
210-
break
224+
clearFuture()
225+
addToHistory(newItem)
226+
tabs.remove(tab)
227+
tabs.insert(newItem, at: index)
228+
self.selectedTab = newItem
229+
temporaryTab = newItem
230+
} else {
231+
// If we couldn't find the current temporary tab (invalid state) we should still do *something*
232+
openTab(file: newItem.file)
233+
temporaryTab = newItem
211234
}
212235
}
213236

@@ -236,16 +259,20 @@ final class Editor: ObservableObject, Identifiable {
236259
do {
237260
try openFile(item: item)
238261
} catch {
239-
print(error)
262+
logger.error("Error opening file: \(error)")
240263
}
241264
}
242265

243266
private func openFile(item: Tab) throws {
244267
// If this isn't attached to a workspace, loading a new NSDocument will cause a loose document we can't close
245-
guard item.file.fileDocument == nil && workspace != nil else {
268+
guard item.file.fileDocument == nil else {
246269
return
247270
}
248271

272+
guard workspace != nil else {
273+
throw EditorError.noWorkspaceAttached
274+
}
275+
249276
try item.file.loadCodeFile()
250277
}
251278

CodeEdit/Features/Editor/Models/EditorLayout/EditorLayout+StateRestoration.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ extension EditorManager {
1313
/// Restores the tab manager from a captured state obtained using `saveRestorationState`
1414
/// - Parameter workspace: The workspace to retrieve state from.
1515
func restoreFromState(_ workspace: WorkspaceDocument) {
16+
defer {
17+
// No matter what, set the workspace on each editor. Even if we fail to read data.
18+
flattenedEditors.forEach { editor in
19+
editor.workspace = workspace
20+
}
21+
}
22+
1623
do {
1724
guard let data = workspace.getFromWorkspaceState(.openTabs) as? Data else {
1825
return

CodeEdit/Features/Editor/Views/EditorAreaView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ struct EditorAreaView: View {
7777
self.codeFile = { [weak latestValue] in latestValue }
7878
}
7979
}
80-
8180
} else {
8281
CEContentUnavailableView("No Editor")
8382
.padding(.top, editorInsetAmount)

CodeEditUITests/Features/NavigatorArea/ProjectNavigator/ProjectNavigatorFileManagementUITests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ final class ProjectNavigatorFileManagementUITests: XCTestCase {
5757
XCTFail("newFile.txt did not appear")
5858
return
5959
}
60+
6061
guard Query.Navigator.getProjectNavigatorRow(
6162
fileTitle: "New Folder",
6263
navigator
@@ -95,6 +96,15 @@ final class ProjectNavigatorFileManagementUITests: XCTestCase {
9596

9697
let newFileRow = selectedRows.firstMatch
9798
XCTAssertEqual(newFileRow.descendants(matching: .textField).firstMatch.value as? String, title)
99+
100+
let tabBar = Query.Window.getTabBar(window)
101+
XCTAssertTrue(tabBar.exists)
102+
let readmeTab = Query.TabBar.getTab(labeled: title, tabBar)
103+
XCTAssertTrue(readmeTab.exists)
104+
105+
let newFileEditor = Query.Window.getFirstEditor(window)
106+
XCTAssertTrue(newFileEditor.exists)
107+
XCTAssertNotNil(newFileEditor.value as? String)
98108
}
99109
}
100110
}

CodeEditUITests/Features/NavigatorArea/ProjectNavigator/ProjectNavigatorUITests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ final class ProjectNavigatorUITests: XCTestCase {
3535
let readmeTab = Query.TabBar.getTab(labeled: "README.md", tabBar)
3636
XCTAssertTrue(readmeTab.exists)
3737

38+
let readmeEditor = Query.Window.getFirstEditor(window)
39+
XCTAssertTrue(readmeEditor.exists)
40+
XCTAssertNotNil(readmeEditor.value as? String)
41+
3842
let rowCount = navigator.descendants(matching: .outlineRow).count
3943

4044
// Open a folder

CodeEditUITests/Query.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ enum Query {
4343
static func getUtilityArea(_ window: XCUIElement) -> XCUIElement {
4444
return window.descendants(matching: .any).matching(identifier: "UtilityArea").element
4545
}
46+
47+
static func getFirstEditor(_ window: XCUIElement) -> XCUIElement {
48+
return window.descendants(matching: .any)
49+
.matching(NSPredicate(format: "label CONTAINS[c] 'Text Editor'"))
50+
.firstMatch
51+
}
4652
}
4753

4854
enum Navigator {

0 commit comments

Comments
 (0)