Showing preview only (1,563K chars total). Download the full file or copy to clipboard to get everything.
Repository: nikitabobko/AeroSpace
Branch: main
Commit: 85f80bda2d7b
Files: 487
Total size: 1.4 MB
Directory structure:
gitextract_klfaytgz/
├── .bundle/
│ └── config
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── DISCUSSION_TEMPLATE/
│ │ └── potential-bugs.yml
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ └── new-issue.yml
│ ├── gh-actions-runner-xcode-select.sh
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build.yml
│ └── close-third-party-issues.yml
├── .gitignore
├── .swift-version
├── .swiftformat
├── .swiftlint.yml
├── AeroSpace.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ └── contents.xcworkspacedata
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── Package.resolved
├── Package.swift
├── README.md
├── ShellParserGenerated/
│ ├── .gitignore
│ ├── Package.swift
│ └── Sources/
│ └── ShellParserGenerated/
│ ├── ShellLexer.swift
│ └── ShellParser.swift
├── Sources/
│ ├── AeroSpaceApp/
│ │ └── AeroSpaceApp.swift
│ ├── AppBundle/
│ │ ├── GlobalObserver.swift
│ │ ├── command/
│ │ │ ├── CmdEnv.swift
│ │ │ ├── CmdIo.swift
│ │ │ ├── Command.swift
│ │ │ ├── cmdManifest.swift
│ │ │ ├── cmdResolveTargetOrReportError.swift
│ │ │ ├── format.swift
│ │ │ ├── formatToJson.swift
│ │ │ ├── impl/
│ │ │ │ ├── BalanceSizesCommand.swift
│ │ │ │ ├── CloseAllWindowsButCurrentCommand.swift
│ │ │ │ ├── CloseCommand.swift
│ │ │ │ ├── ConfigCommand.swift
│ │ │ │ ├── DebugWindowsCommand.swift
│ │ │ │ ├── EnableCommand.swift
│ │ │ │ ├── ExecAndForgetCommand.swift
│ │ │ │ ├── FlattenWorkspaceTreeCommand.swift
│ │ │ │ ├── FocusBackAndForthCommand.swift
│ │ │ │ ├── FocusCommand.swift
│ │ │ │ ├── FocusMonitorCommand.swift
│ │ │ │ ├── FullscreenCommand.swift
│ │ │ │ ├── JoinWithCommand.swift
│ │ │ │ ├── LayoutCommand.swift
│ │ │ │ ├── ListAppsCommand.swift
│ │ │ │ ├── ListExecEnvVarsCommand.swift
│ │ │ │ ├── ListModesCommand.swift
│ │ │ │ ├── ListMonitorsCommand.swift
│ │ │ │ ├── ListWindowsCommand.swift
│ │ │ │ ├── ListWorkspacesCommand.swift
│ │ │ │ ├── MacosNativeFullscreenCommand.swift
│ │ │ │ ├── MacosNativeMinimizeCommand.swift
│ │ │ │ ├── ModeCommand.swift
│ │ │ │ ├── MoveCommand.swift
│ │ │ │ ├── MoveMouseCommand.swift
│ │ │ │ ├── MoveNodeToMonitorCommand.swift
│ │ │ │ ├── MoveNodeToWorkspaceCommand.swift
│ │ │ │ ├── MoveWorkspaceToMonitorCommand.swift
│ │ │ │ ├── ReloadConfigCommand.swift
│ │ │ │ ├── ResizeCommand.swift
│ │ │ │ ├── SplitCommand.swift
│ │ │ │ ├── SummonWorkspaceCommand.swift
│ │ │ │ ├── SwapCommand.swift
│ │ │ │ ├── TriggerBindingCommand.swift
│ │ │ │ ├── VolumeCommand.swift
│ │ │ │ ├── WorkspaceBackAndForthCommand.swift
│ │ │ │ └── WorkspaceCommand.swift
│ │ │ └── parseCommand.swift
│ │ ├── config/
│ │ │ ├── Config.swift
│ │ │ ├── ConfigFile.swift
│ │ │ ├── ConfigFileWatcher.swift
│ │ │ ├── DynamicConfigValue.swift
│ │ │ ├── HotkeyBinding.swift
│ │ │ ├── Mode.swift
│ │ │ ├── keysMap.swift
│ │ │ ├── parseConfig.swift
│ │ │ ├── parseExecEnvVariables.swift
│ │ │ ├── parseGaps.swift
│ │ │ ├── parseKeyMapping.swift
│ │ │ ├── parseOnWindowDetected.swift
│ │ │ ├── parseWorkspaceToMonitorAssignment.swift
│ │ │ └── startAtLogin.swift
│ │ ├── focus.swift
│ │ ├── focusCache.swift
│ │ ├── getNativeFocusedWindow.swift
│ │ ├── initAppBundle.swift
│ │ ├── layout/
│ │ │ ├── layoutRecursive.swift
│ │ │ └── refresh.swift
│ │ ├── model/
│ │ │ ├── AxUiElementWindowType.swift
│ │ │ ├── Json.swift
│ │ │ ├── KnownBundleId.swift
│ │ │ ├── Monitor.swift
│ │ │ ├── MonitorDescriptionEx.swift
│ │ │ ├── MonitorEx.swift
│ │ │ ├── Rect.swift
│ │ │ └── ServerEvent.swift
│ │ ├── mouse/
│ │ │ ├── mouse.swift
│ │ │ ├── moveWithMouse.swift
│ │ │ └── resizeWithMouse.swift
│ │ ├── normalizeLayoutReason.swift
│ │ ├── runLoop.swift
│ │ ├── server.swift
│ │ ├── shell/
│ │ │ └── Shell.swift
│ │ ├── subscriptions.swift
│ │ ├── tree/
│ │ │ ├── AbstractApp.swift
│ │ │ ├── MacApp.swift
│ │ │ ├── MacWindow.swift
│ │ │ ├── MacosUnconventionalWindowsContainer.swift
│ │ │ ├── TilingContainer.swift
│ │ │ ├── TreeNode.swift
│ │ │ ├── TreeNodeCases.swift
│ │ │ ├── TreeNodeEx.swift
│ │ │ ├── Window.swift
│ │ │ ├── Workspace.swift
│ │ │ ├── WorkspaceEx.swift
│ │ │ ├── frozen/
│ │ │ │ ├── FrozenTreeNode.swift
│ │ │ │ ├── FrozenWorld.swift
│ │ │ │ └── closedWindowsCache.swift
│ │ │ └── normalizeContainers.swift
│ │ ├── ui/
│ │ │ ├── AppearanceTheme.swift
│ │ │ ├── ExperimentalUISettings.swift
│ │ │ ├── MenuBar.swift
│ │ │ ├── MenuBarLabel.swift
│ │ │ ├── MessageView.swift
│ │ │ ├── NSPanelHud.swift
│ │ │ ├── SecureInputView.swift
│ │ │ ├── TrayMenuModel.swift
│ │ │ └── VolumeView.swift
│ │ ├── util/
│ │ │ ├── ArrayEx.swift
│ │ │ ├── AwaitableOneTimeBroadcastLatch.swift
│ │ │ ├── AxSubscription.swift
│ │ │ ├── AxUiElementMock.swift
│ │ │ ├── LazySequenceProtocolEx.swift
│ │ │ ├── MruStack.swift
│ │ │ ├── NSRunningApplicationEx.swift
│ │ │ ├── NsApplicationEx.swift
│ │ │ ├── SetEx.swift
│ │ │ ├── ThreadGuardedValue.swift
│ │ │ ├── UniqueToken.swift
│ │ │ ├── accessibility.swift
│ │ │ ├── appBundleUtil.swift
│ │ │ ├── axTrustedCheckOptionPrompt.swift
│ │ │ └── dumpAxRecursive.swift
│ │ └── windowLevelCache.swift
│ ├── AppBundleTests/
│ │ ├── AxUiElementWindowTypeTest.swift
│ │ ├── assert.swift
│ │ ├── command/
│ │ │ ├── BalanceSizesCommandTest.swift
│ │ │ ├── CloseCommandTest.swift
│ │ │ ├── ExecCommandTest.swift
│ │ │ ├── FlattenWorkspaceTreeCommandTest.swift
│ │ │ ├── FocusCommandTest.swift
│ │ │ ├── JoinWithCommandTest.swift
│ │ │ ├── ListAppsTest.swift
│ │ │ ├── ListModesTest.swift
│ │ │ ├── ListMonitorsTest.swift
│ │ │ ├── ListWindowsTest.swift
│ │ │ ├── ListWorkspacesTest.swift
│ │ │ ├── MoveCommandTest.swift
│ │ │ ├── MoveNodeToMonitorCommandTest.swift
│ │ │ ├── MoveNodeToWorkspaceCommandTest.swift
│ │ │ ├── ResizeCommandTest.swift
│ │ │ ├── SplitCommandTest.swift
│ │ │ ├── SubscribeCmdArgsTest.swift
│ │ │ ├── SummonWorkspaceCommandTest.swift
│ │ │ ├── SwapCommandTest.swift
│ │ │ └── WorkspaceCommandTest.swift
│ │ ├── config/
│ │ │ ├── ConfigTest.swift
│ │ │ ├── ParseEnvVariablesTest.swift
│ │ │ └── SplitArgsTest.swift
│ │ ├── model/
│ │ │ └── ClientServerTest.swift
│ │ ├── shell/
│ │ │ └── ShellTest.swift
│ │ ├── testExtensions.swift
│ │ ├── testUtil.swift
│ │ └── tree/
│ │ ├── TestApp.swift
│ │ ├── TestWindow.swift
│ │ ├── TilingContainer.swift
│ │ └── TreeNodeTest.swift
│ ├── Cli/
│ │ ├── _main.swift
│ │ ├── cliUtil.swift
│ │ └── subcommandDescriptionsGenerated.swift
│ ├── Common/
│ │ ├── appMetadata.swift
│ │ ├── cmdArgs/
│ │ │ ├── ArgParser.swift
│ │ │ ├── ArgParserInput.swift
│ │ │ ├── SubArgParser.swift
│ │ │ ├── cmdArgsManifest.swift
│ │ │ ├── impl/
│ │ │ │ ├── BalanceSizesCmdArgs.swift
│ │ │ │ ├── CloseAllWindowsButCurrentCmdArgs.swift
│ │ │ │ ├── CloseCmdArgs.swift
│ │ │ │ ├── ConfigCmdArgs.swift
│ │ │ │ ├── DebugWindowsCmdArgs.swift
│ │ │ │ ├── EnableCmdArgs.swift
│ │ │ │ ├── ExecAndForgetCmdArgs.swift
│ │ │ │ ├── FlattenWorkspaceTreeCmdArgs.swift
│ │ │ │ ├── FocusBackAndForthCmdArgs.swift
│ │ │ │ ├── FocusCmdArgs.swift
│ │ │ │ ├── FocusMonitorCmdArgs.swift
│ │ │ │ ├── FullscreenCmdArgs.swift
│ │ │ │ ├── JoinWithCmdArgs.swift
│ │ │ │ ├── LayoutCmdArgs.swift
│ │ │ │ ├── ListAppsCmdArgs.swift
│ │ │ │ ├── ListExecEnvVarsCmdArgs.swift
│ │ │ │ ├── ListModesCmdArgs.swift
│ │ │ │ ├── ListMonitorsCmdArgs.swift
│ │ │ │ ├── ListWindowsCmdArgs.swift
│ │ │ │ ├── ListWorkspacesCmdArgs.swift
│ │ │ │ ├── MacosNativeFullscreenCmdArgs.swift
│ │ │ │ ├── MacosNativeMinimizeCmdArgs.swift
│ │ │ │ ├── ModeCmdArgs.swift
│ │ │ │ ├── MoveCmdArgs.swift
│ │ │ │ ├── MoveMouseCmdArgs.swift
│ │ │ │ ├── MoveNodeToMonitorCmdArgs.swift
│ │ │ │ ├── MoveNodeToWorkspaceCmdArgs.swift
│ │ │ │ ├── MoveWorkpsaceToMonitorCmdArgs.swift
│ │ │ │ ├── ReloadConfigCmdArgs.swift
│ │ │ │ ├── ResizeCmdArgs.swift
│ │ │ │ ├── SplitCmdArgs.swift
│ │ │ │ ├── SubscribeCmdArgs.swift
│ │ │ │ ├── SummonWorkspaceCmdArgs.swift
│ │ │ │ ├── SwapCmdArgs.swift
│ │ │ │ ├── TriggerBindingCmdArgs.swift
│ │ │ │ ├── VolumeCmdArgs.swift
│ │ │ │ ├── WorkspaceBackAndForthCmdArgs.swift
│ │ │ │ └── WorkspaceCmdArgs.swift
│ │ │ ├── parseCmdArgs.swift
│ │ │ ├── parseSpecificCmdArgs.swift
│ │ │ ├── splitArgs.swift
│ │ │ └── subcommandParsers.swift
│ │ ├── cmdHelpGenerated.swift
│ │ ├── gitHashGenerated.swift
│ │ ├── model/
│ │ │ ├── AeroSpaceEnvVars.swift
│ │ │ ├── AxAppThreadToken.swift
│ │ │ ├── CardinalDirection.swift
│ │ │ ├── CardinalOrDfsDirection.swift
│ │ │ ├── DfsNextPrev.swift
│ │ │ ├── Init.swift
│ │ │ ├── MonitorDescription.swift
│ │ │ ├── NextPrev.swift
│ │ │ ├── Orientation.swift
│ │ │ ├── WorkspaceName.swift
│ │ │ ├── clientServer.swift
│ │ │ └── sponsorshipPrompts.swift
│ │ ├── util/
│ │ │ ├── AeroAny.swift
│ │ │ ├── ArrSlice.swift
│ │ │ ├── BoolEx.swift
│ │ │ ├── CollectionEx.swift
│ │ │ ├── ComparableEx.swift
│ │ │ ├── ConvenienceCopyable.swift
│ │ │ ├── DictionaryEx.swift
│ │ │ ├── EquatableNoop.swift
│ │ │ ├── JsonEncoderEx.swift
│ │ │ ├── Lateinit.swift
│ │ │ ├── MainActorEx.swift
│ │ │ ├── NWConnectionEx.swift
│ │ │ ├── Nullable.swift
│ │ │ ├── OptionalEx.swift
│ │ │ ├── ResultEx.swift
│ │ │ ├── SequenceEx.swift
│ │ │ ├── StringEx.swift
│ │ │ ├── StringLogicalSegments.swift
│ │ │ ├── commonUtil.swift
│ │ │ └── showMessageInGui.swift
│ │ └── versionGenerated.swift
│ └── PrivateApi/
│ └── include/
│ ├── module.modulemap
│ ├── private.h
│ └── private.m
├── axDumps/
│ ├── 1password.json5
│ ├── 1password_large_type_window.json5
│ ├── 1password_mini_window.json5
│ ├── about_this_mac.json5
│ ├── alacritty_decorations_buttonless.json5
│ ├── apple_calendar.json5
│ ├── apple_calendar_settings.json5
│ ├── apple_followup_sign_in_to_a_new_device_confirmation.json5
│ ├── apple_mail.json5
│ ├── apple_mail_new_email.json5
│ ├── apple_mail_settings.json5
│ ├── archiveutility.json5
│ ├── brave.json5
│ ├── brave_pip.json5
│ ├── calculator.json5
│ ├── choose_1_5_0.json5
│ ├── chrome.json5
│ ├── chrome_choose_what_to_share_popup.json5
│ ├── chrome_extensions_popup.json5
│ ├── chrome_find_in_page.json5
│ ├── chrome_pip.json5
│ ├── chrome_sharing_is_in_progress_popup.json5
│ ├── cleanshotx_monitor_1.json5
│ ├── cleanshotx_monitor_2.json5
│ ├── drracket.json5
│ ├── finder.json5
│ ├── finder_quick_look.json5
│ ├── firefox.json5
│ ├── firefox_extensions_popup.json5
│ ├── firefox_mouse_hover_over_extensions_button.json5
│ ├── firefox_mouse_hover_over_tab.json5
│ ├── firefox_non_native_fullscreen.json5
│ ├── firefox_normal_window_when_non_native_fullscreen_in_background.json5
│ ├── firefox_pinterest_sign_in_with_google.json5
│ ├── firefox_pip.json5
│ ├── ghostty.json5
│ ├── ghostty_about.json5
│ ├── ghostty_check_for_updates_1_dialog.json5
│ ├── ghostty_check_for_updates_2_alert.json5
│ ├── ghostty_config_error.json5
│ ├── ghostty_quick_terminal.json5
│ ├── ghostty_window_decorations_false.json5
│ ├── intellij.json5
│ ├── intellij_background_tasks.json5
│ ├── intellij_context_menu.json5
│ ├── intellij_native_open_window.json5
│ ├── intellij_quick_doc_popup.json5
│ ├── intellij_rebase_dialog.json5
│ ├── iphonesimulator.json5
│ ├── iterm2.json5
│ ├── iterm2_hotkey_window.json5
│ ├── iterm2_no_title_bar.json5
│ ├── jetbrains_toolbox.json5
│ ├── karabiner_event_viewer.json5
│ ├── karabiner_settings.json5
│ ├── kitty_quick_access.json5
│ ├── macos_capslock_popup_safari.json5
│ ├── macos_capslock_popup_textedit.json5
│ ├── macos_join_network.json5
│ ├── macos_share_window_purple_pill_sublime.json5
│ ├── marta.json5
│ ├── microsoft_edge.json5
│ ├── microsoft_edge_pip.json5
│ ├── mpv_fullscreen.json5
│ ├── mpv_windowed.json5
│ ├── nomachine_session_1.json5
│ ├── nomachine_session_2.json5
│ ├── nomachine_welcome_window_1.json5
│ ├── nomachine_welcome_window_2.json5
│ ├── qutebrowser.json5
│ ├── qutebrowser_context_menu.json5
│ ├── qutebrowser_hide_decoration.json5
│ ├── raycast.json5
│ ├── raycast_settings.json5
│ ├── safari.json5
│ ├── safari_pinterest_sign_in_with_google.json5
│ ├── scenario_firefox_google_meet_share_window/
│ │ ├── 01_firefox.json5
│ │ ├── 02_firefox.json5
│ │ ├── 03_firefox.json5
│ │ ├── 04_firefox.json5
│ │ ├── 05_apple_controlcenter.json5
│ │ ├── 06_firefox.json5
│ │ ├── 07_firefox.json5
│ │ └── README.md
│ ├── slack.json5
│ ├── slack_chat_in_a_separate_window.json5
│ ├── slack_huddle_share_screen_draw_on_screen_fake_window.json5
│ ├── slack_huddle_share_screen_floating_popup.json5
│ ├── slack_huddle_share_screen_target_picker.json5
│ ├── spotify.json5
│ ├── spotify_miniplayer.json5
│ ├── steam_1.json5
│ ├── steam_2.json5
│ ├── sublime_text_4.json5
│ ├── system_settings.json5
│ ├── telegram.json5
│ ├── telegram_image_viewer.json5
│ ├── terminal_app.json5
│ ├── transmission.json5
│ ├── transmission_torrent_inspector.json5
│ ├── vlc_empty.json5
│ ├── vlc_fullscreen.json5
│ ├── vlc_video_playing.json5
│ ├── vs_code.json5
│ ├── vs_code_nativeFullScreen_false.json5
│ ├── vs_codium.json5
│ ├── vs_codium_nativeFullScreen_false.json5
│ ├── xcode.json5
│ ├── xcode_build_succeeded_popup.json5
│ ├── xcode_installing_system_components.json5
│ ├── xcode_open_quickly.json5
│ ├── xcode_quick_actions.json5
│ ├── xcode_settings.json5
│ ├── xcode_welcome_window.json5
│ ├── zebar.json5
│ ├── zed.json5
│ ├── zen_browser.json5
│ └── zen_browser_pip.json5
├── build-debug.sh
├── build-docs.sh
├── build-release.sh
├── build-shell-completion.sh
├── dev-docs/
│ ├── architecture.md
│ └── development.md
├── docs/
│ ├── aerospace-balance-sizes.adoc
│ ├── aerospace-close-all-windows-but-current.adoc
│ ├── aerospace-close.adoc
│ ├── aerospace-config.adoc
│ ├── aerospace-debug-windows.adoc
│ ├── aerospace-enable.adoc
│ ├── aerospace-exec-and-forget.adoc
│ ├── aerospace-flatten-workspace-tree.adoc
│ ├── aerospace-focus-back-and-forth.adoc
│ ├── aerospace-focus-monitor.adoc
│ ├── aerospace-focus.adoc
│ ├── aerospace-fullscreen.adoc
│ ├── aerospace-join-with.adoc
│ ├── aerospace-layout.adoc
│ ├── aerospace-list-apps.adoc
│ ├── aerospace-list-exec-env-vars.adoc
│ ├── aerospace-list-modes.adoc
│ ├── aerospace-list-monitors.adoc
│ ├── aerospace-list-windows.adoc
│ ├── aerospace-list-workspaces.adoc
│ ├── aerospace-macos-native-fullscreen.adoc
│ ├── aerospace-macos-native-minimize.adoc
│ ├── aerospace-mode.adoc
│ ├── aerospace-move-mouse.adoc
│ ├── aerospace-move-node-to-monitor.adoc
│ ├── aerospace-move-node-to-workspace.adoc
│ ├── aerospace-move-workspace-to-monitor.adoc
│ ├── aerospace-move.adoc
│ ├── aerospace-reload-config.adoc
│ ├── aerospace-resize.adoc
│ ├── aerospace-split.adoc
│ ├── aerospace-subscribe.adoc
│ ├── aerospace-summon-workspace.adoc
│ ├── aerospace-swap.adoc
│ ├── aerospace-trigger-binding.adoc
│ ├── aerospace-volume.adoc
│ ├── aerospace-workspace-back-and-forth.adoc
│ ├── aerospace-workspace.adoc
│ ├── aerospace.adoc
│ ├── commands.adoc
│ ├── config-examples/
│ │ ├── default-config.toml
│ │ └── i3-like-config-example.toml
│ ├── goodies.adoc
│ ├── guide.adoc
│ ├── index.html
│ └── util/
│ ├── all-monitors-option.adoc
│ ├── conditional-arguments-header.adoc
│ ├── conditional-examples-header.adoc
│ ├── conditional-exit-code-header.adoc
│ ├── conditional-options-header.adoc
│ ├── conditional-output-format-header.adoc
│ ├── header.adoc
│ ├── man-attributes.adoc
│ ├── man-footer.adoc
│ ├── monitor-option.adoc
│ ├── site-attributes.adoc
│ ├── window-id-flag-desc.adoc
│ └── workspace-flag-desc.adoc
├── format.sh
├── generate-shell-parser.sh
├── generate.sh
├── grammar/
│ ├── ShellLexer.g4
│ ├── ShellParser.g4
│ └── commands-bnf-grammar.txt
├── install-from-sources.sh
├── legal/
│ ├── README.md
│ └── third-party-license/
│ ├── LICENSE-HotKey.txt
│ ├── LICENSE-ISSoundAdditions.txt
│ ├── LICENSE-TOMLKIT.txt
│ ├── LICENSE-antlr.txt
│ ├── LICENSE-swift-collections.txt
│ └── LICENSE-tomlplusplus.txt
├── lint.sh
├── makefile
├── project.yml
├── resources/
│ ├── AeroSpace.entitlements
│ └── Assets.xcassets/
│ ├── AccentColor.colorset/
│ │ └── Contents.json
│ ├── AppIcon.appiconset/
│ │ └── Contents.json
│ └── Contents.json
├── run-cli.sh
├── run-debug.sh
├── run-swift-test.sh
├── run-tests.sh
└── script/
├── build-brew-cask.sh
├── check-uncommitted-files.sh
├── clean-project.sh
├── clean-xcode.sh
├── generate-cmd-help.sh
├── install-dep.sh
├── publish-release.sh
├── reset-accessibility-permission-for-debug.sh
└── setup.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .bundle/config
================================================
---
BUNDLE_PATH: ".deps/bundler-path"
BUNDLE_DISABLE_SHARED_GEMS: "1"
================================================
FILE: .editorconfig
================================================
root = true
# It's better to use "wrap line" in plain text documents
[*.{adoc,md}]
rulers = 1000
max_line_length = 1000
[*.toml]
rulers = 96
max_line_length = 96
[*.{swift,sh}]
rulers = 120
max_line_length = 120
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[COMMIT_EDITMSG]
max_line_length = 120
================================================
FILE: .gitattributes
================================================
# GitHub: linguist-generated marks paths that you would like to be ignored for the repository's language statistics and hidden by default in diffs
AeroSpace.xcodeproj/project.pbxproj linguist-generated=true
================================================
FILE: .github/DISCUSSION_TEMPLATE/potential-bugs.yml
================================================
body:
- type: textarea
id: body
attributes:
label: Body
value: |
Steps to reproduce:
1.
2.
3.
Expected result:
Actual result:
### Additional info
```shell
$ aerospace -v
```
validations:
required: true
================================================
FILE: .github/FUNDING.yml
================================================
github: [nikitabobko]
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
================================================
FILE: .github/ISSUE_TEMPLATE/new-issue.yml
================================================
name: New Issue
description: New Issue
body:
- type: markdown
attributes:
value: |
AeroSpace project doesn't accept Issues directly. Please prefer GitHub Discussions. See: https://github.com/nikitabobko/AeroSpace/issues/947
- type: checkboxes
id: checkbox
attributes:
label: 'Sanity check'
options:
- label: |
I read https://github.com/nikitabobko/AeroSpace/issues/947
required: true
- type: textarea
id: body
attributes:
label: Body
value: |
YOUR ISSUE WILL BE IMMEDIATELY CLOSED BY BOT, DON'T OPEN NEW ISSUES DIRECTLY, USE DISCUSSIONS INSTEAD
validations:
required: true
================================================
FILE: .github/gh-actions-runner-xcode-select.sh
================================================
#!/bin/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)
sw_vers -productVersion
# Xcode version affects the target macOS SDK that we compile against + different Xcodes bundle different Swift verions
if sw_vers -productVersion | grep -q "^14"; then # macOS 14
sudo xcode-select -s "$XCODE_16_DEVELOPER_DIR"
else
sudo xcode-select -s "$XCODE_26_DEVELOPER_DIR"
fi
================================================
FILE: .github/pull_request_template.md
================================================
## PR checklist
- [ ] Explain your changes in the relevant commit messages rather than in the PR description. The PR description must not contain more information than the commit messages (except for images and other media).
- [ ] Each commit must explain what/why/how and motivation in its description. https://cbea.ms/git-commit/
- [ ] Don't forget to link the appropriate issues/discussions in commit messages (if applicable).
- [ ] Each commit must be an atomic change (a PR may contain several commits). Don't introduce new functional changes together with refactorings in the same commit.
- [ ] `./run-tests.sh` exits with non-zero exit code.
- [ ] Avoid merge commits, always rebase and force push.
Failure to follow the checklist with no apparent reasons will result in silent PR rejection.
================================================
FILE: .github/workflows/build.yml
================================================
name: build
on:
push:
branches:
- 'main'
- 'rr/**' # "rr" stands for "remote run"
pull_request:
branches: [ "main" ]
schedule:
- cron: '0 0 * * *' # every day at 00:00
jobs:
build-debug:
strategy:
# fail-fast: false # Disable fail-fast in matrix
matrix:
# https://docs.github.com/en/actions/reference/runners/github-hosted-runners#standard-github-hosted-runners-for-public-repositories
# https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md
# https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
#
# Xcode versions:
# - https://en.wikipedia.org/wiki/Xcode
# - https://xcodereleases.com/?scope=release
os: [macos-14, macos-15, macos-26]
name: build-debug
runs-on: ${{ matrix.os }}
steps:
- run: env # Debug
- uses: actions/checkout@v6
- run: ./.github/gh-actions-runner-xcode-select.sh
- run: brew install swiftly
- run: swiftly init --skip-install --assume-yes --verbose && swiftly install
- run: ./build-debug.sh
- run: ./run-tests.sh
# We build release artifacts only on the latest macOS versions because:
# 1. It cuts the build time twice on GH Actions
# 2. The latest Xcode version is not available on old macOS, and old Xcode versions bundle too old Swift version
build-release:
strategy:
# fail-fast: false # Disable fail-fast in matrix
matrix:
os: [macos-15, macos-26]
name: build-release
runs-on: ${{ matrix.os }}
steps:
- run: env # Debug
- uses: actions/checkout@v6
- run: ./.github/gh-actions-runner-xcode-select.sh
- run: brew install bash fish xcbeautify swiftly
- run: swiftly init --skip-install --assume-yes --verbose && swiftly install
- name: './build-release.sh'
run: |
# "-" means "Sign to run locally". There is no aerospace-codesign-certificate on GH Actions
./build-release.sh --codesign-identity -
./install-from-sources.sh --dont-rebuild
- name: cat ./.release/xcodebuild.log
if: ${{ always() }}
run: 'if test -f ./.release/xcodebuild.log; then cat ./.release/xcodebuild.log; fi'
================================================
FILE: .github/workflows/close-third-party-issues.yml
================================================
name: close-third-party-issues
on:
issues:
types: [opened]
jobs:
close-third-party-issues:
name: Close third party issues
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Close third party issues
run: |
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
author="$(gh issue view "$ISSUE" --json author --jq '.author.login')"
close() {
gh issue edit "$ISSUE" --add-label bin
gh issue close "$ISSUE" --comment "Please don't open issues directly, use GitHub Discussions instead. See: https://github.com/nikitabobko/AeroSpace/issues/947"
gh issue lock "$ISSUE"
}
test "$author" = nikitabobko || test "$author" = mobile-ar || close
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
ISSUE: ${{ github.event.issue.number }}
================================================
FILE: .gitignore
================================================
/.idea
/.debug
/.release
/.shell-completion
/.site
/.man
/Gemfile.lock
# IDK, AppCode randomly creates this EMPTY file. I have no idea what this is
/default.profraw
.DS_Store
/.xcode-build
# Swift package manager
/.build
/.swiftpm
/.vscode
# External dependencies
/.deps
# For whatever local files that developers might want to keep there (I personally keep a separate `generated-html` git worktree here)
/.local
# XCode User settings
xcuserdata/
xcshareddata/
================================================
FILE: .swift-version
================================================
6.2.4
================================================
FILE: .swiftformat
================================================
# https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
--exclude ./ShellParserGenerated
--indentcase true
--patternlet inline
--indentstrings true
# https://github.com/nicklockwood/SwiftFormat/issues/483 fix indentation for expressions nested in ternary
--wrapternary before-operators
--disable all
# --enable docComments
--enable anyObjectProtocol
--enable blankLineAfterImports
--enable blankLinesAtEndOfScope
--enable blankLinesBetweenImports
--enable conditionalAssignment
--enable docCommentsBeforeModifiers
--enable duplicateImports
--enable elseOnSameLine
--enable emptyBraces
--enable emptyExtensions
--enable enumNamespaces
--enable fileMacro
--enable genericExtensions
--enable indent
--enable leadingDelimiters
--enable linebreakAtEndOfFile
--enable linebreaks
--enable numberFormatting
--enable preferCountWhere
--enable preferFinalClasses
--enable redundantAsync
--enable redundantBackticks
--enable redundantBreak
--enable redundantClosure
--enable redundantEquatable
--enable redundantFileprivate
--enable redundantGet
--enable redundantLet
--enable redundantLetError
--enable redundantMemberwiseInit
--enable redundantObjc
--enable redundantOptionalBinding
--enable redundantThrows
--enable redundantTypedThrows
--enable redundantVoidReturnType
--enable semicolons
--enable spaceAroundBraces
--enable spaceAroundBrackets
--enable spaceAroundGenerics
--enable spaceAroundOperators
--enable spaceAroundParens
--enable spaceInsideBraces
--enable spaceInsideBrackets
--enable spaceInsideGenerics
--enable spaceInsideParens
--enable strongOutlets
--enable trailingClosures
--enable trailingCommas
--enable trailingSpace
--enable urlMacro
--enable wrapMultilineStatementBraces
--enable extensionAccessControl
--extensionacl on-declarations
================================================
FILE: .swiftlint.yml
================================================
excluded:
- .build
- .xcode-build
- ./ShellParserGenerated
# https://realm.github.io/SwiftLint/rule-directory.html
only_rules:
# - line_length # todo fix it
- colon
- computed_accessors_order
- dynamic_inline # Avoid using ‘dynamic’ and ‘@inline(__always)’ together
- empty_enum_arguments
- empty_parameters # Prefer () -> over Void ->
- empty_string # Prefer checking isEmpty over comparing string to an empty string literal
- file_name_no_space
- first_where
- implicit_getter
- is_disjoint
- last_where # Prefer using .last(where:) over .filter { }.last in collections
- legacy_constant
- legacy_constructor # Swift constructors are preferred over legacy convenience functions
- legacy_hashing # Prefer using the hash(into:) function instead of overriding hashValue
- legacy_nsgeometry_functions # Struct extension properties and methods are preferred over legacy functions
- legacy_random # Prefer using type.random(in:) over legacy functions
- local_doc_comment # Prefer regular comments over doc comments in local scopes
- no_fallthrough_only
- no_space_in_method_call
- nsobject_prefer_isequal # NSObject subclasses should implement isEqual instead of ==
- function_name_whitespace
- optional_enum_case_matching
- redundant_discardable_let
- redundant_nil_coalescing # nil coalescing operator is only evaluated if the lhs is nil, coalescing operator with nil as rhs is redundant
- switch_case_alignment
- toggle_bool
- trailing_newline
- trailing_semicolon # Lines should not have trailing semicolons
- trailing_whitespace # Lines should not have trailing whitespace
- unavailable_condition # Use #unavailable/#available instead of #available/#unavailable with an empty body.
- unneeded_break_in_switch # Avoid using unneeded break statements
- unneeded_override # Remove overridden functions that don’t do anything except call their super
# - unused_closure_parameter
- unused_control_flow_label
- unused_enumerated # When the index or the item is not used, .enumerated() can be removed.
- unused_optional_binding # Prefer != nil over let _ =
- unused_setter_value
- weak_delegate # Delegates should be weak to avoid reference cycles
switch_case_alignment:
indented_cases: true
strict: true
# quiet: true
================================================
FILE: AeroSpace.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */ = {isa = PBXBuildFile; fileRef = 8FE45A887100EB70912B07F0 /* default-config.toml */; };
852F88894A3B9FC385563665 /* AppBundle in Frameworks */ = {isa = PBXBuildFile; productRef = 018E55979F61DA6DA6DCB442 /* AppBundle */; };
883A44C8295FECF49F94269D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */; };
C40E0D9C06086C58955237D9 /* AeroSpaceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
09685297933511208058F7CF /* AeroSpace.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AeroSpace.app; sourceTree = BUILT_PRODUCTS_DIR; };
18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AeroSpaceApp.swift; sourceTree = "<group>"; };
6606D24B4B23E9582CFA3B86 /* AeroSpace */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AeroSpace; path = .; sourceTree = SOURCE_ROOT; };
84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
8FE45A887100EB70912B07F0 /* default-config.toml */ = {isa = PBXFileReference; path = "default-config.toml"; sourceTree = "<group>"; };
CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AeroSpace.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2AFAB0BC1A2742132D7CB950 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
852F88894A3B9FC385563665 /* AppBundle in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0E0109AE5F7881520B0D2384 /* config-examples */ = {
isa = PBXGroup;
children = (
8FE45A887100EB70912B07F0 /* default-config.toml */,
);
name = "config-examples";
path = "docs/config-examples";
sourceTree = "<group>";
};
21E15F84087042E63C0150AB /* resources */ = {
isa = PBXGroup;
children = (
CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */,
84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */,
);
path = resources;
sourceTree = "<group>";
};
393942C56466FDBBE35F9EC0 = {
isa = PBXGroup;
children = (
6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */,
0E0109AE5F7881520B0D2384 /* config-examples */,
3A1FF786C84025133F96138D /* Packages */,
21E15F84087042E63C0150AB /* resources */,
62BEA6F49E6648E2EE3C208F /* Products */,
);
sourceTree = "<group>";
};
3A1FF786C84025133F96138D /* Packages */ = {
isa = PBXGroup;
children = (
6606D24B4B23E9582CFA3B86 /* AeroSpace */,
);
name = Packages;
sourceTree = "<group>";
};
62BEA6F49E6648E2EE3C208F /* Products */ = {
isa = PBXGroup;
children = (
09685297933511208058F7CF /* AeroSpace.app */,
);
name = Products;
sourceTree = "<group>";
};
6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */ = {
isa = PBXGroup;
children = (
18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */,
);
name = AeroSpaceApp;
path = Sources/AeroSpaceApp;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B00BE37A79171B0EE995EB83 /* AeroSpace */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1C34EA41A1F045E016D1944D /* Build configuration list for PBXNativeTarget "AeroSpace" */;
buildPhases = (
D7A18303C03F2CB26F7BB54B /* Sources */,
BA5F2F9022B8385637D263E4 /* Resources */,
2AFAB0BC1A2742132D7CB950 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = AeroSpace;
packageProductDependencies = (
018E55979F61DA6DA6DCB442 /* AppBundle */,
);
productName = AeroSpace;
productReference = 09685297933511208058F7CF /* AeroSpace.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0B585B3093DA0FC12E7983E2 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
};
buildConfigurationList = D6982B0C3E92C5AF28BCD315 /* Build configuration list for PBXProject "AeroSpace" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 393942C56466FDBBE35F9EC0;
minimizedProjectReferenceProxies = 1;
packageReferences = (
9A00429279948F2879C9FE30 /* XCLocalSwiftPackageReference "." */,
);
preferredProjectObjectVersion = 77;
projectDirPath = "";
projectRoot = "";
targets = (
B00BE37A79171B0EE995EB83 /* AeroSpace */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
BA5F2F9022B8385637D263E4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
883A44C8295FECF49F94269D /* Assets.xcassets in Resources */,
238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
D7A18303C03F2CB26F7BB54B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C40E0D9C06086C58955237D9 /* AeroSpaceApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
175127AAF914899705FABF12 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
31B702864571F51814E4F12C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = resources/AeroSpace.entitlements;
CODE_SIGN_IDENTITY = "aerospace-codesign-certificate";
COMBINE_HIDPI_IMAGES = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSUIElement = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "0.0.0-SNAPSHOT";
PRODUCT_BUNDLE_IDENTIFIER = bobko.aerospace.debug;
PRODUCT_NAME = "AeroSpace-Debug";
SDKROOT = macosx;
SWIFT_VERSION = 6.2;
};
name = Debug;
};
A991F90908318BCD1655E904 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
D1D1A9E07F0AB40E14CAC0F6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = resources/AeroSpace.entitlements;
CODE_SIGN_IDENTITY = "aerospace-codesign-certificate";
COMBINE_HIDPI_IMAGES = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSUIElement = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "0.0.0-SNAPSHOT";
PRODUCT_BUNDLE_IDENTIFIER = bobko.aerospace;
PRODUCT_NAME = AeroSpace;
SDKROOT = macosx;
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
SWIFT_VERSION = 6.2;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1C34EA41A1F045E016D1944D /* Build configuration list for PBXNativeTarget "AeroSpace" */ = {
isa = XCConfigurationList;
buildConfigurations = (
31B702864571F51814E4F12C /* Debug */,
D1D1A9E07F0AB40E14CAC0F6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
D6982B0C3E92C5AF28BCD315 /* Build configuration list for PBXProject "AeroSpace" */ = {
isa = XCConfigurationList;
buildConfigurations = (
175127AAF914899705FABF12 /* Debug */,
A991F90908318BCD1655E904 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
9A00429279948F2879C9FE30 /* XCLocalSwiftPackageReference "." */ = {
isa = XCLocalSwiftPackageReference;
relativePath = .;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
018E55979F61DA6DA6DCB442 /* AppBundle */ = {
isa = XCSwiftPackageProductDependency;
productName = AppBundle;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 0B585B3093DA0FC12E7983E2 /* Project object */;
}
================================================
FILE: AeroSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## Users cannot create GitHub Issues directly
AeroSpace project doesn't accept Issues directly - we ask you to create a [Discussion](https://github.com/nikitabobko/AeroSpace/discussions) first.
The submitted Issues are often either obvious duplicates, environmental problems, or configuration errors by the users themselves.
For a hobby project, I don't have enough time and energy to process every such submitted Issue.
As an alternative, you can start a Discussion on [GitHub discussions](https://github.com/nikitabobko/AeroSpace/discussions) forum.
Any Discussion which clearly identifies a problem and can be confirmed or reproduced will be converted to an Issue by maintainers.
It's users' responsibility to minimize their bugs as much as possible.
All users play a part in bugs reproduction.
In general the flow is the following:
- Discussions are here to kick-off the discussion and identify what the actionable item exactly is
- Issues are created later. Issues are well-formed, clear and actionable tasks
This pattern makes it easier for maintainers or contributors to find issues to work on since _almost every_ Issue is ready to be worked on.
## Submit bugs and feature ideas
Submit bugs to https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs
Submit feature ideas to https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas
Rules:
* Search for duplicates (in GitHub Issues and Discussions) before creating a new discussion
* Upvote for issues/discussions that you find useful
**Consider including in bug reports**
* `aerospace debug-windows` output, if the problem is about handling some windows
* Screenshots of problematic windows
* Videos of problematic windows
* What did you try to resolve the issue?
* Your config
* AeroSpace version
* macOS version
**Consider including in feature request**
* Use cases!
* Did I mention use cases?
* Alternative approaches
* Links to docs of similar features in other window managers that you know
* Synopsis, if you suggest a new command
* Mental model description
## Discuss issues/discussions
One of the most useful thing you can do is to discuss issues/discussions.
Imagine that you were assigned to fix the issue.
Try to suggest the best approach and design on how to fix the issue.
Suggest the synopsis/config format, reason in written form what is good about it, what is bad about it, what are the alternatives, etc.
Basically, see the "Prior discussion" section in [Submit Pull Requests](#submit-pull-requests).
If you have something to contribute to the conversation. Do it!
Please keep the conversation to the point. Discuss one issue at a time, crossreference other issues
You can take a look at the following issues:
* Most voted issues: https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
* Sometimes conversations happen on old issues that aren’t yet closed. See https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
* Issues that are unclear on how to fix, or issues that require design of the interface (CLI or config interface) are tagged with `design-needed` tag https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+label%3Adesign-needed
## Submit Pull Requests
Small and trivial improvements can be submitted without any discussion.
**Prior discussion**. For non-trivial changes (such as user visible changes), it's always better to ask for prior approval and discuss what you want to do before doing it.
Please create a new discussion and describe you want to do.
Consider including
* What users will observe after your change?
* Feature interaction with existing features or potential future features
* What use cases does it cover
* What is the proposed syntax for the config
* What is the proposed synopsis of CLI command
* How you think it should be implemented (if you can describe it)
* etc.
Discussing that you want to do something doesn't put any obligations on you. If you don't want to start the discussion just because you're afraid that you won't do it. Don't be afraid!
**Commit hygiene**. Each submitted commit must be atomic change (a Pull Request may contain several commits). Don't introduce new functional changes together with refactorings in the same commit.
Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible and do not take this as an opportunity to do some "refactoring along the way".
A good commit message also mentions the motivation of the change (the commit describes what, why and how)
**License Agreement**. By contributing changes to this repository, you agree to license your contributions under the MIT license.
Maintainers can merge your pull request with arbitrary modifications.
**Pull request merge**. It cannot be guaranteed that your pull request will be merged even after the discussion.
Be ready that your pull request might be rejected because the implementation isn't good, or the approach is incorrect.
The prior discussion is here for you to minimize the risk of rejection.
## Spread the word
Do you like the project? Does AeroSpace finally fix your problems with windows management on macOS? Good to hear it!
* Spread the word in social networks! (Don't forget to share the link :) )
* Talk about AeroSpace to your colleagues and friends
* Write a blogpost about your workflows
* Record a YouTube video
## Share your workflow and tips
Submit your tips to [the Goodies page](https://nikitabobko.github.io/AeroSpace/goodies). The source code of the page can be found in `./docs` directory
## Support the project financially
Supporting the project financially counts as a contribution (even if it's just a $1/month). https://github.com/sponsors/nikitabobko
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
ruby '~> 3.0' # >= 3.0 and < 4.0
source "https://rubygems.org"
gem 'asciidoctor', '2.0.23'
gem 'pygments.rb', '3.0'
================================================
FILE: LICENSE.txt
================================================
MIT License
Copyright (c) 2023 Nikita Bobko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Package.resolved
================================================
{
"originHash" : "120ba1d30192b339dc5c9d1461d2b3d1e329a17a4867b471290c07c6b3c0cb73",
"pins" : [
{
"identity" : "antlr4",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antlr/antlr4",
"state" : {
"revision" : "7ed420ff2c78d62883875c442d75f32e73bc86c8",
"version" : "4.13.1"
}
},
{
"identity" : "hotkey",
"kind" : "remoteSourceControl",
"location" : "https://github.com/soffes/HotKey",
"state" : {
"revision" : "a3cf605d7a96f6ff50e04fcb6dea6e2613cfcbe4",
"version" : "0.2.1"
}
},
{
"identity" : "issoundadditions",
"kind" : "remoteSourceControl",
"location" : "https://github.com/InerziaSoft/ISSoundAdditions",
"state" : {
"revision" : "4b555f0354e6c280917bae8a598a258efe87ab98",
"version" : "2.0.1"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections",
"state" : {
"revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e",
"version" : "1.3.0"
}
},
{
"identity" : "tomlkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/LebJe/TOMLKit",
"state" : {
"revision" : "404c4dd011743461bff12d00a5118d0ed59d630c",
"version" : "0.5.5"
}
}
],
"version" : 3
}
================================================
FILE: Package.swift
================================================
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "AeroSpacePackage",
// Runtime support for parameterized protocol types is only available in macOS 13.0.0 or newer
// And it specifies deploymentTarget for CLI
platforms: [.macOS(.v13)],
// Products define the executables and libraries a package produces, making them visible to other packages.
products: [
.executable(name: "aerospace", targets: ["Cli"]),
// Don't use this build for release, use xcode instead
.executable(name: "AeroSpaceApp", targets: ["AeroSpaceApp"]),
// We only need to expose this as a product for xcode
.library(name: "AppBundle", targets: ["AppBundle"]),
],
dependencies: [
.package(path: "./ShellParserGenerated"),
.package(url: "https://github.com/InerziaSoft/ISSoundAdditions.git", exact: "2.0.1"),
.package(url: "https://github.com/LebJe/TOMLKit.git", exact: "0.5.5"),
.package(url: "https://github.com/apple/swift-collections.git", exact: "1.3.0"),
.package(url: "https://github.com/soffes/HotKey.git", exact: "0.2.1"),
],
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
targets: [
// Exposes the private _AXUIElementGetWindow function to swift
.target(
name: "PrivateApi",
path: "Sources/PrivateApi",
publicHeadersPath: "include",
),
.target(
name: "Common",
dependencies: [
.product(name: "Collections", package: "swift-collections"),
],
),
.target(
name: "AppBundle",
dependencies: [
.product(name: "Collections", package: "swift-collections"),
.product(name: "HotKey", package: "HotKey"),
.product(name: "ISSoundAdditions", package: "ISSoundAdditions"),
.product(name: "ShellParserGenerated", package: "ShellParserGenerated"),
.product(name: "TOMLKit", package: "TOMLKit"),
.target(name: "Common"),
.target(name: "PrivateApi"),
],
swiftSettings: [
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
],
),
.executableTarget(
name: "AeroSpaceApp",
dependencies: [
.target(name: "AppBundle"),
],
),
.executableTarget(
name: "Cli",
dependencies: [
.target(name: "Common"),
],
),
.testTarget(
name: "AppBundleTests",
dependencies: [
.target(name: "AppBundle"),
],
),
],
)
================================================
FILE: README.md
================================================
# AeroSpace Beta [](https://github.com/nikitabobko/AeroSpace/actions/workflows/build.yml)
<img src="./resources/Assets.xcassets/AppIcon.appiconset/icon.png" width="40%" align="right">
AeroSpace is an i3-like tiling window manager for macOS
Videos:
- [YouTube 91 sec Demo](https://www.youtube.com/watch?v=UOl7ErqWbrk)
- [YouTube Guide by Josean Martinez](https://www.youtube.com/watch?v=-FoWClVHG5g)
Docs:
- [AeroSpace Guide](https://nikitabobko.github.io/AeroSpace/guide)
- [AeroSpace Commands](https://nikitabobko.github.io/AeroSpace/commands)
- [AeroSpace Goodies](https://nikitabobko.github.io/AeroSpace/goodies)
## Key features
- Tiling window manager based on a [tree paradigm](https://nikitabobko.github.io/AeroSpace/guide#tree)
- [i3](https://i3wm.org/) inspired
- Fast workspaces switching without animations and without the necessity to disable SIP
- AeroSpace employs its [own emulation of virtual workspaces](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces) instead of relying on native macOS Spaces due to [their considerable limitations](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces)
- Plain text configuration (dotfiles friendly). See: [default-config.toml](https://nikitabobko.github.io/AeroSpace/guide#default-config)
- CLI first (manpages and shell completion included)
- Doesn't require disabling SIP (System Integrity Protection)
- [Proper multi-monitor support](https://nikitabobko.github.io/AeroSpace/guide#multiple-monitors) (i3-like paradigm)
## Installation
Install via [Homebrew](https://brew.sh/) to get autoupdates (Preferred)
```
brew install --cask nikitabobko/tap/aerospace
```
In multi-monitor setup please make sure that monitors [are properly arranged](https://nikitabobko.github.io/AeroSpace/guide#proper-monitor-arrangement).
Other installation options: https://nikitabobko.github.io/AeroSpace/guide#installation
> [!NOTE]
> By using AeroSpace, you acknowledge that it's not [notarized](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution).
>
> Notarization is a "security" feature by Apple.
> You send binaries to Apple, and they either approve them or not.
> In reality, notarization is about building binaries the way Apple likes it.
>
> I don't have anything against notarization as a concept.
> I specifically don't like the way Apple does notarization.
> I don't have time to deal with Apple.
>
> [Homebrew installation script](https://github.com/nikitabobko/homebrew-tap/blob/main/Casks/aerospace.rb) is configured to
> automatically delete `com.apple.quarantine` attribute, that's why the app should work out of the box, without any warnings that
> "Apple cannot check AeroSpace for malicious software"
## Community, discussions, issues
AeroSpace project doesn't accept Issues directly - we ask you to create a [Discussion](https://github.com/nikitabobko/AeroSpace/discussions) first.
Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
Community discussions happen at GitHub Discussions.
There you can discuss bugs, propose new features, ask your questions, show off your setup, or just chat.
There are 7 channels:
- [#all](https://github.com/nikitabobko/AeroSpace/discussions).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions.atom?discussions_q=sort%3Adate_created).
Feed with all discussions.
- [#announcements](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements.atom?discussions_q=category%3Aannouncements+sort%3Adate_created).
Only maintainers can post here.
Highly moderated traffic.
- [#announcements-releases](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements-releases).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements-releases.atom?discussions_q=category%3Aannouncements-releases+sort%3Adate_created).
Announcements about non-patch releases.
Only maintainers can post here.
- [#feature-ideas](https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas.atom?discussions_q=category%3Afeature-ideas+sort%3Adate_created).
- [#general](https://github.com/nikitabobko/AeroSpace/discussions/categories/general).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/general.atom?discussions_q=sort%3Adate_created+category%3Ageneral).
- [#potential-bugs](https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs.atom?discussions_q=category%3Apotential-bugs+sort%3Adate_created).
If you think that you have encountered a bug, you can discuss your bugs here.
- [#questions-and-answers](https://github.com/nikitabobko/AeroSpace/discussions/categories/questions-and-answers).
[RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/questions-and-answers.atom?discussions_q=category%3Aquestions-and-answers+sort%3Adate_created).
Everyone is welcome to ask questions.
Everyone is encouraged to answer other people's questions.
## Project status
Public Beta. AeroSpace can be used as a daily driver, but expect breaking changes until 1.0 is reached.
What stops us from 1.0 release:
- [x] https://github.com/nikitabobko/AeroSpace/issues/131 Performance. Implement thread-per-application to circumvent macOS blocking AX API.
- [ ] https://github.com/nikitabobko/AeroSpace/issues/1215 _Big refactoring_. Rewrite mutable double-linked core tree data structure to immutable single-linked persistent tree.
Important for: stability and potential performance
- [ ] https://github.com/nikitabobko/AeroSpace/issues/1216 The big refactoring will help us to fix stability issue that windows may randomly jump to the focused workspace
- [ ] https://github.com/nikitabobko/AeroSpace/issues/68 The big refactoring will help us to support macOS native tabs
- [ ] https://github.com/nikitabobko/AeroSpace/issues/278 Implement shell-like combinators.
Ignore a lot of crazy fuss in the issue,
We are most probably going with the minimal approach to only introduce common shell-combinators: `||`, `&&`, `;` and `eval` command to send multiple commands in one go.
- [ ] https://github.com/nikitabobko/AeroSpace/issues/1012 Investigate a possibility to use `CGEvent.tapCreate` API for global hotkeys
- [ ] https://github.com/nikitabobko/AeroSpace/issues/28 Maybe it will allow to distinguish left and right modifiers. Maybe not
Big and important issues which will go after 1.0 release:
- [ ] https://github.com/nikitabobko/AeroSpace/issues/2 sticky windows
- [ ] https://github.com/nikitabobko/AeroSpace/issues/260 Dynamic TWM
## Development
A notes on how to setup the project, build it, how to run the tests, etc. can be found here: [dev-docs/development.md](./dev-docs/development.md)
## Project values
**Values**
- AeroSpace is targeted at advanced users and developers
- Keyboard centric
- Breaking changes (configuration files, CLI, behavior) are avoided as much as possible, but it must not let the software stagnate.
Thus breaking changes can happen, but with careful considerations and helpful message.
[Semver](https://semver.org/) major version is bumped in case of a breaking change (It's all guaranteed once AeroSpace reaches 1.0 version, until then breaking changes just happen)
- AeroSpace doesn't use GUI, unless necessarily
- AeroSpace will never provide a GUI for configuration.
For advanced users, it's easier to edit a configuration file in text editor rather than navigating through checkboxes in GUI.
- Status menu icon is ok, because visual feedback is needed
- Provide _practical_ features. Fancy appearance features are not _practical_ (e.g. window borders, transparency, animations, etc.)
- "dark magic" (aka "private APIs", "code injections", etc.) must be avoided as much as possible
- Right now, AeroSpace uses only a single private API to get window ID of accessibility object `_AXUIElementGetWindow`.
Everything else is [macOS public accessibility API](https://developer.apple.com/documentation/applicationservices/axuielement_h).
- AeroSpace will never require you to disable SIP (System Integrity Protection).
- The goal is to make AeroSpace easily maintainable, and resistant to macOS updates.
**Non Values**
- Play nicely with existing macOS features.
If limitations are imposed then AeroSpace won't play nicely with existing macOS features
(For example, AeroSpace doesn't acknowledge the existence of macOS Spaces, and it uses [emulation of its own workspaces](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces))
- Ricing.
AeroSpace provides only a very minimal support for ricing - gaps and a few callbacks for integrations with bars.
The current maintainer doesn't care about ricing.
Ricing issues are not a priority, and they are mostly ignored.
The ricing stance can change only with the appearance of more maintainers.
## macOS compatibility table
| | macOS 13 (Ventura) | macOS 14 (Sonoma) | macOS 15 (Sequoia) | macOS 26 (Tahoe) |
| ------------------------------------------------------------------------------ | ------------------ | ----------------- | ------------------ | ---------------- |
| AeroSpace binary runs on ... | + | + | + | + |
| AeroSpace debug build from sources is supported on ... | | + | + | + |
| AeroSpace release build from sources is supported on ... (Requires Xcode 26+) | | | + | + |
## Sponsorship
AeroSpace is developed and maintained in my free time.
If you find it useful, [consider sponsoring](https://github.com/sponsors/nikitabobko#sponsors).
## People who have write access
In alphabetical order:
- [@mobile-ar](https://github.com/mobile-ar/)
- [@nikitabobko](https://github.com/nikitabobko/)
## Tip of the day
```bash
defaults write -g NSWindowShouldDragOnGesture -bool true
```
Now, you can move windows by holding `ctrl`+`cmd` and dragging any part of the window (not necessarily the window title)
Source: [reddit](https://www.reddit.com/r/MacOS/comments/k6hiwk/keyboard_modifier_to_simplify_click_drag_of/)
## Related projects
- [Amethyst](https://github.com/ianyh/Amethyst)
- [yabai](https://github.com/koekeishiya/yabai)
================================================
FILE: ShellParserGenerated/.gitignore
================================================
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
================================================
FILE: ShellParserGenerated/Package.swift
================================================
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "ShellParserGenerated",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "ShellParserGenerated",
targets: ["ShellParserGenerated"]
),
],
dependencies: [
.package(url: "https://github.com/antlr/antlr4", exact: "4.13.1"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "ShellParserGenerated",
dependencies: [
.product(name: "Antlr4Static", package: "antlr4"),
]
),
]
)
================================================
FILE: ShellParserGenerated/Sources/ShellParserGenerated/ShellLexer.swift
================================================
// Generated from ./grammar/ShellLexer.g4 by ANTLR 4.13.1
import Antlr4
open class ShellLexer: Lexer {
internal static var _decisionToDFA: [DFA] = {
var decisionToDFA = [DFA]()
let length = ShellLexer._ATN.getNumberOfDecisions()
for i in 0..<length {
decisionToDFA.append(DFA(ShellLexer._ATN.getDecisionState(i)!, i))
}
return decisionToDFA
}()
internal static let _sharedContextCache = PredictionContextCache()
public
static let TRIPLE_QUOTE=1, SINGLE_QUOTED_STRING=2, LDQUOTE=3, LPAR=4, INTERPOLATION_START=5,
RPAR=6, ELIF=7, IF=8, SWITCH=9, CASE=10, DO=11, THEN=12, ELSE=13,
FOR=14, WHILE=15, CATCH=16, IN=17, END=18, DEFER=19, AND=20,
PIPE=21, OR=22, SEMICOLON=23, NL=24, WORD=25, ARG=26, ESCAPE_NEWLINE=27,
COMMENT=28, SPACES=29, ANY=30, TEXT=31, INTERPOLATION_START_IN_DSTRING=32,
ESCAPE_SEQUENCE=33, RDQUOTE=34
public
static let IN_DSTRING=1
public
static let channelNames: [String] = [
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
]
public
static let modeNames: [String] = [
"DEFAULT_MODE", "IN_DSTRING"
]
public
static let ruleNames: [String] = [
"TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START",
"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE",
"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL",
"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING",
"ESCAPE_SEQUENCE", "RDQUOTE"
]
private static let _LITERAL_NAMES: [String?] = [
nil, nil, nil, nil, "'('", nil, "')'", nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, "'&&'", "'|'", "'||'", "';'"
]
private static let _SYMBOLIC_NAMES: [String?] = [
nil, "TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START",
"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE",
"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL",
"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING",
"ESCAPE_SEQUENCE", "RDQUOTE"
]
public
static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)
override open
func getVocabulary() -> Vocabulary {
return ShellLexer.VOCABULARY
}
public
required init(_ input: CharStream) {
RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION)
super.init(input)
_interp = LexerATNSimulator(self, ShellLexer._ATN, ShellLexer._decisionToDFA, ShellLexer._sharedContextCache)
}
override open
func getGrammarFileName() -> String { return "ShellLexer.g4" }
override open
func getRuleNames() -> [String] { return ShellLexer.ruleNames }
override open
func getSerializedATN() -> [Int] { return ShellLexer._serializedATN }
override open
func getChannelNames() -> [String] { return ShellLexer.channelNames }
override open
func getModeNames() -> [String] { return ShellLexer.modeNames }
override open
func getATN() -> ATN { return ShellLexer._ATN }
override open
func action(_ _localctx: RuleContext?, _ ruleIndex: Int, _ actionIndex: Int) throws {
switch (ruleIndex) {
case 5:
try RPAR_action((_localctx as RuleContext?), actionIndex)
case 33:
try RDQUOTE_action((_localctx as RuleContext?), actionIndex)
default: break
}
}
private func RPAR_action(_ _localctx: RuleContext?, _ actionIndex: Int) throws {
switch (actionIndex) {
case 0:
_ = try? popMode()
default: break
}
}
private func RDQUOTE_action(_ _localctx: RuleContext?, _ actionIndex: Int) throws {
switch (actionIndex) {
case 1:
_ = try? popMode()
default: break
}
}
static let _serializedATN:[Int] = [
4,0,34,316,6,-1,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,
6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,
2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,
2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,
2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33,7,33,1,0,1,0,1,
0,1,0,1,0,1,0,3,0,77,8,0,1,1,1,1,5,1,81,8,1,10,1,12,1,84,9,1,1,1,1,1,1,
2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,6,1,6,
1,6,1,6,1,6,1,6,5,6,110,8,6,10,6,12,6,113,9,6,1,7,1,7,1,7,1,7,5,7,119,
8,7,10,7,12,7,122,9,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,5,8,132,8,8,10,8,
12,8,135,9,8,1,9,1,9,1,9,1,9,1,9,1,9,5,9,143,8,9,10,9,12,9,146,9,9,1,10,
1,10,1,10,1,10,5,10,152,8,10,10,10,12,10,155,9,10,1,11,1,11,1,11,1,11,
1,11,1,11,5,11,163,8,11,10,11,12,11,166,9,11,1,12,1,12,1,12,1,12,1,12,
1,12,5,12,174,8,12,10,12,12,12,177,9,12,1,13,1,13,1,13,1,13,1,13,5,13,
184,8,13,10,13,12,13,187,9,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,5,14,
196,8,14,10,14,12,14,199,9,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,5,15,
208,8,15,10,15,12,15,211,9,15,1,16,1,16,1,16,1,16,5,16,217,8,16,10,16,
12,16,220,9,16,1,17,1,17,1,17,1,17,1,17,5,17,227,8,17,10,17,12,17,230,
9,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,5,18,239,8,18,10,18,12,18,242,
9,18,1,19,1,19,1,19,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,23,3,23,255,8,
23,1,23,3,23,258,8,23,1,23,1,23,1,24,4,24,263,8,24,11,24,12,24,264,1,25,
4,25,268,8,25,11,25,12,25,269,1,26,1,26,3,26,274,8,26,1,26,3,26,277,8,
26,1,26,1,26,1,26,1,26,1,27,1,27,5,27,285,8,27,10,27,12,27,288,9,27,1,
27,1,27,1,28,4,28,293,8,28,11,28,12,28,294,1,28,1,28,1,29,1,29,1,30,4,
30,302,8,30,11,30,12,30,303,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,
33,1,33,1,33,1,82,0,34,2,1,4,2,6,3,8,4,10,5,12,6,14,7,16,8,18,9,20,10,
22,11,24,12,26,13,28,14,30,15,32,16,34,17,36,18,38,19,40,20,42,21,44,22,
46,23,48,24,50,25,52,26,54,27,56,28,58,29,60,30,62,31,64,32,66,33,68,34,
2,0,1,5,4,0,45,47,65,90,95,95,97,122,8,0,33,33,37,37,43,57,61,61,65,90,
94,95,97,123,125,125,1,0,10,10,2,0,9,9,32,32,3,0,34,34,36,36,92,92,338,
0,2,1,0,0,0,0,4,1,0,0,0,0,6,1,0,0,0,0,8,1,0,0,0,0,10,1,0,0,0,0,12,1,0,
0,0,0,14,1,0,0,0,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0,0,
24,1,0,0,0,0,26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1,
0,0,0,0,36,1,0,0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0,
0,46,1,0,0,0,0,48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56,
1,0,0,0,0,58,1,0,0,0,0,60,1,0,0,0,1,62,1,0,0,0,1,64,1,0,0,0,1,66,1,0,0,
0,1,68,1,0,0,0,2,76,1,0,0,0,4,78,1,0,0,0,6,87,1,0,0,0,8,91,1,0,0,0,10,
95,1,0,0,0,12,100,1,0,0,0,14,103,1,0,0,0,16,114,1,0,0,0,18,123,1,0,0,0,
20,136,1,0,0,0,22,147,1,0,0,0,24,156,1,0,0,0,26,167,1,0,0,0,28,178,1,0,
0,0,30,188,1,0,0,0,32,200,1,0,0,0,34,212,1,0,0,0,36,221,1,0,0,0,38,231,
1,0,0,0,40,243,1,0,0,0,42,246,1,0,0,0,44,248,1,0,0,0,46,251,1,0,0,0,48,
254,1,0,0,0,50,262,1,0,0,0,52,267,1,0,0,0,54,271,1,0,0,0,56,282,1,0,0,
0,58,292,1,0,0,0,60,298,1,0,0,0,62,301,1,0,0,0,64,305,1,0,0,0,66,310,1,
0,0,0,68,313,1,0,0,0,70,71,5,34,0,0,71,72,5,34,0,0,72,77,5,34,0,0,73,74,
5,39,0,0,74,75,5,39,0,0,75,77,5,39,0,0,76,70,1,0,0,0,76,73,1,0,0,0,77,
3,1,0,0,0,78,82,5,39,0,0,79,81,9,0,0,0,80,79,1,0,0,0,81,84,1,0,0,0,82,
83,1,0,0,0,82,80,1,0,0,0,83,85,1,0,0,0,84,82,1,0,0,0,85,86,5,39,0,0,86,
5,1,0,0,0,87,88,5,34,0,0,88,89,1,0,0,0,89,90,6,2,0,0,90,7,1,0,0,0,91,92,
5,40,0,0,92,93,1,0,0,0,93,94,6,3,1,0,94,9,1,0,0,0,95,96,5,36,0,0,96,97,
5,40,0,0,97,98,1,0,0,0,98,99,6,4,1,0,99,11,1,0,0,0,100,101,5,41,0,0,101,
102,6,5,2,0,102,13,1,0,0,0,103,104,5,101,0,0,104,105,5,108,0,0,105,106,
5,105,0,0,106,107,5,102,0,0,107,111,1,0,0,0,108,110,3,48,23,0,109,108,
1,0,0,0,110,113,1,0,0,0,111,109,1,0,0,0,111,112,1,0,0,0,112,15,1,0,0,0,
113,111,1,0,0,0,114,115,5,105,0,0,115,116,5,102,0,0,116,120,1,0,0,0,117,
119,3,48,23,0,118,117,1,0,0,0,119,122,1,0,0,0,120,118,1,0,0,0,120,121,
1,0,0,0,121,17,1,0,0,0,122,120,1,0,0,0,123,124,5,115,0,0,124,125,5,119,
0,0,125,126,5,105,0,0,126,127,5,116,0,0,127,128,5,99,0,0,128,129,5,104,
0,0,129,133,1,0,0,0,130,132,3,48,23,0,131,130,1,0,0,0,132,135,1,0,0,0,
133,131,1,0,0,0,133,134,1,0,0,0,134,19,1,0,0,0,135,133,1,0,0,0,136,137,
5,99,0,0,137,138,5,97,0,0,138,139,5,115,0,0,139,140,5,101,0,0,140,144,
1,0,0,0,141,143,3,48,23,0,142,141,1,0,0,0,143,146,1,0,0,0,144,142,1,0,
0,0,144,145,1,0,0,0,145,21,1,0,0,0,146,144,1,0,0,0,147,148,5,100,0,0,148,
149,5,111,0,0,149,153,1,0,0,0,150,152,3,48,23,0,151,150,1,0,0,0,152,155,
1,0,0,0,153,151,1,0,0,0,153,154,1,0,0,0,154,23,1,0,0,0,155,153,1,0,0,0,
156,157,5,116,0,0,157,158,5,104,0,0,158,159,5,101,0,0,159,160,5,110,0,
0,160,164,1,0,0,0,161,163,3,48,23,0,162,161,1,0,0,0,163,166,1,0,0,0,164,
162,1,0,0,0,164,165,1,0,0,0,165,25,1,0,0,0,166,164,1,0,0,0,167,168,5,101,
0,0,168,169,5,108,0,0,169,170,5,115,0,0,170,171,5,101,0,0,171,175,1,0,
0,0,172,174,3,48,23,0,173,172,1,0,0,0,174,177,1,0,0,0,175,173,1,0,0,0,
175,176,1,0,0,0,176,27,1,0,0,0,177,175,1,0,0,0,178,179,5,102,0,0,179,180,
5,111,0,0,180,181,5,114,0,0,181,185,1,0,0,0,182,184,3,48,23,0,183,182,
1,0,0,0,184,187,1,0,0,0,185,183,1,0,0,0,185,186,1,0,0,0,186,29,1,0,0,0,
187,185,1,0,0,0,188,189,5,119,0,0,189,190,5,104,0,0,190,191,5,105,0,0,
191,192,5,108,0,0,192,193,5,101,0,0,193,197,1,0,0,0,194,196,3,48,23,0,
195,194,1,0,0,0,196,199,1,0,0,0,197,195,1,0,0,0,197,198,1,0,0,0,198,31,
1,0,0,0,199,197,1,0,0,0,200,201,5,99,0,0,201,202,5,97,0,0,202,203,5,116,
0,0,203,204,5,99,0,0,204,205,5,104,0,0,205,209,1,0,0,0,206,208,3,48,23,
0,207,206,1,0,0,0,208,211,1,0,0,0,209,207,1,0,0,0,209,210,1,0,0,0,210,
33,1,0,0,0,211,209,1,0,0,0,212,213,5,105,0,0,213,214,5,110,0,0,214,218,
1,0,0,0,215,217,3,48,23,0,216,215,1,0,0,0,217,220,1,0,0,0,218,216,1,0,
0,0,218,219,1,0,0,0,219,35,1,0,0,0,220,218,1,0,0,0,221,222,5,101,0,0,222,
223,5,110,0,0,223,224,5,100,0,0,224,228,1,0,0,0,225,227,3,48,23,0,226,
225,1,0,0,0,227,230,1,0,0,0,228,226,1,0,0,0,228,229,1,0,0,0,229,37,1,0,
0,0,230,228,1,0,0,0,231,232,5,100,0,0,232,233,5,101,0,0,233,234,5,102,
0,0,234,235,5,101,0,0,235,236,5,114,0,0,236,240,1,0,0,0,237,239,3,48,23,
0,238,237,1,0,0,0,239,242,1,0,0,0,240,238,1,0,0,0,240,241,1,0,0,0,241,
39,1,0,0,0,242,240,1,0,0,0,243,244,5,38,0,0,244,245,5,38,0,0,245,41,1,
0,0,0,246,247,5,124,0,0,247,43,1,0,0,0,248,249,5,124,0,0,249,250,5,124,
0,0,250,45,1,0,0,0,251,252,5,59,0,0,252,47,1,0,0,0,253,255,3,58,28,0,254,
253,1,0,0,0,254,255,1,0,0,0,255,257,1,0,0,0,256,258,5,13,0,0,257,256,1,
0,0,0,257,258,1,0,0,0,258,259,1,0,0,0,259,260,5,10,0,0,260,49,1,0,0,0,
261,263,7,0,0,0,262,261,1,0,0,0,263,264,1,0,0,0,264,262,1,0,0,0,264,265,
1,0,0,0,265,51,1,0,0,0,266,268,7,1,0,0,267,266,1,0,0,0,268,269,1,0,0,0,
269,267,1,0,0,0,269,270,1,0,0,0,270,53,1,0,0,0,271,273,5,92,0,0,272,274,
3,58,28,0,273,272,1,0,0,0,273,274,1,0,0,0,274,276,1,0,0,0,275,277,3,56,
27,0,276,275,1,0,0,0,276,277,1,0,0,0,277,278,1,0,0,0,278,279,5,10,0,0,
279,280,1,0,0,0,280,281,6,26,3,0,281,55,1,0,0,0,282,286,5,35,0,0,283,285,
8,2,0,0,284,283,1,0,0,0,285,288,1,0,0,0,286,284,1,0,0,0,286,287,1,0,0,
0,287,289,1,0,0,0,288,286,1,0,0,0,289,290,6,27,3,0,290,57,1,0,0,0,291,
293,7,3,0,0,292,291,1,0,0,0,293,294,1,0,0,0,294,292,1,0,0,0,294,295,1,
0,0,0,295,296,1,0,0,0,296,297,6,28,3,0,297,59,1,0,0,0,298,299,9,0,0,0,
299,61,1,0,0,0,300,302,8,4,0,0,301,300,1,0,0,0,302,303,1,0,0,0,303,301,
1,0,0,0,303,304,1,0,0,0,304,63,1,0,0,0,305,306,5,36,0,0,306,307,5,40,0,
0,307,308,1,0,0,0,308,309,6,31,1,0,309,65,1,0,0,0,310,311,5,92,0,0,311,
312,9,0,0,0,312,67,1,0,0,0,313,314,5,34,0,0,314,315,6,33,4,0,315,69,1,
0,0,0,28,0,1,76,82,111,120,133,144,153,164,175,185,197,209,218,228,240,
254,257,262,264,267,269,273,276,286,294,303,5,5,1,0,5,0,0,1,5,0,6,0,0,
1,33,1
]
public
static let _ATN: ATN = try! ATNDeserializer().deserialize(_serializedATN)
}
================================================
FILE: ShellParserGenerated/Sources/ShellParserGenerated/ShellParser.swift
================================================
// Generated from ./grammar/ShellParser.g4 by ANTLR 4.13.1
import Antlr4
open class ShellParser: Parser {
internal static var _decisionToDFA: [DFA] = {
var decisionToDFA = [DFA]()
let length = ShellParser._ATN.getNumberOfDecisions()
for i in 0..<length {
decisionToDFA.append(DFA(ShellParser._ATN.getDecisionState(i)!, i))
}
return decisionToDFA
}()
internal static let _sharedContextCache = PredictionContextCache()
public
enum Tokens: Int {
case EOF = -1, TRIPLE_QUOTE = 1, SINGLE_QUOTED_STRING = 2, LDQUOTE = 3,
LPAR = 4, INTERPOLATION_START = 5, RPAR = 6, ELIF = 7,
IF = 8, SWITCH = 9, CASE = 10, DO = 11, THEN = 12, ELSE = 13,
FOR = 14, WHILE = 15, CATCH = 16, IN = 17, END = 18, DEFER = 19,
AND = 20, PIPE = 21, OR = 22, SEMICOLON = 23, NL = 24,
WORD = 25, ARG = 26, ESCAPE_NEWLINE = 27, COMMENT = 28,
SPACES = 29, ANY = 30, TEXT = 31, INTERPOLATION_START_IN_DSTRING = 32,
ESCAPE_SEQUENCE = 33, RDQUOTE = 34
}
public
static let RULE_root = 0, RULE_cmds = 1, RULE_cmd = 2, RULE_arg = 3, RULE_dStringFragment = 4
public
static let ruleNames: [String] = [
"root", "cmds", "cmd", "arg", "dStringFragment"
]
private static let _LITERAL_NAMES: [String?] = [
nil, nil, nil, nil, "'('", nil, "')'", nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, "'&&'", "'|'", "'||'", "';'"
]
private static let _SYMBOLIC_NAMES: [String?] = [
nil, "TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START",
"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE",
"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL",
"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING",
"ESCAPE_SEQUENCE", "RDQUOTE"
]
public
static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)
override open
func getGrammarFileName() -> String { return "ShellParser.g4" }
override open
func getRuleNames() -> [String] { return ShellParser.ruleNames }
override open
func getSerializedATN() -> [Int] { return ShellParser._serializedATN }
override open
func getATN() -> ATN { return ShellParser._ATN }
override open
func getVocabulary() -> Vocabulary {
return ShellParser.VOCABULARY
}
override public
init(_ input:TokenStream) throws {
RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION)
try super.init(input)
_interp = ParserATNSimulator(self,ShellParser._ATN,ShellParser._decisionToDFA, ShellParser._sharedContextCache)
}
public class RootContext: ParserRuleContext {
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func EOF() -> TerminalNode? {
return getToken(ShellParser.Tokens.EOF.rawValue, 0)
}
open
func NL() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.NL.rawValue)
}
open
func NL(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.NL.rawValue, i)
}
override open
func getRuleIndex() -> Int {
return ShellParser.RULE_root
}
}
@discardableResult
open func root() throws -> RootContext {
var _localctx: RootContext
_localctx = RootContext(_ctx, getState())
try enterRule(_localctx, 0, ShellParser.RULE_root)
var _la: Int = 0
defer {
try! exitRule()
}
do {
try enterOuterAlt(_localctx, 1)
setState(13)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (_la == ShellParser.Tokens.NL.rawValue) {
setState(10)
try match(ShellParser.Tokens.NL.rawValue)
setState(15)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(20)
try _errHandler.sync(self)
switch (ShellParser.Tokens(rawValue: try _input.LA(1))!) {
case .LPAR:fallthrough
case .IF:fallthrough
case .WORD:
setState(16)
try cmds()
setState(17)
try match(ShellParser.Tokens.EOF.rawValue)
break
case .EOF:
setState(19)
try match(ShellParser.Tokens.EOF.rawValue)
break
default:
throw ANTLRException.recognition(e: NoViableAltException(self))
}
}
catch ANTLRException.recognition(let re) {
_localctx.exception = re
_errHandler.reportError(self, re)
try _errHandler.recover(self, re)
}
return _localctx
}
public class CmdsContext: ParserRuleContext {
override open
func getRuleIndex() -> Int {
return ShellParser.RULE_cmds
}
}
public class IfElseContext: CmdsContext {
open
func IF() -> TerminalNode? {
return getToken(ShellParser.Tokens.IF.rawValue, 0)
}
open
func cmd() -> [CmdContext] {
return getRuleContexts(CmdContext.self)
}
open
func cmd(_ i: Int) -> CmdContext? {
return getRuleContext(CmdContext.self, i)
}
open
func THEN() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.THEN.rawValue)
}
open
func THEN(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.THEN.rawValue, i)
}
open
func END() -> TerminalNode? {
return getToken(ShellParser.Tokens.END.rawValue, 0)
}
open
func cmds() -> [CmdsContext] {
return getRuleContexts(CmdsContext.self)
}
open
func cmds(_ i: Int) -> CmdsContext? {
return getRuleContext(CmdsContext.self, i)
}
open
func ELIF() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.ELIF.rawValue)
}
open
func ELIF(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.ELIF.rawValue, i)
}
open
func ELSE() -> TerminalNode? {
return getToken(ShellParser.Tokens.ELSE.rawValue, 0)
}
public
init(_ ctx: CmdsContext) {
super.init()
copyFrom(ctx)
}
}
public class SeqContext: CmdsContext {
open
func cmd() -> CmdContext? {
return getRuleContext(CmdContext.self, 0)
}
open
func cmds() -> [CmdsContext] {
return getRuleContexts(CmdsContext.self)
}
open
func cmds(_ i: Int) -> CmdsContext? {
return getRuleContext(CmdsContext.self, i)
}
open
func SEMICOLON() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.SEMICOLON.rawValue)
}
open
func SEMICOLON(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.SEMICOLON.rawValue, i)
}
open
func NL() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.NL.rawValue)
}
open
func NL(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.NL.rawValue, i)
}
public
init(_ ctx: CmdsContext) {
super.init()
copyFrom(ctx)
}
}
@discardableResult
open func cmds() throws -> CmdsContext {
var _localctx: CmdsContext
_localctx = CmdsContext(_ctx, getState())
try enterRule(_localctx, 2, ShellParser.RULE_cmds)
var _la: Int = 0
defer {
try! exitRule()
}
do {
var _alt:Int
setState(60)
try _errHandler.sync(self)
switch(try getInterpreter().adaptivePredict(_input,9, _ctx)) {
case 1:
_localctx = IfElseContext(_localctx);
try enterOuterAlt(_localctx, 1)
setState(22)
try match(ShellParser.Tokens.IF.rawValue)
setState(23)
try cmd(0)
setState(24)
try match(ShellParser.Tokens.THEN.rawValue)
setState(26)
try _errHandler.sync(self)
_la = try _input.LA(1)
if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
setState(25)
try cmds()
}
setState(36)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (_la == ShellParser.Tokens.ELIF.rawValue) {
setState(28)
try match(ShellParser.Tokens.ELIF.rawValue)
setState(29)
try cmd(0)
setState(30)
try match(ShellParser.Tokens.THEN.rawValue)
setState(32)
try _errHandler.sync(self)
_la = try _input.LA(1)
if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
setState(31)
try cmds()
}
setState(38)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(43)
try _errHandler.sync(self)
_la = try _input.LA(1)
if (_la == ShellParser.Tokens.ELSE.rawValue) {
setState(39)
try match(ShellParser.Tokens.ELSE.rawValue)
setState(41)
try _errHandler.sync(self)
_la = try _input.LA(1)
if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
setState(40)
try cmds()
}
}
setState(45)
try match(ShellParser.Tokens.END.rawValue)
break
case 2:
_localctx = SeqContext(_localctx);
try enterOuterAlt(_localctx, 2)
setState(47)
try cmd(0)
setState(49);
try _errHandler.sync(self)
_alt = 1;
repeat {
switch (_alt) {
case 1:
setState(48)
_la = try _input.LA(1)
if (!(_la == ShellParser.Tokens.SEMICOLON.rawValue || _la == ShellParser.Tokens.NL.rawValue)) {
try _errHandler.recoverInline(self)
}
else {
_errHandler.reportMatch(self)
try consume()
}
break
default:
throw ANTLRException.recognition(e: NoViableAltException(self))
}
setState(51);
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,7,_ctx)
} while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER)
setState(56)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,8,_ctx)
while (_alt != 1 && _alt != ATN.INVALID_ALT_NUMBER) {
if ( _alt==1+1 ) {
setState(53)
try cmds()
}
setState(58)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,8,_ctx)
}
break
case 3:
_localctx = SeqContext(_localctx);
try enterOuterAlt(_localctx, 3)
setState(59)
try cmd(0)
break
default: break
}
}
catch ANTLRException.recognition(let re) {
_localctx.exception = re
_errHandler.reportError(self, re)
try _errHandler.recover(self, re)
}
return _localctx
}
public class CmdContext: ParserRuleContext {
override open
func getRuleIndex() -> Int {
return ShellParser.RULE_cmd
}
}
public class ArgsContext: CmdContext {
open
func WORD() -> TerminalNode? {
return getToken(ShellParser.Tokens.WORD.rawValue, 0)
}
open
func arg() -> [ArgContext] {
return getRuleContexts(ArgContext.self)
}
open
func arg(_ i: Int) -> ArgContext? {
return getRuleContext(ArgContext.self, i)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public class CmdErrorContext: CmdContext {
open
func LPAR() -> TerminalNode? {
return getToken(ShellParser.Tokens.LPAR.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.RPAR.rawValue)
}
open
func RPAR(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, i)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public class OrContext: CmdContext {
open
func cmd() -> [CmdContext] {
return getRuleContexts(CmdContext.self)
}
open
func cmd(_ i: Int) -> CmdContext? {
return getRuleContext(CmdContext.self, i)
}
open
func OR() -> TerminalNode? {
return getToken(ShellParser.Tokens.OR.rawValue, 0)
}
open
func NL() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.NL.rawValue)
}
open
func NL(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.NL.rawValue, i)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public class ParensContext: CmdContext {
open
func LPAR() -> TerminalNode? {
return getToken(ShellParser.Tokens.LPAR.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public class AndContext: CmdContext {
open
func cmd() -> [CmdContext] {
return getRuleContexts(CmdContext.self)
}
open
func cmd(_ i: Int) -> CmdContext? {
return getRuleContext(CmdContext.self, i)
}
open
func AND() -> TerminalNode? {
return getToken(ShellParser.Tokens.AND.rawValue, 0)
}
open
func NL() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.NL.rawValue)
}
open
func NL(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.NL.rawValue, i)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public class PipeContext: CmdContext {
open
func cmd() -> [CmdContext] {
return getRuleContexts(CmdContext.self)
}
open
func cmd(_ i: Int) -> CmdContext? {
return getRuleContext(CmdContext.self, i)
}
open
func PIPE() -> TerminalNode? {
return getToken(ShellParser.Tokens.PIPE.rawValue, 0)
}
open
func NL() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.NL.rawValue)
}
open
func NL(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.NL.rawValue, i)
}
public
init(_ ctx: CmdContext) {
super.init()
copyFrom(ctx)
}
}
public final func cmd( ) throws -> CmdContext {
return try cmd(0)
}
@discardableResult
private func cmd(_ _p: Int) throws -> CmdContext {
let _parentctx: ParserRuleContext? = _ctx
let _parentState: Int = getState()
var _localctx: CmdContext
_localctx = CmdContext(_ctx, _parentState)
let _startState: Int = 4
try enterRecursionRule(_localctx, 4, ShellParser.RULE_cmd, _p)
var _la: Int = 0
defer {
try! unrollRecursionContexts(_parentctx)
}
do {
var _alt: Int
try enterOuterAlt(_localctx, 1)
setState(84)
try _errHandler.sync(self)
switch(try getInterpreter().adaptivePredict(_input,11, _ctx)) {
case 1:
_localctx = ArgsContext(_localctx)
_ctx = _localctx
setState(63)
try match(ShellParser.Tokens.WORD.rawValue)
setState(67)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,10,_ctx)
while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
if ( _alt==1 ) {
setState(64)
try arg()
}
setState(69)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,10,_ctx)
}
break
case 2:
_localctx = ParensContext(_localctx)
_ctx = _localctx
setState(70)
try match(ShellParser.Tokens.LPAR.rawValue)
setState(71)
try cmds()
setState(72)
try match(ShellParser.Tokens.RPAR.rawValue)
break
case 3:
_localctx = CmdErrorContext(_localctx)
_ctx = _localctx
setState(74)
try match(ShellParser.Tokens.LPAR.rawValue)
setState(75)
try cmds()
setState(76)
try match(ShellParser.Tokens.RPAR.rawValue)
setState(77)
try match(ShellParser.Tokens.RPAR.rawValue)
notifyErrorListeners("Unbalanced parenthesis")
break
case 4:
_localctx = CmdErrorContext(_localctx)
_ctx = _localctx
setState(80)
try match(ShellParser.Tokens.LPAR.rawValue)
setState(81)
try cmds()
notifyErrorListeners("Unbalanced parenthesis")
break
default: break
}
_ctx!.stop = try _input.LT(-1)
setState(115)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,16,_ctx)
while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
if ( _alt==1 ) {
if _parseListeners != nil {
try triggerExitRuleEvent()
}
setState(113)
try _errHandler.sync(self)
switch(try getInterpreter().adaptivePredict(_input,15, _ctx)) {
case 1:
_localctx = PipeContext( CmdContext(_parentctx, _parentState))
try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
setState(86)
if (!(precpred(_ctx, 6))) {
throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 6)"))
}
setState(90)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (_la == ShellParser.Tokens.NL.rawValue) {
setState(87)
try match(ShellParser.Tokens.NL.rawValue)
setState(92)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(93)
try match(ShellParser.Tokens.PIPE.rawValue)
setState(94)
try cmd(7)
break
case 2:
_localctx = AndContext( CmdContext(_parentctx, _parentState))
try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
setState(95)
if (!(precpred(_ctx, 5))) {
throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 5)"))
}
setState(99)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (_la == ShellParser.Tokens.NL.rawValue) {
setState(96)
try match(ShellParser.Tokens.NL.rawValue)
setState(101)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(102)
try match(ShellParser.Tokens.AND.rawValue)
setState(103)
try cmd(6)
break
case 3:
_localctx = OrContext( CmdContext(_parentctx, _parentState))
try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
setState(104)
if (!(precpred(_ctx, 4))) {
throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 4)"))
}
setState(108)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (_la == ShellParser.Tokens.NL.rawValue) {
setState(105)
try match(ShellParser.Tokens.NL.rawValue)
setState(110)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(111)
try match(ShellParser.Tokens.OR.rawValue)
setState(112)
try cmd(5)
break
default: break
}
}
setState(117)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,16,_ctx)
}
}
catch ANTLRException.recognition(let re) {
_localctx.exception = re
_errHandler.reportError(self, re)
try _errHandler.recover(self, re)
}
return _localctx;
}
public class ArgContext: ParserRuleContext {
override open
func getRuleIndex() -> Int {
return ShellParser.RULE_arg
}
}
public class WordContext: ArgContext {
open
func ARG() -> TerminalNode? {
return getToken(ShellParser.Tokens.ARG.rawValue, 0)
}
open
func WORD() -> TerminalNode? {
return getToken(ShellParser.Tokens.WORD.rawValue, 0)
}
public
init(_ ctx: ArgContext) {
super.init()
copyFrom(ctx)
}
}
public class SubstitutionContext: ArgContext {
open
func INTERPOLATION_START() -> TerminalNode? {
return getToken(ShellParser.Tokens.INTERPOLATION_START.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
}
public
init(_ ctx: ArgContext) {
super.init()
copyFrom(ctx)
}
}
public class ArgErrorContext: ArgContext {
open
func LDQUOTE() -> TerminalNode? {
return getToken(ShellParser.Tokens.LDQUOTE.rawValue, 0)
}
open
func RDQUOTE() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.RDQUOTE.rawValue)
}
open
func RDQUOTE(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.RDQUOTE.rawValue, i)
}
open
func dStringFragment() -> [DStringFragmentContext] {
return getRuleContexts(DStringFragmentContext.self)
}
open
func dStringFragment(_ i: Int) -> DStringFragmentContext? {
return getRuleContext(DStringFragmentContext.self, i)
}
open
func INTERPOLATION_START() -> TerminalNode? {
return getToken(ShellParser.Tokens.INTERPOLATION_START.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.RPAR.rawValue)
}
open
func RPAR(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, i)
}
public
init(_ ctx: ArgContext) {
super.init()
copyFrom(ctx)
}
}
public class SQuotedStringContext: ArgContext {
open
func SINGLE_QUOTED_STRING() -> TerminalNode? {
return getToken(ShellParser.Tokens.SINGLE_QUOTED_STRING.rawValue, 0)
}
public
init(_ ctx: ArgContext) {
super.init()
copyFrom(ctx)
}
}
public class DQuotedStringContext: ArgContext {
open
func LDQUOTE() -> TerminalNode? {
return getToken(ShellParser.Tokens.LDQUOTE.rawValue, 0)
}
open
func RDQUOTE() -> TerminalNode? {
return getToken(ShellParser.Tokens.RDQUOTE.rawValue, 0)
}
open
func dStringFragment() -> [DStringFragmentContext] {
return getRuleContexts(DStringFragmentContext.self)
}
open
func dStringFragment(_ i: Int) -> DStringFragmentContext? {
return getRuleContext(DStringFragmentContext.self, i)
}
public
init(_ ctx: ArgContext) {
super.init()
copyFrom(ctx)
}
}
@discardableResult
open func arg() throws -> ArgContext {
var _localctx: ArgContext
_localctx = ArgContext(_ctx, getState())
try enterRule(_localctx, 6, ShellParser.RULE_arg)
var _la: Int = 0
defer {
try! exitRule()
}
do {
var _alt:Int
setState(161)
try _errHandler.sync(self)
switch(try getInterpreter().adaptivePredict(_input,20, _ctx)) {
case 1:
_localctx = WordContext(_localctx);
try enterOuterAlt(_localctx, 1)
setState(118)
try match(ShellParser.Tokens.ARG.rawValue)
break
case 2:
_localctx = WordContext(_localctx);
try enterOuterAlt(_localctx, 2)
setState(119)
try match(ShellParser.Tokens.WORD.rawValue)
break
case 3:
_localctx = SQuotedStringContext(_localctx);
try enterOuterAlt(_localctx, 3)
setState(120)
try match(ShellParser.Tokens.SINGLE_QUOTED_STRING.rawValue)
break
case 4:
_localctx = DQuotedStringContext(_localctx);
try enterOuterAlt(_localctx, 4)
setState(121)
try match(ShellParser.Tokens.LDQUOTE.rawValue)
setState(125)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 15032385536) != 0)) {
setState(122)
try dStringFragment()
setState(127)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(128)
try match(ShellParser.Tokens.RDQUOTE.rawValue)
break
case 5:
_localctx = SubstitutionContext(_localctx);
try enterOuterAlt(_localctx, 5)
setState(129)
try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
setState(130)
try cmds()
setState(131)
try match(ShellParser.Tokens.RPAR.rawValue)
break
case 6:
_localctx = ArgErrorContext(_localctx);
try enterOuterAlt(_localctx, 6)
setState(133)
try match(ShellParser.Tokens.LDQUOTE.rawValue)
setState(137)
try _errHandler.sync(self)
_la = try _input.LA(1)
while (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 15032385536) != 0)) {
setState(134)
try dStringFragment()
setState(139)
try _errHandler.sync(self)
_la = try _input.LA(1)
}
setState(140)
try match(ShellParser.Tokens.RDQUOTE.rawValue)
setState(141)
try match(ShellParser.Tokens.RDQUOTE.rawValue)
notifyErrorListeners("Unbalanced quotes")
break
case 7:
_localctx = ArgErrorContext(_localctx);
try enterOuterAlt(_localctx, 7)
setState(143)
try match(ShellParser.Tokens.LDQUOTE.rawValue)
setState(147)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,19,_ctx)
while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
if ( _alt==1 ) {
setState(144)
try dStringFragment()
}
setState(149)
try _errHandler.sync(self)
_alt = try getInterpreter().adaptivePredict(_input,19,_ctx)
}
notifyErrorListeners("Unbalanced quotes")
break
case 8:
_localctx = ArgErrorContext(_localctx);
try enterOuterAlt(_localctx, 8)
setState(151)
try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
setState(152)
try cmds()
setState(153)
try match(ShellParser.Tokens.RPAR.rawValue)
setState(154)
try match(ShellParser.Tokens.RPAR.rawValue)
notifyErrorListeners("Unbalanced parenthesis")
break
case 9:
_localctx = ArgErrorContext(_localctx);
try enterOuterAlt(_localctx, 9)
setState(157)
try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
setState(158)
try cmds()
notifyErrorListeners("Unbalanced parenthesis")
break
default: break
}
}
catch ANTLRException.recognition(let re) {
_localctx.exception = re
_errHandler.reportError(self, re)
try _errHandler.recover(self, re)
}
return _localctx
}
public class DStringFragmentContext: ParserRuleContext {
override open
func getRuleIndex() -> Int {
return ShellParser.RULE_dStringFragment
}
}
public class InterpolationContext: DStringFragmentContext {
open
func INTERPOLATION_START_IN_DSTRING() -> TerminalNode? {
return getToken(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
}
public
init(_ ctx: DStringFragmentContext) {
super.init()
copyFrom(ctx)
}
}
public class DStringFragmentErrorContext: DStringFragmentContext {
open
func INTERPOLATION_START_IN_DSTRING() -> TerminalNode? {
return getToken(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue, 0)
}
open
func cmds() -> CmdsContext? {
return getRuleContext(CmdsContext.self, 0)
}
open
func RPAR() -> [TerminalNode] {
return getTokens(ShellParser.Tokens.RPAR.rawValue)
}
open
func RPAR(_ i:Int) -> TerminalNode? {
return getToken(ShellParser.Tokens.RPAR.rawValue, i)
}
public
init(_ ctx: DStringFragmentContext) {
super.init()
copyFrom(ctx)
}
}
public class EscapeSequenceContext: DStringFragmentContext {
open
func ESCAPE_SEQUENCE() -> TerminalNode? {
return getToken(ShellParser.Tokens.ESCAPE_SEQUENCE.rawValue, 0)
}
public
init(_ ctx: DStringFragmentContext) {
super.init()
copyFrom(ctx)
}
}
public class TextContext: DStringFragmentContext {
open
func TEXT() -> TerminalNode? {
return getToken(ShellParser.Tokens.TEXT.rawValue, 0)
}
public
init(_ ctx: DStringFragmentContext) {
super.init()
copyFrom(ctx)
}
}
@discardableResult
open func dStringFragment() throws -> DStringFragmentContext {
var _localctx: DStringFragmentContext
_localctx = DStringFragmentContext(_ctx, getState())
try enterRule(_localctx, 8, ShellParser.RULE_dStringFragment)
defer {
try! exitRule()
}
do {
setState(179)
try _errHandler.sync(self)
switch(try getInterpreter().adaptivePredict(_input,21, _ctx)) {
case 1:
_localctx = TextContext(_localctx);
try enterOuterAlt(_localctx, 1)
setState(163)
try match(ShellParser.Tokens.TEXT.rawValue)
break
case 2:
_localctx = EscapeSequenceContext(_localctx);
try enterOuterAlt(_localctx, 2)
setState(164)
try match(ShellParser.Tokens.ESCAPE_SEQUENCE.rawValue)
break
case 3:
_localctx = InterpolationContext(_localctx);
try enterOuterAlt(_localctx, 3)
setState(165)
try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
setState(166)
try cmds()
setState(167)
try match(ShellParser.Tokens.RPAR.rawValue)
break
case 4:
_localctx = DStringFragmentErrorContext(_localctx);
try enterOuterAlt(_localctx, 4)
setState(169)
try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
setState(170)
try cmds()
setState(171)
try match(ShellParser.Tokens.RPAR.rawValue)
setState(172)
try match(ShellParser.Tokens.RPAR.rawValue)
notifyErrorListeners("Unbalanced parenthesis")
break
case 5:
_localctx = DStringFragmentErrorContext(_localctx);
try enterOuterAlt(_localctx, 5)
setState(175)
try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
setState(176)
try cmds()
notifyErrorListeners("Unbalanced parenthesis")
break
default: break
}
}
catch ANTLRException.recognition(let re) {
_localctx.exception = re
_errHandler.reportError(self, re)
try _errHandler.recover(self, re)
}
return _localctx
}
override open
func sempred(_ _localctx: RuleContext?, _ ruleIndex: Int, _ predIndex: Int)throws -> Bool {
switch (ruleIndex) {
case 2:
return try cmd_sempred(_localctx?.castdown(CmdContext.self), predIndex)
default: return true
}
}
private func cmd_sempred(_ _localctx: CmdContext!, _ predIndex: Int) throws -> Bool {
switch (predIndex) {
case 0:return precpred(_ctx, 6)
case 1:return precpred(_ctx, 5)
case 2:return precpred(_ctx, 4)
default: return true
}
}
static let _serializedATN:[Int] = [
4,1,34,182,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,1,0,5,0,12,8,0,10,0,
12,0,15,9,0,1,0,1,0,1,0,1,0,3,0,21,8,0,1,1,1,1,1,1,1,1,3,1,27,8,1,1,1,
1,1,1,1,1,1,3,1,33,8,1,5,1,35,8,1,10,1,12,1,38,9,1,1,1,1,1,3,1,42,8,1,
3,1,44,8,1,1,1,1,1,1,1,1,1,4,1,50,8,1,11,1,12,1,51,1,1,5,1,55,8,1,10,1,
12,1,58,9,1,1,1,3,1,61,8,1,1,2,1,2,1,2,5,2,66,8,2,10,2,12,2,69,9,2,1,2,
1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,3,2,85,8,2,1,2,1,2,
5,2,89,8,2,10,2,12,2,92,9,2,1,2,1,2,1,2,1,2,5,2,98,8,2,10,2,12,2,101,9,
2,1,2,1,2,1,2,1,2,5,2,107,8,2,10,2,12,2,110,9,2,1,2,1,2,5,2,114,8,2,10,
2,12,2,117,9,2,1,3,1,3,1,3,1,3,1,3,5,3,124,8,3,10,3,12,3,127,9,3,1,3,1,
3,1,3,1,3,1,3,1,3,1,3,5,3,136,8,3,10,3,12,3,139,9,3,1,3,1,3,1,3,1,3,1,
3,5,3,146,8,3,10,3,12,3,149,9,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,
3,1,3,3,3,162,8,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,
1,4,1,4,1,4,3,4,180,8,4,1,4,1,56,1,4,5,0,2,4,6,8,0,1,1,0,23,24,212,0,13,
1,0,0,0,2,60,1,0,0,0,4,84,1,0,0,0,6,161,1,0,0,0,8,179,1,0,0,0,10,12,5,
24,0,0,11,10,1,0,0,0,12,15,1,0,0,0,13,11,1,0,0,0,13,14,1,0,0,0,14,20,1,
0,0,0,15,13,1,0,0,0,16,17,3,2,1,0,17,18,5,0,0,1,18,21,1,0,0,0,19,21,5,
0,0,1,20,16,1,0,0,0,20,19,1,0,0,0,21,1,1,0,0,0,22,23,5,8,0,0,23,24,3,4,
2,0,24,26,5,12,0,0,25,27,3,2,1,0,26,25,1,0,0,0,26,27,1,0,0,0,27,36,1,0,
0,0,28,29,5,7,0,0,29,30,3,4,2,0,30,32,5,12,0,0,31,33,3,2,1,0,32,31,1,0,
0,0,32,33,1,0,0,0,33,35,1,0,0,0,34,28,1,0,0,0,35,38,1,0,0,0,36,34,1,0,
0,0,36,37,1,0,0,0,37,43,1,0,0,0,38,36,1,0,0,0,39,41,5,13,0,0,40,42,3,2,
1,0,41,40,1,0,0,0,41,42,1,0,0,0,42,44,1,0,0,0,43,39,1,0,0,0,43,44,1,0,
0,0,44,45,1,0,0,0,45,46,5,18,0,0,46,61,1,0,0,0,47,49,3,4,2,0,48,50,7,0,
0,0,49,48,1,0,0,0,50,51,1,0,0,0,51,49,1,0,0,0,51,52,1,0,0,0,52,56,1,0,
0,0,53,55,3,2,1,0,54,53,1,0,0,0,55,58,1,0,0,0,56,57,1,0,0,0,56,54,1,0,
0,0,57,61,1,0,0,0,58,56,1,0,0,0,59,61,3,4,2,0,60,22,1,0,0,0,60,47,1,0,
0,0,60,59,1,0,0,0,61,3,1,0,0,0,62,63,6,2,-1,0,63,67,5,25,0,0,64,66,3,6,
3,0,65,64,1,0,0,0,66,69,1,0,0,0,67,65,1,0,0,0,67,68,1,0,0,0,68,85,1,0,
0,0,69,67,1,0,0,0,70,71,5,4,0,0,71,72,3,2,1,0,72,73,5,6,0,0,73,85,1,0,
0,0,74,75,5,4,0,0,75,76,3,2,1,0,76,77,5,6,0,0,77,78,5,6,0,0,78,79,6,2,
-1,0,79,85,1,0,0,0,80,81,5,4,0,0,81,82,3,2,1,0,82,83,6,2,-1,0,83,85,1,
0,0,0,84,62,1,0,0,0,84,70,1,0,0,0,84,74,1,0,0,0,84,80,1,0,0,0,85,115,1,
0,0,0,86,90,10,6,0,0,87,89,5,24,0,0,88,87,1,0,0,0,89,92,1,0,0,0,90,88,
1,0,0,0,90,91,1,0,0,0,91,93,1,0,0,0,92,90,1,0,0,0,93,94,5,21,0,0,94,114,
3,4,2,7,95,99,10,5,0,0,96,98,5,24,0,0,97,96,1,0,0,0,98,101,1,0,0,0,99,
97,1,0,0,0,99,100,1,0,0,0,100,102,1,0,0,0,101,99,1,0,0,0,102,103,5,20,
0,0,103,114,3,4,2,6,104,108,10,4,0,0,105,107,5,24,0,0,106,105,1,0,0,0,
107,110,1,0,0,0,108,106,1,0,0,0,108,109,1,0,0,0,109,111,1,0,0,0,110,108,
1,0,0,0,111,112,5,22,0,0,112,114,3,4,2,5,113,86,1,0,0,0,113,95,1,0,0,0,
113,104,1,0,0,0,114,117,1,0,0,0,115,113,1,0,0,0,115,116,1,0,0,0,116,5,
1,0,0,0,117,115,1,0,0,0,118,162,5,26,0,0,119,162,5,25,0,0,120,162,5,2,
0,0,121,125,5,3,0,0,122,124,3,8,4,0,123,122,1,0,0,0,124,127,1,0,0,0,125,
123,1,0,0,0,125,126,1,0,0,0,126,128,1,0,0,0,127,125,1,0,0,0,128,162,5,
34,0,0,129,130,5,5,0,0,130,131,3,2,1,0,131,132,5,6,0,0,132,162,1,0,0,0,
133,137,5,3,0,0,134,136,3,8,4,0,135,134,1,0,0,0,136,139,1,0,0,0,137,135,
1,0,0,0,137,138,1,0,0,0,138,140,1,0,0,0,139,137,1,0,0,0,140,141,5,34,0,
0,141,142,5,34,0,0,142,162,6,3,-1,0,143,147,5,3,0,0,144,146,3,8,4,0,145,
144,1,0,0,0,146,149,1,0,0,0,147,145,1,0,0,0,147,148,1,0,0,0,148,150,1,
0,0,0,149,147,1,0,0,0,150,162,6,3,-1,0,151,152,5,5,0,0,152,153,3,2,1,0,
153,154,5,6,0,0,154,155,5,6,0,0,155,156,6,3,-1,0,156,162,1,0,0,0,157,158,
5,5,0,0,158,159,3,2,1,0,159,160,6,3,-1,0,160,162,1,0,0,0,161,118,1,0,0,
0,161,119,1,0,0,0,161,120,1,0,0,0,161,121,1,0,0,0,161,129,1,0,0,0,161,
133,1,0,0,0,161,143,1,0,0,0,161,151,1,0,0,0,161,157,1,0,0,0,162,7,1,0,
0,0,163,180,5,31,0,0,164,180,5,33,0,0,165,166,5,32,0,0,166,167,3,2,1,0,
167,168,5,6,0,0,168,180,1,0,0,0,169,170,5,32,0,0,170,171,3,2,1,0,171,172,
5,6,0,0,172,173,5,6,0,0,173,174,6,4,-1,0,174,180,1,0,0,0,175,176,5,32,
0,0,176,177,3,2,1,0,177,178,6,4,-1,0,178,180,1,0,0,0,179,163,1,0,0,0,179,
164,1,0,0,0,179,165,1,0,0,0,179,169,1,0,0,0,179,175,1,0,0,0,180,9,1,0,
0,0,22,13,20,26,32,36,41,43,51,56,60,67,84,90,99,108,113,115,125,137,147,
161,179
]
public
static let _ATN = try! ATNDeserializer().deserialize(_serializedATN)
}
================================================
FILE: Sources/AeroSpaceApp/AeroSpaceApp.swift
================================================
import AppBundle
import SwiftUI
// This file is shared between SPM and xcode project
@main
struct AeroSpaceApp: App {
@StateObject var viewModel = TrayMenuModel.shared
@StateObject var messageModel = MessageModel.shared
@Environment(\.openWindow) var openWindow: OpenWindowAction
init() {
initAppBundle()
}
var body: some Scene {
menuBar(viewModel: viewModel)
getMessageWindow(messageModel: messageModel)
.onChange(of: messageModel.message) { message in
if message != nil {
openWindow(id: messageWindowId)
}
}
}
}
================================================
FILE: Sources/AppBundle/GlobalObserver.swift
================================================
import AppKit
import Common
enum GlobalObserver {
private static func onNotif(_ notification: Notification) {
// Third line of defence against lock screen window. See: closedWindowsCache
// Second and third lines of defence are technically needed only to avoid potential flickering
if (notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication)?.bundleIdentifier == lockScreenAppBundleId {
return
}
let notifName = notification.name.rawValue
Task { @MainActor in
if !TrayMenuModel.shared.isEnabled { return }
if notifName == NSWorkspace.didActivateApplicationNotification.rawValue {
scheduleRefreshSession(.globalObserver(notifName), optimisticallyPreLayoutWorkspaces: true)
} else {
scheduleRefreshSession(.globalObserver(notifName))
}
}
}
private static func onHideApp(_ notification: Notification) {
let notifName = notification.name.rawValue
Task { @MainActor in
guard let token: RunSessionGuard = .isServerEnabled else { return }
try await runLightSession(.globalObserver(notifName), token) {
if config.automaticallyUnhideMacosHiddenApps {
if let w = prevFocus?.windowOrNil,
w.macAppUnsafe.nsApp.isHidden,
// "Hide others" (cmd-alt-h) -> don't force focus
// "Hide app" (cmd-h) -> force focus
MacApp.allAppsMap.values.count(where: { $0.nsApp.isHidden }) == 1
{
// Force focus
_ = w.focusWindow()
w.nativeFocus()
}
for app in MacApp.allAppsMap.values {
app.nsApp.unhide()
}
}
}
}
}
@MainActor
static func initObserver() {
let nc = NSWorkspace.shared.notificationCenter
nc.addObserver(forName: NSWorkspace.didLaunchApplicationNotification, object: nil, queue: .main, using: onNotif)
nc.addObserver(forName: NSWorkspace.didActivateApplicationNotification, object: nil, queue: .main, using: onNotif)
nc.addObserver(forName: NSWorkspace.didHideApplicationNotification, object: nil, queue: .main, using: onHideApp)
nc.addObserver(forName: NSWorkspace.didUnhideApplicationNotification, object: nil, queue: .main, using: onNotif)
nc.addObserver(forName: NSWorkspace.activeSpaceDidChangeNotification, object: nil, queue: .main, using: onNotif)
nc.addObserver(forName: NSWorkspace.didTerminateApplicationNotification, object: nil, queue: .main, using: onNotif)
NSEvent.addGlobalMonitorForEvents(matching: .leftMouseUp) { _ in
// todo reduce number of refreshSession in the callback
// resetManipulatedWithMouseIfPossible might call its own refreshSession
// The end of the callback calls refreshSession
Task { @MainActor in
guard let token: RunSessionGuard = .isServerEnabled else { return }
try await resetManipulatedWithMouseIfPossible()
let mouseLocation = mouseLocation
let clickedMonitor = mouseLocation.monitorApproximation
switch true {
// Detect clicks on desktop of different monitors
case clickedMonitor.activeWorkspace != focus.workspace:
_ = try await runLightSession(.globalObserverLeftMouseUp, token) {
clickedMonitor.activeWorkspace.focusWorkspace()
}
// Detect close button clicks for unfocused windows. Yes, kAXUIElementDestroyedNotification is that unreliable
// And trigger new window detection that could be delayed due to mouseDown event
default:
scheduleRefreshSession(.globalObserverLeftMouseUp)
}
}
}
}
}
================================================
FILE: Sources/AppBundle/command/CmdEnv.swift
================================================
import Common
struct CmdEnv: ConvenienceCopyable {
var windowId: UInt32?
var workspaceName: String?
static let defaultEnv: CmdEnv = .init()
func withFocus(_ focus: LiveFocus) -> CmdEnv {
switch focus.asLeaf {
case .window(let wd): .defaultEnv.copy(\.windowId, wd.windowId)
case .emptyWorkspace(let ws): .defaultEnv.copy(\.workspaceName, ws.name)
}
}
var asMap: [String: String] {
var result = [String: String]()
if let windowId {
result[AEROSPACE_WINDOW_ID] = windowId.description
}
if let workspaceName {
result[AEROSPACE_WORKSPACE] = workspaceName.description
}
return result
}
}
================================================
FILE: Sources/AppBundle/command/CmdIo.swift
================================================
struct CmdStdin: ~Copyable {
private var input: String = ""
init(_ input: String) {
self.input = input
}
static var emptyStdin: CmdStdin { .init("") }
mutating func readAll() -> String {
let result = input
input = ""
return result
}
}
final class CmdIo {
private var stdin: CmdStdin
var stdout: [String] = []
var stderr: [String] = []
init(stdin: consuming CmdStdin) { self.stdin = stdin }
@discardableResult func out(_ msg: String) -> Bool { stdout.append(msg); return true }
@discardableResult func err(_ msg: String) -> Bool { stderr.append(msg); return false }
@discardableResult func out(_ msg: [String]) -> Bool { stdout += msg; return true }
// periphery:ignore
@discardableResult func err(_ msg: [String]) -> Bool { stderr += msg; return false }
func readStdin() -> String { stdin.readAll() }
}
struct CmdResult {
let stdout: [String]
let stderr: [String]
let exitCode: Int32
}
================================================
FILE: Sources/AppBundle/command/Command.swift
================================================
import AppKit
import Common
protocol Command: AeroAny, Equatable, Sendable {
associatedtype T where T: CmdArgs
var args: T { get }
@MainActor
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool
/// We should reset closedWindowsCache when the command can potentiall change the tree
var shouldResetClosedWindowsCache: Bool { get }
}
extension Command {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.args.equals(rhs.args)
}
nonisolated func equals(_ other: any Command) -> Bool {
(other as? Self).flatMap { self == $0 } ?? false
}
}
extension Command {
var info: CmdStaticInfo { T.info }
}
extension Command {
@MainActor
@discardableResult
func run(_ env: CmdEnv, _ stdin: consuming CmdStdin) async throws -> CmdResult {
return try await [self].runCmdSeq(env, stdin)
}
var isExec: Bool { self is ExecAndForgetCommand }
}
// There are 4 entry points for running commands:
// 1. config keybindings
// 2. CLI requests to server
// 3. on-window-detected callback
// 4. Tray icon buttons
extension [Command] {
@MainActor
func runCmdSeq(_ env: CmdEnv, _ io: sending CmdIo) async throws -> Bool {
var isSucc = true
for command in self {
isSucc = try await command.run(env, io) && isSucc
if command.shouldResetClosedWindowsCache { resetClosedWindowsCache() }
refreshModel()
}
return isSucc
}
@MainActor
func runCmdSeq(_ env: CmdEnv, _ stdin: consuming CmdStdin) async throws -> CmdResult {
let io: CmdIo = CmdIo(stdin: stdin)
let isSucc = try await runCmdSeq(env, io)
return CmdResult(stdout: io.stdout, stderr: io.stderr, exitCode: isSucc ? 0 : 1)
}
}
================================================
FILE: Sources/AppBundle/command/cmdManifest.swift
================================================
import Common
extension CmdArgs {
func toCommand() -> any Command {
let command: any Command
switch Self.info.kind {
case .balanceSizes:
command = BalanceSizesCommand(args: self as! BalanceSizesCmdArgs)
case .close:
command = CloseCommand(args: self as! CloseCmdArgs)
case .closeAllWindowsButCurrent:
command = CloseAllWindowsButCurrentCommand(args: self as! CloseAllWindowsButCurrentCmdArgs)
case .config:
command = ConfigCommand(args: self as! ConfigCmdArgs)
case .debugWindows:
command = DebugWindowsCommand(args: self as! DebugWindowsCmdArgs)
case .enable:
command = EnableCommand(args: self as! EnableCmdArgs)
case .execAndForget:
die("exec-and-forget is parsed separately")
case .flattenWorkspaceTree:
command = FlattenWorkspaceTreeCommand(args: self as! FlattenWorkspaceTreeCmdArgs)
case .focus:
command = FocusCommand(args: self as! FocusCmdArgs)
case .focusBackAndForth:
command = FocusBackAndForthCommand(args: self as! FocusBackAndForthCmdArgs)
case .focusMonitor:
command = FocusMonitorCommand(args: self as! FocusMonitorCmdArgs)
case .fullscreen:
command = FullscreenCommand(args: self as! FullscreenCmdArgs)
case .joinWith:
command = JoinWithCommand(args: self as! JoinWithCmdArgs)
case .layout:
command = LayoutCommand(args: self as! LayoutCmdArgs)
case .listApps:
command = ListAppsCommand(args: self as! ListAppsCmdArgs)
case .listExecEnvVars:
command = ListExecEnvVarsCommand(args: self as! ListExecEnvVarsCmdArgs)
case .listModes:
command = ListModesCommand(args: self as! ListModesCmdArgs)
case .listMonitors:
command = ListMonitorsCommand(args: self as! ListMonitorsCmdArgs)
case .listWindows:
command = ListWindowsCommand(args: self as! ListWindowsCmdArgs)
case .listWorkspaces:
command = ListWorkspacesCommand(args: self as! ListWorkspacesCmdArgs)
case .macosNativeFullscreen:
command = MacosNativeFullscreenCommand(args: self as! MacosNativeFullscreenCmdArgs)
case .macosNativeMinimize:
command = MacosNativeMinimizeCommand(args: self as! MacosNativeMinimizeCmdArgs)
case .mode:
command = ModeCommand(args: self as! ModeCmdArgs)
case .move:
command = MoveCommand(args: self as! MoveCmdArgs)
case .moveMouse:
command = MoveMouseCommand(args: self as! MoveMouseCmdArgs)
case .moveNodeToMonitor:
command = MoveNodeToMonitorCommand(args: self as! MoveNodeToMonitorCmdArgs)
case .moveNodeToWorkspace:
command = MoveNodeToWorkspaceCommand(args: self as! MoveNodeToWorkspaceCmdArgs)
case .moveWorkspaceToMonitor:
command = MoveWorkspaceToMonitorCommand(args: self as! MoveWorkspaceToMonitorCmdArgs)
case .reloadConfig:
command = ReloadConfigCommand(args: self as! ReloadConfigCmdArgs)
case .resize:
command = ResizeCommand(args: self as! ResizeCmdArgs)
case .split:
command = SplitCommand(args: self as! SplitCmdArgs)
case .subscribe:
die("subscribe is handled separately")
case .summonWorkspace:
command = SummonWorkspaceCommand(args: self as! SummonWorkspaceCmdArgs)
case .swap:
command = SwapCommand(args: self as! SwapCmdArgs)
case .triggerBinding:
command = TriggerBindingCommand(args: self as! TriggerBindingCmdArgs)
case .volume:
command = VolumeCommand(args: self as! VolumeCmdArgs)
case .workspace:
command = WorkspaceCommand(args: self as! WorkspaceCmdArgs)
case .workspaceBackAndForth:
command = WorkspaceBackAndForthCommand(args: self as! WorkspaceBackAndForthCmdArgs)
}
check(command.info == Self.info)
return command
}
}
================================================
FILE: Sources/AppBundle/command/cmdResolveTargetOrReportError.swift
================================================
import Common
extension CmdArgs {
@MainActor
var workspace: Workspace? {
if let workspaceName { Workspace.get(byName: workspaceName.raw) } else { nil }
}
@MainActor
func resolveTargetOrReportError(_ env: CmdEnv, _ io: CmdIo) -> LiveFocus? {
// Flags
if let windowId {
if let wi = Window.get(byId: windowId) {
return wi.toLiveFocusOrReportError(io)
} else {
io.err("Invalid <window-id> \(windowId) passed to --window-id")
return nil
}
}
if let workspace {
return workspace.toLiveFocus()
}
// Env
if let windowId = env.windowId {
if let wi = Window.get(byId: windowId) {
return wi.toLiveFocusOrReportError(io)
} else {
io.err("Invalid <window-id> \(windowId) specified in \(AEROSPACE_WINDOW_ID) env variable")
return nil
}
}
if let wsName = env.workspaceName {
return Workspace.get(byName: wsName).toLiveFocus()
}
// Real Focus
return focus
}
}
extension Window {
@MainActor
func toLiveFocusOrReportError(_ io: CmdIo) -> LiveFocus? {
if let result = toLiveFocusOrNil() {
return result
} else {
io.err("Window \(windowId) doesn't belong to any monitor. And thus can't even define a focused workspace")
return nil
}
}
}
================================================
FILE: Sources/AppBundle/command/format.swift
================================================
import Common
enum AeroObj {
case window(window: Window, title: String)
case workspace(Workspace)
case app(any AbstractApp)
case monitor(Monitor)
var kind: AeroObjKind {
switch self {
case .window: .window
case .workspace: .workspace
case .app: .app
case .monitor: .monitor
}
}
}
extension [AeroObj] {
@MainActor
func format(_ format: [StringInterToken]) -> Result<[String], String> {
var cellTable: [[Cell<String>]] = []
for obj in self {
var line: [Cell<String>] = []
var curCell: String = ""
var errors: [String] = []
for token in format {
switch token {
case .interVar(PlainInterVar.rightPadding.rawValue):
line.append(Cell(value: curCell, rightPadding: true))
curCell = ""
case .literal(let literal):
curCell += literal
case .interVar(let value):
switch value.expandFormatVar(obj: obj) {
case .success(let expanded): curCell += expanded.toString()
case .failure(let error): errors.append(error)
}
}
}
if !errors.isEmpty { return .failure(errors.joinErrors()) }
line.append(Cell(value: curCell, rightPadding: false))
cellTable.append(line)
}
let result = cellTable
.transposed()
.map { column in
let columndWidth = column.map { $0.value.count }.max().orDie()
return column.map {
$0.rightPadding
? $0.value + String(repeating: " ", count: columndWidth - $0.value.count)
: $0.value
}
}
.transposed()
.map { line in line.joined(separator: "") }
return .success(result)
}
}
enum Primitive: Encodable {
case bool(Bool)
case int(Int)
case int32(Int32)
case uint32(UInt32)
case string(String)
func toString() -> String {
switch self {
case .bool(let x): x.description
case .int(let x): x.description
case .int32(let x): x.description
case .uint32(let x): x.description
case .string(let x): x
}
}
func encode(to encoder: any Encoder) throws {
let value: Encodable = switch self {
case .bool(let x): x
case .int(let x): x
case .int32(let x): x
case .uint32(let x): x
case .string(let x): x
}
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
private struct Cell<T> {
let value: T
let rightPadding: Bool
}
extension String {
@MainActor
func expandFormatVar(obj: AeroObj) -> Result<Primitive, String> {
let formatVar = self.toFormatVar()
switch (obj, formatVar) {
case (_, .none): break
case (.window(let w, _), .workspace):
return w.nodeWorkspace.flatMap(AeroObj.workspace).map(expandFormatVar) ?? .success(.string("NULL-WOKRSPACE"))
case (.window(let w, _), .monitor):
return w.nodeMonitor.flatMap(AeroObj.monitor).map(expandFormatVar) ?? .success(.string("NULL-MONITOR"))
case (.window(let w, _), .app):
return expandFormatVar(obj: .app(w.app))
case (.window(_, _), .window): break
case (.workspace(let ws), .monitor):
return expandFormatVar(obj: AeroObj.monitor(ws.workspaceMonitor))
case (.workspace, _): break
case (.app(_), _): break
case (.monitor(_), _): break
}
switch (obj, formatVar) {
case (.window(let w, let title), .window(let f)):
return switch f {
case .windowId: .success(.uint32(w.windowId))
case .windowIsFullscreen: .success(.bool(w.isFullscreen))
case .windowTitle: .success(.string(title))
case .windowLayout, .windowParentContainerLayout: toLayoutResult(w: w)
}
case (.workspace(let w), .workspace(let f)):
return switch f {
case .workspaceName: .success(.string(w.name))
case .workspaceVisible: .success(.bool(w.isVisible))
case .workspaceFocused: .success(.bool(focus.workspace == w))
case .workspaceRootContainerLayout: .success(.string(toLayoutString(tc: w.rootTilingContainer)))
}
case (.monitor(let m), .monitor(let f)):
return switch f {
case .monitorId_oneBased: .success(m.monitorId_oneBased.map { .int($0) } ?? .string("NULL-MONITOR-ID"))
case .monitorAppKitNsScreenScreensId: .success(.int(m.monitorAppKitNsScreenScreensId))
case .monitorName: .success(.string(m.name))
case .monitorIsMain: .success(.bool(m.isMain))
}
case (.app(let a), .app(let f)):
return switch f {
case .appBundleId: .success(.string(a.rawAppBundleId ?? "NULL-APP-BUNDLE-ID"))
case .appName: .success(.string(a.name ?? "NULL-APP-NAME"))
case .appPid: .success(.int32(a.pid))
case .appExecPath: .success(.string(a.execPath ?? "NULL-APP-EXEC-PATH"))
case .appBundlePath: .success(.string(a.bundlePath ?? "NULL-APP-BUNDLE-PATH"))
}
default: break
}
if self == PlainInterVar.newline.rawValue { return .success(.string("\n")) }
if self == PlainInterVar.tab.rawValue { return .success(.string("\t")) }
return .failure("Unknown interpolation variable '\(self)'. " +
"Possible values:\n\(getAvailableInterVars(for: obj.kind).joined(separator: "\n").prependLines(" "))")
}
private func toFormatVar() -> FormatVar? {
FormatVar.WindowFormatVar(rawValue: self).flatMap(FormatVar.window)
?? FormatVar.WorkspaceFormatVar(rawValue: self).flatMap(FormatVar.workspace)
?? FormatVar.AppFormatVar(rawValue: self).flatMap(FormatVar.app)
?? FormatVar.MonitorFormatVar(rawValue: self).flatMap(FormatVar.monitor)
}
}
private func toLayoutString(tc: TilingContainer) -> String {
switch (tc.layout, tc.orientation) {
case (.tiles, .h): return LayoutCmdArgs.LayoutDescription.h_tiles.rawValue
case (.tiles, .v): return LayoutCmdArgs.LayoutDescription.v_tiles.rawValue
case (.accordion, .h): return LayoutCmdArgs.LayoutDescription.h_accordion.rawValue
case (.accordion, .v): return LayoutCmdArgs.LayoutDescription.v_accordion.rawValue
}
}
private func toLayoutResult(w: Window) -> Result<Primitive, String> {
guard let parent = w.parent else { return .failure("NULL-PARENT") }
return switch getChildParentRelation(child: w, parent: parent) {
case .tiling(let tc): .success(.string(toLayoutString(tc: tc)))
case .floatingWindow: .success(.string(LayoutCmdArgs.LayoutDescription.floating.rawValue))
case .macosNativeFullscreenWindow: .success(.string("macos_native_fullscreen"))
case .macosNativeHiddenAppWindow: .success(.string("macos_native_window_of_hidden_app"))
case .macosNativeMinimizedWindow: .success(.string("macos_native_minimized"))
case .macosPopupWindow: .success(.string("NULL-WINDOW-LAYOUT"))
case .rootTilingContainer: .failure("Not possible")
case .shimContainerRelation: .failure("Window cannot have a shim container relation")
}
}
================================================
FILE: Sources/AppBundle/command/formatToJson.swift
================================================
import Common
import Foundation
extension [AeroObj] {
@MainActor
func formatToJson(_ format: [StringInterToken], ignoreRightPaddingVar: Bool) -> Result<String, String> {
var list: [[String: Primitive]] = []
for richObj in self {
var rawObj: [String: Primitive] = [:]
for token in format {
switch token {
case .interVar(PlainInterVar.rightPadding.rawValue) where ignoreRightPaddingVar:
break
case .literal:
break // should be spaces
case .interVar(let varName):
switch varName.expandFormatVar(obj: richObj) {
case .success(let expanded): rawObj[varName] = expanded
case .failure(let error): return .failure(error)
}
}
}
list.append(rawObj)
}
return JSONEncoder.aeroSpaceDefault.encodeToString(list).map(Result.success)
?? .failure("Can't encode '\(list)' to JSON")
}
}
================================================
FILE: Sources/AppBundle/command/impl/BalanceSizesCommand.swift
================================================
import AppKit
import Common
import Foundation
struct BalanceSizesCommand: Command {
let args: BalanceSizesCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
balance(target.workspace.rootTilingContainer)
return true
}
}
@MainActor
private func balance(_ parent: TilingContainer) {
for child in parent.children {
switch parent.layout {
case .tiles: child.setWeight(parent.orientation, 1)
case .accordion: break // Do nothing
}
if let child = child as? TilingContainer {
balance(child)
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/CloseAllWindowsButCurrentCommand.swift
================================================
import AppKit
import Common
struct CloseAllWindowsButCurrentCommand: Command {
let args: CloseAllWindowsButCurrentCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let focused = target.windowOrNil else {
return io.err("Empty workspace")
}
guard let workspace = focused.nodeWorkspace else {
return io.err("Focused window '\(focused.windowId)' doesn't belong to workspace")
}
var result = true
for window in workspace.allLeafWindowsRecursive where window != focused {
result = try await CloseCommand(args: args.closeArgs).run(env.copy(\.windowId, window.windowId), io) && result
}
return result
}
}
================================================
FILE: Sources/AppBundle/command/impl/CloseCommand.swift
================================================
import AppKit
import Common
struct CloseCommand: Command {
let args: CloseCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
try await allowOnlyCancellationError { @MainActor @Sendable in
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err("Empty workspace")
}
// Access ax directly. Not cool :(
if try await args.quitIfLastWindow.andAsync({ @MainActor @Sendable in try await window.macAppUnsafe.getAxWindowsCount() == 1 }) {
let app = window.macAppUnsafe
if app.nsApp.terminate() {
for workspace in Workspace.all {
for window in workspace.allLeafWindowsRecursive where window.app.pid == app.pid {
(window as! MacWindow).garbageCollect(skipClosedWindowsCache: true)
}
}
return true
} else {
return io.err("Failed to quit '\(window.app.name ?? "Unknown app")'")
}
} else {
window.closeAxWindow()
return true
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ConfigCommand.swift
================================================
import AppKit
import Common
struct ConfigCommand: Command {
let args: ConfigCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
switch args.mode {
case .getKey(let key):
return getKey(io, args: args, key: key)
case .majorKeys:
let out = """
.
mode
\(config.modes.keys.map { "mode.\($0).binding" }.joined(separator: "\n"))
"""
return io.out(out)
case .allKeys:
let configMap = buildConfigMap()
var allKeys: [String] = []
configMap.dumpAllKeysRecursive(path: ".", result: &allKeys)
return io.out(allKeys.joined(separator: "\n"))
case .configPath:
return io.out(configUrl.absoluteURL.path)
}
}
}
extension String {
fileprivate func toKeyPath() -> Result<[String], String> {
if self == "." { return .success([]) }
if isEmpty { return .failure("Invalid empty key") }
if self.contains("..") { return .failure("Invalid key '\(self)'") }
if self.hasSuffix(".") { return .failure("Invalid key '\(self)'") }
return .success(self.split(separator: ".", omittingEmptySubsequences: false).map(String.init))
}
}
@MainActor private func getKey(_ io: CmdIo, args: ConfigCmdArgs, key: String) -> Bool {
let keyPath: [String]
switch key.toKeyPath() {
case .success(let _keyPath): keyPath = _keyPath
case .failure(let error):
return io.err(error)
}
var configMap: ConfigMapValue
switch buildConfigMap().find(keyPath: keyPath.slice) {
case .success(let value):
configMap = value
case .failure(let error):
return io.err(error)
}
if args.keys {
switch configMap {
case .scalar(let scalar):
return io.err("--keys flag cannot be applied to scalar object '\(scalar)'")
case .map(let map):
configMap = .array(map.keys.map { .scalar(.string($0)) })
case .array(let array):
configMap = .array((0 ..< array.count).map { .scalar(.int($0)) })
}
}
if args.json {
if let json = JSONEncoder.aeroSpaceDefault.encodeToString(configMap) {
return io.out(json)
} else {
return io.err("Can't convert json Data to String")
}
} else {
switch configMap {
case .scalar(let scalar):
return io.out(scalar.describe)
case .map:
return io.err("Complicated objects can be printed only with --json flag. " +
"Alternatively, you can try to inspect keys of the object with --keys flag")
case .array(let array):
let plainArray: Result<[String], String> = array.mapAllOrFailure {
switch $0 {
case .scalar(let scalar): .success(scalar.describe)
default: .failure("Printing array of non-string objects is supported only with --json flag." +
"Alternatively, you can try to inspect keys of the object with --keys flag")
}
}
return switch plainArray {
case .success(let array): io.out(array.sorted().joined(separator: "\n"))
case .failure(let error): io.err(error)
}
}
}
}
extension ConfigMapValue {
func find(keyPath: StrArrSlice) -> Result<ConfigMapValue, String> {
if let key = keyPath.first {
switch self {
case .scalar(let scalar):
return .failure("Can't dereference scalar value '\(scalar.describe)'")
case .map(let map):
if let child = map[key] {
return child.find(keyPath: keyPath.slice(1...).orDie())
} else {
return .failure("No value at key token '\(key)'")
}
case .array(let array):
if let key = Int(key) {
if let child = array.getOrNil(atIndex: key) {
return child.find(keyPath: keyPath.slice(1...).orDie())
} else {
return .failure("Index out of bounds. Index: \(key), Size: \(array.count)")
}
} else {
return .failure("Can't convert key token '\(key)' to Int")
}
}
} else {
return .success(self)
}
}
func dumpAllKeysRecursive(path: String, result: inout [String]) {
result.append(path)
switch self {
case .scalar: break
case .map(let map):
for (key, value) in map {
let path = path == "." ? key : path + "." + key
value.dumpAllKeysRecursive(path: path, result: &result)
}
case .array(let array):
for (index, value) in array.enumerated() {
let path = path == "." ? String(index) : path + "." + String(index)
value.dumpAllKeysRecursive(path: path, result: &result)
}
}
}
}
extension [Command] {
var prettyDescription: String {
map { $0.args.description }.joined(separator: "; ")
}
}
@MainActor func buildConfigMap() -> ConfigMapValue {
let mode = config.modes.mapValues { (mode: Mode) -> ConfigMapValue in
var keyNotationToScript: [String: ConfigMapValue] = [:]
for binding in mode.bindings.values {
keyNotationToScript[binding.descriptionWithKeyNotation] =
.scalar(.string(binding.commands.prettyDescription))
}
return .map(["binding": .map(keyNotationToScript)])
}
return .map(["mode": .map(mode)])
}
enum ConfigScalarValue: Encodable {
case string(String)
case int(Int)
var describe: String {
return switch self {
case .string(let string): string
case .int(let int): String(int)
}
}
func encode(to encoder: Encoder) throws {
let value: Encodable = switch self {
case .string(let string): string
case .int(let int): int
}
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
enum ConfigMapValue: Encodable {
case scalar(ConfigScalarValue)
case map([String: ConfigMapValue])
case array([ConfigMapValue])
func encode(to encoder: Encoder) throws {
let value: Encodable = switch self {
case .scalar(let scalar): scalar
case .map(let map): map
case .array(let array): array
}
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
================================================
FILE: Sources/AppBundle/command/impl/DebugWindowsCommand.swift
================================================
import AppKit
import Common
import OrderedCollections
private let disclaimer =
"""
!!! DISCLAIMER !!!
!!! 'debug-windows' command is not stable API. Please don't rely on the command existence and output format !!!
!!! The only intended use case is to report bugs about incorrect windows handling !!!
"""
@MainActor private var debugWindowsState: DebugWindowsState = .notRecording
@MainActor private var debugWindowsLog: OrderedDictionary<UInt32, String> = [:]
private let debugWindowsLimit = 10
enum DebugWindowsState {
case recording
case notRecording
case recordingAborted
}
struct DebugWindowsCommand: Command {
let args: DebugWindowsCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
if let windowId = args.windowId {
guard let window = Window.get(byId: windowId) else {
return io.err("Can't find window with the specified window-id: \(windowId)")
}
io.out(try await dumpWindowDebugInfo(window) + "\n")
io.out(disclaimer)
return true
}
switch debugWindowsState {
case .recording:
debugWindowsState = .notRecording
io.out(debugWindowsLog.values.joined(separator: "\n\n"))
io.out("\n" + disclaimer + "\n")
io.out("Debug session finished" + "\n")
debugWindowsLog = [:]
return true
case .notRecording:
debugWindowsState = .recording
debugWindowsLog = [:]
io.out(
"""
Debug windows session has started
1. Focus the problematic window
2. Run 'aerospace debug-windows' once again to finish the session and get the results
""",
)
// Make sure that the Terminal window that started the recording is recorded first
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
if let window = target.windowOrNil {
try await debugWindowsIfRecording(window)
}
return true
case .recordingAborted:
io.out(
"""
Recording of the previous session was aborted after \(debugWindowsLimit) windows has been focused
Run the command one more time to start new debug session
""",
)
debugWindowsState = .notRecording
debugWindowsLog = [:]
return false
}
}
}
@MainActor
private func dumpWindowDebugInfo(_ window: Window) async throws -> String {
let window = window as! MacWindow
let appInfoDic = window.macApp.nsApp.bundleURL.flatMap { Bundle.init(url: $0) }?.infoDictionary ?? [:]
var result: [String: Json] = try await window.dumpAxInfo()
let windowLevel = getWindowLevel(for: window.windowId)
let windowLevelJson = windowLevel?.toJson() ?? .null
result["Aero.windowLevel"] = windowLevelJson
result["Aero.axWindowId"] = .uint32(window.windowId)
result["Aero.workspace"] = .stringOrNull(window.nodeWorkspace?.name)
result["Aero.treeNodeParent"] = .string(String(describing: window.parent))
result["Aero.macOS.version"] = .string(ProcessInfo().operatingSystemVersionString) // because built-in apps might behave differently depending on the OS version
result["Aero.App.appBundleId"] = .stringOrNull(window.app.rawAppBundleId)
result["Aero.App.pid"] = .int(Int(window.app.pid))
result["Aero.App.versionShort"] = .stringOrNull(appInfoDic["CFBundleShortVersionString"] as? String)
result["Aero.App.version"] = .stringOrNull(appInfoDic["CFBundleVersion"] as? String)
result["Aero.App.nsApp.activationPolicy"] = .string(window.macApp.nsApp.activationPolicy.prettyDescription)
result["Aero.App.nsApp.execPath"] = .stringOrNull(window.macApp.nsApp.executableURL?.description)
result["Aero.App.nsApp.appBundlePath"] = .stringOrNull(window.macApp.nsApp.bundleURL?.description)
result["Aero.AXApp"] = .dict(try await window.macApp.dumpAppAxInfo())
let isDialog = try await window.isDialogHeuristic(windowLevel)
let isWindow = try await window.isWindowHeuristic(windowLevel)
result["Aero.AxUiElementWindowType"] = .string(AxUiElementWindowType.new(isWindow: isWindow, isDialog: { isDialog }).rawValue)
result["Aero.AxUiElementWindowType_isDialogHeuristic"] = .bool(isDialog)
var matchingCallbacks: [Json] = []
for callback in config.onWindowDetected where try await callback.matches(window) {
matchingCallbacks.append(callback.debugJson)
}
result["Aero.on-window-detected"] = .array(matchingCallbacks)
return JSONEncoder.aeroSpaceDefault.encodeToString(result).prettyDescription
.prefixLines(with: "\(window.app.rawAppBundleId ?? "nil-bundle-id").\(window.windowId) ||| ")
}
@MainActor
func debugWindowsIfRecording(_ window: Window) async throws {
switch debugWindowsState {
case .recording: break
case .notRecording, .recordingAborted: return
}
if debugWindowsLog.count > debugWindowsLimit {
debugWindowsState = .recordingAborted
debugWindowsLog = [:]
}
if debugWindowsLog.keys.contains(window.windowId) {
return
}
debugWindowsLog[window.windowId] = try await dumpWindowDebugInfo(window)
}
================================================
FILE: Sources/AppBundle/command/impl/EnableCommand.swift
================================================
import AppKit
import Common
struct EnableCommand: Command {
let args: EnableCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
let prevState = TrayMenuModel.shared.isEnabled
let newState: Bool = switch args.targetState.val {
case .on: true
case .off: false
case .toggle: !TrayMenuModel.shared.isEnabled
}
if newState == prevState {
if !args.failIfNoop {
io.out((newState ? "Already enabled" : "Already disabled") +
"Tip: use --fail-if-noop to exit with non-zero code")
}
return !args.failIfNoop
}
TrayMenuModel.shared.isEnabled = newState
if newState {
for workspace in Workspace.all {
for window in workspace.allLeafWindowsRecursive where window.isFloating {
window.lastFloatingSize = try await window.getAxSize() ?? window.lastFloatingSize
}
}
try await activateMode(mainModeId)
} else {
try await activateMode(nil)
}
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/ExecAndForgetCommand.swift
================================================
import AppKit
import Common
struct ExecAndForgetCommand: Command {
let args: ExecAndForgetCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
// todo shall exec-and-forget fork exec session?
// It doesn't throw if exit code is non-zero
let process = Process()
process.environment = config.execConfig.envVariables + env.asMap
process.executableURL = URL(filePath: "/bin/bash")
process.arguments = ["-c", args.bashScript]
return Result { try process.run() }.isSuccess
}
}
================================================
FILE: Sources/AppBundle/command/impl/FlattenWorkspaceTreeCommand.swift
================================================
import AppKit
import Common
struct FlattenWorkspaceTreeCommand: Command {
let args: FlattenWorkspaceTreeCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache: Bool = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
let workspace = target.workspace
let windows = workspace.rootTilingContainer.allLeafWindowsRecursive
for window in windows {
window.bind(to: workspace.rootTilingContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
}
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/FocusBackAndForthCommand.swift
================================================
import AppKit
import Common
struct FocusBackAndForthCommand: Command {
let args: FocusBackAndForthCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
if let prevFocus {
return setFocus(to: prevFocus)
} else {
return io.err("Prev window has been closed")
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/FocusCommand.swift
================================================
import AppKit
import Common
struct FocusCommand: Command {
let args: FocusCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
// todo bug: floating windows break mru
let floatingWindows = args.floatingAsTiling ? try await makeFloatingWindowsSeenAsTiling(workspace: target.workspace) : []
defer {
if args.floatingAsTiling {
restoreFloatingWindows(floatingWindows: floatingWindows, workspace: target.workspace)
}
}
switch args.target {
case .direction(let direction):
let window = target.windowOrNil
if let (parent, ownIndex) = window?.closestParent(hasChildrenInDirection: direction, withLayout: nil) {
guard let windowToFocus = parent.children[ownIndex + direction.focusOffset]
.findLeafWindowRecursive(snappedTo: direction.opposite) else { return false }
return windowToFocus.focusWindow()
} else {
return hitWorkspaceBoundaries(target, io, args, direction)
}
case .windowId(let windowId):
if let windowToFocus = Window.get(byId: windowId) {
return windowToFocus.focusWindow()
} else {
return io.err("Can't find window with ID \(windowId)")
}
case .dfsIndex(let dfsIndex):
if let windowToFocus = target.workspace.rootTilingContainer.allLeafWindowsRecursive.getOrNil(atIndex: Int(dfsIndex)) {
return windowToFocus.focusWindow()
} else {
return io.err("Can't find window with DFS index \(dfsIndex)")
}
case .dfsRelative(let nextPrev):
let windows = target.workspace.rootTilingContainer.allLeafWindowsRecursive
guard let currentIndex = windows.firstIndex(where: { $0 == target.windowOrNil }) else {
return false
}
var targetIndex = switch nextPrev {
case .dfsNext: currentIndex + 1
case .dfsPrev: currentIndex - 1
}
if !(0 ..< windows.count).contains(targetIndex) {
switch args.boundariesAction {
case .stop: return true
case .fail: return false
case .wrapAroundTheWorkspace: targetIndex = (targetIndex + windows.count) % windows.count
case .wrapAroundAllMonitors: return dieT("Must be discarded by args parser")
}
}
return windows[targetIndex].focusWindow()
}
}
}
@MainActor private func hitWorkspaceBoundaries(
_ target: LiveFocus,
_ io: CmdIo,
_ args: FocusCmdArgs,
_ direction: CardinalDirection,
) -> Bool {
switch args.boundaries {
case .workspace:
return switch args.boundariesAction {
case .stop: true
case .fail: false
case .wrapAroundTheWorkspace: wrapAroundTheWorkspace(target, io, direction)
case .wrapAroundAllMonitors: dieT("Must be discarded by args parser")
}
case .allMonitorsOuterFrame:
let currentMonitor = target.workspace.workspaceMonitor
guard let (monitors, index) = currentMonitor.findRelativeMonitor(inDirection: direction) else {
return io.err("Should never happen. Can't find the current monitor")
}
if let targetMonitor = monitors.getOrNil(atIndex: index) {
return targetMonitor.activeWorkspace.focusWorkspace()
} else {
guard let wrapped = monitors.get(wrappingIndex: index) else { return false }
return hitAllMonitorsOuterFrameBoundaries(target, io, args, direction, wrapped)
}
}
}
@MainActor private func hitAllMonitorsOuterFrameBoundaries(
_ target: LiveFocus,
_ io: CmdIo,
_ args: FocusCmdArgs,
_ direction: CardinalDirection,
_ wrappedMonitor: Monitor,
) -> Bool {
switch args.boundariesAction {
case .stop:
return true
case .fail:
return false
case .wrapAroundTheWorkspace:
return wrapAroundTheWorkspace(target, io, direction)
case .wrapAroundAllMonitors:
wrappedMonitor.activeWorkspace.findLeafWindowRecursive(snappedTo: direction.opposite)?.markAsMostRecentChild()
return wrappedMonitor.activeWorkspace.focusWorkspace()
}
}
@MainActor private func wrapAroundTheWorkspace(_ target: LiveFocus, _ io: CmdIo, _ direction: CardinalDirection) -> Bool {
guard let windowToFocus = target.workspace.findLeafWindowRecursive(snappedTo: direction.opposite) else {
return io.err(noWindowIsFocused)
}
return windowToFocus.focusWindow()
}
@MainActor private func makeFloatingWindowsSeenAsTiling(workspace: Workspace) async throws -> [FloatingWindowData] {
let mruBefore = workspace.mostRecentWindowRecursive
defer {
mruBefore?.markAsMostRecentChild()
}
var _floatingWindows: [FloatingWindowData] = []
for window in workspace.floatingWindows {
let center = try await window.getCenter() // todo bug: we shouldn't access ax api here. What if the window was moved but it wasn't committed to ax yet?
guard let center else { continue }
let tilingParent: TilingContainer
let index: Int
if let target = center.coerce(in: workspace.workspaceMonitor.visibleRectPaddedByOuterGaps)?
.findIn(tree: workspace.rootTilingContainer, virtual: true)
{
guard let targetCenter = try await target.getCenter() else { continue }
guard let _tilingParent = target.parent as? TilingContainer else { continue }
tilingParent = _tilingParent
index = center.getProjection(tilingParent.orientation) >= targetCenter.getProjection(tilingParent.orientation)
? target.ownIndex.orDie() + 1
: target.ownIndex.orDie()
} else {
index = 0
tilingParent = workspace.rootTilingContainer
}
let data = window.unbindFromParent()
let floatingWindowData = FloatingWindowData(
window: window,
center: center,
parent: tilingParent,
adaptiveWeight: data.adaptiveWeight,
index: index,
)
_floatingWindows.append(floatingWindowData)
}
let floatingWindows: [FloatingWindowData] = _floatingWindows.sortedBy { $0.center.getProjection($0.parent.orientation) }.reversed()
for floating in floatingWindows { // Make floating windows be seen as tiling
floating.window.bind(to: floating.parent, adaptiveWeight: 1, index: floating.index)
}
return floatingWindows
}
@MainActor private func restoreFloatingWindows(floatingWindows: [FloatingWindowData], workspace: Workspace) {
let mruBefore = workspace.mostRecentWindowRecursive
defer {
mruBefore?.markAsMostRecentChild()
}
for floating in floatingWindows {
floating.window.bind(to: workspace, adaptiveWeight: floating.adaptiveWeight, index: INDEX_BIND_LAST)
}
}
private struct FloatingWindowData {
let window: Window
let center: CGPoint
let parent: TilingContainer
let adaptiveWeight: CGFloat
let index: Int
}
extension TreeNode {
@MainActor
func findLeafWindowRecursive(snappedTo direction: CardinalDirection) -> Window? {
switch nodeCases {
case .workspace(let workspace):
return workspace.rootTilingContainer.findLeafWindowRecursive(snappedTo: direction)
case .window(let window):
return window
case .tilingContainer(let container):
if direction.orientation == container.orientation {
return (direction.isPositive ? container.children.last : container.children.first)?
.findLeafWindowRecursive(snappedTo: direction)
} else {
return mostRecentChild?.findLeafWindowRecursive(snappedTo: direction)
}
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer,
.macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer:
die("Impossible")
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/FocusMonitorCommand.swift
================================================
import AppKit
import Common
struct FocusMonitorCommand: Command {
let args: FocusMonitorCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
return switch args.target.val.resolve(target.workspace.workspaceMonitor, wrapAround: args.wrapAround) {
case .success(let targetMonitor): targetMonitor.activeWorkspace.focusWorkspace()
case .failure(let msg): io.err(msg)
}
}
}
extension MonitorTarget {
func resolve(_ currentMonitor: Monitor, wrapAround: Bool) -> Result<Monitor, String> {
switch self {
case .direction(let direction):
guard let (monitorsInDirection, index) = currentMonitor.findRelativeMonitor(inDirection: direction) else {
return .failure("Should never happen. Can't find the current monitor")
}
let targetMonitor = wrapAround ? monitorsInDirection.get(wrappingIndex: index) : monitorsInDirection.getOrNil(atIndex: index)
guard let targetMonitor else {
return .failure("No monitors in direction \(direction)")
}
return .success(targetMonitor)
case .relative(let nextPrev):
let monitors = sortedMonitors
guard let curIndex = monitors.firstIndex(where: { $0.rect.topLeftCorner == currentMonitor.rect.topLeftCorner }) else {
return .failure("Can't find current monitor")
}
let targetIndex = nextPrev == .next ? curIndex + 1 : curIndex - 1
let targetMonitor = wrapAround ? monitors.get(wrappingIndex: targetIndex) : monitors.getOrNil(atIndex: targetIndex)
guard let targetMonitor else {
return .failure("Can't find target monitor")
}
return .success(targetMonitor)
case .patterns(let patterns):
let monitors = sortedMonitors
guard let targetMonitor = patterns.lazy.compactMap({ $0.resolveMonitor(sortedMonitors: monitors) }).first else {
return .failure("None of the monitors match the pattern(s)")
}
return .success(targetMonitor)
}
}
}
extension Monitor {
func relation(to monitor: Monitor) -> Orientation {
guard let otherYRange = monitor.rect.minY.until(excl: monitor.rect.maxY) else { return .h }
guard let myYRange = rect.minY.until(excl: rect.maxY) else { return .h }
return myYRange.overlaps(otherYRange) ? .h : .v
}
func findRelativeMonitor(inDirection direction: CardinalDirection) -> (monitorsInDirection: [Monitor], index: Int)? {
let currentMonitor = self
let monitors = sortedMonitors.filter {
currentMonitor.rect.topLeftCorner == $0.rect.topLeftCorner ||
$0.relation(to: currentMonitor) == direction.orientation
}
guard let index = monitors.firstIndex(where: { $0.rect.topLeftCorner == currentMonitor.rect.topLeftCorner }) else { return nil }
return (monitors, index + direction.focusOffset)
}
}
================================================
FILE: Sources/AppBundle/command/impl/FullscreenCommand.swift
================================================
import AppKit
import Common
struct FullscreenCommand: Command {
let args: FullscreenCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
let newState: Bool = switch args.toggle {
case .on: true
case .off: false
case .toggle: !window.isFullscreen
}
if newState == window.isFullscreen {
io.err((newState ? "Already fullscreen. " : "Already not fullscreen. ") +
"Tip: use --fail-if-noop to exit with non-zero code")
return !args.failIfNoop
}
window.isFullscreen = newState
window.noOuterGapsInFullscreen = args.noOuterGaps
// Focus on its own workspace
window.markAsMostRecentChild()
return true
}
}
let noWindowIsFocused = "No window is focused"
================================================
FILE: Sources/AppBundle/command/impl/JoinWithCommand.swift
================================================
import AppKit
import Common
struct JoinWithCommand: Command {
let args: JoinWithCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
let direction = args.direction.val
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let currentWindow = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
guard let (parent, ownIndex) = currentWindow.closestParent(hasChildrenInDirection: direction, withLayout: nil) else {
return io.err("No windows in the specified direction")
}
let joinWithTarget = parent.children[ownIndex + direction.focusOffset]
let prevBinding = joinWithTarget.unbindFromParent()
let newParent = TilingContainer(
parent: parent,
adaptiveWeight: prevBinding.adaptiveWeight,
parent.orientation.opposite,
.tiles,
index: prevBinding.index,
)
currentWindow.unbindFromParent()
joinWithTarget.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: 0)
currentWindow.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: direction.isPositive ? 0 : INDEX_BIND_LAST)
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/LayoutCommand.swift
================================================
import AppKit
import Common
struct LayoutCommand: Command {
let args: LayoutCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
let targetDescription = args.toggleBetween.val.first(where: { !window.matchesDescription($0) })
?? args.toggleBetween.val.first.orDie()
if window.matchesDescription(targetDescription) { return false }
switch targetDescription {
case .h_accordion:
return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: .h, window: window)
case .v_accordion:
return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: .v, window: window)
case .h_tiles:
return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: .h, window: window)
case .v_tiles:
return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: .v, window: window)
case .accordion:
return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: nil, window: window)
case .tiles:
return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: nil, window: window)
case .horizontal:
return changeTilingLayout(io, targetLayout: nil, targetOrientation: .h, window: window)
case .vertical:
return changeTilingLayout(io, targetLayout: nil, targetOrientation: .v, window: window)
case .tiling:
guard let parent = window.parent else { return false }
switch parent.cases {
case .macosPopupWindowsContainer:
return false // Impossible
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
return io.err("Can't change layout for macOS minimized, fullscreen windows or windows or hidden apps. This behavior is subject to change")
case .tilingContainer:
return true // Nothing to do
case .workspace(let workspace):
window.lastFloatingSize = try await window.getAxSize() ?? window.lastFloatingSize
try await window.relayoutWindow(on: workspace, forceTile: true)
return true
}
case .floating:
let workspace = target.workspace
window.bindAsFloatingWindow(to: workspace)
if let size = window.lastFloatingSize { window.setAxFrame(nil, size) }
return true
}
}
}
@MainActor private func changeTilingLayout(_ io: CmdIo, targetLayout: Layout?, targetOrientation: Orientation?, window: Window) -> Bool {
guard let parent = window.parent else { return false }
switch parent.cases {
case .tilingContainer(let parent):
let targetOrientation = targetOrientation ?? parent.orientation
let targetLayout = targetLayout ?? parent.layout
parent.layout = targetLayout
parent.changeOrientation(targetOrientation)
return true
case .workspace, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer,
.macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer:
return io.err("The window is non-tiling")
}
}
extension Window {
fileprivate func matchesDescription(_ layout: LayoutCmdArgs.LayoutDescription) -> Bool {
return switch layout {
case .accordion: (parent as? TilingContainer)?.layout == .accordion
case .tiles: (parent as? TilingContainer)?.layout == .tiles
case .horizontal: (parent as? TilingContainer)?.orientation == .h
case .vertical: (parent as? TilingContainer)?.orientation == .v
case .h_accordion: (parent as? TilingContainer).map { $0.layout == .accordion && $0.orientation == .h } == true
case .v_accordion: (parent as? TilingContainer).map { $0.layout == .accordion && $0.orientation == .v } == true
case .h_tiles: (parent as? TilingContainer).map { $0.layout == .tiles && $0.orientation == .h } == true
case .v_tiles: (parent as? TilingContainer).map { $0.layout == .tiles && $0.orientation == .v } == true
case .tiling: parent is TilingContainer
case .floating: parent is Workspace
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListAppsCommand.swift
================================================
import AppKit
import Common
struct ListAppsCommand: Command {
let args: ListAppsCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
var result = Array(MacApp.allAppsMap.values)
if let hidden = args.macosHidden {
result = result.filter { $0.nsApp.isHidden == hidden }
}
if args.outputOnlyCount {
return io.out("\(result.count)")
} else {
let list = result.map { AeroObj.app($0) }
if args.json {
return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
case .success(let json): io.out(json)
case .failure(let msg): io.err(msg)
}
} else {
return switch list.format(args.format) {
case .success(let lines): io.out(lines)
case .failure(let msg): io.err(msg)
}
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListExecEnvVarsCommand.swift
================================================
import AppKit
import Common
struct ListExecEnvVarsCommand: Command {
let args: ListExecEnvVarsCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
for (key, value) in config.execConfig.envVariables {
io.out("\(key)=\(value)")
}
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListModesCommand.swift
================================================
import AppKit
import Common
struct ListModesCommand: Command {
let args: ListModesCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
let modes: [String] = args.current ? [activeMode ?? mainModeId] : config.modes.keys.sorted()
return switch true {
case args.outputOnlyCount:
io.out("\(modes.count)")
case args.json:
JSONEncoder.aeroSpaceDefault.encodeToString(modes.map { ["mode-id": $0] }).map(io.out)
?? io.err("Failed to encode JSON")
default:
io.out(modes)
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListMonitorsCommand.swift
================================================
import AppKit
import Common
struct ListMonitorsCommand: Command {
let args: ListMonitorsCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
let focus = focus
var result = sortedMonitors
if let focused = args.focused {
result = result.filter { (monitor) in (monitor.activeWorkspace == focus.workspace) == focused }
}
if let mouse = args.mouse {
let mouseWorkspace = mouseLocation.monitorApproximation.activeWorkspace
result = result.filter { (monitor) in (monitor.activeWorkspace == mouseWorkspace) == mouse }
}
if args.outputOnlyCount {
return io.out("\(result.count)")
} else {
let list = result.map { AeroObj.monitor($0) }
if args.json {
return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
case .success(let json): io.out(json)
case .failure(let msg): io.err(msg)
}
} else {
return switch list.format(args.format) {
case .success(let lines): io.out(lines)
case .failure(let msg): io.err(msg)
}
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListWindowsCommand.swift
================================================
import AppKit
import Common
struct ListWindowsCommand: Command {
let args: ListWindowsCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
let focus = focus
var windows: [Window] = []
if args.filteringOptions.focused {
if let window = focus.windowOrNil {
windows = [window]
} else {
return io.err(noWindowIsFocused)
}
} else {
var workspaces: Set<Workspace> = args.filteringOptions.workspaces.isEmpty
? Workspace.all.toSet()
: args.filteringOptions.workspaces
.flatMap { filter in
switch filter {
case .focused: [focus.workspace]
case .visible: Workspace.all.filter(\.isVisible)
case .name(let name): [Workspace.get(byName: name.raw)]
}
}
.toSet()
if !args.filteringOptions.monitors.isEmpty {
let monitors: Set<CGPoint> = args.filteringOptions.monitors.resolveMonitors(io)
if monitors.isEmpty { return false }
workspaces = workspaces.filter { monitors.contains($0.workspaceMonitor.rect.topLeftCorner) }
}
windows = workspaces.flatMap(\.allLeafWindowsRecursive)
if let pid = args.filteringOptions.pidFilter {
windows = windows.filter { $0.app.pid == pid }
}
if let appId = args.filteringOptions.appIdFilter {
windows = windows.filter { $0.app.rawAppBundleId == appId }
}
}
if args.outputOnlyCount {
return io.out("\(windows.count)")
} else {
var _list: [(window: Window, title: String)] = [] // todo cleanup
for window in windows {
_list.append((window, try await window.title))
}
_list = _list.filter { $0.window.isBound }
_list = _list.sortedBy([{ $0.window.app.name ?? "" }, \.title])
let list = _list.map { AeroObj.window(window: $0.window, title: $0.title) }
if args.json {
return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
case .success(let json): io.out(json)
case .failure(let msg): io.err(msg)
}
} else {
return switch list.format(args.format) {
case .success(let lines): io.out(lines)
case .failure(let msg): io.err(msg)
}
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ListWorkspacesCommand.swift
================================================
import AppKit
import Common
struct ListWorkspacesCommand: Command {
let args: ListWorkspacesCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
var result: [Workspace] = Workspace.all
if let visible = args.filteringOptions.visible {
result = result.filter { $0.isVisible == visible }
}
if !args.filteringOptions.onMonitors.isEmpty {
let monitors: Set<CGPoint> = args.filteringOptions.onMonitors.resolveMonitors(io)
if monitors.isEmpty { return false }
result = result.filter { monitors.contains($0.workspaceMonitor.rect.topLeftCorner) }
}
if let empty = args.filteringOptions.empty {
result = result.filter { $0.isEffectivelyEmpty == empty }
}
if args.outputOnlyCount {
return io.out("\(result.count)")
} else {
let list = result.map { AeroObj.workspace($0) }
if args.json {
return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
case .success(let json): io.out(json)
case .failure(let msg): io.err(msg)
}
} else {
return switch list.format(args.format) {
case .success(let lines): io.out(lines)
case .failure(let msg): io.err(msg)
}
}
}
}
}
extension [MonitorId] {
@MainActor func resolveMonitors(_ io: CmdIo) -> Set<CGPoint> {
var requested: Set<CGPoint> = []
let sortedMonitors = sortedMonitors
for id in self {
let resolved = id.resolve(io, sortedMonitors: sortedMonitors)
if resolved.isEmpty {
return []
}
for monitor in resolved {
requested.insert(monitor.rect.topLeftCorner)
}
}
return requested
}
}
extension MonitorId {
@MainActor func resolve(_ io: CmdIo, sortedMonitors: [Monitor]) -> [Monitor] {
switch self {
case .focused:
return [focus.workspace.workspaceMonitor]
case .mouse:
return [mouseLocation.monitorApproximation]
case .all:
return monitors
case .index(let index):
if let monitor = sortedMonitors.getOrNil(atIndex: index) {
return [monitor]
} else {
io.err("Invalid monitor ID: \(index + 1)")
return []
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/MacosNativeFullscreenCommand.swift
================================================
import AppKit
import Common
/// Problem ID-B6E178F2: It's not first-class citizen command in AeroSpace model, since it interacts with macOS API directly.
/// Consecutive macos-native-fullscreen commands may not works as expected (because macOS may report correct state with a
/// delay), or may flicker
///
/// The same applies to macos-native-minimize command
struct MacosNativeFullscreenCommand: Command {
let args: MacosNativeFullscreenCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
let prevState = try await window.isMacosFullscreen
let newState: Bool = switch args.toggle {
case .on: true
case .off: false
case .toggle: !prevState
}
if newState == prevState {
if !args.failIfNoop {
io.err((newState ? "Already fullscreen. " : "Already not fullscreen. ") +
"Tip: use --fail-if-noop to exit with non-zero exit code")
}
return !args.failIfNoop
}
window.asMacWindow().setNativeFullscreen(newState)
guard let workspace = window.visualWorkspace else {
return io.err(windowIsntPartOfTree(window))
}
if newState { // Enter fullscreen
window.bind(to: workspace.macOsNativeFullscreenWindowsContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
} else { // Exit fullscreen
switch window.layoutReason {
case .macos(let prevParentKind):
try await exitMacOsNativeUnconventionalState(window: window, prevParentKind: prevParentKind, workspace: workspace)
default:
try await window.relayoutWindow(on: workspace)
}
}
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/MacosNativeMinimizeCommand.swift
================================================
import AppKit
import Common
/// See: MacosNativeFullscreenCommand. Problem ID-B6E178F2
struct MacosNativeMinimizeCommand: Command {
let args: MacosNativeMinimizeCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
// resolveTargetOrReportError on already minimized windows will always fail
// It would be easier if minimized windows were part of the workspace in tree hierarchy
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
let newState: Bool = try await !window.isMacosMinimized
window.asMacWindow().setNativeMinimized(newState)
if newState { // minimize
window.bind(to: macosMinimizedWindowsContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
return true
} else { // unminimize
return io.err("The command is uncapable of unminimizing windows yet. Sorry") // dead code. should never be possible, see the comment above
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ModeCommand.swift
================================================
import AppKit
import Common
struct ModeCommand: Command {
let args: ModeCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
try await activateMode(args.targetMode.val)
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/MoveCommand.swift
================================================
import AppKit
import Common
struct MoveCommand: Command {
let args: MoveCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
let direction = args.direction.val
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let currentWindow = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
guard let parent = currentWindow.parent else { return false }
switch parent.cases {
case .tilingContainer(let parent):
let indexOfCurrent = currentWindow.ownIndex.orDie()
let indexOfSiblingTarget = indexOfCurrent + direction.focusOffset
if parent.orientation == direction.orientation && parent.children.indices.contains(indexOfSiblingTarget) {
switch parent.children[indexOfSiblingTarget].tilingTreeNodeCasesOrDie() {
case .tilingContainer(let topLevelSiblingTargetContainer):
return deepMoveIn(window: currentWindow, into: topLevelSiblingTargetContainer, moveDirection: direction)
case .window: // "swap windows"
let prevBinding = currentWindow.unbindFromParent()
currentWindow.bind(to: parent, adaptiveWeight: prevBinding.adaptiveWeight, index: indexOfSiblingTarget)
return true
}
} else {
return moveOut(window: currentWindow, direction: direction, io, args, env)
}
case .workspace: // floating window
return io.err("moving floating windows isn't yet supported") // todo
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
return io.err(moveOutMacosUnconventionalWindow)
case .macosPopupWindowsContainer:
return false // Impossible
}
}
}
@MainActor private func hitWorkspaceBoundaries(
_ window: Window,
_ workspace: Workspace,
_ io: CmdIo,
_ args: MoveCmdArgs,
_ direction: CardinalDirection,
_ env: CmdEnv,
) -> Bool {
switch args.boundaries {
case .workspace:
switch args.boundariesAction {
case .stop: return true
case .fail: return false
case .createImplicitContainer:
createImplicitContainerAndMoveWindow(window, workspace, direction)
return true
}
case .allMonitorsOuterFrame:
guard let (monitors, index) = window.nodeMonitor?.findRelativeMonitor(inDirection: direction) else {
return io.err("Should never happen. Can't find the current monitor")
}
if monitors.indices.contains(index) {
let moveNodeToMonitorArgs = MoveNodeToMonitorCmdArgs(target: .direction(direction))
.copy(\.windowId, window.windowId)
.copy(\.focusFollowsWindow, focus.windowOrNil == window)
return MoveNodeToMonitorCommand(args: moveNodeToMonitorArgs).run(env, io)
} else {
return hitAllMonitorsOuterFrameBoundaries(window, workspace, args, direction)
}
}
}
@MainActor private func hitAllMonitorsOuterFrameBoundaries(
_ window: Window,
_ workspace: Workspace,
_ args: MoveCmdArgs,
_ direction: CardinalDirection,
) -> Bool {
switch args.boundariesAction {
case .stop: return true
case .fail: return false
case .createImplicitContainer:
createImplicitContainerAndMoveWindow(window, workspace, direction)
return true
}
}
private let moveOutMacosUnconventionalWindow = "moving macOS fullscreen, minimized windows and windows of hidden apps isn't yet supported. This behavior is subject to change"
@MainActor private func moveOut(
window: Window,
direction: CardinalDirection,
_ io: CmdIo,
_ args: MoveCmdArgs,
_ env: CmdEnv,
) -> Bool {
let innerMostChild = window.parents.first(where: {
return switch $0.parent?.cases {
case .tilingContainer(let parent): parent.orientation == direction.orientation
// Stop searching
case .workspace, .macosMinimizedWindowsContainer, nil, .macosFullscreenWindowsContainer,
.macosHiddenAppsWindowsContainer, .macosPopupWindowsContainer: true
}
}) as? TilingContainer
guard let innerMostChild else { return false }
guard let parent = innerMostChild.parent else { return false }
switch parent.cases {
case .tilingContainer(let parent):
check(parent.orientation == direction.orientation)
guard let ownIndex = innerMostChild.ownIndex else { return false }
window.bind(to: parent, adaptiveWeight: WEIGHT_AUTO, index: ownIndex + direction.insertionOffset)
return true
case .workspace(let parent):
return hitWorkspaceBoundaries(window, parent, io, args, direction, env)
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
return io.err(moveOutMacosUnconventionalWindow)
case .macosPopupWindowsContainer:
return false // Impossible
}
}
@MainActor private func createImplicitContainerAndMoveWindow(
_ window: Window,
_ workspace: Workspace,
_ direction: CardinalDirection,
) {
let prevRoot = workspace.rootTilingContainer
prevRoot.unbindFromParent()
// Force tiles layout
_ = TilingContainer(parent: workspace, adaptiveWeight: WEIGHT_AUTO, direction.orientation, .tiles, index: 0)
check(prevRoot != workspace.rootTilingContainer)
prevRoot.bind(to: workspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: 0)
window.bind(to: workspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: direction.insertionOffset)
}
@MainActor private func deepMoveIn(window: Window, into container: TilingContainer, moveDirection: CardinalDirection) -> Bool {
let deepTarget = container.tilingTreeNodeCasesOrDie().findDeepMoveInTargetRecursive(moveDirection.orientation)
switch deepTarget {
case .tilingContainer(let deepTarget):
window.bind(to: deepTarget, adaptiveWeight: WEIGHT_AUTO, index: 0)
case .window(let deepTarget):
guard let parent = deepTarget.parent as? TilingContainer else { return false }
window.bind(
to: parent,
adaptiveWeight: WEIGHT_AUTO,
index: deepTarget.ownIndex.orDie() + 1,
)
}
return true
}
extension TilingTreeNodeCases {
@MainActor fileprivate func findDeepMoveInTargetRecursive(_ orientation: Orientation) -> TilingTreeNodeCases {
return switch self {
case .window:
self
case .tilingContainer(let container):
if container.orientation == orientation {
.tilingContainer(container)
} else {
container.mostRecentChild.orDie("Empty containers must be detached during normalization")
.tilingTreeNodeCasesOrDie()
.findDeepMoveInTargetRecursive(orientation)
}
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/MoveMouseCommand.swift
================================================
import AppKit
import Common
struct MoveMouseCommand: Command {
let args: MoveMouseCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
let mouse = mouseLocation
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
switch args.mouseTarget.val {
case .windowLazyCenter:
guard let rect = try await windowSubjectRectOrReportError(target, io) else { return false }
if rect.contains(mouse) {
if !args.failIfNoop {
io.err("The mouse already belongs to the window. Tip: use --fail-if-noop to exit with non-zero code")
}
return !args.failIfNoop
}
return moveMouse(io, rect.center)
case .windowForceCenter:
guard let rect = try await windowSubjectRectOrReportError(target, io) else { return false }
return moveMouse(io, rect.center)
case .monitorLazyCenter:
let rect = target.workspace.workspaceMonitor.rect
if rect.contains(mouse) {
if !args.failIfNoop {
io.err("The mouse already belongs to the monitor. Tip: use --fail-if-noop to exit with non-zero code")
}
return !args.failIfNoop
}
return moveMouse(io, rect.center)
case .monitorForceCenter:
return moveMouse(io, target.workspace.workspaceMonitor.rect.center)
}
}
}
private func moveMouse(_ io: CmdIo, _ point: CGPoint) -> Bool {
let event = CGEvent(
mouseEventSource: nil,
mouseType: CGEventType.mouseMoved,
mouseCursorPosition: point,
mouseButton: CGMouseButton.left,
)
if let event {
event.post(tap: CGEventTapLocation.cghidEventTap)
return true
} else {
return io.err("Failed to move mouse")
}
}
@MainActor
private func windowSubjectRectOrReportError(_ target: LiveFocus, _ io: CmdIo) async throws -> Rect? {
// todo bug it's bad that we operate on the "ax physical" state directly. command seq won't work correctly
// focus <direction> command has the similar problem
if let window: Window = target.windowOrNil {
if let rect = window.lastAppliedLayoutPhysicalRect {
return rect
} else if let rect = try await window.getAxRect() {
return rect
} else {
io.err("Failed to get rect of window '\(window.windowId)'")
return nil
}
} else {
io.err(noWindowIsFocused)
return nil
}
}
================================================
FILE: Sources/AppBundle/command/impl/MoveNodeToMonitorCommand.swift
================================================
import AppKit
import Common
struct MoveNodeToMonitorCommand: Command {
let args: MoveNodeToMonitorCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
guard let currentMonitor = window.nodeMonitor else {
return io.err(windowIsntPartOfTree(window))
}
switch args.target.val.resolve(currentMonitor, wrapAround: args.wrapAround) {
case .success(let targetMonitor):
let targetWs = targetMonitor.activeWorkspace
let index = true == args.target.val.directionOrNil
.map { dir in dir.isPositive && targetWs.rootTilingContainer.orientation == dir.orientation }
? 0
: INDEX_BIND_LAST
return moveWindowToWorkspace(
window,
targetWs,
io,
focusFollowsWindow: args.focusFollowsWindow,
failIfNoop: args.failIfNoop,
index: index,
)
case .failure(let msg):
return io.err(msg)
}
}
}
func windowIsntPartOfTree(_ window: Window) -> String {
"Window \(window.windowId) is not part of tree (minimized or hidden)"
}
================================================
FILE: Sources/AppBundle/command/impl/MoveNodeToWorkspaceCommand.swift
================================================
import Common
struct MoveNodeToWorkspaceCommand: Command {
let args: MoveNodeToWorkspaceCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache: Bool = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else { return io.err(noWindowIsFocused) }
let subjectWs = window.nodeWorkspace
let targetWorkspace: Workspace
switch args.target.val {
case .relative(let nextPrev):
guard let subjectWs else { return io.err("Window \(window.windowId) doesn't belong to any workspace") }
let ws = getNextPrevWorkspace(
current: subjectWs,
isNext: nextPrev == .next,
wrapAround: args.wrapAround,
stdin: args.useStdin ? io.readStdin() : nil,
target: target,
)
guard let ws else { return io.err("Can't resolve next or prev workspace") }
targetWorkspace = ws
case .direct(let name):
targetWorkspace = Workspace.get(byName: name.raw)
}
return moveWindowToWorkspace(window, targetWorkspace, io, focusFollowsWindow: args.focusFollowsWindow, failIfNoop: args.failIfNoop)
}
}
@MainActor
func moveWindowToWorkspace(_ window: Window, _ targetWorkspace: Workspace, _ io: CmdIo, focusFollowsWindow: Bool, failIfNoop: Bool, index: Int = INDEX_BIND_LAST) -> Bool {
if window.nodeWorkspace == targetWorkspace {
if !failIfNoop {
io.err("Window '\(window.windowId)' already belongs to workspace '\(targetWorkspace.name)'. Tip: use --fail-if-noop to exit with non-zero code")
}
return !failIfNoop
}
let targetContainer: NonLeafTreeNodeObject = window.isFloating ? targetWorkspace : targetWorkspace.rootTilingContainer
window.bind(to: targetContainer, adaptiveWeight: WEIGHT_AUTO, index: index)
return focusFollowsWindow ? window.focusWindow() : true
}
================================================
FILE: Sources/AppBundle/command/impl/MoveWorkspaceToMonitorCommand.swift
================================================
import AppKit
import Common
struct MoveWorkspaceToMonitorCommand: Command {
let args: MoveWorkspaceToMonitorCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
let focusedWorkspace = target.workspace
let prevMonitor = focusedWorkspace.workspaceMonitor
switch args.target.val.resolve(target.workspace.workspaceMonitor, wrapAround: args.wrapAround) {
case .success(let targetMonitor):
if targetMonitor.monitorId_oneBased == prevMonitor.monitorId_oneBased {
return true
}
if targetMonitor.setActiveWorkspace(focusedWorkspace) {
let stubWorkspace = getStubWorkspace(for: prevMonitor)
check(
prevMonitor.setActiveWorkspace(stubWorkspace),
"getStubWorkspace generated incompatible stub workspace (\(stubWorkspace)) for the monitor (\(prevMonitor)",
)
return true
} else {
return io.err(
"Can't move workspace '\(focusedWorkspace.name)' to monitor '\(targetMonitor.name)'. workspace-to-monitor-force-assignment doesn't allow it",
)
}
case .failure(let msg):
return io.err(msg)
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/ReloadConfigCommand.swift
================================================
import AppKit
import Common
struct ReloadConfigCommand: Command {
let args: ReloadConfigCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = false
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
var stdout = ""
let isOk = try await reloadConfig(args: args, stdout: &stdout)
if !stdout.isEmpty {
io.out(stdout)
}
return isOk
}
}
@MainActor func reloadConfig(forceConfigUrl: URL? = nil) async throws -> Bool {
var devNull = ""
return try await reloadConfig(forceConfigUrl: forceConfigUrl, stdout: &devNull)
}
@MainActor func reloadConfig(
args: ReloadConfigCmdArgs = ReloadConfigCmdArgs(rawArgs: []),
forceConfigUrl: URL? = nil,
stdout: inout String,
) async throws -> Bool {
let result: Bool
switch readConfig(forceConfigUrl: forceConfigUrl) {
case .success(let (parsedConfig, url)):
if !args.dryRun {
resetHotKeys()
config = parsedConfig
configUrl = url
try await activateMode(activeMode)
syncStartAtLogin()
MessageModel.shared.message = nil
}
result = true
case .failure(let msg):
stdout.append(msg)
if !args.noGui {
Task { @MainActor in
MessageModel.shared.message = Message(description: "AeroSpace Config Error", body: msg)
}
}
result = false
}
if !args.dryRun {
syncConfigFileWatcher()
}
return result
}
================================================
FILE: Sources/AppBundle/command/impl/ResizeCommand.swift
================================================
import AppKit
import Common
struct ResizeCommand: Command { // todo cover with tests
let args: ResizeCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
let candidates = target.windowOrNil?.parentsWithSelf
.filter { ($0.parent as? TilingContainer)?.layout == .tiles }
?? []
let orientation: Orientation?
let parent: TilingContainer?
let node: TreeNode?
switch args.dimension.val {
case .width:
orientation = .h
node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
parent = node?.parent as? TilingContainer
case .height:
orientation = .v
node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
parent = node?.parent as? TilingContainer
case .smart:
node = candidates.first
parent = node?.parent as? TilingContainer
orientation = parent?.orientation
case .smartOpposite:
orientation = (candidates.first?.parent as? TilingContainer)?.orientation.opposite
node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
parent = node?.parent as? TilingContainer
}
guard let parent else { return io.err("resize command doesn't support floating windows yet https://github.com/nikitabobko/AeroSpace/issues/9") }
guard let orientation else { return false }
guard let node else { return false }
let diff: CGFloat = switch args.units.val {
case .set(let unit): CGFloat(unit) - node.getWeight(orientation)
case .add(let unit): CGFloat(unit)
case .subtract(let unit): -CGFloat(unit)
}
guard let childDiff = diff.div(parent.children.count - 1) else { return false }
parent.children.lazy
.filter { $0 != node }
.forEach { $0.setWeight(parent.orientation, $0.getWeight(parent.orientation) - childDiff) }
node.setWeight(orientation, node.getWeight(orientation) + diff)
return true
}
}
================================================
FILE: Sources/AppBundle/command/impl/SplitCommand.swift
================================================
import AppKit
import Common
struct SplitCommand: Command {
let args: SplitCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
if config.enableNormalizationFlattenContainers {
return io.err("'split' has no effect when 'enable-normalization-flatten-containers' normalization enabled. My recommendation: keep the normalizations enabled, and prefer 'join-with' over 'split'.")
}
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
guard let window = target.windowOrNil else {
return io.err(noWindowIsFocused)
}
guard let parent = window.parent else { return false }
switch parent.cases {
case .workspace:
// Nothing to do for floating and macOS native fullscreen windows
return io.err("Can't split floating windows")
case .tilingContainer(let parent):
let orientation: Orientation = switch args.arg.val {
case .vertical: .v
case .horizontal: .h
case .opposite: parent.orientation.opposite
}
if parent.children.count == 1 {
parent.changeOrientation(orientation)
} else {
let data = window.unbindFromParent()
let newParent = TilingContainer(
parent: parent,
adaptiveWeight: data.adaptiveWeight,
orientation,
.tiles,
index: data.index,
)
window.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: 0)
}
return true
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
return io.err("Can't split macos fullscreen, minimized windows and windows of hidden apps. This behavior may change in the future")
case .macosPopupWindowsContainer:
return false // Impossible
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/SummonWorkspaceCommand.swift
================================================
import AppKit
import Common
struct SummonWorkspaceCommand: Command {
let args: SummonWorkspaceCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache = true
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
let workspace = Workspace.get(byName: args.target.val.raw)
let monitor = focus.workspace.workspaceMonitor
if monitor.activeWorkspace == workspace {
if !args.failIfNoop {
io.err("Workspace '\(workspace.name)' is already visible on the focused monitor. Tip: use --fail-if-noop to exit with non-zero code")
}
return !args.failIfNoop
}
if monitor.setActiveWorkspace(workspace) {
return workspace.focusWorkspace()
} else {
return io.err("Can't move workspace '\(workspace.name)' to monitor '\(monitor.name)'. workspace-to-monitor-force-assignment doesn't allow it")
}
}
}
================================================
FILE: Sources/AppBundle/command/impl/SwapCommand.swift
================================================
import AppKit
import Common
struct SwapCommand: Command {
let args: SwapCmdArgs
/*conforms*/ let shouldResetClosedWindowsCache: Bool = true
func r
gitextract_klfaytgz/
├── .bundle/
│ └── config
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── DISCUSSION_TEMPLATE/
│ │ └── potential-bugs.yml
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── config.yml
│ │ └── new-issue.yml
│ ├── gh-actions-runner-xcode-select.sh
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build.yml
│ └── close-third-party-issues.yml
├── .gitignore
├── .swift-version
├── .swiftformat
├── .swiftlint.yml
├── AeroSpace.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ └── contents.xcworkspacedata
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── Package.resolved
├── Package.swift
├── README.md
├── ShellParserGenerated/
│ ├── .gitignore
│ ├── Package.swift
│ └── Sources/
│ └── ShellParserGenerated/
│ ├── ShellLexer.swift
│ └── ShellParser.swift
├── Sources/
│ ├── AeroSpaceApp/
│ │ └── AeroSpaceApp.swift
│ ├── AppBundle/
│ │ ├── GlobalObserver.swift
│ │ ├── command/
│ │ │ ├── CmdEnv.swift
│ │ │ ├── CmdIo.swift
│ │ │ ├── Command.swift
│ │ │ ├── cmdManifest.swift
│ │ │ ├── cmdResolveTargetOrReportError.swift
│ │ │ ├── format.swift
│ │ │ ├── formatToJson.swift
│ │ │ ├── impl/
│ │ │ │ ├── BalanceSizesCommand.swift
│ │ │ │ ├── CloseAllWindowsButCurrentCommand.swift
│ │ │ │ ├── CloseCommand.swift
│ │ │ │ ├── ConfigCommand.swift
│ │ │ │ ├── DebugWindowsCommand.swift
│ │ │ │ ├── EnableCommand.swift
│ │ │ │ ├── ExecAndForgetCommand.swift
│ │ │ │ ├── FlattenWorkspaceTreeCommand.swift
│ │ │ │ ├── FocusBackAndForthCommand.swift
│ │ │ │ ├── FocusCommand.swift
│ │ │ │ ├── FocusMonitorCommand.swift
│ │ │ │ ├── FullscreenCommand.swift
│ │ │ │ ├── JoinWithCommand.swift
│ │ │ │ ├── LayoutCommand.swift
│ │ │ │ ├── ListAppsCommand.swift
│ │ │ │ ├── ListExecEnvVarsCommand.swift
│ │ │ │ ├── ListModesCommand.swift
│ │ │ │ ├── ListMonitorsCommand.swift
│ │ │ │ ├── ListWindowsCommand.swift
│ │ │ │ ├── ListWorkspacesCommand.swift
│ │ │ │ ├── MacosNativeFullscreenCommand.swift
│ │ │ │ ├── MacosNativeMinimizeCommand.swift
│ │ │ │ ├── ModeCommand.swift
│ │ │ │ ├── MoveCommand.swift
│ │ │ │ ├── MoveMouseCommand.swift
│ │ │ │ ├── MoveNodeToMonitorCommand.swift
│ │ │ │ ├── MoveNodeToWorkspaceCommand.swift
│ │ │ │ ├── MoveWorkspaceToMonitorCommand.swift
│ │ │ │ ├── ReloadConfigCommand.swift
│ │ │ │ ├── ResizeCommand.swift
│ │ │ │ ├── SplitCommand.swift
│ │ │ │ ├── SummonWorkspaceCommand.swift
│ │ │ │ ├── SwapCommand.swift
│ │ │ │ ├── TriggerBindingCommand.swift
│ │ │ │ ├── VolumeCommand.swift
│ │ │ │ ├── WorkspaceBackAndForthCommand.swift
│ │ │ │ └── WorkspaceCommand.swift
│ │ │ └── parseCommand.swift
│ │ ├── config/
│ │ │ ├── Config.swift
│ │ │ ├── ConfigFile.swift
│ │ │ ├── ConfigFileWatcher.swift
│ │ │ ├── DynamicConfigValue.swift
│ │ │ ├── HotkeyBinding.swift
│ │ │ ├── Mode.swift
│ │ │ ├── keysMap.swift
│ │ │ ├── parseConfig.swift
│ │ │ ├── parseExecEnvVariables.swift
│ │ │ ├── parseGaps.swift
│ │ │ ├── parseKeyMapping.swift
│ │ │ ├── parseOnWindowDetected.swift
│ │ │ ├── parseWorkspaceToMonitorAssignment.swift
│ │ │ └── startAtLogin.swift
│ │ ├── focus.swift
│ │ ├── focusCache.swift
│ │ ├── getNativeFocusedWindow.swift
│ │ ├── initAppBundle.swift
│ │ ├── layout/
│ │ │ ├── layoutRecursive.swift
│ │ │ └── refresh.swift
│ │ ├── model/
│ │ │ ├── AxUiElementWindowType.swift
│ │ │ ├── Json.swift
│ │ │ ├── KnownBundleId.swift
│ │ │ ├── Monitor.swift
│ │ │ ├── MonitorDescriptionEx.swift
│ │ │ ├── MonitorEx.swift
│ │ │ ├── Rect.swift
│ │ │ └── ServerEvent.swift
│ │ ├── mouse/
│ │ │ ├── mouse.swift
│ │ │ ├── moveWithMouse.swift
│ │ │ └── resizeWithMouse.swift
│ │ ├── normalizeLayoutReason.swift
│ │ ├── runLoop.swift
│ │ ├── server.swift
│ │ ├── shell/
│ │ │ └── Shell.swift
│ │ ├── subscriptions.swift
│ │ ├── tree/
│ │ │ ├── AbstractApp.swift
│ │ │ ├── MacApp.swift
│ │ │ ├── MacWindow.swift
│ │ │ ├── MacosUnconventionalWindowsContainer.swift
│ │ │ ├── TilingContainer.swift
│ │ │ ├── TreeNode.swift
│ │ │ ├── TreeNodeCases.swift
│ │ │ ├── TreeNodeEx.swift
│ │ │ ├── Window.swift
│ │ │ ├── Workspace.swift
│ │ │ ├── WorkspaceEx.swift
│ │ │ ├── frozen/
│ │ │ │ ├── FrozenTreeNode.swift
│ │ │ │ ├── FrozenWorld.swift
│ │ │ │ └── closedWindowsCache.swift
│ │ │ └── normalizeContainers.swift
│ │ ├── ui/
│ │ │ ├── AppearanceTheme.swift
│ │ │ ├── ExperimentalUISettings.swift
│ │ │ ├── MenuBar.swift
│ │ │ ├── MenuBarLabel.swift
│ │ │ ├── MessageView.swift
│ │ │ ├── NSPanelHud.swift
│ │ │ ├── SecureInputView.swift
│ │ │ ├── TrayMenuModel.swift
│ │ │ └── VolumeView.swift
│ │ ├── util/
│ │ │ ├── ArrayEx.swift
│ │ │ ├── AwaitableOneTimeBroadcastLatch.swift
│ │ │ ├── AxSubscription.swift
│ │ │ ├── AxUiElementMock.swift
│ │ │ ├── LazySequenceProtocolEx.swift
│ │ │ ├── MruStack.swift
│ │ │ ├── NSRunningApplicationEx.swift
│ │ │ ├── NsApplicationEx.swift
│ │ │ ├── SetEx.swift
│ │ │ ├── ThreadGuardedValue.swift
│ │ │ ├── UniqueToken.swift
│ │ │ ├── accessibility.swift
│ │ │ ├── appBundleUtil.swift
│ │ │ ├── axTrustedCheckOptionPrompt.swift
│ │ │ └── dumpAxRecursive.swift
│ │ └── windowLevelCache.swift
│ ├── AppBundleTests/
│ │ ├── AxUiElementWindowTypeTest.swift
│ │ ├── assert.swift
│ │ ├── command/
│ │ │ ├── BalanceSizesCommandTest.swift
│ │ │ ├── CloseCommandTest.swift
│ │ │ ├── ExecCommandTest.swift
│ │ │ ├── FlattenWorkspaceTreeCommandTest.swift
│ │ │ ├── FocusCommandTest.swift
│ │ │ ├── JoinWithCommandTest.swift
│ │ │ ├── ListAppsTest.swift
│ │ │ ├── ListModesTest.swift
│ │ │ ├── ListMonitorsTest.swift
│ │ │ ├── ListWindowsTest.swift
│ │ │ ├── ListWorkspacesTest.swift
│ │ │ ├── MoveCommandTest.swift
│ │ │ ├── MoveNodeToMonitorCommandTest.swift
│ │ │ ├── MoveNodeToWorkspaceCommandTest.swift
│ │ │ ├── ResizeCommandTest.swift
│ │ │ ├── SplitCommandTest.swift
│ │ │ ├── SubscribeCmdArgsTest.swift
│ │ │ ├── SummonWorkspaceCommandTest.swift
│ │ │ ├── SwapCommandTest.swift
│ │ │ └── WorkspaceCommandTest.swift
│ │ ├── config/
│ │ │ ├── ConfigTest.swift
│ │ │ ├── ParseEnvVariablesTest.swift
│ │ │ └── SplitArgsTest.swift
│ │ ├── model/
│ │ │ └── ClientServerTest.swift
│ │ ├── shell/
│ │ │ └── ShellTest.swift
│ │ ├── testExtensions.swift
│ │ ├── testUtil.swift
│ │ └── tree/
│ │ ├── TestApp.swift
│ │ ├── TestWindow.swift
│ │ ├── TilingContainer.swift
│ │ └── TreeNodeTest.swift
│ ├── Cli/
│ │ ├── _main.swift
│ │ ├── cliUtil.swift
│ │ └── subcommandDescriptionsGenerated.swift
│ ├── Common/
│ │ ├── appMetadata.swift
│ │ ├── cmdArgs/
│ │ │ ├── ArgParser.swift
│ │ │ ├── ArgParserInput.swift
│ │ │ ├── SubArgParser.swift
│ │ │ ├── cmdArgsManifest.swift
│ │ │ ├── impl/
│ │ │ │ ├── BalanceSizesCmdArgs.swift
│ │ │ │ ├── CloseAllWindowsButCurrentCmdArgs.swift
│ │ │ │ ├── CloseCmdArgs.swift
│ │ │ │ ├── ConfigCmdArgs.swift
│ │ │ │ ├── DebugWindowsCmdArgs.swift
│ │ │ │ ├── EnableCmdArgs.swift
│ │ │ │ ├── ExecAndForgetCmdArgs.swift
│ │ │ │ ├── FlattenWorkspaceTreeCmdArgs.swift
│ │ │ │ ├── FocusBackAndForthCmdArgs.swift
│ │ │ │ ├── FocusCmdArgs.swift
│ │ │ │ ├── FocusMonitorCmdArgs.swift
│ │ │ │ ├── FullscreenCmdArgs.swift
│ │ │ │ ├── JoinWithCmdArgs.swift
│ │ │ │ ├── LayoutCmdArgs.swift
│ │ │ │ ├── ListAppsCmdArgs.swift
│ │ │ │ ├── ListExecEnvVarsCmdArgs.swift
│ │ │ │ ├── ListModesCmdArgs.swift
│ │ │ │ ├── ListMonitorsCmdArgs.swift
│ │ │ │ ├── ListWindowsCmdArgs.swift
│ │ │ │ ├── ListWorkspacesCmdArgs.swift
│ │ │ │ ├── MacosNativeFullscreenCmdArgs.swift
│ │ │ │ ├── MacosNativeMinimizeCmdArgs.swift
│ │ │ │ ├── ModeCmdArgs.swift
│ │ │ │ ├── MoveCmdArgs.swift
│ │ │ │ ├── MoveMouseCmdArgs.swift
│ │ │ │ ├── MoveNodeToMonitorCmdArgs.swift
│ │ │ │ ├── MoveNodeToWorkspaceCmdArgs.swift
│ │ │ │ ├── MoveWorkpsaceToMonitorCmdArgs.swift
│ │ │ │ ├── ReloadConfigCmdArgs.swift
│ │ │ │ ├── ResizeCmdArgs.swift
│ │ │ │ ├── SplitCmdArgs.swift
│ │ │ │ ├── SubscribeCmdArgs.swift
│ │ │ │ ├── SummonWorkspaceCmdArgs.swift
│ │ │ │ ├── SwapCmdArgs.swift
│ │ │ │ ├── TriggerBindingCmdArgs.swift
│ │ │ │ ├── VolumeCmdArgs.swift
│ │ │ │ ├── WorkspaceBackAndForthCmdArgs.swift
│ │ │ │ └── WorkspaceCmdArgs.swift
│ │ │ ├── parseCmdArgs.swift
│ │ │ ├── parseSpecificCmdArgs.swift
│ │ │ ├── splitArgs.swift
│ │ │ └── subcommandParsers.swift
│ │ ├── cmdHelpGenerated.swift
│ │ ├── gitHashGenerated.swift
│ │ ├── model/
│ │ │ ├── AeroSpaceEnvVars.swift
│ │ │ ├── AxAppThreadToken.swift
│ │ │ ├── CardinalDirection.swift
│ │ │ ├── CardinalOrDfsDirection.swift
│ │ │ ├── DfsNextPrev.swift
│ │ │ ├── Init.swift
│ │ │ ├── MonitorDescription.swift
│ │ │ ├── NextPrev.swift
│ │ │ ├── Orientation.swift
│ │ │ ├── WorkspaceName.swift
│ │ │ ├── clientServer.swift
│ │ │ └── sponsorshipPrompts.swift
│ │ ├── util/
│ │ │ ├── AeroAny.swift
│ │ │ ├── ArrSlice.swift
│ │ │ ├── BoolEx.swift
│ │ │ ├── CollectionEx.swift
│ │ │ ├── ComparableEx.swift
│ │ │ ├── ConvenienceCopyable.swift
│ │ │ ├── DictionaryEx.swift
│ │ │ ├── EquatableNoop.swift
│ │ │ ├── JsonEncoderEx.swift
│ │ │ ├── Lateinit.swift
│ │ │ ├── MainActorEx.swift
│ │ │ ├── NWConnectionEx.swift
│ │ │ ├── Nullable.swift
│ │ │ ├── OptionalEx.swift
│ │ │ ├── ResultEx.swift
│ │ │ ├── SequenceEx.swift
│ │ │ ├── StringEx.swift
│ │ │ ├── StringLogicalSegments.swift
│ │ │ ├── commonUtil.swift
│ │ │ └── showMessageInGui.swift
│ │ └── versionGenerated.swift
│ └── PrivateApi/
│ └── include/
│ ├── module.modulemap
│ ├── private.h
│ └── private.m
├── axDumps/
│ ├── 1password.json5
│ ├── 1password_large_type_window.json5
│ ├── 1password_mini_window.json5
│ ├── about_this_mac.json5
│ ├── alacritty_decorations_buttonless.json5
│ ├── apple_calendar.json5
│ ├── apple_calendar_settings.json5
│ ├── apple_followup_sign_in_to_a_new_device_confirmation.json5
│ ├── apple_mail.json5
│ ├── apple_mail_new_email.json5
│ ├── apple_mail_settings.json5
│ ├── archiveutility.json5
│ ├── brave.json5
│ ├── brave_pip.json5
│ ├── calculator.json5
│ ├── choose_1_5_0.json5
│ ├── chrome.json5
│ ├── chrome_choose_what_to_share_popup.json5
│ ├── chrome_extensions_popup.json5
│ ├── chrome_find_in_page.json5
│ ├── chrome_pip.json5
│ ├── chrome_sharing_is_in_progress_popup.json5
│ ├── cleanshotx_monitor_1.json5
│ ├── cleanshotx_monitor_2.json5
│ ├── drracket.json5
│ ├── finder.json5
│ ├── finder_quick_look.json5
│ ├── firefox.json5
│ ├── firefox_extensions_popup.json5
│ ├── firefox_mouse_hover_over_extensions_button.json5
│ ├── firefox_mouse_hover_over_tab.json5
│ ├── firefox_non_native_fullscreen.json5
│ ├── firefox_normal_window_when_non_native_fullscreen_in_background.json5
│ ├── firefox_pinterest_sign_in_with_google.json5
│ ├── firefox_pip.json5
│ ├── ghostty.json5
│ ├── ghostty_about.json5
│ ├── ghostty_check_for_updates_1_dialog.json5
│ ├── ghostty_check_for_updates_2_alert.json5
│ ├── ghostty_config_error.json5
│ ├── ghostty_quick_terminal.json5
│ ├── ghostty_window_decorations_false.json5
│ ├── intellij.json5
│ ├── intellij_background_tasks.json5
│ ├── intellij_context_menu.json5
│ ├── intellij_native_open_window.json5
│ ├── intellij_quick_doc_popup.json5
│ ├── intellij_rebase_dialog.json5
│ ├── iphonesimulator.json5
│ ├── iterm2.json5
│ ├── iterm2_hotkey_window.json5
│ ├── iterm2_no_title_bar.json5
│ ├── jetbrains_toolbox.json5
│ ├── karabiner_event_viewer.json5
│ ├── karabiner_settings.json5
│ ├── kitty_quick_access.json5
│ ├── macos_capslock_popup_safari.json5
│ ├── macos_capslock_popup_textedit.json5
│ ├── macos_join_network.json5
│ ├── macos_share_window_purple_pill_sublime.json5
│ ├── marta.json5
│ ├── microsoft_edge.json5
│ ├── microsoft_edge_pip.json5
│ ├── mpv_fullscreen.json5
│ ├── mpv_windowed.json5
│ ├── nomachine_session_1.json5
│ ├── nomachine_session_2.json5
│ ├── nomachine_welcome_window_1.json5
│ ├── nomachine_welcome_window_2.json5
│ ├── qutebrowser.json5
│ ├── qutebrowser_context_menu.json5
│ ├── qutebrowser_hide_decoration.json5
│ ├── raycast.json5
│ ├── raycast_settings.json5
│ ├── safari.json5
│ ├── safari_pinterest_sign_in_with_google.json5
│ ├── scenario_firefox_google_meet_share_window/
│ │ ├── 01_firefox.json5
│ │ ├── 02_firefox.json5
│ │ ├── 03_firefox.json5
│ │ ├── 04_firefox.json5
│ │ ├── 05_apple_controlcenter.json5
│ │ ├── 06_firefox.json5
│ │ ├── 07_firefox.json5
│ │ └── README.md
│ ├── slack.json5
│ ├── slack_chat_in_a_separate_window.json5
│ ├── slack_huddle_share_screen_draw_on_screen_fake_window.json5
│ ├── slack_huddle_share_screen_floating_popup.json5
│ ├── slack_huddle_share_screen_target_picker.json5
│ ├── spotify.json5
│ ├── spotify_miniplayer.json5
│ ├── steam_1.json5
│ ├── steam_2.json5
│ ├── sublime_text_4.json5
│ ├── system_settings.json5
│ ├── telegram.json5
│ ├── telegram_image_viewer.json5
│ ├── terminal_app.json5
│ ├── transmission.json5
│ ├── transmission_torrent_inspector.json5
│ ├── vlc_empty.json5
│ ├── vlc_fullscreen.json5
│ ├── vlc_video_playing.json5
│ ├── vs_code.json5
│ ├── vs_code_nativeFullScreen_false.json5
│ ├── vs_codium.json5
│ ├── vs_codium_nativeFullScreen_false.json5
│ ├── xcode.json5
│ ├── xcode_build_succeeded_popup.json5
│ ├── xcode_installing_system_components.json5
│ ├── xcode_open_quickly.json5
│ ├── xcode_quick_actions.json5
│ ├── xcode_settings.json5
│ ├── xcode_welcome_window.json5
│ ├── zebar.json5
│ ├── zed.json5
│ ├── zen_browser.json5
│ └── zen_browser_pip.json5
├── build-debug.sh
├── build-docs.sh
├── build-release.sh
├── build-shell-completion.sh
├── dev-docs/
│ ├── architecture.md
│ └── development.md
├── docs/
│ ├── aerospace-balance-sizes.adoc
│ ├── aerospace-close-all-windows-but-current.adoc
│ ├── aerospace-close.adoc
│ ├── aerospace-config.adoc
│ ├── aerospace-debug-windows.adoc
│ ├── aerospace-enable.adoc
│ ├── aerospace-exec-and-forget.adoc
│ ├── aerospace-flatten-workspace-tree.adoc
│ ├── aerospace-focus-back-and-forth.adoc
│ ├── aerospace-focus-monitor.adoc
│ ├── aerospace-focus.adoc
│ ├── aerospace-fullscreen.adoc
│ ├── aerospace-join-with.adoc
│ ├── aerospace-layout.adoc
│ ├── aerospace-list-apps.adoc
│ ├── aerospace-list-exec-env-vars.adoc
│ ├── aerospace-list-modes.adoc
│ ├── aerospace-list-monitors.adoc
│ ├── aerospace-list-windows.adoc
│ ├── aerospace-list-workspaces.adoc
│ ├── aerospace-macos-native-fullscreen.adoc
│ ├── aerospace-macos-native-minimize.adoc
│ ├── aerospace-mode.adoc
│ ├── aerospace-move-mouse.adoc
│ ├── aerospace-move-node-to-monitor.adoc
│ ├── aerospace-move-node-to-workspace.adoc
│ ├── aerospace-move-workspace-to-monitor.adoc
│ ├── aerospace-move.adoc
│ ├── aerospace-reload-config.adoc
│ ├── aerospace-resize.adoc
│ ├── aerospace-split.adoc
│ ├── aerospace-subscribe.adoc
│ ├── aerospace-summon-workspace.adoc
│ ├── aerospace-swap.adoc
│ ├── aerospace-trigger-binding.adoc
│ ├── aerospace-volume.adoc
│ ├── aerospace-workspace-back-and-forth.adoc
│ ├── aerospace-workspace.adoc
│ ├── aerospace.adoc
│ ├── commands.adoc
│ ├── config-examples/
│ │ ├── default-config.toml
│ │ └── i3-like-config-example.toml
│ ├── goodies.adoc
│ ├── guide.adoc
│ ├── index.html
│ └── util/
│ ├── all-monitors-option.adoc
│ ├── conditional-arguments-header.adoc
│ ├── conditional-examples-header.adoc
│ ├── conditional-exit-code-header.adoc
│ ├── conditional-options-header.adoc
│ ├── conditional-output-format-header.adoc
│ ├── header.adoc
│ ├── man-attributes.adoc
│ ├── man-footer.adoc
│ ├── monitor-option.adoc
│ ├── site-attributes.adoc
│ ├── window-id-flag-desc.adoc
│ └── workspace-flag-desc.adoc
├── format.sh
├── generate-shell-parser.sh
├── generate.sh
├── grammar/
│ ├── ShellLexer.g4
│ ├── ShellParser.g4
│ └── commands-bnf-grammar.txt
├── install-from-sources.sh
├── legal/
│ ├── README.md
│ └── third-party-license/
│ ├── LICENSE-HotKey.txt
│ ├── LICENSE-ISSoundAdditions.txt
│ ├── LICENSE-TOMLKIT.txt
│ ├── LICENSE-antlr.txt
│ ├── LICENSE-swift-collections.txt
│ └── LICENSE-tomlplusplus.txt
├── lint.sh
├── makefile
├── project.yml
├── resources/
│ ├── AeroSpace.entitlements
│ └── Assets.xcassets/
│ ├── AccentColor.colorset/
│ │ └── Contents.json
│ ├── AppIcon.appiconset/
│ │ └── Contents.json
│ └── Contents.json
├── run-cli.sh
├── run-debug.sh
├── run-swift-test.sh
├── run-tests.sh
└── script/
├── build-brew-cask.sh
├── check-uncommitted-files.sh
├── clean-project.sh
├── clean-xcode.sh
├── generate-cmd-help.sh
├── install-dep.sh
├── publish-release.sh
├── reset-accessibility-permission-for-debug.sh
└── setup.sh
Condensed preview — 487 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,598K chars).
[
{
"path": ".bundle/config",
"chars": 70,
"preview": "---\nBUNDLE_PATH: \".deps/bundler-path\"\nBUNDLE_DISABLE_SHARED_GEMS: \"1\"\n"
},
{
"path": ".editorconfig",
"chars": 368,
"preview": "root = true\n\n# It's better to use \"wrap line\" in plain text documents\n[*.{adoc,md}]\nrulers = 1000\nmax_line_length = 1000"
},
{
"path": ".gitattributes",
"chars": 207,
"preview": "# GitHub: linguist-generated marks paths that you would like to be ignored for the repository's language statistics and "
},
{
"path": ".github/DISCUSSION_TEMPLATE/potential-bugs.yml",
"chars": 317,
"preview": "body:\n - type: textarea\n id: body\n attributes:\n label: Body\n value: |\n Steps to reproduce:\n "
},
{
"path": ".github/FUNDING.yml",
"chars": 22,
"preview": "github: [nikitabobko]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 28,
"preview": "blank_issues_enabled: false\n"
},
{
"path": ".github/ISSUE_TEMPLATE/new-issue.yml",
"chars": 685,
"preview": "name: New Issue\ndescription: New Issue\nbody:\n - type: markdown\n attributes:\n value: |\n AeroSpace project"
},
{
"path": ".github/gh-actions-runner-xcode-select.sh",
"chars": 652,
"preview": "#!/bin/bash\nset -e # Exit if one of commands exit with non-zero exit code\nset -u # Treat unset variables and parameters "
},
{
"path": ".github/pull_request_template.md",
"chars": 801,
"preview": "## PR checklist\n\n- [ ] Explain your changes in the relevant commit messages rather than in the PR description. The PR de"
},
{
"path": ".github/workflows/build.yml",
"chars": 2386,
"preview": "name: build\n\non:\n push:\n branches:\n - 'main'\n - 'rr/**' # \"rr\" stands for \"remote run\"\n pull_request:\n "
},
{
"path": ".github/workflows/close-third-party-issues.yml",
"chars": 1104,
"preview": "name: close-third-party-issues\n\non:\n issues:\n types: [opened]\n\njobs:\n close-third-party-issues:\n name: Close thi"
},
{
"path": ".gitignore",
"chars": 463,
"preview": "/.idea\n/.debug\n/.release\n/.shell-completion\n/.site\n/.man\n/Gemfile.lock\n# IDK, AppCode randomly creates this EMPTY file. "
},
{
"path": ".swift-version",
"chars": 6,
"preview": "6.2.4\n"
},
{
"path": ".swiftformat",
"chars": 1765,
"preview": "# https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md\n--exclude ./ShellParserGenerated\n--indentcase true\n--pa"
},
{
"path": ".swiftlint.yml",
"chars": 2302,
"preview": "excluded:\n - .build\n - .xcode-build\n - ./ShellParserGenerated\n\n# https://realm.github.io/SwiftLint/rule-directory.htm"
},
{
"path": "AeroSpace.xcodeproj/project.pbxproj",
"chars": 13112,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 77;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "AeroSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "CONTRIBUTING.md",
"chars": 5845,
"preview": "# Contributing\n\n## Users cannot create GitHub Issues directly\n\nAeroSpace project doesn't accept Issues directly - we ask"
},
{
"path": "Gemfile",
"chars": 147,
"preview": "# frozen_string_literal: true\nruby '~> 3.0' # >= 3.0 and < 4.0\n\nsource \"https://rubygems.org\"\ngem 'asciidoctor', '2.0.23"
},
{
"path": "LICENSE.txt",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2023 Nikita Bobko\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "Package.resolved",
"chars": 1439,
"preview": "{\n \"originHash\" : \"120ba1d30192b339dc5c9d1461d2b3d1e329a17a4867b471290c07c6b3c0cb73\",\n \"pins\" : [\n {\n \"identit"
},
{
"path": "Package.swift",
"chars": 2988,
"preview": "// swift-tools-version: 6.2\n// The swift-tools-version declares the minimum version of Swift required to build this pack"
},
{
"path": "README.md",
"chars": 10916,
"preview": "# AeroSpace Beta []("
},
{
"path": "ShellParserGenerated/.gitignore",
"chars": 159,
"preview": ".DS_Store\n/.build\n/Packages\nxcuserdata/\nDerivedData/\n.swiftpm/configuration/registries.json\n.swiftpm/xcode/package.xcwor"
},
{
"path": "ShellParserGenerated/Package.swift",
"chars": 963,
"preview": "// swift-tools-version: 5.9\n// The swift-tools-version declares the minimum version of Swift required to build this pack"
},
{
"path": "ShellParserGenerated/Sources/ShellParserGenerated/ShellLexer.swift",
"chars": 11712,
"preview": "// Generated from ./grammar/ShellLexer.g4 by ANTLR 4.13.1\nimport Antlr4\n\nopen class ShellLexer: Lexer {\n\n\tinternal stati"
},
{
"path": "ShellParserGenerated/Sources/ShellParserGenerated/ShellParser.swift",
"chars": 34206,
"preview": "// Generated from ./grammar/ShellParser.g4 by ANTLR 4.13.1\nimport Antlr4\n\nopen class ShellParser: Parser {\n\n\tinternal st"
},
{
"path": "Sources/AeroSpaceApp/AeroSpaceApp.swift",
"chars": 646,
"preview": "import AppBundle\nimport SwiftUI\n\n// This file is shared between SPM and xcode project\n\n@main\nstruct AeroSpaceApp: App {\n"
},
{
"path": "Sources/AppBundle/GlobalObserver.swift",
"chars": 4141,
"preview": "import AppKit\nimport Common\n\nenum GlobalObserver {\n private static func onNotif(_ notification: Notification) {\n "
},
{
"path": "Sources/AppBundle/command/CmdEnv.swift",
"chars": 725,
"preview": "import Common\n\nstruct CmdEnv: ConvenienceCopyable {\n var windowId: UInt32?\n var workspaceName: String?\n\n static"
},
{
"path": "Sources/AppBundle/command/CmdIo.swift",
"chars": 1000,
"preview": "struct CmdStdin: ~Copyable {\n private var input: String = \"\"\n init(_ input: String) {\n self.input = input\n "
},
{
"path": "Sources/AppBundle/command/Command.swift",
"chars": 1774,
"preview": "import AppKit\nimport Common\n\nprotocol Command: AeroAny, Equatable, Sendable {\n associatedtype T where T: CmdArgs\n "
},
{
"path": "Sources/AppBundle/command/cmdManifest.swift",
"chars": 4465,
"preview": "import Common\n\nextension CmdArgs {\n func toCommand() -> any Command {\n let command: any Command\n switch"
},
{
"path": "Sources/AppBundle/command/cmdResolveTargetOrReportError.swift",
"chars": 1511,
"preview": "import Common\n\nextension CmdArgs {\n @MainActor\n var workspace: Workspace? {\n if let workspaceName { Workspa"
},
{
"path": "Sources/AppBundle/command/format.swift",
"chars": 7917,
"preview": "import Common\n\nenum AeroObj {\n case window(window: Window, title: String)\n case workspace(Workspace)\n case app("
},
{
"path": "Sources/AppBundle/command/formatToJson.swift",
"chars": 1117,
"preview": "import Common\nimport Foundation\n\nextension [AeroObj] {\n @MainActor\n func formatToJson(_ format: [StringInterToken]"
},
{
"path": "Sources/AppBundle/command/impl/BalanceSizesCommand.swift",
"chars": 749,
"preview": "import AppKit\nimport Common\nimport Foundation\n\nstruct BalanceSizesCommand: Command {\n let args: BalanceSizesCmdArgs\n "
},
{
"path": "Sources/AppBundle/command/impl/CloseAllWindowsButCurrentCommand.swift",
"chars": 884,
"preview": "import AppKit\nimport Common\n\nstruct CloseAllWindowsButCurrentCommand: Command {\n let args: CloseAllWindowsButCurrentC"
},
{
"path": "Sources/AppBundle/command/impl/CloseCommand.swift",
"chars": 1362,
"preview": "import AppKit\nimport Common\n\nstruct CloseCommand: Command {\n let args: CloseCmdArgs\n /*conforms*/ let shouldResetC"
},
{
"path": "Sources/AppBundle/command/impl/ConfigCommand.swift",
"chars": 7089,
"preview": "import AppKit\nimport Common\n\nstruct ConfigCommand: Command {\n let args: ConfigCmdArgs\n /*conforms*/ let shouldRese"
},
{
"path": "Sources/AppBundle/command/impl/DebugWindowsCommand.swift",
"chars": 5567,
"preview": "import AppKit\nimport Common\nimport OrderedCollections\n\nprivate let disclaimer =\n \"\"\"\n !!! DISCLAIMER !!!\n !!! '"
},
{
"path": "Sources/AppBundle/command/impl/EnableCommand.swift",
"chars": 1215,
"preview": "import AppKit\nimport Common\n\nstruct EnableCommand: Command {\n let args: EnableCmdArgs\n /*conforms*/ let shouldRese"
},
{
"path": "Sources/AppBundle/command/impl/ExecAndForgetCommand.swift",
"chars": 602,
"preview": "import AppKit\nimport Common\n\nstruct ExecAndForgetCommand: Command {\n let args: ExecAndForgetCmdArgs\n /*conforms*/ "
},
{
"path": "Sources/AppBundle/command/impl/FlattenWorkspaceTreeCommand.swift",
"chars": 612,
"preview": "import AppKit\nimport Common\n\nstruct FlattenWorkspaceTreeCommand: Command {\n let args: FlattenWorkspaceTreeCmdArgs\n "
},
{
"path": "Sources/AppBundle/command/impl/FocusBackAndForthCommand.swift",
"chars": 384,
"preview": "import AppKit\nimport Common\n\nstruct FocusBackAndForthCommand: Command {\n let args: FocusBackAndForthCmdArgs\n /*con"
},
{
"path": "Sources/AppBundle/command/impl/FocusCommand.swift",
"chars": 8680,
"preview": "import AppKit\nimport Common\n\nstruct FocusCommand: Command {\n let args: FocusCmdArgs\n /*conforms*/ let shouldResetC"
},
{
"path": "Sources/AppBundle/command/impl/FocusMonitorCommand.swift",
"chars": 3282,
"preview": "import AppKit\nimport Common\n\nstruct FocusMonitorCommand: Command {\n let args: FocusMonitorCmdArgs\n /*conforms*/ le"
},
{
"path": "Sources/AppBundle/command/impl/FullscreenCommand.swift",
"chars": 1067,
"preview": "import AppKit\nimport Common\n\nstruct FullscreenCommand: Command {\n let args: FullscreenCmdArgs\n /*conforms*/ let sh"
},
{
"path": "Sources/AppBundle/command/impl/JoinWithCommand.swift",
"chars": 1301,
"preview": "import AppKit\nimport Common\n\nstruct JoinWithCommand: Command {\n let args: JoinWithCmdArgs\n /*conforms*/ let should"
},
{
"path": "Sources/AppBundle/command/impl/LayoutCommand.swift",
"chars": 4791,
"preview": "import AppKit\nimport Common\n\nstruct LayoutCommand: Command {\n let args: LayoutCmdArgs\n /*conforms*/ let shouldRese"
},
{
"path": "Sources/AppBundle/command/impl/ListAppsCommand.swift",
"chars": 1039,
"preview": "import AppKit\nimport Common\n\nstruct ListAppsCommand: Command {\n let args: ListAppsCmdArgs\n /*conforms*/ let should"
},
{
"path": "Sources/AppBundle/command/impl/ListExecEnvVarsCommand.swift",
"chars": 355,
"preview": "import AppKit\nimport Common\n\nstruct ListExecEnvVarsCommand: Command {\n let args: ListExecEnvVarsCmdArgs\n /*conform"
},
{
"path": "Sources/AppBundle/command/impl/ListModesCommand.swift",
"chars": 671,
"preview": "import AppKit\nimport Common\n\nstruct ListModesCommand: Command {\n let args: ListModesCmdArgs\n /*conforms*/ let shou"
},
{
"path": "Sources/AppBundle/command/impl/ListMonitorsCommand.swift",
"chars": 1333,
"preview": "import AppKit\nimport Common\n\nstruct ListMonitorsCommand: Command {\n let args: ListMonitorsCmdArgs\n /*conforms*/ le"
},
{
"path": "Sources/AppBundle/command/impl/ListWindowsCommand.swift",
"chars": 2785,
"preview": "import AppKit\nimport Common\n\nstruct ListWindowsCommand: Command {\n let args: ListWindowsCmdArgs\n /*conforms*/ let "
},
{
"path": "Sources/AppBundle/command/impl/ListWorkspacesCommand.swift",
"chars": 2661,
"preview": "import AppKit\nimport Common\n\nstruct ListWorkspacesCommand: Command {\n let args: ListWorkspacesCmdArgs\n /*conforms*"
},
{
"path": "Sources/AppBundle/command/impl/MacosNativeFullscreenCommand.swift",
"chars": 2036,
"preview": "import AppKit\nimport Common\n\n/// Problem ID-B6E178F2: It's not first-class citizen command in AeroSpace model, since it "
},
{
"path": "Sources/AppBundle/command/impl/MacosNativeMinimizeCommand.swift",
"chars": 1159,
"preview": "import AppKit\nimport Common\n\n/// See: MacosNativeFullscreenCommand. Problem ID-B6E178F2\nstruct MacosNativeMinimizeComman"
},
{
"path": "Sources/AppBundle/command/impl/ModeCommand.swift",
"chars": 289,
"preview": "import AppKit\nimport Common\n\nstruct ModeCommand: Command {\n let args: ModeCmdArgs\n /*conforms*/ let shouldResetClo"
},
{
"path": "Sources/AppBundle/command/impl/MoveCommand.swift",
"chars": 7466,
"preview": "import AppKit\nimport Common\n\nstruct MoveCommand: Command {\n let args: MoveCmdArgs\n /*conforms*/ let shouldResetClo"
},
{
"path": "Sources/AppBundle/command/impl/MoveMouseCommand.swift",
"chars": 2764,
"preview": "import AppKit\nimport Common\n\nstruct MoveMouseCommand: Command {\n let args: MoveMouseCmdArgs\n /*conforms*/ let shou"
},
{
"path": "Sources/AppBundle/command/impl/MoveNodeToMonitorCommand.swift",
"chars": 1498,
"preview": "import AppKit\nimport Common\n\nstruct MoveNodeToMonitorCommand: Command {\n let args: MoveNodeToMonitorCmdArgs\n /*con"
},
{
"path": "Sources/AppBundle/command/impl/MoveNodeToWorkspaceCommand.swift",
"chars": 2088,
"preview": "import Common\n\nstruct MoveNodeToWorkspaceCommand: Command {\n let args: MoveNodeToWorkspaceCmdArgs\n /*conforms*/ le"
},
{
"path": "Sources/AppBundle/command/impl/MoveWorkspaceToMonitorCommand.swift",
"chars": 1506,
"preview": "import AppKit\nimport Common\n\nstruct MoveWorkspaceToMonitorCommand: Command {\n let args: MoveWorkspaceToMonitorCmdArgs"
},
{
"path": "Sources/AppBundle/command/impl/ReloadConfigCommand.swift",
"chars": 1596,
"preview": "import AppKit\nimport Common\n\nstruct ReloadConfigCommand: Command {\n let args: ReloadConfigCmdArgs\n /*conforms*/ le"
},
{
"path": "Sources/AppBundle/command/impl/ResizeCommand.swift",
"chars": 2397,
"preview": "import AppKit\nimport Common\n\nstruct ResizeCommand: Command { // todo cover with tests\n let args: ResizeCmdArgs\n /*"
},
{
"path": "Sources/AppBundle/command/impl/SplitCommand.swift",
"chars": 2197,
"preview": "import AppKit\nimport Common\n\nstruct SplitCommand: Command {\n let args: SplitCmdArgs\n /*conforms*/ let shouldResetC"
},
{
"path": "Sources/AppBundle/command/impl/SummonWorkspaceCommand.swift",
"chars": 920,
"preview": "import AppKit\nimport Common\n\nstruct SummonWorkspaceCommand: Command {\n let args: SummonWorkspaceCmdArgs\n /*conform"
},
{
"path": "Sources/AppBundle/command/impl/SwapCommand.swift",
"chars": 2074,
"preview": "import AppKit\nimport Common\n\nstruct SwapCommand: Command {\n let args: SwapCmdArgs\n /*conforms*/ let shouldResetClo"
},
{
"path": "Sources/AppBundle/command/impl/TriggerBindingCommand.swift",
"chars": 868,
"preview": "import AppKit\nimport Common\n\nstruct TriggerBindingCommand: Command {\n let args: TriggerBindingCmdArgs\n /*conforms*"
},
{
"path": "Sources/AppBundle/command/impl/VolumeCommand.swift",
"chars": 982,
"preview": "import AppKit\nimport Common\nimport ISSoundAdditions\n\nstruct VolumeCommand: Command {\n let args: VolumeCmdArgs\n /*c"
},
{
"path": "Sources/AppBundle/command/impl/WorkspaceBackAndForthCommand.swift",
"chars": 299,
"preview": "import AppKit\nimport Common\n\nstruct WorkspaceBackAndForthCommand: Command {\n let args: WorkspaceBackAndForthCmdArgs\n "
},
{
"path": "Sources/AppBundle/command/impl/WorkspaceCommand.swift",
"chars": 2469,
"preview": "import AppKit\nimport Common\nimport Foundation\n\nstruct WorkspaceCommand: Command {\n let args: WorkspaceCmdArgs\n /*c"
},
{
"path": "Sources/AppBundle/command/parseCommand.swift",
"chars": 1032,
"preview": "import Common\nimport TOMLKit\n\nfunc parseCommand(_ raw: String) -> ParsedCmd<any Command> {\n if raw.starts(with: \"exec"
},
{
"path": "Sources/AppBundle/config/Config.swift",
"chars": 2611,
"preview": "import AppKit\nimport Common\nimport HotKey\nimport OrderedCollections\n\nfunc getDefaultConfigUrlFromProject() -> URL {\n "
},
{
"path": "Sources/AppBundle/config/ConfigFile.swift",
"chars": 1296,
"preview": "import Common\nimport Foundation\n\nlet configDotfileName = \".aerospace.toml\"\nfunc findCustomConfigUrl() -> ConfigFile {\n "
},
{
"path": "Sources/AppBundle/config/ConfigFileWatcher.swift",
"chars": 1395,
"preview": "import Common\nimport Foundation\n\nprivate struct ConfigFileWatcher: ~Copyable {\n private let source: DispatchSourceFil"
},
{
"path": "Sources/AppBundle/config/DynamicConfigValue.swift",
"chars": 3419,
"preview": "import Common\nimport TOMLKit\n\nstruct PerMonitorValue<Value: Equatable>: Equatable {\n let description: MonitorDescript"
},
{
"path": "Sources/AppBundle/config/HotkeyBinding.swift",
"chars": 5340,
"preview": "import AppKit\nimport Common\nimport Foundation\nimport HotKey\nimport TOMLKit\n\n@MainActor private var hotkeys: [String: Hot"
},
{
"path": "Sources/AppBundle/config/Mode.swift",
"chars": 1476,
"preview": "import Common\nimport HotKey\nimport TOMLKit\n\nstruct Mode: ConvenienceCopyable, Equatable, Sendable {\n var bindings: [S"
},
{
"path": "Sources/AppBundle/config/keysMap.swift",
"chars": 9507,
"preview": "import AppKit\nimport Common\nimport HotKey\n\nprivate let minus = \"minus\"\nprivate let equal = \"equal\"\n\nprivate let q = \"q\"\n"
},
{
"path": "Sources/AppBundle/config/parseConfig.swift",
"chars": 17899,
"preview": "import AppKit\nimport Common\nimport HotKey\nimport TOMLKit\nimport OrderedCollections\n\n@MainActor\nfunc readConfig(forceConf"
},
{
"path": "Sources/AppBundle/config/parseExecEnvVariables.swift",
"chars": 2384,
"preview": "import AppKit\nimport Common\nimport TOMLKit\n\nlet testEnv = [\"PATH\": \"AEROSPACE_TEST_PATH\", \"AEROSPACE_INHERITED_TEST_ENV\""
},
{
"path": "Sources/AppBundle/config/parseGaps.swift",
"chars": 4031,
"preview": "import Common\nimport TOMLKit\n\nstruct Gaps: ConvenienceCopyable, Equatable, Sendable {\n var inner: Inner\n var outer"
},
{
"path": "Sources/AppBundle/config/parseKeyMapping.swift",
"chars": 2384,
"preview": "import Common\nimport HotKey\nimport TOMLKit\n\nprivate let keyMappingParser: [String: any ParserProtocol<KeyMapping>] = [\n "
},
{
"path": "Sources/AppBundle/config/parseOnWindowDetected.swift",
"chars": 4699,
"preview": "import Common\nimport TOMLKit\n\nstruct WindowDetectedCallback: ConvenienceCopyable, Equatable {\n var matcher: WindowDet"
},
{
"path": "Sources/AppBundle/config/parseWorkspaceToMonitorAssignment.swift",
"chars": 1581,
"preview": "import Common\nimport TOMLKit\n\nfunc parseWorkspaceToMonitorAssignment(_ raw: TOMLValueConvertible, _ backtrace: TomlBackt"
},
{
"path": "Sources/AppBundle/config/startAtLogin.swift",
"chars": 872,
"preview": "import AppKit\nimport Common\nimport ServiceManagement\n\n@MainActor\nfunc syncStartAtLogin() {\n cleanupPlistFromPrevVersi"
},
{
"path": "Sources/AppBundle/focus.swift",
"chars": 7910,
"preview": "import AppKit\nimport Common\n\nenum EffectiveLeaf {\n case window(Window)\n case emptyWorkspace(Workspace)\n}\nextension"
},
{
"path": "Sources/AppBundle/focusCache.swift",
"chars": 681,
"preview": "@MainActor private var lastKnownNativeFocusedWindowId: UInt32? = nil\n\n/// The data should flow (from nativeFocused to fo"
},
{
"path": "Sources/AppBundle/getNativeFocusedWindow.swift",
"chars": 578,
"preview": "import AppKit\nimport Common\n\n@MainActor\nvar appForTests: (any AbstractApp)? = nil\n\n@MainActor\nprivate var focusedApp: (a"
},
{
"path": "Sources/AppBundle/initAppBundle.swift",
"chars": 3810,
"preview": "import AppKit\nimport Common\nimport Foundation\n\n@MainActor public func initAppBundle() {\n Task {\n initTerminati"
},
{
"path": "Sources/AppBundle/layout/layoutRecursive.swift",
"chars": 8639,
"preview": "import AppKit\n\nextension Workspace {\n @MainActor\n func layoutWorkspace() async throws {\n if isEffectivelyEm"
},
{
"path": "Sources/AppBundle/layout/refresh.swift",
"chars": 7478,
"preview": "import AppKit\nimport Common\n\n@MainActor\nprivate var activeRefreshTask: Task<(), any Error>? = nil\n\n@MainActor\nfunc sched"
},
{
"path": "Sources/AppBundle/model/AxUiElementWindowType.swift",
"chars": 9205,
"preview": "import AppKit\n\nenum AxUiElementWindowType: String {\n case window\n case dialog\n /// Not even a real window\n c"
},
{
"path": "Sources/AppBundle/model/Json.swift",
"chars": 2121,
"preview": "import AppKit\nimport Common\n\nenum Json: Encodable, Equatable {\n // vector\n case dict([String: Json])\n case arra"
},
{
"path": "Sources/AppBundle/model/KnownBundleId.swift",
"chars": 1540,
"preview": "enum KnownBundleId: String, Equatable {\n\n case _1password = \"com.1password.1password\"\n case activityMonitor = \"com"
},
{
"path": "Sources/AppBundle/model/Monitor.swift",
"chars": 3989,
"preview": "import AppKit\nimport Common\n\nprivate struct MonitorImpl {\n let monitorAppKitNsScreenScreensId: Int\n let name: Stri"
},
{
"path": "Sources/AppBundle/model/MonitorDescriptionEx.swift",
"chars": 568,
"preview": "import Common\n\nextension MonitorDescription {\n func resolveMonitor(sortedMonitors: [Monitor]) -> Monitor? {\n r"
},
{
"path": "Sources/AppBundle/model/MonitorEx.swift",
"chars": 759,
"preview": "extension Monitor {\n @MainActor\n var visibleRectPaddedByOuterGaps: Rect {\n let topLeft = visibleRect.topLef"
},
{
"path": "Sources/AppBundle/model/Rect.swift",
"chars": 1851,
"preview": "import AppKit\nimport Common\n\nstruct Rect: ConvenienceCopyable, AeroAny {\n var topLeftX: CGFloat\n var topLeftY: CGF"
},
{
"path": "Sources/AppBundle/model/ServerEvent.swift",
"chars": 2412,
"preview": "import Common\n\npublic struct ServerEvent: Codable, Sendable {\n private let _event: ServerEventType\n\n // periphery:"
},
{
"path": "Sources/AppBundle/mouse/mouse.swift",
"chars": 817,
"preview": "import AppKit\n\n@MainActor var currentlyManipulatedWithMouseWindowId: UInt32? = nil\nvar isLeftMouseButtonDown: Bool { NSE"
},
{
"path": "Sources/AppBundle/mouse/moveWithMouse.swift",
"chars": 4512,
"preview": "import AppKit\nimport Common\n\n@MainActor\nprivate var moveWithMouseTask: Task<(), any Error>? = nil\n\nfunc movedObs(_: AXOb"
},
{
"path": "Sources/AppBundle/mouse/resizeWithMouse.swift",
"chars": 5090,
"preview": "import AppKit\nimport Common\n\n@MainActor\nprivate var resizeWithMouseTask: Task<(), any Error>? = nil\n\nfunc resizedObs(_: "
},
{
"path": "Sources/AppBundle/normalizeLayoutReason.swift",
"chars": 3538,
"preview": "@MainActor\nfunc normalizeLayoutReason() async throws {\n for workspace in Workspace.all {\n let windows: [Window"
},
{
"path": "Sources/AppBundle/runLoop.swift",
"chars": 2793,
"preview": "import Common\nimport Foundation\n\nextension Thread {\n @discardableResult\n func runInLoopAsync(\n job: RunLoop"
},
{
"path": "Sources/AppBundle/server.swift",
"chars": 5623,
"preview": "import AppKit\nimport Common\nimport Network\n\nfunc startUnixSocketServer() {\n try? FileManager.default.removeItem(atPat"
},
{
"path": "Sources/AppBundle/shell/Shell.swift",
"chars": 10977,
"preview": "// periphery:ignore:all - This is WIP file todo\nimport Antlr4\nimport Common\nimport ShellParserGenerated\n\ntypealias TK = "
},
{
"path": "Sources/AppBundle/subscriptions.swift",
"chars": 2126,
"preview": "import Common\nimport Foundation\nimport Network\n\nprivate struct Subscriber {\n let connection: NWConnection\n let eve"
},
{
"path": "Sources/AppBundle/tree/AbstractApp.swift",
"chars": 704,
"preview": "import Common\n\nprotocol AbstractApp: AnyObject, Hashable, AeroAny {\n var pid: Int32 { get }\n var rawAppBundleId: S"
},
{
"path": "Sources/AppBundle/tree/MacApp.swift",
"chars": 17521,
"preview": "import AppKit\nimport Common\n\n// Potential alternative implementation\n// https://github.com/swiftlang/swift-evolution/blo"
},
{
"path": "Sources/AppBundle/tree/MacWindow.swift",
"chars": 13398,
"preview": "import AppKit\nimport Common\n\nfinal class MacWindow: Window {\n let macApp: MacApp\n private var prevUnhiddenProporti"
},
{
"path": "Sources/AppBundle/tree/MacosUnconventionalWindowsContainer.swift",
"chars": 1252,
"preview": "import Common\n\nfinal class MacosFullscreenWindowsContainer: TreeNode, NonLeafTreeNodeObject {\n @MainActor\n init(pa"
},
{
"path": "Sources/AppBundle/tree/TilingContainer.swift",
"chars": 2345,
"preview": "import AppKit\nimport Common\n\nfinal class TilingContainer: TreeNode, NonLeafTreeNodeObject { // todo consider renaming to"
},
{
"path": "Sources/AppBundle/tree/TreeNode.swift",
"chars": 6615,
"preview": "import AppKit\nimport Common\n\nopen class TreeNode: Equatable, AeroAny {\n private var _children: [TreeNode] = []\n va"
},
{
"path": "Sources/AppBundle/tree/TreeNodeCases.swift",
"chars": 6273,
"preview": "import Common\n\nenum TreeNodeCases {\n case window(Window)\n case tilingContainer(TilingContainer)\n case workspace"
},
{
"path": "Sources/AppBundle/tree/TreeNodeEx.swift",
"chars": 3815,
"preview": "import AppKit\nimport Common\n\nextension TreeNode {\n private func visit(node: TreeNode, result: inout [Window]) {\n "
},
{
"path": "Sources/AppBundle/tree/Window.swift",
"chars": 2362,
"preview": "import AppKit\nimport Common\n\nopen class Window: TreeNode, Hashable {\n let windowId: UInt32\n let app: any AbstractA"
},
{
"path": "Sources/AppBundle/tree/Workspace.swift",
"chars": 7615,
"preview": "import AppKit\nimport Common\n\n@MainActor private var workspaceNameToWorkspace: [String: Workspace] = [:]\n\n@MainActor priv"
},
{
"path": "Sources/AppBundle/tree/WorkspaceEx.swift",
"chars": 2213,
"preview": "import Common\n\nextension Workspace {\n @MainActor var rootTilingContainer: TilingContainer {\n let containers = "
},
{
"path": "Sources/AppBundle/tree/frozen/FrozenTreeNode.swift",
"chars": 1385,
"preview": "import AppKit\nimport Common\n\nenum FrozenTreeNode: Sendable {\n case container(FrozenContainer)\n case window(FrozenW"
},
{
"path": "Sources/AppBundle/tree/frozen/FrozenWorld.swift",
"chars": 1069,
"preview": "struct FrozenWorld {\n let workspaces: [FrozenWorkspace]\n let monitors: [FrozenMonitor]\n let windowIds: Set<UInt"
},
{
"path": "Sources/AppBundle/tree/frozen/closedWindowsCache.swift",
"chars": 5474,
"preview": "import AppKit\n\n/// First line of defence against lock screen\n///\n/// When you lock the screen, all accessibility API bec"
},
{
"path": "Sources/AppBundle/tree/normalizeContainers.swift",
"chars": 1350,
"preview": "extension Workspace {\n @MainActor func normalizeContainers() {\n rootTilingContainer.unbindEmptyAndAutoFlatten("
},
{
"path": "Sources/AppBundle/ui/AppearanceTheme.swift",
"chars": 645,
"preview": "import SwiftUI\nimport Common\n\nenum AppearanceTheme {\n case light\n case dark\n\n /// System Settings -> Appearance"
},
{
"path": "Sources/AppBundle/ui/ExperimentalUISettings.swift",
"chars": 2170,
"preview": "import SwiftUI\n\nstruct ExperimentalUISettings {\n var displayStyle: MenuBarStyle {\n get {\n if let va"
},
{
"path": "Sources/AppBundle/ui/MenuBar.swift",
"chars": 4608,
"preview": "import Common\nimport Foundation\nimport SwiftUI\n\n@MainActor\npublic func menuBar(viewModel: TrayMenuModel) -> some Scene {"
},
{
"path": "Sources/AppBundle/ui/MenuBarLabel.swift",
"chars": 6659,
"preview": "import Common\nimport Foundation\nimport SwiftUI\n\n@MainActor\nstruct MenuBarLabel: View {\n @Environment(\\.colorScheme) v"
},
{
"path": "Sources/AppBundle/ui/MessageView.swift",
"chars": 4956,
"preview": "import Common\nimport SwiftUI\n\n@MainActor\npublic func getMessageWindow(messageModel: MessageModel) -> some Scene {\n //"
},
{
"path": "Sources/AppBundle/ui/NSPanelHud.swift",
"chars": 609,
"preview": "import AppKit\n\nopen class NSPanelHud: NSPanel {\n init() {\n super.init(\n contentRect: .zero,\n "
},
{
"path": "Sources/AppBundle/ui/SecureInputView.swift",
"chars": 3097,
"preview": "import AppKit\nimport Carbon\nimport SwiftUI\n\nprivate let iconSize = CGSize(width: 50, height: 50)\nprivate let textSize = "
},
{
"path": "Sources/AppBundle/ui/TrayMenuModel.swift",
"chars": 3967,
"preview": "import AppKit\nimport Common\n\npublic final class TrayMenuModel: ObservableObject {\n @MainActor public static let share"
},
{
"path": "Sources/AppBundle/ui/VolumeView.swift",
"chars": 2755,
"preview": "import AppKit\nimport SwiftUI\n\npublic final class VolumePanel: NSPanelHud {\n @MainActor public static var shared: Volu"
},
{
"path": "Sources/AppBundle/util/ArrayEx.swift",
"chars": 801,
"preview": "import Common\n\nextension Array {\n func singleOrNil(where predicate: (Self.Element) throws -> Bool) rethrows -> Self.E"
},
{
"path": "Sources/AppBundle/util/AwaitableOneTimeBroadcastLatch.swift",
"chars": 1401,
"preview": "import Common\nimport Foundation\n\nactor AwaitableOneTimeBroadcastLatch {\n private var done = false\n private var awa"
},
{
"path": "Sources/AppBundle/util/AxSubscription.swift",
"chars": 2137,
"preview": "import AppKit\nimport Common\n\n/// The subscription is active as long as you keep this class in memory\nfinal class AxSubsc"
},
{
"path": "Sources/AppBundle/util/AxUiElementMock.swift",
"chars": 288,
"preview": "import AppKit\nimport Common\n\n/// Alternative name: AttrAddressibleStorage\nprotocol AxUiElementMock {\n func get<Attr: "
},
{
"path": "Sources/AppBundle/util/LazySequenceProtocolEx.swift",
"chars": 248,
"preview": "// periphery:ignore\nextension LazySequenceProtocol {\n func filterNotNil<Unwrapped>() -> LazyMapSequence<LazyFilterSeq"
},
{
"path": "Sources/AppBundle/util/MruStack.swift",
"chars": 1493,
"preview": "/// Stack with most recently element on top\nfinal class MruStack<T: Equatable>: Sequence {\n typealias Element = T\n\n "
},
{
"path": "Sources/AppBundle/util/NSRunningApplicationEx.swift",
"chars": 181,
"preview": "import AppKit\n\nextension NSRunningApplication {\n var idForDebug: String {\n \"PID: \\(processIdentifier) ID: \\(bu"
},
{
"path": "Sources/AppBundle/util/NsApplicationEx.swift",
"chars": 313,
"preview": "import AppKit\n\nextension NSApplication.ActivationPolicy {\n var prettyDescription: String {\n switch self {\n "
},
{
"path": "Sources/AppBundle/util/SetEx.swift",
"chars": 325,
"preview": "// periphery:ignore\nextension Set {\n func toArray() -> [Element] { Array(self) }\n\n @inlinable static func += (lhs:"
},
{
"path": "Sources/AppBundle/util/ThreadGuardedValue.swift",
"chars": 897,
"preview": "import Common\n\nfinal class ThreadGuardedValue<Value>: Sendable {\n nonisolated(unsafe) private var _threadGuarded: Val"
},
{
"path": "Sources/AppBundle/util/UniqueToken.swift",
"chars": 344,
"preview": "final class UniqueToken: Equatable, Hashable, CustomStringConvertible, Sendable {\n private let hash = Int.random(in: "
},
{
"path": "Sources/AppBundle/util/accessibility.swift",
"chars": 11852,
"preview": "import AppKit\nimport Common\nimport PrivateApi\n\n@MainActor\nfunc checkAccessibilityPermissions() {\n let options = [axTr"
},
{
"path": "Sources/AppBundle/util/appBundleUtil.swift",
"chars": 4656,
"preview": "import AppKit\nimport Common\nimport Foundation\nimport os\n\nlet signposter = OSSignposter(subsystem: aeroSpaceAppId, catego"
},
{
"path": "Sources/AppBundle/util/axTrustedCheckOptionPrompt.swift",
"chars": 143,
"preview": "@preconcurrency import ApplicationServices\n\nlet axTrustedCheckOptionPrompt: String = kAXTrustedCheckOptionPrompt.takeRet"
},
{
"path": "Sources/AppBundle/util/dumpAxRecursive.swift",
"chars": 4876,
"preview": "import AppKit\nimport Common\n\nfunc dumpAxRecursive(_ ax: AXUIElement, _ kind: AxKind, recursionDepth: Int = 0) -> [String"
},
{
"path": "Sources/AppBundle/windowLevelCache.swift",
"chars": 1936,
"preview": "import CoreGraphics\nimport Foundation\n\n@MainActor\nprivate var cache: [UInt32: MacOsWindowLevel] = [:]\n\n@MainActor\nfunc g"
},
{
"path": "Sources/AppBundleTests/AxUiElementWindowTypeTest.swift",
"chars": 3146,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class AxWindowKindTest: XCTestCase {\n func test() throw"
},
{
"path": "Sources/AppBundleTests/assert.swift",
"chars": 3062,
"preview": "// periphery:ignore:all - Those are utils that can become useful any moment\n@testable import AppBundle\nimport Common\nimp"
},
{
"path": "Sources/AppBundleTests/command/BalanceSizesCommandTest.swift",
"chars": 993,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class BalanceSizesCommandTest: XCTestCase {\n "
},
{
"path": "Sources/AppBundleTests/command/CloseCommandTest.swift",
"chars": 1381,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class CloseCommandTest: XCTestCase {\n overri"
},
{
"path": "Sources/AppBundleTests/command/ExecCommandTest.swift",
"chars": 329,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class ExecCommandTest: XCTestCase {\n overrid"
},
{
"path": "Sources/AppBundleTests/command/FlattenWorkspaceTreeCommandTest.swift",
"chars": 960,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class FlattenWorkspaceTreeCommandTest: XCTestCa"
},
{
"path": "Sources/AppBundleTests/command/FocusCommandTest.swift",
"chars": 11124,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n// todo write tests\n//\n// test 1\n// horizontal\n// wi"
},
{
"path": "Sources/AppBundleTests/command/JoinWithCommandTest.swift",
"chars": 792,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class JoinWithCommandTest: XCTestCase {\n ove"
},
{
"path": "Sources/AppBundleTests/command/ListAppsTest.swift",
"chars": 577,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ListAppsTest: XCTestCase {\n func testParse() {\n "
},
{
"path": "Sources/AppBundleTests/command/ListModesTest.swift",
"chars": 3015,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ListModesTest: XCTestCase {\n func testParseListMo"
},
{
"path": "Sources/AppBundleTests/command/ListMonitorsTest.swift",
"chars": 602,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ListMonitorsTest: XCTestCase {\n func testParseLis"
},
{
"path": "Sources/AppBundleTests/command/ListWindowsTest.swift",
"chars": 4560,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class ListWindowsTest: XCTestCase {\n overrid"
},
{
"path": "Sources/AppBundleTests/command/ListWorkspacesTest.swift",
"chars": 1286,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ListWorkspacesTest: XCTestCase {\n func testParse("
},
{
"path": "Sources/AppBundleTests/command/MoveCommandTest.swift",
"chars": 12081,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class MoveCommandTest: XCTestCase {\n overrid"
},
{
"path": "Sources/AppBundleTests/command/MoveNodeToMonitorCommandTest.swift",
"chars": 805,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class MoveNodeToMonitorCommandTest: XCTestCase "
},
{
"path": "Sources/AppBundleTests/command/MoveNodeToWorkspaceCommandTest.swift",
"chars": 3607,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class MoveNodeToWorkspaceCommandTest: XCTestCas"
},
{
"path": "Sources/AppBundleTests/command/ResizeCommandTest.swift",
"chars": 1381,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ResizeCommandTest: XCTestCase {\n func testParseCo"
},
{
"path": "Sources/AppBundleTests/command/SplitCommandTest.swift",
"chars": 2492,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class SplitCommandTest: XCTestCase {\n overri"
},
{
"path": "Sources/AppBundleTests/command/SubscribeCmdArgsTest.swift",
"chars": 2340,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class SubscribeCmdArgsTest: XCTestCase {\n func testPars"
},
{
"path": "Sources/AppBundleTests/command/SummonWorkspaceCommandTest.swift",
"chars": 333,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class SummonWorkspaceCommandTest: XCTestCase {\n"
},
{
"path": "Sources/AppBundleTests/command/SwapCommandTest.swift",
"chars": 6150,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class SwapCommandTest: XCTestCase {\n overrid"
},
{
"path": "Sources/AppBundleTests/command/WorkspaceCommandTest.swift",
"chars": 1596,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class WorkspaceCommandTest: XCTestCase {\n ov"
},
{
"path": "Sources/AppBundleTests/config/ConfigTest.swift",
"chars": 17253,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class ConfigTest: XCTestCase {\n func testPar"
},
{
"path": "Sources/AppBundleTests/config/ParseEnvVariablesTest.swift",
"chars": 2689,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\n@MainActor\nfinal class ParseEnvVariablesTest: XCTestCase {\n f"
},
{
"path": "Sources/AppBundleTests/config/SplitArgsTest.swift",
"chars": 1039,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class SplitArgsTest: XCTestCase {\n func testSplit() {\n "
},
{
"path": "Sources/AppBundleTests/model/ClientServerTest.swift",
"chars": 5027,
"preview": "@testable import AppBundle\nimport Common\nimport XCTest\n\nfinal class ClientServerTest: XCTestCase {\n func testClientRe"
},
{
"path": "Sources/AppBundleTests/shell/ShellTest.swift",
"chars": 5301,
"preview": "// todo do something about ShellTest. They are commented out to reduce test log verbosity\n// @testable import AppBundle\n"
},
{
"path": "Sources/AppBundleTests/testExtensions.swift",
"chars": 160,
"preview": "@testable import AppBundle\nimport Common\nimport Foundation\nimport TOMLKit\n\nextension [TomlParseError] {\n var descript"
},
{
"path": "Sources/AppBundleTests/testUtil.swift",
"chars": 3130,
"preview": "@testable import AppBundle\nimport Common\nimport Foundation\nimport HotKey\nimport XCTest\n\nlet projectRoot: URL = {\n var"
},
{
"path": "Sources/AppBundleTests/tree/TestApp.swift",
"chars": 1010,
"preview": "@testable import AppBundle\nimport Common\n\nfinal class TestApp: AbstractApp {\n let pid: Int32\n let rawAppBundleId: "
},
{
"path": "Sources/AppBundleTests/tree/TestWindow.swift",
"chars": 1256,
"preview": "@testable import AppBundle\nimport AppKit\n\nfinal class TestWindow: Window, CustomStringConvertible {\n private var _rec"
},
{
"path": "Sources/AppBundleTests/tree/TilingContainer.swift",
"chars": 501,
"preview": "@testable import AppBundle\nimport AppKit\n\nextension TilingContainer {\n @MainActor\n static func newHTiles(parent: N"
},
{
"path": "Sources/AppBundleTests/tree/TreeNodeTest.swift",
"chars": 3374,
"preview": "@testable import AppBundle\nimport XCTest\n\n@MainActor\nfinal class TreeNodeTest: XCTestCase {\n override func setUp() as"
},
{
"path": "Sources/Cli/_main.swift",
"chars": 6647,
"preview": "import Common\nimport Darwin\nimport Foundation\nimport Network\n\nlet usage =\n \"\"\"\n USAGE: \\(CommandLine.arguments.fir"
},
{
"path": "Sources/Cli/cliUtil.swift",
"chars": 180,
"preview": "import Common\nimport Darwin\nimport Foundation\n\nlet cliClientVersionAndHash: String = \"\\(aeroSpaceAppVersion) \\(gitHash)\""
},
{
"path": "Sources/Cli/subcommandDescriptionsGenerated.swift",
"chars": 3042,
"preview": "// FILE IS GENERATED FROM docs/aerospace-*.adoc files\n// TO REGENERATE THE FILE RUN generate.sh\n\nlet subcommandDescripti"
},
{
"path": "Sources/Common/appMetadata.swift",
"chars": 322,
"preview": "public let stableAeroSpaceAppId: String = \"bobko.aerospace\"\n#if DEBUG\n public let aeroSpaceAppId: String = \"bobko.aer"
},
{
"path": "Sources/Common/cmdArgs/ArgParser.swift",
"chars": 3895,
"preview": "public typealias SendableWritableKeyPath<Root, Value> = Sendable & WritableKeyPath<Root, Value>\ntypealias ArgParserFun<I"
},
{
"path": "Sources/Common/cmdArgs/ArgParserInput.swift",
"chars": 905,
"preview": "struct PosArgParserInput: ArgParserInput {\n /*conforms*/ let index: Int\n /*conforms*/ let args: StrArrSlice\n\n v"
},
{
"path": "Sources/Common/cmdArgs/SubArgParser.swift",
"chars": 2465,
"preview": "typealias SubArgParser<Root, Value> = ArgParser<SubArgParserInput, Root, Value, ()>\n\nfunc parseUInt32SubArg(i: SubArgPar"
},
{
"path": "Sources/Common/cmdArgs/cmdArgsManifest.swift",
"chars": 6214,
"preview": "public enum CmdKind: String, CaseIterable, Equatable, Sendable {\n // Sorted\n\n case balanceSizes = \"balance-sizes\"\n"
},
{
"path": "Sources/Common/cmdArgs/impl/BalanceSizesCmdArgs.swift",
"chars": 447,
"preview": "public struct BalanceSizesCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public init"
},
{
"path": "Sources/Common/cmdArgs/impl/CloseAllWindowsButCurrentCmdArgs.swift",
"chars": 572,
"preview": "public struct CloseAllWindowsButCurrentCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n "
},
{
"path": "Sources/Common/cmdArgs/impl/CloseCmdArgs.swift",
"chars": 542,
"preview": "public struct CloseCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public init(rawArg"
},
{
"path": "Sources/Common/cmdArgs/impl/ConfigCmdArgs.swift",
"chars": 2274,
"preview": "public struct ConfigCmdArgs: CmdArgs, Equatable {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public"
},
{
"path": "Sources/Common/cmdArgs/impl/DebugWindowsCmdArgs.swift",
"chars": 485,
"preview": "public struct DebugWindowsCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public init"
},
{
"path": "Sources/Common/cmdArgs/impl/EnableCmdArgs.swift",
"chars": 1325,
"preview": "public struct EnableCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n fileprivate init("
},
{
"path": "Sources/Common/cmdArgs/impl/ExecAndForgetCmdArgs.swift",
"chars": 479,
"preview": "public struct ExecAndForgetCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public sta"
},
{
"path": "Sources/Common/cmdArgs/impl/FlattenWorkspaceTreeCmdArgs.swift",
"chars": 472,
"preview": "public struct FlattenWorkspaceTreeCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n pub"
},
{
"path": "Sources/Common/cmdArgs/impl/FocusBackAndForthCmdArgs.swift",
"chars": 404,
"preview": "public struct FocusBackAndForthCmdArgs: CmdArgs {\n /*conforms*/ public var commonState: CmdArgsCommonState\n public"
}
]
// ... and 287 more files (download for full content)
About this extraction
This page contains the full source code of the nikitabobko/AeroSpace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 487 files (1.4 MB), approximately 426.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.