Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork1.1k
Fix Navigator Key Navigation Explosion#1803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
- Loading branch information
Uh oh!
There was an error while loading.Please reload this page.
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // | ||
| // Editor+History.swift | ||
| // CodeEdit | ||
| // | ||
| // Created by Khan Winter on 7/9/24. | ||
| // | ||
| import Foundation | ||
| /// Methods for modifying the history list on the editor. | ||
| extension Editor { | ||
| /// Add the tab to the history list. | ||
| /// - Parameter tab: The tab to add to the history. | ||
| func addToHistory(_ tab: Tab) { | ||
| if history.first != tab { | ||
| history.prepend(tab) | ||
| } | ||
| } | ||
| /// Clear any tabs in the "future" on the history list. Resets the history offset and removes any tabs that were | ||
| /// available to navigate forwards to. | ||
| func clearFuture() { | ||
| guard historyOffset > 0 else { return } // nothing to clear, avoid an out of bounds error | ||
| history.removeFirst(historyOffset) | ||
| historyOffset = 0 | ||
| } | ||
| /// Move backwards in the history list by one place. | ||
| func goBackInHistory() { | ||
| if canGoBackInHistory { | ||
| historyOffset += 1 | ||
| } | ||
| } | ||
| /// Move forwards in the history list by one place. | ||
| func goForwardInHistory() { | ||
| if canGoForwardInHistory { | ||
| historyOffset -= 1 | ||
| } | ||
| } | ||
| // TODO: move to @Observable so this works better | ||
| /// Warning: NOT published! | ||
| var canGoBackInHistory: Bool { | ||
| historyOffset != history.count - 1 && !history.isEmpty | ||
| } | ||
| // TODO: move to @Observable so this works better | ||
| /// Warning: NOT published! | ||
| var canGoForwardInHistory: Bool { | ||
| historyOffset != 0 | ||
| } | ||
| /// Called by the ``Editor`` class when the history offset is changed. | ||
| /// | ||
| /// This method updates the selected tab to the current tab in the history offset. | ||
| /// If the tab is not opened, it is opened without modifying the history list. | ||
| /// - Warning: Do not use except in the ``historyOffset``'s `didSet`. | ||
| func historyOffsetDidChange() { | ||
| let tab = history[historyOffset] | ||
| if !tabs.contains(tab) { | ||
| if let temporaryTab, tabs.contains(temporaryTab) { | ||
| closeTab(file: temporaryTab.file, fromHistory: true) | ||
| } | ||
| temporaryTab = tab | ||
| openTab(file: tab.file, fromHistory: true) | ||
| } | ||
| selectedTab = tab | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -35,22 +35,16 @@ final class Editor: ObservableObject, Identifiable { | ||
| } | ||
| /// The current offset in the history list. | ||
| /// When set, updates the ``selectedTab`` to the tab indicated by the offset. | ||
| /// See the ``historyOffsetDidChange()`` method for more details. | ||
| @Published var historyOffset: Int = 0 { | ||
| didSet { | ||
| historyOffsetDidChange() | ||
| } | ||
| } | ||
| /// Maintains the list of tabs that have been switched to. | ||
| /// - Warning: Use the ``addToHistory(_:)`` or ``clearFuture()`` methods to modify this. Do not modify directly. | ||
| @Published var history: Deque<Tab> = [] | ||
thecoolwinter marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| /// Currently selected tab. | ||
| @@ -106,22 +100,26 @@ final class Editor: ObservableObject, Identifiable { | ||
| /// Closes a tab in the editor. | ||
| /// This will also write any changes to the file on disk and will add the tab to the tab history. | ||
thecoolwinter marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| /// - Parameters: | ||
| /// - file: The tab to close | ||
| /// - fromHistory: If `true`, does not clear tabs ahead of the ``historyOffset`` | ||
| /// Used when opening tabs from the history queue where tabs ahead of the ``historyOffset`` should | ||
| /// not be removed. | ||
| func closeTab(file: CEWorkspaceFile, fromHistory: Bool = false) { | ||
| guard canCloseTab(file: file) else { return } | ||
| if temporaryTab?.file == file { | ||
| temporaryTab = nil | ||
| } | ||
| if !fromHistory { | ||
| clearFuture() | ||
| } | ||
| if file != selectedTab?.file { | ||
| addToHistory(EditorInstance(file: file)) | ||
| } | ||
| removeTab(file) | ||
| if let selectedTab { | ||
| addToHistory(selectedTab) | ||
| } | ||
| // Reset change count to 0 | ||
| file.fileDocument?.updateChangeCount(.changeCleared) | ||
| @@ -148,16 +146,15 @@ final class Editor: ObservableObject, Identifiable { | ||
| // Item is already opened in a tab. | ||
| guard !tabs.contains(item) || !asTemporary else { | ||
| selectedTab = item | ||
| addToHistory(item) | ||
| return | ||
| } | ||
| 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 | ||
| @@ -198,9 +195,8 @@ final class Editor: ObservableObject, Identifiable { | ||
| selectedTab = item | ||
| if !fromHistory { | ||
| clearFuture() | ||
| addToHistory(item) | ||
| } | ||
| do { | ||
| try openFile(item: item) | ||
| @@ -225,31 +221,8 @@ final class Editor: ObservableObject, Identifiable { | ||
| CodeEditDocumentController.shared.addDocument(codeFile) | ||
| } | ||
| /// Check if tab can be closed | ||
| /// | ||
| /// If document edited it will show dialog where user can save document before closing or cancel. | ||
| private func canCloseTab(file: CEWorkspaceFile) -> Bool { | ||
| guard let codeFile = file.fileDocument else { return true } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // | ||
| // EditorHistoryMenus.swift | ||
| // CodeEdit | ||
| // | ||
| // Created by Khan Winter on 7/8/24. | ||
| // | ||
| import SwiftUI | ||
| struct EditorHistoryMenus: View { | ||
| @EnvironmentObject private var editorManager: EditorManager | ||
| @EnvironmentObject private var editor: Editor | ||
| var body: some View { | ||
| Group { | ||
| Menu { | ||
| ForEach( | ||
| Array(editor.history.dropFirst(editor.historyOffset+1).enumerated()), | ||
| id: \.offset | ||
| ) { index, tab in | ||
| Button { | ||
| editorManager.activeEditor = editor | ||
| editor.historyOffset += index + 1 | ||
| } label: { | ||
| HStack { | ||
| tab.file.icon | ||
| Text(tab.file.name) | ||
| } | ||
| } | ||
| } | ||
| } label: { | ||
| Image(systemName: "chevron.left") | ||
| .opacity(editor.historyOffset == editor.history.count - 1 || editor.history.isEmpty ? 0.5 : 1) | ||
| .frame(height: EditorTabBarView.height - 2) | ||
| .padding(.horizontal, 4) | ||
| } primaryAction: { | ||
| editorManager.activeEditor = editor | ||
| editor.goBackInHistory() | ||
| } | ||
| .disabled(editor.historyOffset == editor.history.count - 1 || editor.history.isEmpty) | ||
| .help("Navigate back") | ||
| Menu { | ||
| ForEach( | ||
| Array(editor.history.prefix(editor.historyOffset).reversed().enumerated()), | ||
| id: \.offset | ||
| ) { index, tab in | ||
| Button { | ||
| editorManager.activeEditor = editor | ||
| editor.historyOffset -= index + 1 | ||
| } label: { | ||
| HStack { | ||
| tab.file.icon | ||
| Text(tab.file.name) | ||
| } | ||
| } | ||
| } | ||
| } label: { | ||
| Image(systemName: "chevron.right") | ||
| .opacity(editor.historyOffset == 0 ? 0.5 : 1) | ||
| .frame(height: EditorTabBarView.height - 2) | ||
| .padding(.horizontal, 4) | ||
| } primaryAction: { | ||
| editorManager.activeEditor = editor | ||
| editor.goForwardInHistory() | ||
| } | ||
| .disabled(editor.historyOffset == 0) | ||
| .help("Navigate forward") | ||
| } | ||
| .buttonStyle(.icon) | ||
| .controlSize(.small) | ||
| .font(EditorTabBarAccessoryIcon.iconFont) | ||
| } | ||
| } | ||
| #Preview { | ||
| EditorHistoryMenus() | ||
| } |
Uh oh!
There was an error while loading.Please reload this page.