Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 42 additions & 15 deletions CodeEdit/Features/Editor/Models/Editor/Editor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ import Foundation
import OrderedCollections
import DequeModule
import AppKit
import OSLog

final class Editor: ObservableObject, Identifiable {
enum EditorError: Error {
case noWorkspaceAttached
}

typealias Tab = EditorInstance

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

private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: "Editor")

init() {
self.tabs = []
self.temporaryTab = nil
Expand Down Expand Up @@ -185,29 +192,45 @@ final class Editor: ObservableObject, Identifiable {

switch (temporaryTab, asTemporary) {
case (.some(let tab), true):
if let index = tabs.firstIndex(of: tab) {
clearFuture()
addToHistory(item)
tabs.remove(tab)
tabs.insert(item, at: index)
self.selectedTab = item
temporaryTab = item
}

replaceTemporaryTab(tab: tab, with: item)
case (.some(let tab), false) where tab == item:
temporaryTab = nil
case (.some(let tab), false) where tab != item:
openTab(file: item.file)

case (.some, false):
// A temporary tab exists, but we don't want to open this one as temporary.
// Clear the temp tab and open the new one.
openTab(file: item.file)
case (.none, true):
openTab(file: item.file)
temporaryTab = item

case (.none, false):
openTab(file: item.file)
}
}

/// Replaces the given temporary tab with a new tab item.
/// - Parameters:
/// - tab: The temporary tab to replace.
/// - newItem: The new tab to replace it with and open as a temporary tab.
private func replaceTemporaryTab(tab: Tab, with newItem: Tab) {
if let index = tabs.firstIndex(of: tab) {
do {
try openFile(item: newItem)
} catch {
logger.error("Error opening file: \(error)")
}

default:
break
clearFuture()
addToHistory(newItem)
tabs.remove(tab)
tabs.insert(newItem, at: index)
self.selectedTab = newItem
temporaryTab = newItem
} else {
// If we couldn't find the current temporary tab (invalid state) we should still do *something*
openTab(file: newItem.file)
temporaryTab = newItem
}
}

Expand Down Expand Up @@ -236,16 +259,20 @@ final class Editor: ObservableObject, Identifiable {
do {
try openFile(item: item)
} catch {
print(error)
logger.error("Error opening file: \(error)")
}
}

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

guard workspace != nil else {
throw EditorError.noWorkspaceAttached
}

try item.file.loadCodeFile()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ extension EditorManager {
/// Restores the tab manager from a captured state obtained using `saveRestorationState`
/// - Parameter workspace: The workspace to retrieve state from.
func restoreFromState(_ workspace: WorkspaceDocument) {
defer {
// No matter what, set the workspace on each editor. Even if we fail to read data.
flattenedEditors.forEach { editor in
editor.workspace = workspace
}
}

do {
guard let data = workspace.getFromWorkspaceState(.openTabs) as? Data else {
return
Expand Down
1 change: 0 additions & 1 deletion CodeEdit/Features/Editor/Views/EditorAreaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ struct EditorAreaView: View {
self.codeFile = { [weak latestValue] in latestValue }
}
}

} else {
CEContentUnavailableView("No Editor")
.padding(.top, editorInsetAmount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ final class ProjectNavigatorFileManagementUITests: XCTestCase {
XCTFail("newFile.txt did not appear")
return
}

guard Query.Navigator.getProjectNavigatorRow(
fileTitle: "New Folder",
navigator
Expand Down Expand Up @@ -95,6 +96,15 @@ final class ProjectNavigatorFileManagementUITests: XCTestCase {

let newFileRow = selectedRows.firstMatch
XCTAssertEqual(newFileRow.descendants(matching: .textField).firstMatch.value as? String, title)

let tabBar = Query.Window.getTabBar(window)
XCTAssertTrue(tabBar.exists)
let readmeTab = Query.TabBar.getTab(labeled: title, tabBar)
XCTAssertTrue(readmeTab.exists)

let newFileEditor = Query.Window.getFirstEditor(window)
XCTAssertTrue(newFileEditor.exists)
XCTAssertNotNil(newFileEditor.value as? String)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ final class ProjectNavigatorUITests: XCTestCase {
let readmeTab = Query.TabBar.getTab(labeled: "README.md", tabBar)
XCTAssertTrue(readmeTab.exists)

let readmeEditor = Query.Window.getFirstEditor(window)
XCTAssertTrue(readmeEditor.exists)
XCTAssertNotNil(readmeEditor.value as? String)

let rowCount = navigator.descendants(matching: .outlineRow).count

// Open a folder
Expand Down
6 changes: 6 additions & 0 deletions CodeEditUITests/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ enum Query {
static func getUtilityArea(_ window: XCUIElement) -> XCUIElement {
return window.descendants(matching: .any).matching(identifier: "UtilityArea").element
}

static func getFirstEditor(_ window: XCUIElement) -> XCUIElement {
return window.descendants(matching: .any)
.matching(NSPredicate(format: "label CONTAINS[c] 'Text Editor'"))
.firstMatch
}
}

enum Navigator {
Expand Down
Loading