aerospace fork
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262import Commonimport Foundation
struct WindowWithPrefetchedTitle { let window: Window let title: String?
private init(window: Window, title: String?) { self.window = window self.title = title }
static func resolveWindow(_ window: Window, for formatVar: FormatVar, _ cm: CancellationMode) async throws -> Self { try await resolveWindow(window, needsTitle: formatVar == .window(.windowTitle), cm) }
static func resolveWindow(_ window: Window, for format: [InterToken<InterVar>], _ cm: CancellationMode) async throws -> Self { let needsTitle = format.contains { $0 == .interVar(.formatVar(.window(.windowTitle))) } return try await resolveWindow(window, needsTitle: needsTitle, cm) }
private static func resolveWindow(_ window: Window, needsTitle: Bool, _ cm: CancellationMode) async throws -> Self { let title = needsTitle ? try await window.getTitle(cm) : nil return .init(window: window, title: title) }
static func forTest(window: Window, title: String?) -> Self { .init(window: window, title: title) }}
enum AeroObj { case window(WindowWithPrefetchedTitle) case workspace(Workspace) case app(any AbstractApp) case monitor(MonitorInfo)
var kind: AeroObjKind { switch self { case .window: .window case .workspace: .workspace case .app: .app case .monitor: .monitor } }}
extension [AeroObj] { @MainActor func format(_ format: [InterToken<InterVar>]) -> Result<[String], [InterVarExpansionError]> { var cellTable: [[Cell<String>]] = [] for obj in self { var line: [Cell<String>] = [] var curCell: String = "" var errors = [InterVarExpansionError]() for token in format { switch token { case .interVar(.plainInterVar(.rightPadding)): 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) } 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(Int64) case string(String)
enum Kind: String { case bool = "Bool" case int = "Int" case string = "String" }
var kind: Kind { switch self { case .bool: .bool case .int: .int case .string: .string } }
func toString() -> String { switch self { case .bool(let x): x.description case .int(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 .string(let x): x } var container = encoder.singleValueContainer() try container.encode(value) }
public static func int(_ int: UInt32) -> Self { .int(Int64(exactly: int).orDie()) } public static func int(_ int: Int32) -> Self { .int(Int64(exactly: int).orDie()) } public static func int(_ int: Int) -> Self { .int(Int64(exactly: int).orDie()) }}
private struct Cell<T> { let value: T let rightPadding: Bool}
extension FormatVar { @MainActor func expandFormatVar(obj: AeroObj) -> Result<Primitive, InterVarExpansionError> { switch (obj, self) { case (.window(let w), .workspace): return w.window.nodeWorkspace.flatMap(AeroObj.workspace).map(expandFormatVar) ?? .success(.string("NULL-WORKSPACE")) case (.window(let w), .monitor): return w.window.nodeMonitor.flatMap(AeroObj.monitor).map(expandFormatVar) ?? .success(.string("NULL-MONITOR")) case (.window(let w), .app): return expandFormatVar(obj: .app(w.window.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, self) { case (.window(let w), .window(let f)): return switch f { case .windowId: .success(.int(w.window.windowId)) case .windowIsFullscreen: .success(.bool(w.window.isFullscreen)) case .windowTitle: .success(.string(w.title.orDie("Title wasn't prefetched"))) case .windowLayout, .windowParentContainerLayout: toLayoutResult(w: w.window) } 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(.int(a.pid)) case .appExecPath: .success(.string(a.execPath ?? "NULL-APP-EXEC-PATH")) case .appBundlePath: .success(.string(a.bundlePath ?? "NULL-APP-BUNDLE-PATH")) } default: break } return .failure(.unknownInterpolationVariable(unknownInterpolationVariable(variable: rawValue, obj))) }}
enum InterVarExpansionError: LocalizedError, CustomStringConvertible { case unknownInterpolationVariable(String) case nullParent(String) case notPossible(String) case windowParentIllegalRelation(String) case rightPaddingCannotBeExpanded(String)
public var description: String { switch self { case .unknownInterpolationVariable(let msg): msg case .nullParent(let msg): msg case .notPossible(let msg): msg case .windowParentIllegalRelation(let msg): msg case .rightPaddingCannotBeExpanded(let msg): msg } }
public var errorDescription: String? { description }}
extension PlainInterVar { @MainActor func expandFormatVar() -> Result<Primitive, InterVarExpansionError> { switch self { case .newline: .success(.string("\n")) case .tab: .success(.string("\t")) case .rightPadding: .failure(.rightPaddingCannotBeExpanded("\(PlainInterVar.rightPadding.rawValue.singleQuoted) interpolation variable cannot be expanded")) } }}
extension InterVar { @MainActor func expandFormatVar(obj: AeroObj) -> Result<Primitive, InterVarExpansionError> { switch self { case .formatVar(let it): it.expandFormatVar(obj: obj) case .plainInterVar(let it): it.expandFormatVar() } }}
func unknownInterpolationVariable(variable: String, _ obj: AeroObj) -> String { "Unknown interpolation variable '\(variable)'. " + "Possible values:\n\(getAvailableInterVars(for: obj.kind).joined(separator: "\n").prependLines(" "))"}
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 case (.masterStack, .h): return LayoutCmdArgs.LayoutDescription.h_master_stack.rawValue case (.masterStack, .v): return LayoutCmdArgs.LayoutDescription.v_master_stack.rawValue }}
private func toLayoutResult(w: Window) -> Result<Primitive, InterVarExpansionError> { guard let parent = w.parent else { return .failure(.nullParent("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(.notPossible("Not possible")) case .shimContainerRelation: .failure(.windowParentIllegalRelation("Window cannot have a shim container relation")) }}