Skip to content

Commit

Permalink
✨ Added more context menu tools and and setup for the future
Browse files Browse the repository at this point in the history
  • Loading branch information
thom1606 committed Nov 19, 2024
1 parent 1899e76 commit bfbc548
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 112 deletions.
2 changes: 1 addition & 1 deletion Astrix/Sources/Utilities/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public enum SupportedApps: String, CaseIterable {
case hyper = "co.zeit.hyper"
// MARK: - Editors
case none = "NONE"
case xcode = "com.apple.Xcode"
case xcode = "com.apple.dt.Xcode"
case vsCode = "com.microsoft.VSCode"
case vsCodeInsiders = "com.microsoft.VSCodeInsiders"
case atom = "com.github.atom"
Expand Down
7 changes: 2 additions & 5 deletions Astrix/Sources/Utilities/Scripting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ class Scripting {
.appendingPathExtension(Constants.Scripting.ToolsFileExtension)
let toolsScript = """
on runCommand(command)
tell application "Finder"
activate
do shell script command
end tell
do shell script command
end runCommand
"""
try writeScriptIfNeeded(at: toolsPath, with: toolsScript)
Expand Down Expand Up @@ -81,7 +78,7 @@ class Scripting {
return event
}

private func isAppInstalled(bundleIdentifier: String) -> Bool {
public func isAppInstalled(bundleIdentifier: String) -> Bool {
let workspace = NSWorkspace.shared
return workspace.urlForApplication(withBundleIdentifier: bundleIdentifier) != nil
}
Expand Down
163 changes: 84 additions & 79 deletions FinderTools/FinderSync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,99 +45,33 @@ class FinderSync: FIFinderSync {

switch menuKind {
case .contextualMenuForContainer,
.contextualMenuForItems:
let itemPaths = FIFinderSyncController.default().selectedItemURLs()
let workspacePath = FIFinderSyncController.default().targetedURL()

.contextualMenuForItems:
let astrixMenu = NSMenu(title: "")

// Check if there are any selected files which could be copied to the clipboard
if itemPaths != nil && !itemPaths!.isEmpty {
if itemPaths!.first!.relativePath != workspacePath?.relativePath {
// Create items menu title
astrixMenu.addItem(Utilities.createTitleItem(title: "Item"))

// Create the copy path for the items
var title = NSLocalizedString("Copy Path", comment: "")
if itemPaths!.count > 1 {
title = NSLocalizedString("Copy Paths", comment: "")
}
let copyPathItem = NSMenuItem(title: title, action: #selector(copyItemPath(_:)), keyEquivalent: "")
astrixMenu.addItem(copyPathItem)
}
}

// Create workspace menu title
astrixMenu.addItem(Utilities.createTitleItem(title: "Workspace"))

// Add all the workspace items
for item in getWorkspaceItems() {
astrixMenu.addItem(item)
}
addSection(ItemSection(), to: astrixMenu)
addSection(WorkspaceSection(), to: astrixMenu)

// Create the main menu button
let mainMenuItem = NSMenuItem(title: "Astrix", action: nil, keyEquivalent: "")
mainMenuItem.submenu = astrixMenu
menu.addItem(mainMenuItem)
case .toolbarItemMenu:
for item in getWorkspaceItems() {
menu.addItem(item)
}
addSection(WorkspaceSection(), to: menu)
addSection(SuggestionsSection(), to: menu)
default:
return nil
}

return menu
}

private func getWorkspaceItems() -> [NSMenuItem] {
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let terminalKey = userDefaults?.string(forKey: Constants.Id.DefaultTerminalKey) ?? SupportedApps.none.rawValue
let editorKey = userDefaults?.string(forKey: Constants.Id.DefaultEditorKey) ?? SupportedApps.none.rawValue

var result: [NSMenuItem] = []

// Open a terminal in this workspace
if terminalKey != SupportedApps.none.rawValue {
let openInTerminalItem = NSMenuItem(title: NSLocalizedString("Open in Terminal", comment: ""), action: #selector(openInTerminal(_:)), keyEquivalent: "")
result.append(openInTerminalItem)
private func addSection(_ section: AstrixSection, to menu: NSMenu, showTitle: Bool = true) {
let items = section.getSectionItems()
if showTitle && !items.isEmpty {
menu.addItem(Utilities.createTitleItem(title: section.sectionName))
}
// Open the editor in this workspace
if editorKey != SupportedApps.none.rawValue {
let openInEditorItem = NSMenuItem(title: NSLocalizedString("Open in Editor", comment: ""), action: #selector(openInEditor(_:)), keyEquivalent: "")
result.append(openInEditorItem)
}

// Copy the path of the current workspace
let copyPathItem = NSMenuItem(title: NSLocalizedString("Copy Workspace Path", comment: ""), action: #selector(copyWorkspacePath(_:)), keyEquivalent: "")
result.append(copyPathItem)

return result
}

// MARK: -- Actions
/// Open the current workspace in a new terminal
@objc func openInTerminal(_ sender: AnyObject?) {
// Get the preferred terminal
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let bundleIdString = userDefaults?.string(forKey: Constants.Id.DefaultTerminalKey) ?? SupportedApps.terminal.rawValue
let bundleId = SupportedApps(rawValue: bundleIdString) ?? .terminal
// Try to open the app with the bundle Id
if !Utilities.openApp(bundleId: bundleId) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open your terminal.", comment: ""))
}
}

/// Open the current workspace in the users preferred editor
@objc func openInEditor(_ sender: AnyObject?) {
// Get the preferred editor
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let bundleIdString = userDefaults?.string(forKey: Constants.Id.DefaultEditorKey) ?? SupportedApps.none.rawValue
let bundleId = SupportedApps(rawValue: bundleIdString) ?? .none
// Try to open the app with the bundle Id
if !Utilities.openApp(bundleId: bundleId) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open your editor of choice.", comment: ""))
for item in items {
menu.addItem(item)
}
}

Expand Down Expand Up @@ -166,8 +100,35 @@ class FinderSync: FIFinderSync {
}
}

// MARK: -- Workspace Actions
/// Open the current workspace in the users preferred terminal
@objc public func openInTerminal(_ sender: AnyObject?) {
// Get the preferred terminal
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let bundleIdString = userDefaults?.string(forKey: Constants.Id.DefaultTerminalKey) ?? SupportedApps.terminal.rawValue
let bundleId = SupportedApps(rawValue: bundleIdString) ?? .terminal
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: bundleId) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open your terminal.", comment: ""))
}
}

/// Open the current workspace in the users preferred editor
@objc open func openInEditor(_ sender: AnyObject?) {
// Get the preferred editor
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let bundleIdString = userDefaults?.string(forKey: Constants.Id.DefaultEditorKey) ?? SupportedApps.none.rawValue
let bundleId = SupportedApps(rawValue: bundleIdString) ?? .none
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: bundleId) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open your editor of choice.", comment: ""))
}
}

/// Copy the path of the workspace folder
@objc func copyWorkspacePath(_ sender: AnyObject?) {
@objc open func copyWorkspacePath(_ sender: AnyObject?) {
// Get the workspace path
let workspacePath = FIFinderSyncController.default().targetedURL()

Expand All @@ -183,4 +144,48 @@ class FinderSync: FIFinderSync {
pasteboard.setString(workspacePath!.relativePath, forType: .string)
Utilities.showNotification(title: NSLocalizedString("Copied!", comment: ""), body: NSLocalizedString("The path is copied to your clipboard.", comment: ""))
}

// MARK: -- Item Actions
/// Open the current selected file/folder in the users preferred editor
@objc open func openItemInEditor(_ sender: AnyObject?) {
// Get the preferred editor
let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let bundleIdString = userDefaults?.string(forKey: Constants.Id.DefaultEditorKey) ?? SupportedApps.none.rawValue
let bundleId = SupportedApps(rawValue: bundleIdString) ?? .none
// Try to open the app with the bundle Id
if !Utilities.openSelectedInApp(bundleId: bundleId) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open your editor of choice.", comment: ""))
}
}

// MARK: -- Suggestion Actions
@objc func openInVSCode(_ sender: Any) {
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: .vsCode) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open the workspace in \(Utilities.getBundleApplicationName(bundleId: .vsCode)).", comment: ""))
}
}
@objc func openInVSCodeInsiders(_ sender: Any) {
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: .vsCodeInsiders) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open the workspace in \(Utilities.getBundleApplicationName(bundleId: .vsCodeInsiders)).", comment: ""))
}
}
@objc func openInCursor(_ sender: Any) {
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: .cursor) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open the workspace in \(Utilities.getBundleApplicationName(bundleId: .cursor)).", comment: ""))
}
}
@objc func openInXCode(_ sender: Any) {
// Try to open the app with the bundle Id
if !Utilities.openWorkspaceInApp(bundleId: .xcode) {
// Warn the user if it failed
Utilities.showNotification(title: NSLocalizedString("Oops!", comment: ""), body: NSLocalizedString("We were not able to open the workspace in \(Utilities.getBundleApplicationName(bundleId: .xcode)).", comment: ""))
}
}
}
7 changes: 7 additions & 0 deletions FinderTools/Sources/AstrixSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import SwiftUI

protocol AstrixSection {
var sectionName: String { get }

func getSectionItems() -> [NSMenuItem]
}
33 changes: 33 additions & 0 deletions FinderTools/Sources/ItemSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// SuggestionsSection.swift
// FinderTools
//
// Created by Thom van den Broek on 19/11/2024.
//

import SwiftUI
import FinderSync

class ItemSection: AstrixSection {
var sectionName: String { NSLocalizedString("Item", comment: "Item section") }

func getSectionItems() -> [NSMenuItem] {
var result: [NSMenuItem] = []

if let workspacePath = FIFinderSyncController.default().targetedURL(),
let itemPaths = FIFinderSyncController.default().selectedItemURLs() {
// Only allow it on single items
if itemPaths.count > 1 || workspacePath.relativePath == itemPaths.first?.relativePath { return [] }

let userDefaults = UserDefaults(suiteName: Constants.Id.DefaultsDomain)
let editorKey = userDefaults?.string(forKey: Constants.Id.DefaultEditorKey) ?? SupportedApps.none.rawValue

// Open the editor in this workspace
if editorKey != SupportedApps.none.rawValue {
let openInEditorItem = NSMenuItem(title: NSLocalizedString("Open in Editor", comment: ""), action: #selector(FinderSync.openItemInEditor(_:)), keyEquivalent: "")
result.append(openInEditorItem)
}
}
return result
}
}
62 changes: 62 additions & 0 deletions FinderTools/Sources/SuggestionsSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// SuggestionsSection.swift
// FinderTools
//
// Created by Thom van den Broek on 19/11/2024.
//

import SwiftUI
import FinderSync

class SuggestionsSection: AstrixSection {
var sectionName: String { NSLocalizedString("Suggestions", comment: "Suggestions section") }

func getSectionItems() -> [NSMenuItem] {
var result: [NSMenuItem] = []

if let workspacePath = FIFinderSyncController.default().targetedURL() {
// check if the folder contains a .vscode folder
if FileManager.default.fileExists(atPath: (workspacePath.appendingPathComponent(".vscode")).path) {
if Scripting.shared.isAppInstalled(bundleIdentifier: SupportedApps.vsCode.rawValue) {
let item = NSMenuItem(
title: NSLocalizedString("Open in \(Utilities.getBundleApplicationName(bundleId: .vsCode))", comment: ""),
action: #selector(FinderSync.openInVSCode(_:)),
keyEquivalent: ""
)
result.append(item)
}
if Scripting.shared.isAppInstalled(bundleIdentifier: SupportedApps.vsCodeInsiders.rawValue) {
let item = NSMenuItem(
title: NSLocalizedString("Open in \(Utilities.getBundleApplicationName(bundleId: .vsCodeInsiders))", comment: ""),
action: #selector(FinderSync.openInVSCodeInsiders(_:)),
keyEquivalent: ""
)
result.append(item)
}
if Scripting.shared.isAppInstalled(bundleIdentifier: SupportedApps.cursor.rawValue) {
let item = NSMenuItem(
title: NSLocalizedString("Open in \(Utilities.getBundleApplicationName(bundleId: .cursor))", comment: ""),
action: #selector(FinderSync.openInCursor(_:)),
keyEquivalent: ""
)
result.append(item)
}
}

// check if there is any file ending with .xcodeproj
let (success, response) = Utilities.runCommand("cd '\(workspacePath.relativePath)' && find *.xcodeproj -d 0")
NSLog("success: \(success), res: \(response)")
NSLog("installed: \(Scripting.shared.isAppInstalled(bundleIdentifier: SupportedApps.xcode.rawValue))")
if success && Scripting.shared.isAppInstalled(bundleIdentifier: SupportedApps.xcode.rawValue) {
let item = NSMenuItem(
title: NSLocalizedString("Open in \(Utilities.getBundleApplicationName(bundleId: .xcode))", comment: ""),
action: #selector(FinderSync.openInXCode(_:)),
keyEquivalent: ""
)
result.append(item)
}
}

return result
}
}
Loading

0 comments on commit bfbc548

Please sign in to comment.