Skip to content

Commit 00624d2

Browse files
feat: Add ⇧⌘T shortcut to reopen recently closed tabs
Implements a recently-closed tab stack on the Editor model. When a tab is closed, its file reference is pushed onto the stack (up to 20 entries). Pressing ⇧⌘T (Shift+Command+T) pops the most recently closed tab and reopens it in the active editor. Changes: - Editor: Add recentlyClosedTabs stack, addToRecentlyClosed(), reopenClosedTab() - CodeEditWindowController: Add reopenClosedTab(_:) action - FileCommands: Add 'Reopen Closed Tab' menu item with ⇧⌘T shortcut Closes #1656
1 parent cec6287 commit 00624d2

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs
198198
}
199199
}
200200

201+
@IBAction func reopenClosedTab(_ sender: Any) {
202+
workspace?.editorManager?.activeEditor.reopenClosedTab()
203+
}
204+
201205
@IBAction func closeActiveEditor(_ sender: Any) {
202206
if workspace?.editorManager?.editorLayout.findSomeEditor(
203207
except: workspace?.editorManager?.activeEditor

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ final class Editor: ObservableObject, Identifiable {
5757

5858
@Published var temporaryTab: Tab?
5959

60+
/// Stack of recently closed files, used to support reopening closed tabs via ⇧⌘T.
61+
@Published var recentlyClosedTabs: [CEWorkspaceFile] = []
62+
63+
/// Maximum number of recently closed tabs to remember.
64+
private static let maxRecentlyClosedTabs = 20
65+
6066
var id = UUID()
6167

6268
weak var parent: SplitViewData?
@@ -145,6 +151,9 @@ final class Editor: ObservableObject, Identifiable {
145151
func closeTab(file: CEWorkspaceFile, fromHistory: Bool = false) {
146152
guard canCloseTab(file: file) else { return }
147153

154+
// Remember the closed tab so it can be reopened with ⇧⌘T
155+
addToRecentlyClosed(file)
156+
148157
if temporaryTab?.file == file {
149158
temporaryTab = nil
150159
}
@@ -322,6 +331,29 @@ final class Editor: ObservableObject, Identifiable {
322331

323332
/// Remove the given file from tabs.
324333
/// - Parameter file: The file to remove.
334+
// MARK: - Recently Closed Tabs
335+
336+
/// Pushes a file onto the recently-closed stack.
337+
private func addToRecentlyClosed(_ file: CEWorkspaceFile) {
338+
// Remove duplicates so the most recent close is always on top
339+
recentlyClosedTabs.removeAll(where: { $0 == file })
340+
recentlyClosedTabs.append(file)
341+
if recentlyClosedTabs.count > Self.maxRecentlyClosedTabs {
342+
recentlyClosedTabs.removeFirst()
343+
}
344+
}
345+
346+
/// Reopens the most recently closed tab, if any.
347+
func reopenClosedTab() {
348+
guard let file = recentlyClosedTabs.popLast() else { return }
349+
openTab(file: file)
350+
}
351+
352+
/// Whether there are any recently closed tabs that can be reopened.
353+
var canReopenClosedTab: Bool {
354+
!recentlyClosedTabs.isEmpty
355+
}
356+
325357
func removeTab(_ file: CEWorkspaceFile) {
326358
tabs.removeAll(where: { tab in tab.file == file })
327359
if temporaryTab?.file == file {

CodeEdit/Features/WindowCommands/FileCommands.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ struct FileCommands: Commands {
5151
}
5252
.keyboardShortcut("w")
5353

54+
Button("Reopen Closed Tab") {
55+
NSApp.sendAction(#selector(CodeEditWindowController.reopenClosedTab(_:)), to: nil, from: nil)
56+
}
57+
.keyboardShortcut("t", modifiers: [.shift, .command])
58+
59+
Divider()
60+
5461
Button("Close Editor") {
5562
if NSApp.target(forAction: #selector(CodeEditWindowController.closeActiveEditor(_:))) != nil {
5663
NSApp.sendAction(

0 commit comments

Comments
 (0)