mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-07 20:10:08 +02:00
♻️ Change app detection, detect apps upfront
This commit is contained in:
@ -9,6 +9,8 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
|
54AB03262763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
||||||
|
54AB03272763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
@ -120,6 +122,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
||||||
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
|
54AB03252763858F00A29D5F /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
|
||||||
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
||||||
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
||||||
@ -328,6 +331,7 @@
|
|||||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
||||||
|
54AB03252763858F00A29D5F /* Timer.swift */,
|
||||||
);
|
);
|
||||||
path = Helpers;
|
path = Helpers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -552,6 +556,7 @@
|
|||||||
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
|
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
||||||
@ -577,6 +582,7 @@
|
|||||||
files = (
|
files = (
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||||
|
54AB03272763858F00A29D5F /* Timer.swift in Sources */,
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
|
||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
|
@ -57,10 +57,13 @@ class App {
|
|||||||
var currentInstall: ActivePhpInstallation? = nil
|
var currentInstall: ActivePhpInstallation? = nil
|
||||||
|
|
||||||
/** All available versions of PHP. */
|
/** All available versions of PHP. */
|
||||||
var availablePhpVersions : [String] = []
|
var availablePhpVersions: [String] = []
|
||||||
|
|
||||||
/** Cached information about the PHP installations. */
|
/** Cached information about the PHP installations. */
|
||||||
var cachedPhpInstallations : [String: PhpInstallation] = [:]
|
var cachedPhpInstallations: [String: PhpInstallation] = [:]
|
||||||
|
|
||||||
|
/** List of detected (installed) applications that PHP Monitor can work with. */
|
||||||
|
var detectedApplications: [Application] = []
|
||||||
|
|
||||||
/** Timer that will periodically reload info about the user's PHP installation. */
|
/** Timer that will periodically reload info about the user's PHP installation. */
|
||||||
var timer: Timer?
|
var timer: Timer?
|
||||||
|
@ -8,43 +8,26 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
/// An application that is capable of opening a particular directory (usually of a PHP project).
|
/// An application that is capable of opening a particular directory (usually of a PHP project).
|
||||||
/// In most cases this is going to be a code editor, but it could also be another application
|
/// In most cases this is going to be a code editor, but it could also be another application
|
||||||
/// that supports opening those directories, like a visual Git client or a terminal app.
|
/// that supports opening those directories, like a visual Git client or a terminal app.
|
||||||
class Application {
|
class Application {
|
||||||
|
|
||||||
/// Name of the app. Used for display purposes.
|
enum AppType {
|
||||||
|
case editor, browser, gitgui, terminal
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name of the app. Used for display purposes and to determine `name.app` exists.
|
||||||
let name: String
|
let name: String
|
||||||
|
|
||||||
/// Paths to check whether the application is actually installed.
|
/// Application type. Depending on the type, a different action might occur.
|
||||||
/// If the app finds any of these, the app is considered installed.
|
let type: AppType
|
||||||
let pathsToVerifyInstalled: [String]
|
|
||||||
|
|
||||||
/// Path to the binary that actually opens the directory.
|
/// Initializer.
|
||||||
let pathToBinary: String
|
init(_ name: String, _ type: AppType) {
|
||||||
|
|
||||||
/// Instruction that needs to be followed in order to ensure that the app can open with this editor.
|
|
||||||
var missingBinaryInstruction: String? = nil
|
|
||||||
|
|
||||||
/// Callback that is executed to open a particular folder.
|
|
||||||
/// Must open the directory in the requested app, usually by using `pathToBinary`.
|
|
||||||
@objc let openCallback: (String) -> Void
|
|
||||||
|
|
||||||
/**
|
|
||||||
- Parameter name: Name of the application.
|
|
||||||
- Parameter installPath: Files to verify, if any file exists here the app is considered present on the system.
|
|
||||||
- Parameter binaryPath: Additional file that is used to open a specific path.
|
|
||||||
- Parameter open: Callback used to open a specific directory in the editor in question.
|
|
||||||
- Parameter instruction: Instruction for end user that needs to be followed in order to ensure the `binaryPath exists.
|
|
||||||
*/
|
|
||||||
init(name: String, installPaths: [String], binaryPath: String, open: @escaping ((String) -> Void), instruction: String? = nil) {
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.pathsToVerifyInstalled = installPaths.map({ path in
|
self.type = type
|
||||||
return path.replacingOccurrences(of: " ", with: "\\ ")
|
|
||||||
})
|
|
||||||
self.pathToBinary = binaryPath.replacingOccurrences(of: " ", with: "\\ ")
|
|
||||||
self.openCallback = open
|
|
||||||
self.missingBinaryInstruction = instruction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,22 +35,17 @@ class Application {
|
|||||||
This will open the editor if it isn't open yet.
|
This will open the editor if it isn't open yet.
|
||||||
*/
|
*/
|
||||||
@objc public func openDirectory(file: String) {
|
@objc public func openDirectory(file: String) {
|
||||||
self.openCallback(file)
|
return Shell.run("/usr/bin/open -a \(self.name) \(file)")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks if the app is installed. */
|
/** Checks if the app is installed. */
|
||||||
func isInstalled() -> Bool {
|
func isInstalled() -> Bool {
|
||||||
// TODO: Alternative way to detect if an app is installed:
|
// If this script does not complain, the app exists!
|
||||||
// mdfind "kMDItemKind == 'Application'" | grep AppName.app
|
return Shell.user.execute(
|
||||||
// This will return the path to the application. Worth a refactor?
|
"/usr/bin/open -Ra \"\(self.name)\"",
|
||||||
self.pathsToVerifyInstalled.map({ path in
|
requiresPath: false,
|
||||||
Shell.fileExists(path)
|
waitUntilExit: true
|
||||||
}).contains(true)
|
).task.terminationStatus == 0
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks if the correct binary required to open directories and/or files exists. */
|
|
||||||
func hasBinary() -> Bool {
|
|
||||||
return Shell.fileExists(self.pathToBinary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,66 +53,13 @@ class Application {
|
|||||||
*/
|
*/
|
||||||
static public func detectPresetApplications() -> [Application] {
|
static public func detectPresetApplications() -> [Application] {
|
||||||
return [
|
return [
|
||||||
Application(
|
Application("PhpStorm", .editor),
|
||||||
name: "PhpStorm",
|
Application("Visual Studio Code", .editor),
|
||||||
installPaths: [
|
Application("Sublime Text", .editor),
|
||||||
"/Applications/PhpStorm.app/Contents/Info.plist",
|
Application("Sublime Merge", .gitgui),
|
||||||
"/usr/local/bin/pstorm"
|
Application("iTerm", .terminal)
|
||||||
],
|
].filter {
|
||||||
binaryPath: "/usr/local/bin/pstorm",
|
return $0.isInstalled()
|
||||||
open: { path in
|
}
|
||||||
Shell.run("/usr/local/bin/pstorm \(path)")
|
|
||||||
},
|
|
||||||
instruction: "editors.pstorm_binary_not_linked.desc".localized
|
|
||||||
),
|
|
||||||
Application(
|
|
||||||
name: "PhpStorm (via Toolbox)",
|
|
||||||
installPaths: [
|
|
||||||
"~/Applications/JetBrains Toolbox/PhpStorm.app/Contents/Info.plist",
|
|
||||||
"/usr/local/bin/phpstorm"
|
|
||||||
],
|
|
||||||
binaryPath: "/usr/local/bin/phpstorm",
|
|
||||||
open: { path in
|
|
||||||
Shell.run("/usr/local/bin/phpstorm \(path)")
|
|
||||||
},
|
|
||||||
instruction: "editors.phpstorm_binary_not_linked.desc".localized
|
|
||||||
),
|
|
||||||
Application(
|
|
||||||
name: "Visual Studio Code",
|
|
||||||
installPaths: [
|
|
||||||
"/Applications/Visual Studio Code.app/Contents/Info.plist",
|
|
||||||
"/usr/local/bin/code"
|
|
||||||
],
|
|
||||||
binaryPath: "/usr/local/bin/code",
|
|
||||||
open: { path in
|
|
||||||
Shell.run("/usr/local/bin/code \(path)")
|
|
||||||
},
|
|
||||||
instruction: "editors.code_binary_not_linked.desc".localized
|
|
||||||
),
|
|
||||||
Application(
|
|
||||||
name: "Sublime Text",
|
|
||||||
installPaths: ["/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl"],
|
|
||||||
binaryPath: "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl",
|
|
||||||
open: { path in
|
|
||||||
Shell.run("/Applications/Sublime\\ Text.app/Contents/SharedSupport/bin/subl \(path)")
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Application(
|
|
||||||
name: "Sublime Merge",
|
|
||||||
installPaths: ["/Applications/Sublime Merge.app/Contents/SharedSupport/bin/smerge"],
|
|
||||||
binaryPath: "/Applications/Sublime Merge.app/Contents/SharedSupport/bin/smerge",
|
|
||||||
open: { path in
|
|
||||||
Shell.run("/Applications/Sublime\\ Merge.app/Contents/SharedSupport/bin/smerge \(path)")
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Application(
|
|
||||||
name: "iTerm",
|
|
||||||
installPaths: ["/Applications/iTerm.app/Contents/Info.plist"],
|
|
||||||
binaryPath: "/Applications/iTerm.app/Contents/Info.plist",
|
|
||||||
open: { path in
|
|
||||||
Shell.run("open -a iTerm \(path)")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
].filter { return $0.isInstalled() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
phpmon/Domain/Helpers/Timer.swift
Normal file
32
phpmon/Domain/Helpers/Timer.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// BenchmarkTimer.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/12/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class BenchmarkTimer {
|
||||||
|
let startTime: CFAbsoluteTime
|
||||||
|
var endTime: CFAbsoluteTime?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() -> CFAbsoluteTime {
|
||||||
|
endTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
return duration!
|
||||||
|
}
|
||||||
|
|
||||||
|
var duration: CFAbsoluteTime? {
|
||||||
|
if let endTime = endTime {
|
||||||
|
return endTime - startTime
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -55,10 +55,19 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
|
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
|
|
||||||
|
print("Determining broken PHP-FPM...")
|
||||||
// Attempt to find out if PHP-FPM is broken
|
// Attempt to find out if PHP-FPM is broken
|
||||||
let installation = App.phpInstall!
|
let installation = App.phpInstall!
|
||||||
installation.notifyAboutBrokenPhpFpm()
|
installation.notifyAboutBrokenPhpFpm()
|
||||||
|
|
||||||
|
print("Detecting applications...")
|
||||||
|
// Attempt to load list of applications
|
||||||
|
App.shared.detectedApplications = Application.detectPresetApplications()
|
||||||
|
let appNames = App.shared.detectedApplications.map { app in
|
||||||
|
return app.name
|
||||||
|
}
|
||||||
|
print("Detected applications: \(appNames)")
|
||||||
|
|
||||||
// Attempt to find out more info about Valet
|
// Attempt to find out more info about Valet
|
||||||
print("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)")
|
print("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)")
|
||||||
print("PHP Monitor is ready to serve!")
|
print("PHP Monitor is ready to serve!")
|
||||||
|
@ -41,7 +41,9 @@ class SiteListCell: NSTableCellView
|
|||||||
|
|
||||||
// Show the green or red lock based on whether the site was secured
|
// Show the green or red lock based on whether the site was secured
|
||||||
imageViewLock.image = NSImage(named: site.secured ? "Lock" : "LockUnlocked")
|
imageViewLock.image = NSImage(named: site.secured ? "Lock" : "LockUnlocked")
|
||||||
imageViewLock.contentTintColor = site.secured ? NSColor.systemGreen : NSColor.systemRed
|
imageViewLock.contentTintColor = site.secured ?
|
||||||
|
NSColor.init(red: 63/255, green: 195/255, blue: 128/255, alpha: 1.0) // green
|
||||||
|
: NSColor.init(red: 246/255, green: 71/255, blue: 71/255, alpha: 1.0) // red
|
||||||
|
|
||||||
// Show the current driver
|
// Show the current driver
|
||||||
labelDriver.stringValue = site.driver ?? "???"
|
labelDriver.stringValue = site.driver ?? "???"
|
||||||
|
@ -23,7 +23,9 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
var sites: [Valet.Site] = []
|
var sites: [Valet.Site] = []
|
||||||
|
|
||||||
/// Array that contains various apps that might open a particular site directory.
|
/// Array that contains various apps that might open a particular site directory.
|
||||||
var editors: [Application] = Application.detectPresetApplications()
|
var applications: [Application] {
|
||||||
|
return App.shared.detectedApplications
|
||||||
|
}
|
||||||
|
|
||||||
/// String that was last searched for. Empty by default.
|
/// String that was last searched for. Empty by default.
|
||||||
var lastSearchedFor = ""
|
var lastSearchedFor = ""
|
||||||
@ -283,11 +285,11 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
keyEquivalent: "L"
|
keyEquivalent: "L"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (editors.count > 0) {
|
if (applications.count > 0) {
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "site_list.detected_apps".localized, action: nil, keyEquivalent: "")
|
menu.addItem(withTitle: "site_list.detected_apps".localized, action: nil, keyEquivalent: "")
|
||||||
|
|
||||||
for (index, editor) in editors.enumerated() {
|
for (index, editor) in applications.enumerated() {
|
||||||
let editorMenuItem = EditorMenuItem(
|
let editorMenuItem = EditorMenuItem(
|
||||||
title: "Open with \(editor.name)",
|
title: "Open with \(editor.name)",
|
||||||
action: #selector(self.openWithEditor(sender:)),
|
action: #selector(self.openWithEditor(sender:)),
|
||||||
@ -296,10 +298,9 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
editorMenuItem.editor = editor
|
editorMenuItem.editor = editor
|
||||||
menu.addItem(editorMenuItem)
|
menu.addItem(editorMenuItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "site_list.system_apps".localized, action: nil, keyEquivalent: "")
|
menu.addItem(withTitle: "site_list.system_apps".localized, action: nil, keyEquivalent: "")
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.open_in_finder".localized,
|
withTitle: "site_list.open_in_finder".localized,
|
||||||
@ -322,29 +323,8 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
|
|
||||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||||
guard let editor = sender.editor else { return }
|
guard let editor = sender.editor else { return }
|
||||||
|
editor.openDirectory(file: selectedSite!.absolutePath!)
|
||||||
if editor.hasBinary() {
|
|
||||||
editor.openDirectory(file: selectedSite!.absolutePath!)
|
|
||||||
} else {
|
|
||||||
presentAlertForMissingEditorBinary(editor, sender)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentAlertForMissingEditorBinary(_ editor: Application, _ sender: EditorMenuItem) {
|
|
||||||
Alert.confirm(
|
|
||||||
onWindow: self.view.window!,
|
|
||||||
messageText: "editors.binary_missing.title".localized(editor.pathToBinary),
|
|
||||||
informativeText:
|
|
||||||
editor.missingBinaryInstruction
|
|
||||||
?? "editors.binary_missing.desc".localized(editor.pathToBinary),
|
|
||||||
buttonTitle: "editors.alert.try_again".localized,
|
|
||||||
secondButtonTitle: "editors.alert.cancel".localized,
|
|
||||||
onFirstButtonPressed: {
|
|
||||||
self.openWithEditor(sender: sender)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Deinitialization
|
// MARK: - Deinitialization
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -62,6 +62,26 @@ class Shell {
|
|||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false
|
requiresPath: Bool = false
|
||||||
) -> String {
|
) -> String {
|
||||||
|
let shellOutput = self.execute(command, requiresPath: requiresPath)
|
||||||
|
let hasError = (
|
||||||
|
shellOutput.standardOutput == ""
|
||||||
|
&& shellOutput.errorOutput.lengthOfBytes(using: .utf8) > 0
|
||||||
|
)
|
||||||
|
return !hasError ? shellOutput.standardOutput : shellOutput.errorOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Runs the command and returns a `ShellOutput` object, which contains info about the process.
|
||||||
|
|
||||||
|
- Parameter command: The command to run
|
||||||
|
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
||||||
|
- Parameter waitUntilExit: Waits for the command to complete before returning the `ShellOutput`
|
||||||
|
*/
|
||||||
|
func execute(
|
||||||
|
_ command: String,
|
||||||
|
requiresPath: Bool = false,
|
||||||
|
waitUntilExit: Bool = false
|
||||||
|
) -> ShellOutput {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
let errorPipe = Pipe()
|
let errorPipe = Pipe()
|
||||||
@ -76,14 +96,19 @@ class Shell {
|
|||||||
task.standardError = errorPipe
|
task.standardError = errorPipe
|
||||||
task.launch()
|
task.launch()
|
||||||
|
|
||||||
let error = String(data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
|
if waitUntilExit {
|
||||||
let output = String(data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
|
task.waitUntilExit()
|
||||||
|
|
||||||
if (output == "" && error.lengthOfBytes(using: .utf8) > 0) {
|
|
||||||
return error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return ShellOutput(
|
||||||
|
standardOutput: String(
|
||||||
|
data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8
|
||||||
|
)!,
|
||||||
|
errorOutput: String(
|
||||||
|
data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8
|
||||||
|
)!,
|
||||||
|
task: task
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,3 +119,17 @@ class Shell {
|
|||||||
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShellOutput {
|
||||||
|
let standardOutput: String
|
||||||
|
let errorOutput: String
|
||||||
|
let task: Process
|
||||||
|
|
||||||
|
init(standardOutput: String,
|
||||||
|
errorOutput: String,
|
||||||
|
task: Process) {
|
||||||
|
self.standardOutput = standardOutput
|
||||||
|
self.errorOutput = errorOutput
|
||||||
|
self.task = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -75,45 +75,10 @@
|
|||||||
"site_list.system_apps" = "System Applications";
|
"site_list.system_apps" = "System Applications";
|
||||||
|
|
||||||
// EDITORS
|
// EDITORS
|
||||||
"editors.binary_missing.title" = "`%@` missing";
|
|
||||||
"editors.binary_missing.desc" = "The associated binary (`%@`) is missing. Because of this, PHP Monitor cannot open the domain’s folder.";
|
|
||||||
"editors.alert.try_again" = "Try Again";
|
"editors.alert.try_again" = "Try Again";
|
||||||
"editors.alert.cancel" = "Cancel";
|
"editors.alert.cancel" = "Cancel";
|
||||||
|
|
||||||
// - PHPSTORM + TOOLBOX
|
|
||||||
"editors.phpstorm_binary_not_linked.desc" =
|
|
||||||
"PHP Monitor makes use of PhpStorm’s launcher to open a specific directory.
|
|
||||||
|
|
||||||
The required launcher does not seem to be found in the usual location. In JetBrains Toolbox go to Settings, and select 'Generate shell scripts'.
|
|
||||||
|
|
||||||
As the directory, enter `/usr/local/bin`.
|
|
||||||
|
|
||||||
After this is done, PHP Monitor should be able to open the domain’s folder in PhpStorm.";
|
|
||||||
|
|
||||||
// - PHPSTORM (standalone)
|
|
||||||
"editors.pstorm_binary_not_linked.desc" =
|
|
||||||
"PHP Monitor makes use of PhpStorm’s launcher to open a specific directory.
|
|
||||||
|
|
||||||
The required launcher does not seem to be found in the usual location. Please set up the launcher by going to 'Tools > Create command-line launcher' in PhpStorm.
|
|
||||||
|
|
||||||
After this is done, PHP Monitor should be able to open the domain’s folder in PhpStorm.";
|
|
||||||
"editors.code_binary_not_linked.desc" =
|
|
||||||
"PHP Monitor makes use of Visual Studio Code‘s helper binary, `code`, but it seems not to exist on your system.
|
|
||||||
|
|
||||||
You can fix this by selecting 'Install `code` command in PATH' in Visual Studio Code’s command palette.
|
|
||||||
(Type `>code` to find it.)
|
|
||||||
|
|
||||||
After this is done, PHP Monitor should be able to open the domain’s folder in PhpStorm.";
|
|
||||||
|
|
||||||
// - VS CODE
|
|
||||||
"editors.code_binary_not_linked.desc" =
|
|
||||||
"PHP Monitor makes use of Visual Studio Code‘s helper binary, `code`, but it seems not to exist on your system.
|
|
||||||
|
|
||||||
You can fix this by selecting 'Install `code` command in PATH' in Visual Studio Code’s command palette.
|
|
||||||
(Type `>code` to find it.)
|
|
||||||
|
|
||||||
After this is done, PHP Monitor should be able to open the domain’s folder in PhpStorm.";
|
|
||||||
|
|
||||||
// PREFERENCES
|
// PREFERENCES
|
||||||
|
|
||||||
"prefs.title" = "PHP Monitor";
|
"prefs.title" = "PHP Monitor";
|
||||||
|
Reference in New Issue
Block a user