From a09eaf72175260814aea26481c9b3e704242580e Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Sun, 21 Apr 2024 18:00:43 -0400 Subject: [PATCH 1/9] keyboard shortcut for tab switching --- .../Editor/TabBar/Tabs/Views/EditorTabs.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift index dc0d3cd57b..f23b408783 100644 --- a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift +++ b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift @@ -114,6 +114,8 @@ struct EditorTabs: View { CGFloat(140) ) } + + // Disable the rule because this function is implementing the drag gesture and its animations. // It is fairly complicated, so ignore the function body length limitation for now. @@ -271,6 +273,17 @@ struct EditorTabs: View { } } } + private func selectNextTab() { + guard let currentIndex = openedTabs.firstIndex(of: editor.selectedTab?.file.id ?? "") else { return } + let nextIndex = (currentIndex + 1) % openedTabs.count // Wraps around to the first tab if it's the last one + editor.selectedTab = editor.tabs.first { $0.file.id == openedTabs[nextIndex] } + } + + private func selectPreviousTab() { + guard let currentIndex = openedTabs.firstIndex(of: editor.selectedTab?.file.id ?? ""), !openedTabs.isEmpty else { return } + let previousIndex = (currentIndex - 1 + openedTabs.count) % openedTabs.count // Wraps around to the last tab if it's the first one + editor.selectedTab = editor.tabs.first { $0.file.id == openedTabs[previousIndex] } + } // swiftlint:enable function_body_length cyclomatic_complexity @@ -385,6 +398,17 @@ struct EditorTabs: View { EditorTabBarNativeInactiveBackground() } } + // keyboard shortcuts to go to next and previous tabs + Button(action: selectNextTab) { + EmptyView() + } + .hidden() + .keyboardShortcut("]", modifiers: [.command, .shift]) + Button(action: selectPreviousTab) { + EmptyView() + } + .hidden() + .keyboardShortcut("[", modifiers: [.command, .shift]) } .background { if tabBarStyle == .native { @@ -412,6 +436,7 @@ struct EditorTabs: View { } } } + } private struct EditorTabOnDropDelegate: DropDelegate { From 5aff25fb67eae1e4a83661b50a0e048d19a48d62 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Mon, 22 Apr 2024 11:46:12 -0400 Subject: [PATCH 2/9] extension and menu bar --- CodeEdit.xcodeproj/project.pbxproj | 4 +++ .../Models/EditorTabSwitchExtension.swift | 32 +++++++++++++++++++ .../Editor/TabBar/Tabs/Views/EditorTabs.swift | 23 ------------- .../WindowCommands/NavigateCommands.swift | 15 +++++---- 4 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 CodeEdit/Features/Editor/Models/EditorTabSwitchExtension.swift diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 5594d6f41c..01fa7af19b 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -248,6 +248,7 @@ 58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 58F2EB1D292FB954004A9BDE /* Sparkle */; }; 58FD7608291EA1CB0051D6E4 /* QuickActionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7605291EA1CB0051D6E4 /* QuickActionsViewModel.swift */; }; 58FD7609291EA1CB0051D6E4 /* QuickActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD7607291EA1CB0051D6E4 /* QuickActionsView.swift */; }; + 5994B6DA2BD6B408006A4C5F /* EditorTabSwitchExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5994B6D92BD6B408006A4C5F /* EditorTabSwitchExtension.swift */; }; 5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */; }; 5B698A0A2B262FA000DE9392 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A092B262FA000DE9392 /* SearchSettingsView.swift */; }; 5B698A0D2B26327800DE9392 /* SearchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B698A0C2B26327800DE9392 /* SearchSettings.swift */; }; @@ -810,6 +811,7 @@ 58F2EAE1292FB2B0004A9BDE /* SoftwareUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareUpdater.swift; sourceTree = ""; }; 58FD7605291EA1CB0051D6E4 /* QuickActionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionsViewModel.swift; sourceTree = ""; }; 58FD7607291EA1CB0051D6E4 /* QuickActionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionsView.swift; sourceTree = ""; }; + 5994B6D92BD6B408006A4C5F /* EditorTabSwitchExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorTabSwitchExtension.swift; sourceTree = ""; }; 5B241BF22B6DDBFF0016E616 /* IgnorePatternListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IgnorePatternListItemView.swift; sourceTree = ""; }; 5B698A092B262FA000DE9392 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = ""; }; 5B698A0C2B26327800DE9392 /* SearchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettings.swift; sourceTree = ""; }; @@ -2752,6 +2754,7 @@ 6CA1AE942B46950000378EAB /* EditorInstance.swift */, 6CC9E4B129B5669900C97388 /* Environment+ActiveEditor.swift */, 6C092ED92A53A58600489202 /* EditorLayout+StateRestoration.swift */, + 5994B6D92BD6B408006A4C5F /* EditorTabSwitchExtension.swift */, ); path = Models; sourceTree = ""; @@ -3475,6 +3478,7 @@ 581550CF29FBD30400684881 /* StandardTableViewCell.swift in Sources */, B62AEDB82A1FE2DC009A9F52 /* UtilityAreaOutputView.swift in Sources */, B67DB0FC2AFDF71F002DC647 /* IconToggleStyle.swift in Sources */, + 5994B6DA2BD6B408006A4C5F /* EditorTabSwitchExtension.swift in Sources */, 587B9E5C29301D8F00AC7927 /* Parameters.swift in Sources */, 61538B932B11201900A88846 /* String+Character.swift in Sources */, 613DF55E2B08DD5D00E9D902 /* FileHelper.swift in Sources */, diff --git a/CodeEdit/Features/Editor/Models/EditorTabSwitchExtension.swift b/CodeEdit/Features/Editor/Models/EditorTabSwitchExtension.swift new file mode 100644 index 0000000000..80729e592a --- /dev/null +++ b/CodeEdit/Features/Editor/Models/EditorTabSwitchExtension.swift @@ -0,0 +1,32 @@ +// +// EditorTabSwitchExtension.swift +// CodeEdit +// +// Created by Roscoe Rubin-Rottenberg on 4/22/24. +// + +import Foundation + +extension Editor { + func selectNextTab() { + guard let currentTab = selectedTab, let currentIndex = tabs.firstIndex(of: currentTab) else { return } + let nextIndex = tabs.index(after: currentIndex) + if nextIndex < tabs.endIndex { + selectedTab = tabs[nextIndex] + } else { + // Wrap around to the first tab if it's the last one + selectedTab = tabs.first + } + } + + func selectPreviousTab() { + guard let currentTab = selectedTab, let currentIndex = tabs.firstIndex(of: currentTab) else { return } + let previousIndex = tabs.index(before: currentIndex) + if previousIndex >= tabs.startIndex { + selectedTab = tabs[previousIndex] + } else { + // Wrap around to the last tab if it's the first one + selectedTab = tabs.last + } + } +} diff --git a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift index f23b408783..32d2e53133 100644 --- a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift +++ b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift @@ -273,17 +273,6 @@ struct EditorTabs: View { } } } - private func selectNextTab() { - guard let currentIndex = openedTabs.firstIndex(of: editor.selectedTab?.file.id ?? "") else { return } - let nextIndex = (currentIndex + 1) % openedTabs.count // Wraps around to the first tab if it's the last one - editor.selectedTab = editor.tabs.first { $0.file.id == openedTabs[nextIndex] } - } - - private func selectPreviousTab() { - guard let currentIndex = openedTabs.firstIndex(of: editor.selectedTab?.file.id ?? ""), !openedTabs.isEmpty else { return } - let previousIndex = (currentIndex - 1 + openedTabs.count) % openedTabs.count // Wraps around to the last tab if it's the first one - editor.selectedTab = editor.tabs.first { $0.file.id == openedTabs[previousIndex] } - } // swiftlint:enable function_body_length cyclomatic_complexity @@ -398,17 +387,6 @@ struct EditorTabs: View { EditorTabBarNativeInactiveBackground() } } - // keyboard shortcuts to go to next and previous tabs - Button(action: selectNextTab) { - EmptyView() - } - .hidden() - .keyboardShortcut("]", modifiers: [.command, .shift]) - Button(action: selectPreviousTab) { - EmptyView() - } - .hidden() - .keyboardShortcut("[", modifiers: [.command, .shift]) } .background { if tabBarStyle == .native { @@ -436,7 +414,6 @@ struct EditorTabs: View { } } } - } private struct EditorTabOnDropDelegate: DropDelegate { diff --git a/CodeEdit/Features/WindowCommands/NavigateCommands.swift b/CodeEdit/Features/WindowCommands/NavigateCommands.swift index 4757ece15c..113e0c0e6f 100644 --- a/CodeEdit/Features/WindowCommands/NavigateCommands.swift +++ b/CodeEdit/Features/WindowCommands/NavigateCommands.swift @@ -39,18 +39,20 @@ struct NavigateCommands: Commands { Divider() } - Group { Button("Show Previous Tab") { - + editor?.selectPreviousTab() } - .disabled(true) + .keyboardShortcut("{", modifiers: [.command]) + .disabled(editor?.tabs.count ?? 0 <= 1) // Disable if there's one or no tabs Button("Show Next Tab") { - + editor?.selectNextTab() } - .disabled(true) - + .keyboardShortcut("}", modifiers: [.command]) + .disabled(editor?.tabs.count ?? 0 <= 1) // Disable if there's one or no tabs + } + Group { Divider() Button("Go Forward") { @@ -62,6 +64,7 @@ struct NavigateCommands: Commands { editor?.goBackInHistory() } .disabled(!(editor?.canGoBackInHistory ?? false)) + } .disabled(editor == nil) } From d6e944e3253e96e65403f0d8c544f452bd142126 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Mon, 22 Apr 2024 14:36:03 -0400 Subject: [PATCH 3/9] resolve swiftlint violations --- CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift | 3 --- CodeEdit/Features/WindowCommands/NavigateCommands.swift | 1 - 2 files changed, 4 deletions(-) diff --git a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift index 32d2e53133..2cfbb8f62f 100644 --- a/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift +++ b/CodeEdit/Features/Editor/TabBar/Tabs/Views/EditorTabs.swift @@ -114,9 +114,6 @@ struct EditorTabs: View { CGFloat(140) ) } - - - // Disable the rule because this function is implementing the drag gesture and its animations. // It is fairly complicated, so ignore the function body length limitation for now. // swiftlint:disable function_body_length cyclomatic_complexity diff --git a/CodeEdit/Features/WindowCommands/NavigateCommands.swift b/CodeEdit/Features/WindowCommands/NavigateCommands.swift index 113e0c0e6f..785dd9db60 100644 --- a/CodeEdit/Features/WindowCommands/NavigateCommands.swift +++ b/CodeEdit/Features/WindowCommands/NavigateCommands.swift @@ -64,7 +64,6 @@ struct NavigateCommands: Commands { editor?.goBackInHistory() } .disabled(!(editor?.canGoBackInHistory ?? false)) - } .disabled(editor == nil) } From ff43881fed83b61ea8b9de2caee55f4779978d35 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 00:17:28 -0400 Subject: [PATCH 4/9] searchable in spotlight --- .../Welcome/Views/RecentProjectsListView.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index 084b1231a3..ff1b72c36e 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import CoreSpotlight struct RecentProjectsListView: View { @@ -25,6 +26,20 @@ struct RecentProjectsListView: View { let projectsURL = recentProjectPaths.map { URL(filePath: $0) } _selection = .init(initialValue: Set(projectsURL.prefix(1))) _recentProjects = .init(initialValue: projectsURL) + donateSearchableItems() + } + func donateSearchableItems() { + let searchableItems = recentProjects.map { entity -> CSSearchableItem in + let attributeSet = CSSearchableItemAttributeSet(contentType: .content) + attributeSet.title = String(entity.lastPathComponent) + attributeSet.relatedUniqueIdentifier = String(entity.path()) + return CSSearchableItem( + uniqueIdentifier: String(entity.path()), + domainIdentifier: "app.codeedit.CodeEdit.ProjectItem", + attributeSet: attributeSet + ) + } + CSSearchableIndex.default().indexSearchableItems(searchableItems) } var listEmptyView: some View { From 029f57a16d0bd71b494124367b80db52dd665772 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 08:50:26 -0400 Subject: [PATCH 5/9] move function to bottom --- .../Views/RecentProjectsListView.swift | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index ff1b72c36e..ac5071f792 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -28,19 +28,6 @@ struct RecentProjectsListView: View { _recentProjects = .init(initialValue: projectsURL) donateSearchableItems() } - func donateSearchableItems() { - let searchableItems = recentProjects.map { entity -> CSSearchableItem in - let attributeSet = CSSearchableItemAttributeSet(contentType: .content) - attributeSet.title = String(entity.lastPathComponent) - attributeSet.relatedUniqueIdentifier = String(entity.path()) - return CSSearchableItem( - uniqueIdentifier: String(entity.path()), - domainIdentifier: "app.codeedit.CodeEdit.ProjectItem", - attributeSet: attributeSet - ) - } - CSSearchableIndex.default().indexSearchableItems(searchableItems) - } var listEmptyView: some View { VStack { @@ -133,4 +120,18 @@ struct RecentProjectsListView: View { let projectsURL = recentProjectPaths.map { URL(filePath: $0) } recentProjects = projectsURL } + + func donateSearchableItems() { + let searchableItems = recentProjects.map { entity -> CSSearchableItem in + let attributeSet = CSSearchableItemAttributeSet(contentType: .content) + attributeSet.title = String(entity.lastPathComponent) + attributeSet.relatedUniqueIdentifier = String(entity.path()) + return CSSearchableItem( + uniqueIdentifier: String(entity.path()), + domainIdentifier: "app.codeedit.CodeEdit.ProjectItem", + attributeSet: attributeSet + ) + } + CSSearchableIndex.default().indexSearchableItems(searchableItems) + } } From 89dc6986698f46a2446157be16a0b43b0bcc51a6 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 09:01:24 -0400 Subject: [PATCH 6/9] remove unneeded casting and add error handling --- .../Welcome/Views/RecentProjectsListView.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index ac5071f792..ca256178ea 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -124,14 +124,21 @@ struct RecentProjectsListView: View { func donateSearchableItems() { let searchableItems = recentProjects.map { entity -> CSSearchableItem in let attributeSet = CSSearchableItemAttributeSet(contentType: .content) - attributeSet.title = String(entity.lastPathComponent) - attributeSet.relatedUniqueIdentifier = String(entity.path()) + attributeSet.title = entity.lastPathComponent + attributeSet.relatedUniqueIdentifier = entity.path() return CSSearchableItem( - uniqueIdentifier: String(entity.path()), + uniqueIdentifier: entity.path(), domainIdentifier: "app.codeedit.CodeEdit.ProjectItem", attributeSet: attributeSet ) } - CSSearchableIndex.default().indexSearchableItems(searchableItems) + CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in + if let error = error { + print(error) + } else { + print("Successfully indexed") + } + + } } } From a9952a04a45716dbfcb8f06ed374226369658076 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 10:38:47 -0400 Subject: [PATCH 7/9] remove CSSearchableItem type --- .../Features/Welcome/Views/RecentProjectsListView.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index ca256178ea..ade3732179 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -120,9 +120,8 @@ struct RecentProjectsListView: View { let projectsURL = recentProjectPaths.map { URL(filePath: $0) } recentProjects = projectsURL } - - func donateSearchableItems() { - let searchableItems = recentProjects.map { entity -> CSSearchableItem in + public func donateSearchableItems() { + let searchableItems = recentProjects.map { entity in let attributeSet = CSSearchableItemAttributeSet(contentType: .content) attributeSet.title = entity.lastPathComponent attributeSet.relatedUniqueIdentifier = entity.path() @@ -138,7 +137,6 @@ struct RecentProjectsListView: View { } else { print("Successfully indexed") } - } } } From 13f9db9a38b8ad7248b1760bc76069c3a228f87a Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 11:00:02 -0400 Subject: [PATCH 8/9] no success message, space between funcs, and no public func --- CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index ade3732179..1abb49807d 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -120,7 +120,8 @@ struct RecentProjectsListView: View { let projectsURL = recentProjectPaths.map { URL(filePath: $0) } recentProjects = projectsURL } - public func donateSearchableItems() { + + func donateSearchableItems() { let searchableItems = recentProjects.map { entity in let attributeSet = CSSearchableItemAttributeSet(contentType: .content) attributeSet.title = entity.lastPathComponent @@ -134,8 +135,6 @@ struct RecentProjectsListView: View { CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in if let error = error { print(error) - } else { - print("Successfully indexed") } } } From 95f34edee3486c997530da066ff1013c476ac074 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 23 Apr 2024 11:05:42 -0400 Subject: [PATCH 9/9] fix whitespace violation --- CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift index 1abb49807d..0350a7fd33 100644 --- a/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift +++ b/CodeEdit/Features/Welcome/Views/RecentProjectsListView.swift @@ -120,7 +120,7 @@ struct RecentProjectsListView: View { let projectsURL = recentProjectPaths.map { URL(filePath: $0) } recentProjects = projectsURL } - + func donateSearchableItems() { let searchableItems = recentProjects.map { entity in let attributeSet = CSSearchableItemAttributeSet(contentType: .content)