@@ -9,8 +9,11 @@ import Combine
99import Foundation
1010import DequeModule
1111import OrderedCollections
12+ import os
1213
1314class EditorManager : ObservableObject {
15+ let logger = Logger ( subsystem: Bundle . main. bundleIdentifier ?? " " , category: " EditorManager " )
16+
1417 /// The complete editor layout.
1518 @Published var editorLayout : EditorLayout
1619
@@ -33,6 +36,8 @@ class EditorManager: ObservableObject {
3336 var tabBarTabIdSubject = PassthroughSubject < String ? , Never > ( )
3437 var cancellable : AnyCancellable ?
3538
39+ // MARK: - Init
40+
3641 init ( ) {
3742 let tab = Editor ( )
3843 self . activeEditor = tab
@@ -42,10 +47,24 @@ class EditorManager: ObservableObject {
4247 switchToActiveEditor ( )
4348 }
4449
50+ /// Initializes the editor manager's state to the "initial" state.
51+ ///
52+ /// Functionally identical to the initializer for this class.
53+ func initCleanState( ) {
54+ let tab = Editor ( )
55+ self . activeEditor = tab
56+ self . activeEditorHistory. prepend { [ weak tab] in tab }
57+ self . editorLayout = . horizontal( . init( . horizontal, editorLayouts: [ . one( tab) ] ) )
58+ self . isFocusingActiveEditor = false
59+ switchToActiveEditor ( )
60+ }
61+
4562 /// Flattens the splitviews.
4663 func flatten( ) {
4764 if case . horizontal( let data) = editorLayout {
4865 data. flatten ( )
66+ } else if case . vertical( let data) = editorLayout {
67+ data. flatten ( )
4968 }
5069 }
5170
@@ -68,74 +87,48 @@ class EditorManager: ObservableObject {
6887 }
6988 }
7089
71- /// Restores the tab manager from a captured state obtained using `saveRestorationState`
72- /// - Parameter workspace: The workspace to retrieve state from.
73- func restoreFromState( _ workspace: WorkspaceDocument ) {
74- guard let fileManager = workspace. workspaceFileManager,
75- let data = workspace. getFromWorkspaceState ( . openTabs) as? Data ,
76- let state = try ? JSONDecoder ( ) . decode ( EditorRestorationState . self, from: data) else {
77- return
78- }
79- fixRestoredEditorLayout ( state. groups, fileManager: fileManager)
80- self . editorLayout = state. groups
81- self . activeEditor = findEditorLayout (
82- group: state. groups,
83- searchFor: state. focus. id
84- ) ?? editorLayout. findSomeEditor ( ) !
85- switchToActiveEditor ( )
86- }
90+ // MARK: - Close Editor
8791
88- /// Fix any hanging files after restoring from saved state.
89- ///
90- /// After decoding the state, we're left with `CEWorkspaceFile`s that don't exist in the file manager
91- /// so this function maps all those to 'real' files. Works recursively on all the tab groups.
92- /// - Parameters:
93- /// - group: The tab group to fix.
94- /// - fileManager: The file manager to use to map files.
95- private func fixRestoredEditorLayout( _ group: EditorLayout , fileManager: CEWorkspaceFileManager ) {
96- switch group {
97- case let . one( data) :
98- fixEditor ( data, fileManager: fileManager)
99- case let . vertical( splitData) :
100- splitData. editorLayouts. forEach { group in
101- fixRestoredEditorLayout ( group, fileManager: fileManager)
102- }
103- case let . horizontal( splitData) :
104- splitData. editorLayouts. forEach { group in
105- fixRestoredEditorLayout ( group, fileManager: fileManager)
106- }
92+ /// Close an editor and fix editor manager state, updating active editor, etc.
93+ /// - Parameter editor: The editor to close
94+ func closeEditor( _ editor: Editor ) {
95+ editor. close ( )
96+ if activeEditor == editor {
97+ setNewActiveEditor ( excluding: editor)
10798 }
99+
100+ flatten ( )
101+ objectWillChange. send ( )
108102 }
109103
110- private func findEditorLayout ( group : EditorLayout , searchFor id : UUID ) -> Editor ? {
111- switch group {
112- case let . one ( data ) :
113- return data . id == id ? data : nil
114- case let . vertical ( splitData ) :
115- return splitData . editorLayouts . compactMap { findEditorLayout ( group : $0 , searchFor : id ) } . first
116- case let . horizontal ( splitData ) :
117- return splitData . editorLayouts . compactMap { findEditorLayout ( group : $0 , searchFor : id ) } . first
104+ /// Set a new active editor.
105+ /// - Parameter editor: The editor to exclude.
106+ func setNewActiveEditor ( excluding editor : Editor ) {
107+ activeEditorHistory . removeAll { $0 ( ) == nil || $0 ( ) == editor }
108+ if activeEditorHistory . isEmpty {
109+ activeEditor = findSomeEditor ( excluding : editor )
110+ } else {
111+ activeEditor = activeEditorHistory . removeFirst ( ) ( ) !
118112 }
119113 }
120114
121- /// Fixes any hanging files after restoring from saved state.
122- /// - Parameters:
123- /// - data: The tab group to fix.
124- /// - fileManager: The file manager to use to map files.a
125- private func fixEditor( _ editor: Editor , fileManager: CEWorkspaceFileManager ) {
126- editor. tabs = OrderedSet ( editor. tabs. compactMap { fileManager. getFile ( $0. url. path, createIfNotFound: true ) } )
127- if let selectedTab = editor. selectedTab {
128- editor. selectedTab = fileManager. getFile ( selectedTab. url. path, createIfNotFound: true )
115+ /// Find some editor, or if one cannot be found set up the editor manager with a clean state.
116+ /// - Parameter editor: The editor to exclude.
117+ /// - Returns: Some editor, order is not guaranteed.
118+ func findSomeEditor( excluding editor: Editor ) -> Editor {
119+ guard let someEditor = editorLayout. findSomeEditor ( except: editor) else {
120+ initCleanState ( )
121+ return activeEditor
129122 }
123+ return someEditor
130124 }
131125
132- func saveRestorationState( _ workspace: WorkspaceDocument ) {
133- if let data = try ? JSONEncoder ( ) . encode (
134- EditorRestorationState ( focus: activeEditor, groups: editorLayout)
135- ) {
136- workspace. addToWorkspaceState ( key: . openTabs, value: data)
137- } else {
138- workspace. addToWorkspaceState ( key: . openTabs, value: nil )
126+ // MARK: - Focus
127+
128+ func toggleFocusingEditor( from editor: Editor ) {
129+ if !isFocusingActiveEditor {
130+ activeEditor = editor
139131 }
132+ isFocusingActiveEditor. toggle ( )
140133 }
141134}
0 commit comments