@@ -22,12 +22,27 @@ import CodeEditKit
2222 @Published var selectionState : WorkspaceSelectionState = . init( )
2323 @Published var fileItems : [ WorkspaceClient . FileItem ] = [ ]
2424
25+ var workspaceState : [ String : Any ] {
26+ get {
27+ let key = " workspaceState- \( self . fileURL? . absoluteString ?? " " ) "
28+ return UserDefaults . standard. object ( forKey: key) as? [ String : Any ] ?? [ : ]
29+ }
30+ set {
31+ let key = " workspaceState- \( self . fileURL? . absoluteString ?? " " ) "
32+ UserDefaults . standard. set ( newValue, forKey: key)
33+ }
34+ }
35+
2536 var statusBarModel : StatusBarViewModel ?
2637 var searchState : SearchState ?
2738 var quickOpenViewModel : QuickOpenViewModel ?
2839 var commandsPaletteState : CommandPaletteViewModel ?
2940 var listenerModel : WorkspaceNotificationModel = . init( )
41+
3042 private var cancellables = Set < AnyCancellable > ( )
43+ private let openTabsStateName : String = " \( String ( describing: WorkspaceDocument . self) ) -OpenTabs "
44+ private let activeTabStateName : String = " \( String ( describing: WorkspaceDocument . self) ) -ActiveTab "
45+ private var openedTabsFromState = false
3146
3247 @Published var targets : [ Target ] = [ ]
3348
@@ -36,6 +51,14 @@ import CodeEditKit
3651 NotificationCenter . default. removeObserver ( self )
3752 }
3853
54+ func getFromWorkspaceState( key: String ) -> Any ? {
55+ return workspaceState [ key]
56+ }
57+
58+ func addToWorkspaceState( key: String , value: Any ) {
59+ workspaceState. updateValue ( value, forKey: key)
60+ }
61+
3962 // MARK: Open Tabs
4063 /// Opens new tab
4164 /// - Parameter item: any item which can be represented as a tab
@@ -147,6 +170,30 @@ import CodeEditKit
147170 closeTabs ( items: range)
148171 }
149172
173+ /// Switched the active tab to current tab
174+ /// - Parameter item: tab item that is now active.
175+ func switchedTab( item: TabBarItemRepresentable ) {
176+ selectionState. selectedId = item. tabID
177+ guard let fileItem = item as? WorkspaceClient . FileItem else { return }
178+ self . addToWorkspaceState ( key: activeTabStateName, value: fileItem. url. absoluteString)
179+ }
180+
181+ /// Tabs reordered
182+ /// - Parameter openedTabs: reordered tabs
183+ func reorderedTabs( openedTabs: [ TabBarItemID ] ) {
184+ selectionState. openedTabs = openedTabs
185+
186+ if openedTabsFromState {
187+ var openTabsInState : [ String ] = [ ]
188+ for openTabId in openedTabs {
189+ guard let item = selectionState. getItemByTab ( id: openTabId) as? WorkspaceClient . FileItem
190+ else { continue }
191+ openTabsInState. append ( item. url. absoluteString)
192+ }
193+ self . addToWorkspaceState ( key: openTabsStateName, value: openTabsInState)
194+ }
195+ }
196+
150197 /// Closes an open temporary tab, does not save the temporary tab's file.
151198 /// Removes the tab item from `openedCodeFiles`, `openedExtensions`, and `openFileItems`.
152199 private func closeTemporaryTab( ) {
@@ -196,6 +243,14 @@ import CodeEditKit
196243 selectionState. openedCodeFiles. removeValue ( forKey: item)
197244 selectionState. openFileItems. remove ( at: openFileItemIndex)
198245 removeTab ( id: item. tabID)
246+
247+ if openedTabsFromState {
248+ var openTabsInState = self . getFromWorkspaceState ( key: openTabsStateName) as? [ String ] ?? [ ]
249+ if let index = openTabsInState. firstIndex ( of: item. url. absoluteString) {
250+ openTabsInState. remove ( at: index)
251+ self . addToWorkspaceState ( key: openTabsStateName, value: openTabsInState)
252+ }
253+ }
199254 }
200255
201256 private func closeExtensionTab( item: Plugin ) {
@@ -209,8 +264,19 @@ import CodeEditKit
209264 @objc func convertTemporaryTab( ) {
210265 if selectionState. selectedId == selectionState. temporaryTab &&
211266 selectionState. temporaryTab != nil {
267+ let item = selectionState. getItemByTab ( id: selectionState. temporaryTab!)
212268 selectionState. previousTemporaryTab = selectionState. temporaryTab
213269 selectionState. temporaryTab = nil
270+
271+ guard let file = item as? WorkspaceClient . FileItem else { return }
272+
273+ if openedTabsFromState && item != nil {
274+ var openTabsInState = self . getFromWorkspaceState ( key: openTabsStateName) as? [ String ] ?? [ ]
275+ if !openTabsInState. contains ( file. url. absoluteString) {
276+ openTabsInState. append ( file. url. absoluteString)
277+ self . addToWorkspaceState ( key: openTabsStateName, value: openTabsInState)
278+ }
279+ }
214280 }
215281 }
216282
@@ -269,6 +335,27 @@ import CodeEditKit
269335 windowController. shouldCascadeWindows = false
270336 windowController. window? . setFrameAutosaveName ( self . fileURL? . absoluteString ?? " Untitled " )
271337 self . addWindowController ( windowController)
338+
339+ var activeTabID : TabBarItemID ?
340+ var activeTabInState = self . getFromWorkspaceState ( key: activeTabStateName) as? String ?? " "
341+ var openTabsInState = self . getFromWorkspaceState ( key: openTabsStateName) as? [ String ] ?? [ ]
342+ for openTab in openTabsInState {
343+ let tabUrl = URL ( string: openTab) !
344+ if FileManager . default. fileExists ( atPath: tabUrl. path) {
345+ let item = WorkspaceClient . FileItem ( url: tabUrl)
346+ self . openTab ( item: item)
347+ self . convertTemporaryTab ( )
348+ if activeTabInState == openTab {
349+ activeTabID = item. tabID
350+ }
351+ }
352+ }
353+
354+ if activeTabID != nil {
355+ selectionState. selectedId = activeTabID
356+ }
357+
358+ self . openedTabsFromState = true
272359 }
273360
274361 // MARK: Set Up Workspace
@@ -282,7 +369,7 @@ import CodeEditKit
282369 self . searchState = . init( self )
283370 self . quickOpenViewModel = . init( fileURL: url)
284371 self . commandsPaletteState = . init( )
285- self . statusBarModel = . init( workspaceURL: url)
372+ self . statusBarModel = . init( workspace : self , workspaceURL: url)
286373
287374 NotificationCenter . default. addObserver (
288375 self ,
@@ -292,26 +379,10 @@ import CodeEditKit
292379 )
293380 }
294381
295- /// Retrieves selection state from UserDefaults using SHA256 hash of project path as key
296- /// - Throws: `DecodingError.dataCorrupted` error if retrived data from UserDefaults is not decodable
297- /// - Returns: retrived state from UserDefaults or default state if not found
298- private func readSelectionState( ) throws -> WorkspaceSelectionState {
299- guard let path = fileURL? . path,
300- let data = UserDefaults . standard. value ( forKey: path. sha256 ( ) ) as? Data else { return selectionState }
301- let state = try PropertyListDecoder ( ) . decode ( WorkspaceSelectionState . self, from: data)
302- return state
303- }
304-
305382 override func read( from url: URL , ofType typeName: String ) throws {
306383 try initWorkspaceState ( url)
307384
308385 // Initialize Workspace
309- do {
310- selectionState = try readSelectionState ( )
311- } catch {
312- Swift . print ( " couldn't retrieve selection state from user defaults " )
313- }
314-
315386 workspaceClient?
316387 . getFiles
317388 . sink { [ weak self] files in
@@ -355,22 +426,7 @@ import CodeEditKit
355426
356427 // MARK: Close Workspace
357428
358- /// Saves selection state to UserDefaults using SHA256 hash of project path as key
359- /// - Throws: `EncodingError.invalidValue` error if sellection state is not encodable
360- private func saveSelectionState( ) throws {
361- guard let path = fileURL? . path else { return }
362- let hash = path. sha256 ( )
363- let data = try PropertyListEncoder ( ) . encode ( selectionState)
364- UserDefaults . standard. set ( data, forKey: hash)
365- }
366-
367429 override func close( ) {
368- do {
369- try saveSelectionState ( )
370- } catch {
371- Swift . print ( " couldn't save selection state from user defaults " )
372- }
373-
374430 selectionState. selectedId = nil
375431 selectionState. openedCodeFiles. removeAll ( )
376432
0 commit comments