diff --git a/AeroSpace.xcodeproj/project.pbxproj b/AeroSpace.xcodeproj/project.pbxproj index beac53df..526a689f 100644 --- a/AeroSpace.xcodeproj/project.pbxproj +++ b/AeroSpace.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 1311398A83B998908773C54D /* FocusCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EAADE8D2FB5D05FA5456B0 /* FocusCommandTest.swift */; }; 1C46EBB55D401C0D1AFD50F0 /* CollectionEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CE37C1B8D858C81A396F40 /* CollectionEx.swift */; }; 1D408CDF1A489E527327EB15 /* CompositeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82CD9670B7A6050073E0F76 /* CompositeCommand.swift */; }; + 1FD8762CC7C132D01D4B3090 /* cliUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB6F0170422A7606D89BCF0 /* cliUtil.swift */; }; 22175400298B985658E774EE /* ResizeCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */; }; 238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */ = {isa = PBXBuildFile; fileRef = 8FE45A887100EB70912B07F0 /* default-config.toml */; }; 2821C1C7AAEF4C290633EA72 /* FullscreenCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECC990B6C2D66D343216A12 /* FullscreenCommand.swift */; }; @@ -25,15 +26,18 @@ 2E06134604F2510189F1FA85 /* ExecAndWaitCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EDB8770FBA8B2E6FE91BBB /* ExecAndWaitCommand.swift */; }; 374CE35B85B941B8F584C113 /* FlattenWorkspaceTreeCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FDECECC773EBA30661EB8A /* FlattenWorkspaceTreeCommandTest.swift */; }; 3774857EF024E97B7AA5DE78 /* MoveNodeToWorkspaceCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25AC44D0E9450867215FCBEC /* MoveNodeToWorkspaceCommand.swift */; }; + 3940B09E1002CE55A77605E4 /* VersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F187A6469837B279A528F0AF /* VersionCommand.swift */; }; 39EC2CF418F086457E900177 /* showMessageToUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14FABE2521026C8394254D97 /* showMessageToUser.swift */; }; 3BD6FF4CC51532977DA0C05A /* AeroAny.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82476B9BEBAC00EB9E32256F /* AeroAny.swift */; }; 42197B9C71A0CDDE65804A6A /* accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D31BF26EAFA96F675D2C14B /* accessibility.swift */; }; + 43E3628E37D2439B820FFC82 /* server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796713A1B3AEEBF4D0D180C7 /* server.swift */; }; 45AA5FD4A023AF751922BC22 /* BundleEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7A2DF0D1F72B80B1F04240 /* BundleEx.swift */; }; 45EA2D1C90430C432E123B51 /* keysMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0D40CBD65704BA9595C2FA /* keysMap.swift */; }; 4D62EC19C6A1E3BBF54306BA /* Copyable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B4C778D2594EB23C295741 /* Copyable.swift */; }; 56E72B24303F5F337B31B776 /* TrayMenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */; }; 5BC4AA58A69956C8B6379E47 /* JoinWithCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27A7AF7E9A049700BDE276B /* JoinWithCommand.swift */; }; 5DA2DA21600E8B5BCA3DCFC0 /* LayoutCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B673C67B00DBFCC27FFE7 /* LayoutCommand.swift */; }; + 61CF8A4E50A48ECBFD5522D0 /* versionGenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DBBA6572D2AC38AF68F804 /* versionGenerated.swift */; }; 6317AB471F4C4F5D66A25784 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EEDBFFCA7A77D96B18FB0732 /* Assets.xcassets */; }; 635733FDDF37E44364372B74 /* MruStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954A434EE57D76F5A9D4140D /* MruStack.swift */; }; 64A058E536F1EEF7F01043AF /* TOMLKit in Frameworks */ = {isa = PBXBuildFile; productRef = EC8E4F2CA4FF8884F9F59975 /* TOMLKit */; }; @@ -74,11 +78,13 @@ BD6301B2CFC16FDE4223ACB8 /* MacApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DB782C527ABE0CF31740EB /* MacApp.swift */; }; BF16873111EEDE60A8AACD6B /* Workspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F068BCC50ED846DCBFDE57 /* Workspace.swift */; }; C0A88261ECF505FC5648FC0A /* OptionalEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9EDFD4A9F45182CA6E0BD7B /* OptionalEx.swift */; }; + C830FD347D7F9C06CF53103F /* Socket in Frameworks */ = {isa = PBXBuildFile; productRef = 998F8F4D88E65F73B0B2894D /* Socket */; }; D24D02B1FD87424B908986AF /* ResizeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB371951459F8CB0137788C2 /* ResizeCommand.swift */; }; D4281F11EC10B5CD6470DF97 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083785CBAE36EC57F5F51BC8 /* main.swift */; }; DCCC05496BDAAFB745E99624 /* SplitCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D96FF3675C63A83DC8B8969 /* SplitCommandTest.swift */; }; E1E35DC5D9D74D54E92AB5F3 /* ExecCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A6EF465EF4129BCB10FE247 /* ExecCommandTest.swift */; }; E22ACB36C90695FBAC78226E /* TestWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1905935B0C61590A96EFEF /* TestWindow.swift */; }; + E23E3E87A0C2798E9F87EDEA /* Socket in Frameworks */ = {isa = PBXBuildFile; productRef = 82F718CE1C9CF9EB5A80DDE7 /* Socket */; }; E5682579AEC6B84CF6FCE90D /* TilingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C848D6E57FDF22AAF0FB45E6 /* TilingContainer.swift */; }; E7F53669DD11FA7C6C3540A0 /* ConfigTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54679FCD1EC1196B27E964D5 /* ConfigTest.swift */; }; ED96E36786C941AB3AF780BC /* startAtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9164C9401F7DDCACE9278DA4 /* startAtLogin.swift */; }; @@ -86,6 +92,7 @@ EECC59858691B99A95542D72 /* MacWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9245C6FACF389672EA71173B /* MacWindow.swift */; }; F892B5DCB4F731B3E173FF4C /* TreeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A262B442A94C1964509B691 /* TreeNode.swift */; }; F982DB924450BBBB4FDF4C2C /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD90FB41FD41602120F67FF5 /* Command.swift */; }; + FA06CD76F0297DA2C1149432 /* gitHashGenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1408CD8F480BC8974EDE2470 /* gitHashGenerated.swift */; }; FC35D6D0A678CC802972C6FE /* ReloadConfigCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF3C9038C846369FDD71D1D2 /* ReloadConfigCommand.swift */; }; FD4386BC632BAA6A4105FFD8 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9752080BBA547C2A0EF076F0 /* Config.swift */; }; /* End PBXBuildFile section */ @@ -108,6 +115,7 @@ 0AEE5470AF418906B180A593 /* mouse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mouse.swift; sourceTree = ""; }; 0D9301F99737BE4888DBC2A3 /* FocusedWorkspaceSourceOfTruth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedWorkspaceSourceOfTruth.swift; sourceTree = ""; }; 0FF221FD5C0CB4163E322D6D /* MoveWorkspaceToDisplayCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveWorkspaceToDisplayCommand.swift; sourceTree = ""; }; + 1408CD8F480BC8974EDE2470 /* gitHashGenerated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = gitHashGenerated.swift; sourceTree = ""; }; 14FABE2521026C8394254D97 /* showMessageToUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = showMessageToUser.swift; sourceTree = ""; }; 1A2B673C67B00DBFCC27FFE7 /* LayoutCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutCommand.swift; sourceTree = ""; }; 1C0D40CBD65704BA9595C2FA /* keysMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = keysMap.swift; sourceTree = ""; }; @@ -131,12 +139,14 @@ 54B4C778D2594EB23C295741 /* Copyable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Copyable.swift; sourceTree = ""; }; 569422C0C4C23EF3E024C8E6 /* ExecAndForgetCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExecAndForgetCommand.swift; sourceTree = ""; }; 57B60F7A58DAD4D3AF2788E4 /* WorkspaceEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceEx.swift; sourceTree = ""; }; + 5EB6F0170422A7606D89BCF0 /* cliUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cliUtil.swift; sourceTree = ""; }; 5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrayMenuModel.swift; sourceTree = ""; }; 6352ADEE6625D9703CFCA99A /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; 67B9FFF81EB0327ABD51A7FE /* MoveThroughCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveThroughCommand.swift; sourceTree = ""; }; 67DBAF4ECF8A0B931FC34EAD /* parseConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = parseConfig.swift; sourceTree = ""; }; 6D9C5ED5AC77D80F1CCD103F /* JoinWithCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinWithCommandTest.swift; sourceTree = ""; }; 6F1905935B0C61590A96EFEF /* TestWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestWindow.swift; sourceTree = ""; }; + 796713A1B3AEEBF4D0D180C7 /* server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = server.swift; sourceTree = ""; }; 7A247B616D1951777C565D02 /* MoveThroughCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveThroughCommandTest.swift; sourceTree = ""; }; 7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeCommandTest.swift; sourceTree = ""; }; 82476B9BEBAC00EB9E32256F /* AeroAny.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AeroAny.swift; sourceTree = ""; }; @@ -175,12 +185,14 @@ D27A7AF7E9A049700BDE276B /* JoinWithCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinWithCommand.swift; sourceTree = ""; }; D61FE8B343B068F0FFFC2373 /* focused.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = focused.swift; sourceTree = ""; }; D82CD9670B7A6050073E0F76 /* CompositeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositeCommand.swift; sourceTree = ""; }; + D9DBBA6572D2AC38AF68F804 /* versionGenerated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = versionGenerated.swift; sourceTree = ""; }; DAC5AF4019D1F5DAB5AF2B56 /* layoutRecursive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = layoutRecursive.swift; sourceTree = ""; }; E761155C73F06E2CF5E292A4 /* FocusCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusCommand.swift; sourceTree = ""; }; E9589EFDEBA4EB9C7DBAFCFD /* MoveNodeToWorkspaceCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveNodeToWorkspaceCommandTest.swift; sourceTree = ""; }; EC2F56249A233EC9806D0F08 /* Bridged-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridged-Header.h"; sourceTree = ""; }; EED7EE20910D7BE4D0150CED /* WorkspaceBackAndForthCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceBackAndForthCommand.swift; sourceTree = ""; }; EEDBFFCA7A77D96B18FB0732 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F187A6469837B279A528F0AF /* VersionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionCommand.swift; sourceTree = ""; }; F3C5B6719EF0BC14D7CF868C /* FlattenWorkspaceTreeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlattenWorkspaceTreeCommand.swift; sourceTree = ""; }; F6507EBAA795220FD0C05384 /* Monitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monitor.swift; sourceTree = ""; }; F77C03164B8BFD1E59779C6E /* testUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = testUtil.swift; sourceTree = ""; }; @@ -188,12 +200,21 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 26C0EAF7FC93D982ADCE70A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E23E3E87A0C2798E9F87EDEA /* Socket in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2AFAB0BC1A2742132D7CB950 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 852F88894A3B9FC385563665 /* HotKey in Frameworks */, 64A058E536F1EEF7F01043AF /* TOMLKit in Frameworks */, + C830FD347D7F9C06CF53103F /* Socket in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -203,7 +224,10 @@ 0C2FD7C5A12AF81966A50406 /* cli */ = { isa = PBXGroup; children = ( + 5EB6F0170422A7606D89BCF0 /* cliUtil.swift */, + 1408CD8F480BC8974EDE2470 /* gitHashGenerated.swift */, 083785CBAE36EC57F5F51BC8 /* main.swift */, + D9DBBA6572D2AC38AF68F804 /* versionGenerated.swift */, ); path = cli; sourceTree = ""; @@ -312,6 +336,7 @@ 49B99DD05BF656B8351E12CE /* gitHashGenerated.swift */, 3C2E5977331398421A4FC168 /* GlobalObserver.swift */, 526B113159987FA43EA41120 /* refresh.swift */, + 796713A1B3AEEBF4D0D180C7 /* server.swift */, 5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */, A711A25EEC57098295B90C17 /* command */, 50A41C4CCFC95C514CA1EAD5 /* config */, @@ -322,6 +347,14 @@ path = src; sourceTree = ""; }; + 88F5F202E4D39BE05D077944 /* cli */ = { + isa = PBXGroup; + children = ( + F187A6469837B279A528F0AF /* VersionCommand.swift */, + ); + path = cli; + sourceTree = ""; + }; A6E1E25AB6F33B13B0633B9C /* test */ = { isa = PBXGroup; children = ( @@ -356,6 +389,7 @@ B65F0D298D61D85CEB29CE77 /* SplitCommand.swift */, EED7EE20910D7BE4D0150CED /* WorkspaceBackAndForthCommand.swift */, 43DD32B1711B8EFCC834B68E /* WorkspaceCommand.swift */, + 88F5F202E4D39BE05D077944 /* cli */, ); path = command; sourceTree = ""; @@ -413,6 +447,7 @@ packageProductDependencies = ( 42BC1E757EF69233C2262FF4 /* HotKey */, EC8E4F2CA4FF8884F9F59975 /* TOMLKit */, + 998F8F4D88E65F73B0B2894D /* Socket */, ); productName = AeroSpace; productReference = 09685297933511208058F7CF /* AeroSpace.app */; @@ -439,12 +474,16 @@ buildConfigurationList = 864ED77DD1B09CFD612863EC /* Build configuration list for PBXNativeTarget "AeroSpace-cli" */; buildPhases = ( 0CFF67628941154DC17DDD8D /* Sources */, + 26C0EAF7FC93D982ADCE70A0 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "AeroSpace-cli"; + packageProductDependencies = ( + 82F718CE1C9CF9EB5A80DDE7 /* Socket */, + ); productName = "AeroSpace-cli"; productReference = 458AD0CD907B56A99DA821C4 /* AeroSpace-cli */; productType = "com.apple.product-type.tool"; @@ -477,6 +516,7 @@ mainGroup = 393942C56466FDBBE35F9EC0; packageReferences = ( E28C4FCC0A02BC0BF9E71D67 /* XCRemoteSwiftPackageReference "HotKey" */, + 4F1AD32341B10DF7D687D5EA /* XCRemoteSwiftPackageReference "BlueSocket" */, D95CBB0F220F4BED0648272B /* XCRemoteSwiftPackageReference "TOMLKit" */, ); projectDirPath = ""; @@ -506,7 +546,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1FD8762CC7C132D01D4B3090 /* cliUtil.swift in Sources */, + FA06CD76F0297DA2C1149432 /* gitHashGenerated.swift in Sources */, D4281F11EC10B5CD6470DF97 /* main.swift in Sources */, + 61CF8A4E50A48ECBFD5522D0 /* versionGenerated.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -577,6 +620,7 @@ F892B5DCB4F731B3E173FF4C /* TreeNode.swift in Sources */, 03D8ABD4597EDB00D79807E1 /* TreeNodeEx.swift in Sources */, AC439551A737C73168CC5BF9 /* TreeNodeKind.swift in Sources */, + 3940B09E1002CE55A77605E4 /* VersionCommand.swift in Sources */, 2CC46A952773F148AC398144 /* Window.swift in Sources */, BF16873111EEDE60A8AACD6B /* Workspace.swift in Sources */, AC47DF43EFF42358F033C87C /* WorkspaceBackAndForthCommand.swift in Sources */, @@ -594,6 +638,7 @@ A0765C31043BCFB0420BF1C9 /* parseConfig.swift in Sources */, B3702BB393A9B03CCAE4C60E /* refresh.swift in Sources */, 8086A22EDCDC4C906C337D0B /* resizeWithMouse.swift in Sources */, + 43E3628E37D2439B820FFC82 /* server.swift in Sources */, 39EC2CF418F086457E900177 /* showMessageToUser.swift in Sources */, ED96E36786C941AB3AF780BC /* startAtLogin.swift in Sources */, 93D44EA41776738B4758C28D /* util.swift in Sources */, @@ -884,6 +929,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 4F1AD32341B10DF7D687D5EA /* XCRemoteSwiftPackageReference "BlueSocket" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Kitura/BlueSocket"; + requirement = { + kind = exactVersion; + version = 2.0.4; + }; + }; D95CBB0F220F4BED0648272B /* XCRemoteSwiftPackageReference "TOMLKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/LebJe/TOMLKit"; @@ -908,6 +961,16 @@ package = E28C4FCC0A02BC0BF9E71D67 /* XCRemoteSwiftPackageReference "HotKey" */; productName = HotKey; }; + 82F718CE1C9CF9EB5A80DDE7 /* Socket */ = { + isa = XCSwiftPackageProductDependency; + package = 4F1AD32341B10DF7D687D5EA /* XCRemoteSwiftPackageReference "BlueSocket" */; + productName = Socket; + }; + 998F8F4D88E65F73B0B2894D /* Socket */ = { + isa = XCSwiftPackageProductDependency; + package = 4F1AD32341B10DF7D687D5EA /* XCRemoteSwiftPackageReference "BlueSocket" */; + productName = Socket; + }; EC8E4F2CA4FF8884F9F59975 /* TOMLKit */ = { isa = XCSwiftPackageProductDependency; package = D95CBB0F220F4BED0648272B /* XCRemoteSwiftPackageReference "TOMLKit" */; diff --git a/AeroSpace.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AeroSpace.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1606d4af..40397e68 100644 --- a/AeroSpace.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AeroSpace.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "bluesocket", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/BlueSocket", + "state" : { + "revision" : "7b23a867008e0027bfd6f4d398d44720707bc8ca", + "version" : "2.0.4" + } + }, { "identity" : "hotkey", "kind" : "remoteSourceControl", diff --git a/build-debug.sh b/build-debug.sh index 606359fb..23a9bc25 100755 --- a/build-debug.sh +++ b/build-debug.sh @@ -6,7 +6,7 @@ set -o pipefail # Any command failed in the pipe fails the whole pipe cd "$(dirname "$0")" -xcodegen # https://github.com/yonaskolb/XcodeGen +./generate.sh xcodebuild -scheme AeroSpace build -configuration Debug # no clean because it may lead to accessibility permission loss xcodebuild -scheme AeroSpace-cli build -configuration Debug # no clean because it may lead to accessibility permission loss @@ -19,4 +19,4 @@ pushd ~/Library/Developer/Xcode/DerivedData > /dev/null fi popd > /dev/null cp -r ~/Library/Developer/Xcode/DerivedData/AeroSpace*/Build/Products/Debug/AeroSpace-Debug.app .build -cp -r ~/Library/Developer/Xcode/DerivedData/AeroSpace*/Build/Products/Debug/AeroSpace-cli .build/aerospace +cp -r ~/Library/Developer/Xcode/DerivedData/AeroSpace*/Build/Products/Debug/AeroSpace-cli .build/aerospace-debug diff --git a/build-release.sh b/build-release.sh index 68cf33c8..4bf076e7 100755 --- a/build-release.sh +++ b/build-release.sh @@ -14,19 +14,20 @@ checkCleanGitWorkingDir() { } generateGitHash() { -cat > src/gitHashGenerated.swift <<-EOF +tee src/gitHashGenerated.swift cli/gitHashGenerated.swift > /dev/null < /dev/null diff --git a/clean-project.sh b/clean-project.sh index 4746de0f..b30bc013 100755 --- a/clean-project.sh +++ b/clean-project.sh @@ -6,6 +6,6 @@ set -o pipefail # Any command failed in the pipe fails the whole pipe cd "$(dirname "$0")" rm -rf AeroSpace.xcodeproj -xcodegen # https://github.com/yonaskolb/XcodeGen +./generate.sh xcodebuild clean rm -rf ~/Library/Developer/Xcode/DerivedData/AeroSpace-* diff --git a/cli/cliUtil.swift b/cli/cliUtil.swift new file mode 100644 index 00000000..8de6658d --- /dev/null +++ b/cli/cliUtil.swift @@ -0,0 +1,19 @@ +import Foundation +import Darwin + +#if DEBUG +let appId: String = "bobko.debug.aerospace" +#else +let appId: String = "bobko.aerospace" +#endif + +public func error(_ message: String = "") -> Never { + errorT(message) +} + +public func errorT(_ message: String = "") -> T { + print(message) + exit(1) +} + +let cliClientVersionAndHash: String = "\(cliClientVersion) \(gitHash)" diff --git a/cli/gitHashGenerated.swift b/cli/gitHashGenerated.swift new file mode 100644 index 00000000..46c55711 --- /dev/null +++ b/cli/gitHashGenerated.swift @@ -0,0 +1,3 @@ +// BEWARE! This file is auto-updated by build-release.sh +public let gitHash = "DEBUG" +public let gitShortHash = "DEBUG" diff --git a/cli/main.swift b/cli/main.swift index 9effe8f6..d4d0fb03 100644 --- a/cli/main.swift +++ b/cli/main.swift @@ -1,3 +1,57 @@ +import Socket import Foundation -print("Hello, World!") \ No newline at end of file +let command: [String] = Array(CommandLine.arguments.dropFirst()) + +for word in command { + if word.contains(" ") { + error("Spaces in arguments are not permitted. '\(word)' argument contains spaces.") + } +} + +let usage = + """ + USAGE: \(CommandLine.arguments.first ?? "aerospace") COMMAND + + See https://github.com/nikitabobko/AeroSpace/blob/main/docs/commands.md for the list of all available commands + """ +if command.first == "--help" || command.first == "-h" { + print(usage) +} else { + let socket = try! Socket.create(family: .unix, type: .stream, proto: .unix) + defer { + socket.close() + } + let socketFile = "/tmp/\(appId).sock" + (try? socket.connect(to: socketFile)) ?? + errorT("Can't connect to AeroSpace server. Is AeroSpace.app running?") + + func run(_ command: String) -> String { + try! socket.write(from: command) + _ = try! Socket.wait(for: [socket], timeout: 0, waitForever: true) + return try! socket.readString() ?? errorT("fatal error: received nil from socket") + } + + let serverVersionAndHash = run("version") + if serverVersionAndHash != cliClientVersionAndHash { + error( + """ + Corrupted AeroSpace installation + + - CLI client version: \(cliClientVersionAndHash) + - AeroSpace.app server version: \(serverVersionAndHash) + + The versions don't match. Please reinstall AeroSpace + """ + ) + } + + if command.isEmpty { + error(usage) + } else { + let output = run(command.joined(separator: " ")) + if output != "PASS" { + print(output) + } + } +} diff --git a/cli/versionGenerated.swift b/cli/versionGenerated.swift new file mode 100644 index 00000000..6ba8b234 --- /dev/null +++ b/cli/versionGenerated.swift @@ -0,0 +1,2 @@ +// FILE IS GENERATED BY generate.sh +let cliClientVersion = "0.4.0-Beta" diff --git a/docs/cli-commands.md b/docs/cli-commands.md new file mode 100644 index 00000000..a2fe5bb9 --- /dev/null +++ b/docs/cli-commands.md @@ -0,0 +1,20 @@ +# CLI commands + +In addition to [regular commands](./commands.md), the CLI provides commands listed in this file + +**Table of contents** +- [version](#version) + +## version + +``` +version +--version +-v +``` + +- Available since: 0.4.0-Beta + +Prints the version and commit hash to stdout + +This command doesn't have any arguments diff --git a/docs/commands.md b/docs/commands.md index 03536e5d..6469bb51 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,7 @@ # Commands +Commands listed in this file can be used in the config and CLI + **Table of contents** - [close-all-windows-but-current](#close-all-windows-but-current) - [exec-and-forget](#exec-and-forget) diff --git a/generate.sh b/generate.sh new file mode 100755 index 00000000..3ef5395e --- /dev/null +++ b/generate.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e # Exit if one of commands exit with non-zero exit code +set -u # Treat unset variables and parameters other than the special parameters ‘@’ or ‘*’ as an error +set -o pipefail # Any command failed in the pipe fails the whole pipe +# set -x # Print shell commands as they are executed (or you can try -v which is less verbose) + +cd "$(dirname "$0")" + +version=$(head -1 ./version.txt) # Build number CFBundleVersion +build_number=$(tail -1 ./version.txt) # User visible version CFBundleShortVersionString + +cat > cli/versionGenerated.swift < String +} + extension Command { @MainActor - func run() async { + func run() async { refresh(layout: false) await runWithoutLayout() refresh() diff --git a/src/command/cli/VersionCommand.swift b/src/command/cli/VersionCommand.swift new file mode 100644 index 00000000..d6596f84 --- /dev/null +++ b/src/command/cli/VersionCommand.swift @@ -0,0 +1,7 @@ +struct VersionCommand: QueryCommand { + @MainActor + func run() -> String { + check(Thread.current.isMainThread) + return "\(Bundle.appVersion) \(gitHash)" + } +} diff --git a/src/command/parseCommand.swift b/src/command/parseCommand.swift index 881fef32..bd353913 100644 --- a/src/command/parseCommand.swift +++ b/src/command/parseCommand.swift @@ -3,6 +3,18 @@ import TOMLKit typealias ParsedCommand = Result extension String: Error {} +func parseQueryCommand(_ raw: String) -> ParsedCommand { + if raw.contains("'") { + return .failure("Single quotation mark is reserved for future use") + } else if raw == "version" || raw == "--version" || raw == "-v" { + return .success(VersionCommand()) + } else if raw == "" { + return .failure("Can't parse empty string query command") + } else { + return .failure("Unrecognized query command '\(raw)'") + } +} + func parseCommand(_ raw: TOMLValueConvertible) -> ParsedCommand { if let rawString = raw.string { return parseSingleCommand(rawString) @@ -18,10 +30,12 @@ func parseCommand(_ raw: TOMLValueConvertible) -> ParsedCommand { } func parseSingleCommand(_ raw: String) -> ParsedCommand { - let words = raw.split(separator: " ") - let args = words[1...].map { String($0) } + let words: [String] = raw.split(separator: " ").map { String($0) } + let args: [String] = Array(words[1...]) let firstWord = String(words.first ?? "") - if firstWord == "workspace" { + if raw.contains("'") { + return .failure("Single quotation mark is reserved for future use") + } else if firstWord == "workspace" { return parseSingleArg(args, firstWord).map { WorkspaceCommand(workspaceName: $0) } } else if firstWord == "move-node-to-workspace" { return parseSingleArg(args, firstWord).map { MoveNodeToWorkspaceCommand(targetWorkspaceName: $0) } @@ -71,9 +85,9 @@ func parseSingleCommand(_ raw: String) -> ParsedCommand { } else if raw == "close-all-windows-but-current" { return .success(CloseAllWindowsButCurrentCommand()) } else if raw == "" { - return .failure("Can't parse empty string command") + return .failure("Can't parse empty string action command") } else { - return .failure("Unrecognized command '\(raw)'") + return .failure("Unrecognized action command '\(raw)'") } } diff --git a/src/config/parseConfig.swift b/src/config/parseConfig.swift index 085c3f8d..f2209dae 100644 --- a/src/config/parseConfig.swift +++ b/src/config/parseConfig.swift @@ -1,13 +1,10 @@ import TOMLKit import HotKey -private let isRelease = !Bundle.appId.contains("debug") - func reloadConfig() { let configUrl = FileManager.default.homeDirectoryForCurrentUser .appending(path: isRelease ? ".aerospace.toml" : ".aerospace.debug.toml") - let rawConfig = try? String(contentsOf: configUrl) - let (parsedConfig, errors) = rawConfig?.lets { parseConfig($0) } ?? (defaultConfig, []) + let (parsedConfig, errors) = parseConfig((try? String(contentsOf: configUrl)) ?? "") if !errors.isEmpty { activateMode(mainModeId) diff --git a/src/server.swift b/src/server.swift new file mode 100644 index 00000000..03bce872 --- /dev/null +++ b/src/server.swift @@ -0,0 +1,45 @@ +import Socket + +func startServer() { + let socket = (try? Socket.create(family: .unix, type: .stream, proto: .unix)) ?? errorT("Can't create socket") + let socketFile = "/tmp/\(Bundle.appId).sock" + (try? socket.listen(on: socketFile, maxBacklogSize: 1)) ?? errorT("Can't listen to socket \(socketFile)") + DispatchQueue.global().async { + while true { + guard let connection = try? socket.acceptClientConnection() else { continue } + Task { await newConnection(connection) } + } + } +} + +private func newConnection(_ socket: Socket) async { + defer { + debug("Close connection") + socket.close() + } + while true { + _ = try? Socket.wait(for: [socket], timeout: 0, waitForever: true) + guard let string = (try? socket.readString()) else { return } + let (action, error1) = parseSingleCommand(string).getOrNils() + let (query, error2) = parseQueryCommand(string).getOrNils() + if let error1, let error2 { + _ = try? socket.write(from: error1 + "\n" + error2) + continue + } + if action is ExecAndForgetCommand || action is ExecAndWaitCommand { + _ = try? socket.write(from: "exec commands are prohibited from CLI") + continue + } + if let action { + await action.run() + _ = try? socket.write(from: "PASS") + continue + } + if let query { + let result = await query.run() + _ = try? socket.write(from: result) + continue + } + error("Unreachable") + } +} diff --git a/src/util/util.swift b/src/util/util.swift index d51dbcc4..bf791aba 100644 --- a/src/util/util.swift +++ b/src/util/util.swift @@ -199,10 +199,14 @@ extension Set { func toArray() -> [Element] { Array(self) } } -private let DEBUG = true - func debug(_ msg: Any) { - if DEBUG { + if !isRelease { print(msg) } } + +#if DEBUG +let isRelease = false +#else +let isRelease = true +#endif diff --git a/version.txt b/version.txt new file mode 100644 index 00000000..a79b27a9 --- /dev/null +++ b/version.txt @@ -0,0 +1,2 @@ +0.4.0-Beta +0.4.0