mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-04-02 17:40:08 +02:00
♻️ Refactor file watchers
This commit is contained in:
@@ -79,6 +79,14 @@
|
|||||||
037F44192EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
037F44192EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
||||||
037F441A2EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
037F441A2EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
||||||
037F441B2EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
037F441B2EDB27BA002EBF75 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44172EDB27B7002EBF75 /* Debouncer.swift */; };
|
||||||
|
037F441D2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */; };
|
||||||
|
037F441E2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */; };
|
||||||
|
037F441F2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */; };
|
||||||
|
037F44202EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */; };
|
||||||
|
037F44222EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */; };
|
||||||
|
037F44232EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */; };
|
||||||
|
037F44242EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */; };
|
||||||
|
037F44252EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */; };
|
||||||
0386B0B42ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
0386B0B42ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
||||||
0386B0B52ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
0386B0B52ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
||||||
0386B0B62ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
0386B0B62ED36C3D00CA6795 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0386B0B32ED36C3D00CA6795 /* Locked.swift */; };
|
||||||
@@ -318,10 +326,6 @@
|
|||||||
C4415E8E2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
C4415E8E2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
||||||
C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
||||||
C4415E902B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
C4415E902B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */; };
|
||||||
C441CC562AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; };
|
|
||||||
C441CC572AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; };
|
|
||||||
C441CC582AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; };
|
|
||||||
C441CC592AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; };
|
|
||||||
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
|
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
|
||||||
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
|
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
|
||||||
C4463FCC29804BCB007B93D5 /* RCFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4463FCB29804BCB007B93D5 /* RCFile.swift */; };
|
C4463FCC29804BCB007B93D5 /* RCFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4463FCB29804BCB007B93D5 /* RCFile.swift */; };
|
||||||
@@ -604,8 +608,6 @@
|
|||||||
C471E87728F9BB650021E251 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
C471E87728F9BB650021E251 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
||||||
C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
||||||
C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
||||||
C471E87B28F9BB650021E251 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
|
||||||
C471E87C28F9BB650021E251 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; };
|
|
||||||
C471E87D28F9BB650021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
C471E87D28F9BB650021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
||||||
C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
||||||
C471E87F28F9BB650021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
C471E87F28F9BB650021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
||||||
@@ -690,8 +692,6 @@
|
|||||||
C471E8DA28F9BB8F0021E251 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
C471E8DA28F9BB8F0021E251 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
||||||
C471E8DB28F9BB8F0021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
C471E8DB28F9BB8F0021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
||||||
C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
||||||
C471E8DE28F9BB8F0021E251 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
|
||||||
C471E8DF28F9BB8F0021E251 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; };
|
|
||||||
C471E8E028F9BB8F0021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
C471E8E028F9BB8F0021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
||||||
C471E8E128F9BB8F0021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
C471E8E128F9BB8F0021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
||||||
C471E8E228F9BB8F0021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
C471E8E228F9BB8F0021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
||||||
@@ -755,10 +755,6 @@
|
|||||||
C48DDD0E29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
C48DDD0E29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
||||||
C48DDD0F29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
C48DDD0F29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
||||||
C48DDD1029C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
C48DDD1029C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */; };
|
||||||
C490E3B629BCA367006D2DE6 /* App+BrewWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */; };
|
|
||||||
C490E3B829BCA367006D2DE6 /* App+BrewWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */; };
|
|
||||||
C490E3B929BCA368006D2DE6 /* App+BrewWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */; };
|
|
||||||
C490E3BA29BCA368006D2DE6 /* App+BrewWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */; };
|
|
||||||
C490E3BB29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
C490E3BB29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
||||||
C490E3BC29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
C490E3BC29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
||||||
C490E3BD29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
C490E3BD29BCA375006D2DE6 /* Measurements.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAA5129B12A5A00AB28FC /* Measurements.swift */; };
|
||||||
@@ -845,10 +841,6 @@
|
|||||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||||
C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||||
C4C8900528F0E3D100CE5E97 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
C4C8900528F0E3D100CE5E97 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
|
||||||
C4C8E81B276F54E5003AC782 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; };
|
|
||||||
C4C8E81C276F54E5003AC782 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; };
|
|
||||||
C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB250429B28BB800CA4492 /* MainMenuTest.swift */; };
|
C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB250429B28BB800CA4492 /* MainMenuTest.swift */; };
|
||||||
C4CB6E65292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; };
|
C4CB6E65292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; };
|
||||||
C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; };
|
C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; };
|
||||||
@@ -1057,6 +1049,8 @@
|
|||||||
0379C4A32ED7201D0035D7EA /* App+DetectApps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+DetectApps.swift"; sourceTree = "<group>"; };
|
0379C4A32ED7201D0035D7EA /* App+DetectApps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+DetectApps.swift"; sourceTree = "<group>"; };
|
||||||
037F44152EDB0AA8002EBF75 /* FSNotifierTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSNotifierTest.swift; sourceTree = "<group>"; };
|
037F44152EDB0AA8002EBF75 /* FSNotifierTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSNotifierTest.swift; sourceTree = "<group>"; };
|
||||||
037F44172EDB27B7002EBF75 /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
|
037F44172EDB27B7002EBF75 /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
|
||||||
|
037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigWatchManager.swift; sourceTree = "<group>"; };
|
||||||
|
037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewWatchManager.swift; sourceTree = "<group>"; };
|
||||||
0386B0B32ED36C3D00CA6795 /* Locked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locked.swift; sourceTree = "<group>"; };
|
0386B0B32ED36C3D00CA6795 /* Locked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locked.swift; sourceTree = "<group>"; };
|
||||||
0386B0B82ED36DF800CA6795 /* LockedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockedTests.swift; sourceTree = "<group>"; };
|
0386B0B82ED36DF800CA6795 /* LockedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockedTests.swift; sourceTree = "<group>"; };
|
||||||
0392CDE52EB23B8F009176DA /* CertificateValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateValidator.swift; sourceTree = "<group>"; };
|
0392CDE52EB23B8F009176DA /* CertificateValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateValidator.swift; sourceTree = "<group>"; };
|
||||||
@@ -1175,7 +1169,6 @@
|
|||||||
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
|
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
|
||||||
C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewFormulaeObservable.swift; sourceTree = "<group>"; };
|
C4415E8C2B0287E90035F520 /* BrewFormulaeObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewFormulaeObservable.swift; sourceTree = "<group>"; };
|
||||||
C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigFSNotifier.swift; sourceTree = "<group>"; };
|
|
||||||
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; };
|
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; };
|
||||||
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; };
|
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; };
|
||||||
C4463FCB29804BCB007B93D5 /* RCFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RCFile.swift; sourceTree = "<group>"; };
|
C4463FCB29804BCB007B93D5 /* RCFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RCFile.swift; sourceTree = "<group>"; };
|
||||||
@@ -1246,7 +1239,6 @@
|
|||||||
C49DA9BC2D67AC49006F9CF4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
C49DA9BC2D67AC49006F9CF4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
C49DA9BD2D67B298006F9CF4 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
C49DA9BD2D67B298006F9CF4 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
C49EAA5129B12A5A00AB28FC /* Measurements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurements.swift; sourceTree = "<group>"; };
|
C49EAA5129B12A5A00AB28FC /* Measurements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurements.swift; sourceTree = "<group>"; };
|
||||||
C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+BrewWatch.swift"; sourceTree = "<group>"; };
|
|
||||||
C4A81CA328C67101008DD9D1 /* PMTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMTableView.swift; sourceTree = "<group>"; };
|
C4A81CA328C67101008DD9D1 /* PMTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMTableView.swift; sourceTree = "<group>"; };
|
||||||
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
||||||
@@ -1282,8 +1274,6 @@
|
|||||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
||||||
C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemProtocol.swift; sourceTree = "<group>"; };
|
C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemProtocol.swift; sourceTree = "<group>"; };
|
||||||
C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealFileSystem.swift; sourceTree = "<group>"; };
|
C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealFileSystem.swift; sourceTree = "<group>"; };
|
||||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
|
||||||
C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigWatchManager.swift; sourceTree = "<group>"; };
|
|
||||||
C4CB250429B28BB800CA4492 /* MainMenuTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTest.swift; sourceTree = "<group>"; };
|
C4CB250429B28BB800CA4492 /* MainMenuTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTest.swift; sourceTree = "<group>"; };
|
||||||
C4CB6E64292C362C002E9027 /* Homebrew.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Homebrew.swift; sourceTree = "<group>"; };
|
C4CB6E64292C362C002E9027 /* Homebrew.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Homebrew.swift; sourceTree = "<group>"; };
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||||
@@ -2336,12 +2326,10 @@
|
|||||||
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
037F44172EDB27B7002EBF75 /* Debouncer.swift */,
|
|
||||||
C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */,
|
|
||||||
C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */,
|
|
||||||
C41ADCE72970CCC700120423 /* FSNotifier.swift */,
|
C41ADCE72970CCC700120423 /* FSNotifier.swift */,
|
||||||
C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */,
|
037F44172EDB27B7002EBF75 /* Debouncer.swift */,
|
||||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */,
|
037F441C2EDB9195002EBF75 /* ConfigWatchManager.swift */,
|
||||||
|
037F44212EDB92EC002EBF75 /* HomebrewWatchManager.swift */,
|
||||||
);
|
);
|
||||||
path = Watcher;
|
path = Watcher;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2837,7 +2825,7 @@
|
|||||||
0309E6672B0D4B2F002AC007 /* BrewExtensionsObservable.swift in Sources */,
|
0309E6672B0D4B2F002AC007 /* BrewExtensionsObservable.swift in Sources */,
|
||||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||||
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
037F441F2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
|
||||||
C43B8FD52BA9BAD3000C02BE /* UnavailableContentView.swift in Sources */,
|
C43B8FD52BA9BAD3000C02BE /* UnavailableContentView.swift in Sources */,
|
||||||
032DAC2A2E8BEB5B0018E01C /* RealWebApi.swift in Sources */,
|
032DAC2A2E8BEB5B0018E01C /* RealWebApi.swift in Sources */,
|
||||||
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
@@ -2850,6 +2838,7 @@
|
|||||||
C4D5576429C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
|
C4D5576429C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
|
||||||
C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */,
|
C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */,
|
||||||
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
|
037F44222EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */,
|
||||||
C40C7F2827721FF600DDDCDC /* Valet+Alerts.swift in Sources */,
|
C40C7F2827721FF600DDDCDC /* Valet+Alerts.swift in Sources */,
|
||||||
C463E380284930EE00422731 /* PresetHelper.swift in Sources */,
|
C463E380284930EE00422731 /* PresetHelper.swift in Sources */,
|
||||||
C41C02A927E61A65009F26CB /* FakeValetSite.swift in Sources */,
|
C41C02A927E61A65009F26CB /* FakeValetSite.swift in Sources */,
|
||||||
@@ -2938,9 +2927,7 @@
|
|||||||
03DAD3A72EB3B08F003417BD /* DomainListVC+Certs.swift in Sources */,
|
03DAD3A72EB3B08F003417BD /* DomainListVC+Certs.swift in Sources */,
|
||||||
C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */,
|
C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */,
|
||||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
C441CC562AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */,
|
|
||||||
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
|
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
|
||||||
C4C8E81B276F54E5003AC782 /* ConfigWatchManager.swift in Sources */,
|
|
||||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
@@ -2948,7 +2935,6 @@
|
|||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C422DDAA28A2C49900CEAC97 /* PhpDoctorView.swift in Sources */,
|
C422DDAA28A2C49900CEAC97 /* PhpDoctorView.swift in Sources */,
|
||||||
C469E6FE294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
|
C469E6FE294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
|
||||||
C490E3B629BCA367006D2DE6 /* App+BrewWatch.swift in Sources */,
|
|
||||||
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
|
||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
@@ -3147,8 +3133,6 @@
|
|||||||
C471E87728F9BB650021E251 /* Keys.swift in Sources */,
|
C471E87728F9BB650021E251 /* Keys.swift in Sources */,
|
||||||
C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */,
|
C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */,
|
||||||
C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */,
|
C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */,
|
||||||
C471E87B28F9BB650021E251 /* App+ConfigWatch.swift in Sources */,
|
|
||||||
C471E87C28F9BB650021E251 /* ConfigWatchManager.swift in Sources */,
|
|
||||||
C471E87D28F9BB650021E251 /* Preset.swift in Sources */,
|
C471E87D28F9BB650021E251 /* Preset.swift in Sources */,
|
||||||
C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */,
|
C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */,
|
||||||
C471E87F28F9BB650021E251 /* WarningView.swift in Sources */,
|
C471E87F28F9BB650021E251 /* WarningView.swift in Sources */,
|
||||||
@@ -3179,6 +3163,7 @@
|
|||||||
C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */,
|
C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */,
|
||||||
C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */,
|
C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */,
|
||||||
03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||||
|
037F44242EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */,
|
||||||
C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */,
|
C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */,
|
||||||
C46DC7A62C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
C46DC7A62C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
||||||
C471E7E728F9BAC20021E251 /* Constants.swift in Sources */,
|
C471E7E728F9BAC20021E251 /* Constants.swift in Sources */,
|
||||||
@@ -3201,7 +3186,6 @@
|
|||||||
C40D72612A018AE30054A067 /* BrewFormula+UI.swift in Sources */,
|
C40D72612A018AE30054A067 /* BrewFormula+UI.swift in Sources */,
|
||||||
C471E82528F9BB2E0021E251 /* ComposerWindow.swift in Sources */,
|
C471E82528F9BB2E0021E251 /* ComposerWindow.swift in Sources */,
|
||||||
0396160D2E74A61E002DD7F6 /* LoggableEvent.swift in Sources */,
|
0396160D2E74A61E002DD7F6 /* LoggableEvent.swift in Sources */,
|
||||||
C441CC582AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */,
|
|
||||||
C471E80828F9BAD40021E251 /* PhpExtension.swift in Sources */,
|
C471E80828F9BAD40021E251 /* PhpExtension.swift in Sources */,
|
||||||
C471E7F928F9BACB0021E251 /* PhpSwitcher.swift in Sources */,
|
C471E7F928F9BACB0021E251 /* PhpSwitcher.swift in Sources */,
|
||||||
03ACC6482ECCBA130070D4CD /* CaskFile+API.swift in Sources */,
|
03ACC6482ECCBA130070D4CD /* CaskFile+API.swift in Sources */,
|
||||||
@@ -3209,8 +3193,8 @@
|
|||||||
031E2B6B2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */,
|
031E2B6B2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */,
|
||||||
C471E82728F9BB310021E251 /* BrewDiagnostics.swift in Sources */,
|
C471E82728F9BB310021E251 /* BrewDiagnostics.swift in Sources */,
|
||||||
C471E7DB28F9BA8F0021E251 /* RealShell.swift in Sources */,
|
C471E7DB28F9BA8F0021E251 /* RealShell.swift in Sources */,
|
||||||
C490E3B929BCA368006D2DE6 /* App+BrewWatch.swift in Sources */,
|
|
||||||
C471E7FF28F9BAD10021E251 /* Xdebug.swift in Sources */,
|
C471E7FF28F9BAD10021E251 /* Xdebug.swift in Sources */,
|
||||||
|
037F441D2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
|
||||||
C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||||
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||||
C471E7F228F9BAC70021E251 /* PhpEnvironments.swift in Sources */,
|
C471E7F228F9BAC70021E251 /* PhpEnvironments.swift in Sources */,
|
||||||
@@ -3272,7 +3256,6 @@
|
|||||||
036C39022E5C883B008DAEDF /* Packagist.swift in Sources */,
|
036C39022E5C883B008DAEDF /* Packagist.swift in Sources */,
|
||||||
0379C4A72ED720220035D7EA /* App+DetectApps.swift in Sources */,
|
0379C4A72ED720220035D7EA /* App+DetectApps.swift in Sources */,
|
||||||
C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */,
|
C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */,
|
||||||
C441CC592AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */,
|
|
||||||
C40934A5298EEB2C00D25014 /* CaskFile.swift in Sources */,
|
C40934A5298EEB2C00D25014 /* CaskFile.swift in Sources */,
|
||||||
C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */,
|
C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C40D725D2A018ACC0054A067 /* BusyStatus.swift in Sources */,
|
C40D725D2A018ACC0054A067 /* BusyStatus.swift in Sources */,
|
||||||
@@ -3375,8 +3358,6 @@
|
|||||||
C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */,
|
C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */,
|
||||||
03D846262EB6344E006EFE3C /* DomainListVC+Window.swift in Sources */,
|
03D846262EB6344E006EFE3C /* DomainListVC+Window.swift in Sources */,
|
||||||
C490E3BF29BCA376006D2DE6 /* Measurements.swift in Sources */,
|
C490E3BF29BCA376006D2DE6 /* Measurements.swift in Sources */,
|
||||||
C471E8DE28F9BB8F0021E251 /* App+ConfigWatch.swift in Sources */,
|
|
||||||
C471E8DF28F9BB8F0021E251 /* ConfigWatchManager.swift in Sources */,
|
|
||||||
C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */,
|
C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */,
|
||||||
C40D72622A018AE30054A067 /* BrewFormula+UI.swift in Sources */,
|
C40D72622A018AE30054A067 /* BrewFormula+UI.swift in Sources */,
|
||||||
C4B79ECE29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */,
|
C4B79ECE29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */,
|
||||||
@@ -3398,6 +3379,7 @@
|
|||||||
C456A0CE2AA6166F0080144F /* BytePhpPreference.swift in Sources */,
|
C456A0CE2AA6166F0080144F /* BytePhpPreference.swift in Sources */,
|
||||||
C4FD87A829AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */,
|
C4FD87A829AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */,
|
||||||
C45B9151295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
|
C45B9151295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
|
||||||
|
037F44202EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
|
||||||
C47015032C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */,
|
C47015032C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */,
|
||||||
C471E8EC28F9BB8F0021E251 /* SwiftUIHelper.swift in Sources */,
|
C471E8EC28F9BB8F0021E251 /* SwiftUIHelper.swift in Sources */,
|
||||||
C471E8EE28F9BB8F0021E251 /* HotKey.swift in Sources */,
|
C471E8EE28F9BB8F0021E251 /* HotKey.swift in Sources */,
|
||||||
@@ -3419,7 +3401,6 @@
|
|||||||
C4611E5A2AEAD2E20010BE24 /* ConfigManagerWindowController.swift in Sources */,
|
C4611E5A2AEAD2E20010BE24 /* ConfigManagerWindowController.swift in Sources */,
|
||||||
C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */,
|
C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */,
|
||||||
036C390F2E5C8D42008DAEDF /* PackagistError.swift in Sources */,
|
036C390F2E5C8D42008DAEDF /* PackagistError.swift in Sources */,
|
||||||
C490E3BA29BCA368006D2DE6 /* App+BrewWatch.swift in Sources */,
|
|
||||||
03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||||
C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */,
|
C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */,
|
||||||
C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
|
C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
|
||||||
@@ -3435,6 +3416,7 @@
|
|||||||
C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */,
|
C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */,
|
||||||
C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */,
|
C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */,
|
||||||
C471E82128F9BB2E0021E251 /* ProjectTypeDetection.swift in Sources */,
|
C471E82128F9BB2E0021E251 /* ProjectTypeDetection.swift in Sources */,
|
||||||
|
037F44232EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */,
|
||||||
037F441A2EDB27BA002EBF75 /* Debouncer.swift in Sources */,
|
037F441A2EDB27BA002EBF75 /* Debouncer.swift in Sources */,
|
||||||
032DAC282E8BEB5B0018E01C /* RealWebApi.swift in Sources */,
|
032DAC282E8BEB5B0018E01C /* RealWebApi.swift in Sources */,
|
||||||
C471E7EF28F9BAC30021E251 /* Actions.swift in Sources */,
|
C471E7EF28F9BAC30021E251 /* Actions.swift in Sources */,
|
||||||
@@ -3548,7 +3530,6 @@
|
|||||||
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */,
|
||||||
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */,
|
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewDecodable.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewDecodable.swift in Sources */,
|
||||||
C4C8E81C276F54E5003AC782 /* ConfigWatchManager.swift in Sources */,
|
|
||||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */,
|
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
@@ -3567,7 +3548,6 @@
|
|||||||
0392CDE92EB23B8F009176DA /* CertificateValidator.swift in Sources */,
|
0392CDE92EB23B8F009176DA /* CertificateValidator.swift in Sources */,
|
||||||
C4821C5B2C2DEDE200357A68 /* AppMenu.swift in Sources */,
|
C4821C5B2C2DEDE200357A68 /* AppMenu.swift in Sources */,
|
||||||
C463E381284930EE00422731 /* PresetHelper.swift in Sources */,
|
C463E381284930EE00422731 /* PresetHelper.swift in Sources */,
|
||||||
C441CC572AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */,
|
|
||||||
C4F520672AF03791006787F2 /* ExtensionEnumeratorTest.swift in Sources */,
|
C4F520672AF03791006787F2 /* ExtensionEnumeratorTest.swift in Sources */,
|
||||||
C46FA98C2822F08F00D78807 /* PhpConfigurationFileTest.swift in Sources */,
|
C46FA98C2822F08F00D78807 /* PhpConfigurationFileTest.swift in Sources */,
|
||||||
C4D5576529C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
|
C4D5576529C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
|
||||||
@@ -3582,7 +3562,6 @@
|
|||||||
C42106672AFA9FF400DF3732 /* PhpVersionManagerView+Actions.swift in Sources */,
|
C42106672AFA9FF400DF3732 /* PhpVersionManagerView+Actions.swift in Sources */,
|
||||||
C46DC7A52C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
C46DC7A52C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
||||||
032DAC2F2E8BEB6B0018E01C /* WebApiProtocol.swift in Sources */,
|
032DAC2F2E8BEB6B0018E01C /* WebApiProtocol.swift in Sources */,
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
|
||||||
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */,
|
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */,
|
||||||
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||||
@@ -3597,12 +3576,14 @@
|
|||||||
C4E2E85D28FC282B003B070C /* TestableConfiguration.swift in Sources */,
|
C4E2E85D28FC282B003B070C /* TestableConfiguration.swift in Sources */,
|
||||||
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */,
|
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */,
|
||||||
C4BB393A2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
|
C4BB393A2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
|
||||||
|
037F44252EDB92EC002EBF75 /* HomebrewWatchManager.swift in Sources */,
|
||||||
C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */,
|
C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */,
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
|
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||||
C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */,
|
C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */,
|
||||||
|
037F441E2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
|
||||||
C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */,
|
C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */,
|
||||||
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
||||||
C4513F962B13E30C001AD760 /* BrewExtensionsObservable.swift in Sources */,
|
C4513F962B13E30C001AD760 /* BrewExtensionsObservable.swift in Sources */,
|
||||||
@@ -3694,7 +3675,6 @@
|
|||||||
033D45992B0D4EC600070080 /* InstallPhpExtensionCommand.swift in Sources */,
|
033D45992B0D4EC600070080 /* InstallPhpExtensionCommand.swift in Sources */,
|
||||||
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
|
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
||||||
C490E3B829BCA367006D2DE6 /* App+BrewWatch.swift in Sources */,
|
|
||||||
C44AD3F72912EF7100997FF4 /* RealFileSystemTest.swift in Sources */,
|
C44AD3F72912EF7100997FF4 /* RealFileSystemTest.swift in Sources */,
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||||
C4C0E8E027F88AEB002D32A9 /* FakeDomainScanner.swift in Sources */,
|
C4C0E8E027F88AEB002D32A9 /* FakeDomainScanner.swift in Sources */,
|
||||||
|
|||||||
@@ -136,15 +136,16 @@ class App {
|
|||||||
|
|
||||||
// MARK: - App Watchers
|
// MARK: - App Watchers
|
||||||
|
|
||||||
/** Individual filesystem watchers, which are, i.e. responsible for watching the Homebrew folders. */
|
|
||||||
var watchers: [String: FSNotifier] = [:]
|
|
||||||
|
|
||||||
/** Individual debouncers for filesystem watchers. */
|
|
||||||
var debouncers: [String: Debouncer] = [:]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The `ConfigWatchManager` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
The `ConfigWatchManager` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
||||||
This manager object can immediately start or stop all watchers (or pause them) all at once.
|
This manager object can immediately start or stop all watchers (or pause them) all at once.
|
||||||
*/
|
*/
|
||||||
var watchManager: ConfigWatchManager!
|
var configWatchManager: ConfigWatchManager?
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `HomebrewWatchManager` is responsible for watching the Homebrew binaries folder.
|
||||||
|
This allows PHP Monitor to respond to external `brew` changes executed by the user.
|
||||||
|
*/
|
||||||
|
var homebrewWatchManager: HomebrewWatchManager?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ extension Startup {
|
|||||||
await container.phpEnvs.reloadPhpVersions()
|
await container.phpEnvs.reloadPhpVersions()
|
||||||
|
|
||||||
// Set up the filesystem watcher for the Homebrew binaries
|
// Set up the filesystem watcher for the Homebrew binaries
|
||||||
App.shared.prepareHomebrewWatchers()
|
await HomebrewWatchManager.prepare()
|
||||||
|
|
||||||
// Check for other problems
|
// Check for other problems
|
||||||
container.warningManager.evaluateWarnings()
|
container.warningManager.evaluateWarnings()
|
||||||
|
|
||||||
// Set up the config watchers on launch (updated automatically when switching)
|
// Set up the config watchers on launch (updated automatically when switching)
|
||||||
App.shared.handlePhpConfigWatcher()
|
await ConfigWatchManager.handleWatcher()
|
||||||
|
|
||||||
// Detect built-in and custom applications
|
// Detect built-in and custom applications
|
||||||
await App.shared.detectApplications()
|
await App.shared.detectApplications()
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ extension MainMenu {
|
|||||||
await PhpEnvironments.switcher.performSwitch(to: version)
|
await PhpEnvironments.switcher.performSwitch(to: version)
|
||||||
|
|
||||||
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
||||||
App.shared.handlePhpConfigWatcher()
|
await ConfigWatchManager.handleWatcher()
|
||||||
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ extension MainMenu {
|
|||||||
await PhpEnvironments.switcher.performSwitch(to: version)
|
await PhpEnvironments.switcher.performSwitch(to: version)
|
||||||
|
|
||||||
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
||||||
App.shared.handlePhpConfigWatcher()
|
await ConfigWatchManager.handleWatcher()
|
||||||
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,7 +333,7 @@ extension MainMenu {
|
|||||||
await PhpEnvironments.switcher.performSwitch(to: version)
|
await PhpEnvironments.switcher.performSwitch(to: version)
|
||||||
|
|
||||||
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
container.phpEnvs.currentInstall = ActivePhpInstallation(container)
|
||||||
App.shared.handlePhpConfigWatcher()
|
await ConfigWatchManager.handleWatcher()
|
||||||
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
container.phpEnvs.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// App+BrewWatch.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 03/03/2023.
|
|
||||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension App {
|
|
||||||
|
|
||||||
/**
|
|
||||||
Performs a particular action while suspending the Homebrew watcher,
|
|
||||||
until the task is completed.
|
|
||||||
*/
|
|
||||||
public func withSuspendedHomebrewWatcher<T>(_ action: () async throws -> T) async rethrows -> T {
|
|
||||||
await suspendHomebrewWatcher()
|
|
||||||
defer { resumeHomebrewWatcher() }
|
|
||||||
return try await action()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Prepares the `homebrew/bin` directory watcher. This allows PHP Monitor to quickly respond to
|
|
||||||
external `brew` changes executed by the user.
|
|
||||||
*/
|
|
||||||
public func prepareHomebrewWatchers() {
|
|
||||||
let notifier = FSNotifier(
|
|
||||||
for: URL(fileURLWithPath: container.paths.binPath),
|
|
||||||
eventMask: .all,
|
|
||||||
onChange: { Task { await self.onHomebrewPhpModification() } }
|
|
||||||
)
|
|
||||||
|
|
||||||
self.watchers["homebrewBinaries"] = notifier
|
|
||||||
self.debouncers["homebrewBinaries"] = Debouncer()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func suspendHomebrewWatcher() async {
|
|
||||||
watchers["homebrewBinaries"]?.suspend()
|
|
||||||
await debouncers["homebrewBinaries"]?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func resumeHomebrewWatcher() {
|
|
||||||
watchers["homebrewBinaries"]?.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func onHomebrewPhpModification() async {
|
|
||||||
if let debouncer = self.debouncers["homebrewBinaries"] {
|
|
||||||
await debouncer.debounce(for: 5.0) {
|
|
||||||
Log.info("No changes in `\(self.container.paths.binPath)` occurred for 5 seconds. Reloading now.")
|
|
||||||
|
|
||||||
// We reload the PHP versions in the background
|
|
||||||
await self.container.phpEnvs.reloadPhpVersions()
|
|
||||||
|
|
||||||
// Finally, refresh the active installation
|
|
||||||
await MainMenu.shared.refreshActiveInstallation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
//
|
|
||||||
// App+ConfigWatch.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 30/03/2021.
|
|
||||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension App {
|
|
||||||
|
|
||||||
func startWatchManager(_ url: URL) {
|
|
||||||
Log.perf("Starting config watch manager...")
|
|
||||||
self.watchManager = ConfigWatchManager(for: url)
|
|
||||||
|
|
||||||
self.watchManager.didChange = { url in
|
|
||||||
Log.perf("Something has changed in: \(url)")
|
|
||||||
|
|
||||||
// Check if the watcher has last updated the menu less than 0.75s ago
|
|
||||||
let distance = self.watchManager.lastUpdate?.distance(to: Date().timeIntervalSince1970)
|
|
||||||
if distance == nil || distance != nil && distance! > 0.75 {
|
|
||||||
Log.perf("Refreshing menu...")
|
|
||||||
Task { @MainActor in MainMenu.shared.reloadPhpMonitorMenuInBackground() }
|
|
||||||
self.watchManager.lastUpdate = Date().timeIntervalSince1970
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlePhpConfigWatcher(forceReload: Bool = false) {
|
|
||||||
if container.filesystem is TestableFileSystem {
|
|
||||||
Log.warn("Config watch manager is disabled when using testable filesystem.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let install = container.phpEnvs.phpInstall else {
|
|
||||||
Log.info("It appears as if no PHP installation is currently active.")
|
|
||||||
Log.info("The config watch manager be disabled until a PHP install is active.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = URL(fileURLWithPath: "\(container.paths.etcPath)/php/\(install.version.short)")
|
|
||||||
|
|
||||||
// Check whether the manager exists and schedule on the main thread
|
|
||||||
// if we don't consistently do this, the app will create duplicate watchers
|
|
||||||
// due to timing issues, which creates retain cycles
|
|
||||||
Task { @MainActor in
|
|
||||||
// Watcher needs to be created
|
|
||||||
if self.watchManager == nil {
|
|
||||||
self.startWatchManager(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watcher needs to be updated
|
|
||||||
if self.watchManager.url != url || forceReload {
|
|
||||||
self.watchManager.disable()
|
|
||||||
self.watchManager = nil
|
|
||||||
Log.perf("Watcher has stopped watching files. Starting new one...")
|
|
||||||
self.startWatchManager(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
//
|
|
||||||
// ConfigFSNotifier.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 24/10/2023.
|
|
||||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class ConfigFSNotifier {
|
|
||||||
|
|
||||||
enum Behaviour {
|
|
||||||
case reloadsMenu
|
|
||||||
case reloadsWatchers
|
|
||||||
}
|
|
||||||
|
|
||||||
let url: URL
|
|
||||||
|
|
||||||
private var parent: ConfigWatchManager!
|
|
||||||
private var monitoredFolderFileDescriptor: CInt = -1
|
|
||||||
private var folderMonitorSource: DispatchSourceFileSystemObject?
|
|
||||||
|
|
||||||
init(
|
|
||||||
for url: URL,
|
|
||||||
eventMask: DispatchSource.FileSystemEvent,
|
|
||||||
parent: ConfigWatchManager,
|
|
||||||
behaviour: ConfigFSNotifier.Behaviour = .reloadsMenu
|
|
||||||
) {
|
|
||||||
self.url = url
|
|
||||||
self.parent = parent
|
|
||||||
self.startMonitoring(eventMask, behaviour: behaviour)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startMonitoring(
|
|
||||||
_ eventMask: DispatchSource.FileSystemEvent,
|
|
||||||
behaviour: ConfigFSNotifier.Behaviour
|
|
||||||
) {
|
|
||||||
// Ensure our starting state is correct, we may already be monitoring!
|
|
||||||
guard folderMonitorSource == nil && monitoredFolderFileDescriptor == -1 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll try to open a file descriptor and validate it
|
|
||||||
monitoredFolderFileDescriptor = open(url.path, O_EVTONLY)
|
|
||||||
|
|
||||||
// If our file descriptor here is still -1, there may have been an issue and we abort
|
|
||||||
guard monitoredFolderFileDescriptor >= 0 else {
|
|
||||||
Log.err("Failed to open file descriptor for \(url.path), not monitoring.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the source (with proper file descriptor, event mask and using the right queue)
|
|
||||||
folderMonitorSource = DispatchSource.makeFileSystemObjectSource(
|
|
||||||
fileDescriptor: monitoredFolderFileDescriptor,
|
|
||||||
eventMask: eventMask,
|
|
||||||
queue: parent.queue
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set the event handler (fires depending on the event mask)
|
|
||||||
folderMonitorSource?.setEventHandler { [weak self] in
|
|
||||||
if behaviour == .reloadsWatchers
|
|
||||||
&& !ConfigWatchManager.ignoresModificationsToConfigValues {
|
|
||||||
// Reload all configuration watchers
|
|
||||||
return App.shared.handlePhpConfigWatcher(forceReload: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let url = self?.url {
|
|
||||||
self?.parent.didChange?(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancellation handler, fired when we stop monitoring files
|
|
||||||
folderMonitorSource?.setCancelHandler { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
close(self.monitoredFolderFileDescriptor)
|
|
||||||
self.monitoredFolderFileDescriptor = -1
|
|
||||||
self.folderMonitorSource = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
folderMonitorSource?.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopMonitoring() {
|
|
||||||
folderMonitorSource?.cancel()
|
|
||||||
self.parent = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,24 +8,71 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class ConfigWatchManager {
|
actor ConfigWatchManager {
|
||||||
|
|
||||||
|
enum Behaviour {
|
||||||
|
case reloadsMenu
|
||||||
|
case reloadsWatchers
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Global state (applicable to ALL watchers)
|
// MARK: Global state (applicable to ALL watchers)
|
||||||
|
|
||||||
static var ignoresModificationsToConfigValues: Bool = false
|
static var ignoresModificationsToConfigValues: Bool = false
|
||||||
|
|
||||||
// MARK: Public variables
|
// MARK: Static methods
|
||||||
|
|
||||||
private var watchers: [ConfigFSNotifier] = []
|
/**
|
||||||
|
Handles the PHP config watcher lifecycle. Creates a new watcher if needed,
|
||||||
|
or recreates it if the PHP version has changed.
|
||||||
|
|
||||||
let queue = DispatchQueue(label: "com.nicoverbruggen.phpmon.config_watch")
|
Actor isolation ensures no duplicate watchers or retain cycles.
|
||||||
let url: URL
|
*/
|
||||||
var lastUpdate: TimeInterval?
|
@MainActor
|
||||||
var didChange: ((URL) -> Void)?
|
public static func handleWatcher(forceReload: Bool = false) async {
|
||||||
|
let container = App.shared.container
|
||||||
|
|
||||||
|
if container.filesystem is TestableFileSystem {
|
||||||
|
Log.warn("Config watch manager is disabled when using testable filesystem.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let install = container.phpEnvs.phpInstall else {
|
||||||
|
Log.info("It appears as if no PHP installation is currently active.")
|
||||||
|
Log.info("The config watch manager is disabled until a PHP install is active.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = URL(fileURLWithPath: "\(container.paths.etcPath)/php/\(install.version.short)")
|
||||||
|
|
||||||
|
// Create watcher if missing
|
||||||
|
guard let manager = App.shared.configWatchManager else {
|
||||||
|
let manager = ConfigWatchManager(for: url)
|
||||||
|
await manager.setupWatchers()
|
||||||
|
App.shared.configWatchManager = manager
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing watcher if needed
|
||||||
|
if await manager.url != url {
|
||||||
|
// URL changed - update to different PHP version
|
||||||
|
await manager.updateUrl(to: url)
|
||||||
|
} else if forceReload {
|
||||||
|
// Same URL - just reload watchers (e.g., conf.d files added/removed)
|
||||||
|
await manager.reloadWatchers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Instance variables
|
||||||
|
|
||||||
|
private var watchers: [FSNotifier] = []
|
||||||
|
private var debouncer: Debouncer
|
||||||
|
|
||||||
|
private(set) var url: URL
|
||||||
|
nonisolated private let debounceInterval: TimeInterval
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
init(for url: URL) {
|
init(for url: URL, debounceInterval: TimeInterval = 0.75) {
|
||||||
if App.shared.container.filesystem is TestableFileSystem {
|
if App.shared.container.filesystem is TestableFileSystem {
|
||||||
fatalError("""
|
fatalError("""
|
||||||
ConfigWatchManager is currently incompatible with a testable filesystem!"
|
ConfigWatchManager is currently incompatible with a testable filesystem!"
|
||||||
@@ -34,6 +81,13 @@ class ConfigWatchManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.debounceInterval = debounceInterval
|
||||||
|
self.debouncer = Debouncer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupWatchers() {
|
||||||
|
// Guard against double setup
|
||||||
|
assert(watchers.isEmpty, "setupWatchers() called when watchers already exist")
|
||||||
|
|
||||||
// Add a watcher for php.ini
|
// Add a watcher for php.ini
|
||||||
self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write)
|
self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write)
|
||||||
@@ -62,25 +116,64 @@ class ConfigWatchManager {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func addWatcher(
|
private func clearWatchers() {
|
||||||
|
for watcher in self.watchers {
|
||||||
|
watcher.terminate()
|
||||||
|
}
|
||||||
|
self.watchers.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadWatchers() {
|
||||||
|
Log.perf("Reloading configuration watchers...")
|
||||||
|
clearWatchers()
|
||||||
|
setupWatchers()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUrl(to newUrl: URL) {
|
||||||
|
Log.perf("Updating watcher URL from \(self.url.path) to \(newUrl.path)...")
|
||||||
|
clearWatchers()
|
||||||
|
self.url = newUrl
|
||||||
|
setupWatchers()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleConfigChange(at url: URL) async {
|
||||||
|
await debouncer.debounce(for: debounceInterval) {
|
||||||
|
Log.perf("Config file changed at \(url.path), debounce completed. Refreshing menu...")
|
||||||
|
Task { @MainActor in MainMenu.shared.reloadPhpMonitorMenuInBackground() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addWatcher(
|
||||||
for url: URL,
|
for url: URL,
|
||||||
eventMask: DispatchSource.FileSystemEvent,
|
eventMask: DispatchSource.FileSystemEvent,
|
||||||
behaviour: ConfigFSNotifier.Behaviour = .reloadsMenu
|
behaviour: Behaviour = .reloadsMenu
|
||||||
) {
|
) {
|
||||||
if !App.shared.container.filesystem.anyExists(url.path) {
|
if !App.shared.container.filesystem.anyExists(url.path) {
|
||||||
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
|
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let watcher = ConfigFSNotifier(for: url, eventMask: eventMask, parent: self, behaviour: behaviour)
|
let watcher = FSNotifier(for: url, eventMask: eventMask) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
Task {
|
||||||
|
if behaviour == .reloadsWatchers
|
||||||
|
&& !ConfigWatchManager.ignoresModificationsToConfigValues {
|
||||||
|
// Reload all configuration watchers on this manager
|
||||||
|
await self.reloadWatchers()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await self.handleConfigChange(at: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
self.watchers.append(watcher)
|
self.watchers.append(watcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
func disable() {
|
func disable() async {
|
||||||
Log.perf("Turning off all individual existing watchers...")
|
Log.perf("Turning off all individual existing watchers...")
|
||||||
self.watchers.forEach { (watcher) in
|
await debouncer.cancel()
|
||||||
watcher.stopMonitoring()
|
clearWatchers()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|||||||
@@ -8,26 +8,37 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class FSNotifier {
|
actor FSNotifier {
|
||||||
|
|
||||||
public static var shared: FSNotifier! = nil
|
// MARK: Variables
|
||||||
|
|
||||||
// MARK: Public variables
|
/** The URL of the file or folder that is being observed. */
|
||||||
|
nonisolated let url: URL
|
||||||
|
|
||||||
let queue = DispatchQueue(label: "com.nicoverbruggen.phpmon.fs_notifier")
|
/** Whether responding to events is currently on hold. */
|
||||||
let url: URL
|
private(set) var isSuspended = false
|
||||||
|
|
||||||
// MARK: Private variables
|
// MARK: Internal Variables
|
||||||
|
|
||||||
private var fileDescriptor: CInt = -1
|
/** The queue that is used for the `dispatchSource`. */
|
||||||
private var dispatchSource: DispatchSourceFileSystemObject?
|
private nonisolated let queue: DispatchQueue
|
||||||
|
|
||||||
private var isSuspended = false
|
/** An open file or folder required for observation. */
|
||||||
|
private nonisolated(unsafe) var fileDescriptor: CInt = -1
|
||||||
|
|
||||||
|
/** A dispatch source that monitors events associated with a file or folder. */
|
||||||
|
private nonisolated(unsafe) var dispatchSource: DispatchSourceFileSystemObject?
|
||||||
|
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
|
|
||||||
init(for url: URL, eventMask: DispatchSource.FileSystemEvent, onChange: @escaping () -> Void) {
|
init(
|
||||||
|
for url: URL,
|
||||||
|
eventMask: DispatchSource.FileSystemEvent,
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
onChange: @escaping () -> Void
|
||||||
|
) {
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.queue = queue ?? DispatchQueue(label: "com.nicoverbruggen.phpmon.fs_notifier")
|
||||||
|
|
||||||
fileDescriptor = open(url.path, O_EVTONLY)
|
fileDescriptor = open(url.path, O_EVTONLY)
|
||||||
|
|
||||||
@@ -42,19 +53,19 @@ class FSNotifier {
|
|||||||
queue: self.queue
|
queue: self.queue
|
||||||
)
|
)
|
||||||
|
|
||||||
dispatchSource?.setEventHandler(handler: {
|
dispatchSource?.setEventHandler(handler: { [weak self] in
|
||||||
self.queue.async {
|
Task { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
// If our notifier is suspended, don't fire
|
// If our notifier is suspended, don't fire
|
||||||
guard !self.isSuspended else { return }
|
guard await !self.isSuspended else { return }
|
||||||
|
|
||||||
// If our notifier is not suspended, fire
|
// If our notifier is not suspended, fire
|
||||||
Task { onChange() }
|
onChange()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatchSource?.setCancelHandler(handler: { [weak self] in
|
dispatchSource?.setCancelHandler(handler: {
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
close(self.fileDescriptor)
|
close(self.fileDescriptor)
|
||||||
self.fileDescriptor = -1
|
self.fileDescriptor = -1
|
||||||
self.dispatchSource = nil
|
self.dispatchSource = nil
|
||||||
@@ -63,26 +74,25 @@ class FSNotifier {
|
|||||||
dispatchSource?.resume()
|
dispatchSource?.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspend() {
|
/** Suspends responding to filesystem events. This does not stop events from being observed! */
|
||||||
self.queue.async {
|
func suspend() async {
|
||||||
self.isSuspended = true
|
self.isSuspended = true
|
||||||
Log.perf("FSNotifier for \(self.url) has been suspended.")
|
Log.perf("FSNotifier for \(self.url.path) has been suspended.")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func resume() {
|
/** Resumes responding to filesystem events. */
|
||||||
self.queue.async {
|
func resume() async {
|
||||||
self.isSuspended = false
|
self.isSuspended = false
|
||||||
Log.perf("FSNotifier for \(self.url) has been resumed.")
|
Log.perf("FSNotifier for \(self.url.path) has been resumed.")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func terminate() {
|
/** Terminates the file monitor, which will cause `deinit` to fire. */
|
||||||
|
nonisolated func terminate() {
|
||||||
dispatchSource?.cancel()
|
dispatchSource?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
nonisolated deinit {
|
||||||
Log.perf("FSNotifier for \(self.url) will be deinitialized.")
|
Log.perf("deinit: FSNotifier @ \(self.url.path)")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
173
phpmon/Domain/Watcher/HomebrewWatchManager.swift
Normal file
173
phpmon/Domain/Watcher/HomebrewWatchManager.swift
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
//
|
||||||
|
// HomebrewWatchManager.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 29/11/2025.
|
||||||
|
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
actor HomebrewWatchManager {
|
||||||
|
|
||||||
|
// MARK: Public API
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prepares the Homebrew watcher. This allows PHP Monitor to quickly respond to
|
||||||
|
external `brew` changes executed by the user.
|
||||||
|
*/
|
||||||
|
@MainActor
|
||||||
|
public static func prepare() async {
|
||||||
|
let binPath = App.shared.container.paths.binPath
|
||||||
|
let manager = HomebrewWatchManager(
|
||||||
|
for: URL(fileURLWithPath: binPath),
|
||||||
|
debounceInterval: 5.0
|
||||||
|
)
|
||||||
|
await manager.setupWatcher()
|
||||||
|
App.shared.homebrewWatchManager = manager
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Performs a particular action while suspending the Homebrew watcher,
|
||||||
|
until the task is completed.
|
||||||
|
|
||||||
|
Any operations that cause Homebrew to perform tasks (installing,
|
||||||
|
updating, removing packages) should be wrapped in this helper method,
|
||||||
|
to prevent the app from doing duplicate work.
|
||||||
|
*/
|
||||||
|
public static func withSuspended<T>(_ action: () async throws -> T) async rethrows -> T {
|
||||||
|
guard let manager = App.shared.homebrewWatchManager else {
|
||||||
|
// If there's no manager, run the task as-is
|
||||||
|
return try await action()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend, execute the action, and resume
|
||||||
|
return try await manager.withSuspended(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Instance variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
The underlying `FSNotifier` which will respond to filesystem events.
|
||||||
|
*/
|
||||||
|
private var watcher: FSNotifier?
|
||||||
|
|
||||||
|
/**
|
||||||
|
The debouncer, responsible for ensuring events stop firing before
|
||||||
|
finally responding to changes in `homebrew/bin`.
|
||||||
|
*/
|
||||||
|
private var debouncer: Debouncer
|
||||||
|
|
||||||
|
/**
|
||||||
|
The URL of the `homebrew/bin` path, that we will be watching, too.
|
||||||
|
*/
|
||||||
|
nonisolated let url: URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
The interval for the debounce. Prevents bulk changes from triggering
|
||||||
|
too many fired events.
|
||||||
|
*/
|
||||||
|
nonisolated let debounceInterval: TimeInterval
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
init(for url: URL, debounceInterval: TimeInterval = 5.0) {
|
||||||
|
if App.shared.container.filesystem is TestableFileSystem {
|
||||||
|
fatalError("""
|
||||||
|
HomebrewWatchManager is currently incompatible with a testable filesystem!
|
||||||
|
You are not allowed to instantiate these while using a testable filesystem.
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.url = url
|
||||||
|
self.debounceInterval = debounceInterval
|
||||||
|
self.debouncer = Debouncer()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Log.perf("deinit: \(String(describing: self)).\(#function)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets up the watcher, assuming one does not exist.
|
||||||
|
The target directory must exist.
|
||||||
|
*/
|
||||||
|
private func setupWatcher() {
|
||||||
|
// Guard against double setup
|
||||||
|
assert(watcher == nil, "setupWatcher() called when watcher already exists")
|
||||||
|
|
||||||
|
// Ensure that the target directory exists
|
||||||
|
if !App.shared.container.filesystem.anyExists(url.path) {
|
||||||
|
Log.warn("No watcher was created for \(url.path) because the requested directory does not exist.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new FSNotifier which will respond to all events.
|
||||||
|
// If files are created, removed, etc. in this `homebrew/bin` folder, the handler will fire.
|
||||||
|
self.watcher = FSNotifier(for: url, eventMask: .all) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await self.onHomebrewPhpModification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.perf("A watcher exists for Homebrew binaries at: \(url.relativePath)")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Reloads PHP versions and refreshes the active PHP installation if any changes
|
||||||
|
are made to Homebrew binaries. Usually external changes to packages will trigger this.
|
||||||
|
|
||||||
|
As such, PHP Monitor will check if anything has changed with PHP.
|
||||||
|
*/
|
||||||
|
private func onHomebrewPhpModification() async {
|
||||||
|
await debouncer.debounce(for: debounceInterval) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
Log.info("No changes in `\(self.url.path)` occurred for \(self.debounceInterval) seconds. Reloading now.")
|
||||||
|
|
||||||
|
// We reload the PHP versions in the background
|
||||||
|
await App.shared.container.phpEnvs.reloadPhpVersions()
|
||||||
|
|
||||||
|
// Finally, refresh the active installation
|
||||||
|
await MainMenu.shared.refreshActiveInstallation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Suspend and resume
|
||||||
|
|
||||||
|
/**
|
||||||
|
Suspends the `HomebrewWatchManager`.
|
||||||
|
This prevents any changes to `/homebrew/bin` from causing events to fire.
|
||||||
|
*/
|
||||||
|
private func suspend() async {
|
||||||
|
await watcher?.suspend()
|
||||||
|
await debouncer.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resumes the `HomebrewWatchManager`.
|
||||||
|
Any changes to `/homebrew/bin` are picked up again.
|
||||||
|
*/
|
||||||
|
private func resume() async {
|
||||||
|
await watcher?.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Executes an `action` callback after suspending the watcher.
|
||||||
|
*/
|
||||||
|
private func withSuspended<T>(_ action: () async throws -> T) async rethrows -> T {
|
||||||
|
await suspend()
|
||||||
|
do {
|
||||||
|
let result = try await action()
|
||||||
|
await resume()
|
||||||
|
return result
|
||||||
|
} catch {
|
||||||
|
await resume()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ extension PhpVersionManagerView {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
self.setBusyStatus(true)
|
self.setBusyStatus(true)
|
||||||
try await App.shared.withSuspendedHomebrewWatcher {
|
try await HomebrewWatchManager.withSuspended {
|
||||||
try await command.execute(shell: container.shell) { progress in
|
try await command.execute(shell: container.shell) { progress in
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
self.status.title = progress.title
|
self.status.title = progress.title
|
||||||
@@ -99,7 +99,7 @@ extension PhpVersionManagerView {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
self.setBusyStatus(true)
|
self.setBusyStatus(true)
|
||||||
try await App.shared.withSuspendedHomebrewWatcher {
|
try await HomebrewWatchManager.withSuspended {
|
||||||
try await command.execute(shell: container.shell) { progress in
|
try await command.execute(shell: container.shell) { progress in
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
self.status.title = progress.title
|
self.status.title = progress.title
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ struct FSNotifierTest {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
defer {
|
|
||||||
notifier.terminate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify the file, twice
|
// Modify the file, twice
|
||||||
try "hello".write(to: testFile, atomically: false, encoding: .utf8)
|
try "hello".write(to: testFile, atomically: false, encoding: .utf8)
|
||||||
try "hello".write(to: testFile, atomically: false, encoding: .utf8)
|
try "hello".write(to: testFile, atomically: false, encoding: .utf8)
|
||||||
@@ -59,5 +55,8 @@ struct FSNotifierTest {
|
|||||||
// Verify after another second, our second write is actually noted
|
// Verify after another second, our second write is actually noted
|
||||||
await delay(seconds: 1.2)
|
await delay(seconds: 1.2)
|
||||||
#expect(eventFired.value == 2)
|
#expect(eventFired.value == 2)
|
||||||
|
|
||||||
|
// Clean up notifier
|
||||||
|
notifier.terminate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user