Skip to content

Commit

Permalink
Implement list-monitors
Browse files Browse the repository at this point in the history
#16 WIP
  • Loading branch information
nikitabobko committed Dec 26, 2023
1 parent cbc8b02 commit 547c55c
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 0 deletions.
8 changes: 8 additions & 0 deletions AeroSpace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
64A058E536F1EEF7F01043AF /* TOMLKit in Frameworks */ = {isa = PBXBuildFile; productRef = EC8E4F2CA4FF8884F9F59975 /* TOMLKit */; };
66E6CDA75DDD5E4B9647EDE2 /* AeroSpaceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E81623E8954701269A22322 /* AeroSpaceApp.swift */; };
6898172108196A4694FD6A42 /* MoveCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9857501E54FC080D2A62DCE4 /* MoveCommandTest.swift */; };
6A107B84BFF7A37ABA225C79 /* ListMonitorsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC4A8803D3A23C26287D449E /* ListMonitorsTest.swift */; };
6E4E235FDA41307B19F16182 /* ModeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CD3C2A0E86CDB9DF312AB /* ModeCommand.swift */; };
70A82A4A9DFC89286C4F7696 /* TilingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00ECDFE176777828D560A737 /* TilingContainer.swift */; };
74ED0272EF2A3BA6387FD413 /* Rect.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE5DD3E9DC82D247853956B /* Rect.swift */; };
Expand All @@ -54,6 +55,7 @@
9305D5380C7A1D293F24FF41 /* MoveNodeToWorkspaceCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9589EFDEBA4EB9C7DBAFCFD /* MoveNodeToWorkspaceCommandTest.swift */; };
93D44EA41776738B4758C28D /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 976540EBEACF846D598CD6E1 /* util.swift */; };
9484763B3A9F7F8FAD1A2952 /* ListAppsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE27ED039913415411E38C5D /* ListAppsCommand.swift */; };
988D8647779567FEB306B3FB /* ListMonitorsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47052DA50C58DB9DCBE5EC53 /* ListMonitorsCommand.swift */; };
991943D50DF9EDBF321A66F1 /* SelectorComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1316FFED5D1044CB693EA45 /* SelectorComparator.swift */; };
9A138A729245BD2723148583 /* focused.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61FE8B343B068F0FFFC2373 /* focused.swift */; };
9D34BD7DE311254BF52F5EA2 /* testUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C03164B8BFD1E59779C6E /* testUtil.swift */; };
Expand Down Expand Up @@ -127,6 +129,7 @@
3C2E5977331398421A4FC168 /* GlobalObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalObserver.swift; sourceTree = "<group>"; };
43DD32B1711B8EFCC834B68E /* WorkspaceCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceCommand.swift; sourceTree = "<group>"; };
458AD0CD907B56A99DA821C4 /* AeroSpace-cli */ = {isa = PBXFileReference; includeInIndex = 0; path = "AeroSpace-cli"; sourceTree = BUILT_PRODUCTS_DIR; };
47052DA50C58DB9DCBE5EC53 /* ListMonitorsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListMonitorsCommand.swift; sourceTree = "<group>"; };
4B0CD3C2A0E86CDB9DF312AB /* ModeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModeCommand.swift; sourceTree = "<group>"; };
51CC651BEB897CB7A555D230 /* MoveWorkspaceToMonitorCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveWorkspaceToMonitorCommand.swift; sourceTree = "<group>"; };
526B113159987FA43EA41120 /* refresh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = refresh.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -180,6 +183,7 @@
C848D6E57FDF22AAF0FB45E6 /* TilingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TilingContainer.swift; sourceTree = "<group>"; };
CB03A4736BC3F6D19E4E69F3 /* parseCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = parseCommand.swift; sourceTree = "<group>"; };
CB371951459F8CB0137788C2 /* ResizeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeCommand.swift; sourceTree = "<group>"; };
CC4A8803D3A23C26287D449E /* ListMonitorsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListMonitorsTest.swift; sourceTree = "<group>"; };
CCDFB9D7321F08B5CCEF6AFB /* parseGaps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = parseGaps.swift; sourceTree = "<group>"; };
CD90FB41FD41602120F67FF5 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
CF3C9038C846369FDD71D1D2 /* ReloadConfigCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReloadConfigCommand.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -247,6 +251,7 @@
53FDECECC773EBA30661EB8A /* FlattenWorkspaceTreeCommandTest.swift */,
D0EAADE8D2FB5D05FA5456B0 /* FocusCommandTest.swift */,
6D9C5ED5AC77D80F1CCD103F /* JoinWithCommandTest.swift */,
CC4A8803D3A23C26287D449E /* ListMonitorsTest.swift */,
9857501E54FC080D2A62DCE4 /* MoveCommandTest.swift */,
E9589EFDEBA4EB9C7DBAFCFD /* MoveNodeToWorkspaceCommandTest.swift */,
7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */,
Expand Down Expand Up @@ -395,6 +400,7 @@
D27A7AF7E9A049700BDE276B /* JoinWithCommand.swift */,
1A2B673C67B00DBFCC27FFE7 /* LayoutCommand.swift */,
DE27ED039913415411E38C5D /* ListAppsCommand.swift */,
47052DA50C58DB9DCBE5EC53 /* ListMonitorsCommand.swift */,
4B0CD3C2A0E86CDB9DF312AB /* ModeCommand.swift */,
1376845BAB666880919B9AA2 /* MoveCommand.swift */,
25AC44D0E9450867215FCBEC /* MoveNodeToWorkspaceCommand.swift */,
Expand Down Expand Up @@ -571,6 +577,7 @@
374CE35B85B941B8F584C113 /* FlattenWorkspaceTreeCommandTest.swift in Sources */,
1311398A83B998908773C54D /* FocusCommandTest.swift in Sources */,
8FD14C98B263A6CA0174DDF1 /* JoinWithCommandTest.swift in Sources */,
6A107B84BFF7A37ABA225C79 /* ListMonitorsTest.swift in Sources */,
6898172108196A4694FD6A42 /* MoveCommandTest.swift in Sources */,
9305D5380C7A1D293F24FF41 /* MoveNodeToWorkspaceCommandTest.swift in Sources */,
22175400298B985658E774EE /* ResizeCommandTest.swift in Sources */,
Expand Down Expand Up @@ -608,6 +615,7 @@
5DA2DA21600E8B5BCA3DCFC0 /* LayoutCommand.swift in Sources */,
D12277DFEAAAC7AE19D0B629 /* LazySequenceProtocolEx.swift in Sources */,
9484763B3A9F7F8FAD1A2952 /* ListAppsCommand.swift in Sources */,
988D8647779567FEB306B3FB /* ListMonitorsCommand.swift in Sources */,
BD6301B2CFC16FDE4223ACB8 /* MacApp.swift in Sources */,
EECC59858691B99A95542D72 /* MacWindow.swift in Sources */,
6E4E235FDA41307B19F16182 /* ModeCommand.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions LocalPackage/Sources/Common/cmdArgs/CmdKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum CmdKind: String, CaseIterable, Equatable {
case joinWith = "join-with"
case layout
case listApps = "list-apps"
case listMonitors = "list-monitors"
case mode
case move = "move"
case moveNodeToWorkspace = "move-node-to-workspace"
Expand Down
2 changes: 2 additions & 0 deletions LocalPackage/Sources/Common/cmdArgs/parseCmdArgs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ private func initSubcommands() -> [String: any SubCommandParserProtocol] {
result[kind.rawValue] = SubCommandParser(parseLayoutCmdArgs)
case .listApps:
result[kind.rawValue] = noArgsSubCommandParser(ListAppsCmdArgs())
case .listMonitors:
result[kind.rawValue] = SubCommandParser(parseListMonitorsCmdArgs)
case .mode:
result[kind.rawValue] = SubCommandParser(parseModeCmdArgs)
case .moveNodeToWorkspace:
Expand Down
5 changes: 5 additions & 0 deletions LocalPackage/Sources/Common/cmdArgs/parseCmdArgsUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ public protocol RawCmdArgs: Copyable {

public extension RawCmdArgs {
static var info: CmdStaticInfo { Self.parser.info }

func getOptionWithDefault<K>(_ path: KeyPath<Self, K?>) -> K {
// If Swift had covariant / contravariant generics, I'd not need this runtime check
self[keyPath: path] ?? errorT("\(path) must provide a default value")
}
}

public protocol CmdArgs {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
public struct ListMonitorsCmdArgs: RawCmdArgs, CmdArgs, Equatable {
public static let parser: CmdParser<Self> = cmdParser(
kind: .listMonitors,
allowInConfig: false,
help: """
USAGE: list-monitors [-h|--help] [--focused] [--mouse]
OPTIONS:
-h, --help Print help
--focused Only print the focused monitor
--mouse Only print the monitor with the mouse
""",
options: [
"--focused": trueBoolFlag(\.focused),
"--mouse": trueBoolFlag(\.mouse)
],
arguments: []
)

public var focused: Bool? = false
public var mouse: Bool? = false

public init() {}
}

public func parseListMonitorsCmdArgs(_ args: [String]) -> ParsedCmd<ListMonitorsCmdArgs> {
parseRawCmdArgs(ListMonitorsCmdArgs(), args)
}
41 changes: 41 additions & 0 deletions docs/aerospace-list-monitors.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
= aerospace-list-monitors(1)
include::util/man-attributes.adoc[]
:manname: aerospace-list-monitors
// tag::purpose[]
:manpurpose: Prints the list of monitors
// end::purpose[]

== Synopsis
// tag::synopsis[]
list-monitors [-h|--help] [--focused] [--mouse]
// end::synopsis[]

== Description

// tag::body[]
{manpurpose}

Output format is the table with 2 columns:

* Monitor unique sequence ID (ID orders monitors from left to right)
* Monitor name

Output example:

----
1 | Built-in Retina Display
2 | DELL U2723QE
----

NOTE: monitor sequence ID may change if you plug or unplug monitors

You can use `awk` to get values of particular column: `awk -F '|' '{print $2}'`
// end::body[]

include::util/conditional-options-header.adoc[]

-h, --help:: Print help
--focused:: Only print the focused monitor
--mouse:: Only print the monitor with the mouse

include::util/man-footer.adoc[]
7 changes: 7 additions & 0 deletions docs/commands.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ include::aerospace-list-apps.adoc[tags=synopsis]
include::aerospace-list-apps.adoc[tags=purpose]
include::aerospace-list-apps.adoc[tags=body]

=== list-monitors
----
include::aerospace-list-monitors.adoc[tags=synopsis]
----
include::aerospace-list-monitors.adoc[tags=purpose]
include::aerospace-list-monitors.adoc[tags=body]

=== version
----
include::aerospace-version.adoc[tags=synopsis]
Expand Down
28 changes: 28 additions & 0 deletions src/command/ListMonitorsCommand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Common

struct ListMonitorsCommand: Command {
let info: CmdStaticInfo = ListMonitorsCmdArgs.info
let args: ListMonitorsCmdArgs

func _run(_ subject: inout CommandSubject, _ stdout: inout String) -> Bool {
check(Thread.current.isMainThread)
var result: [(Int, Monitor)] = sortedMonitors.withIndex
if args.getOptionWithDefault(\.focused) {
result = result.filter { (_, monitor) in
monitor.activeWorkspace.name == focusedWorkspaceName
}
}
if args.getOptionWithDefault(\.mouse) {
result = result.filter { (_, monitor) in
monitor.activeWorkspace.name == mouseLocation.monitorApproximation.activeWorkspace.name
}
}
stdout += result
.map { (index, monitor) in
[String(index + 1), monitor.name]
}
.toPaddingTable()
+ "\n"
return true
}
}
2 changes: 2 additions & 0 deletions src/command/parseCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ extension CmdArgs {
command = LayoutCommand(args: self as! LayoutCmdArgs)
case .listApps:
command = ListAppsCommand()
case .listMonitors:
command = ListMonitorsCommand(args: self as! ListMonitorsCmdArgs)
case .mode:
command = ModeCommand(args: self as! ModeCmdArgs)
case .moveNodeToWorkspace:
Expand Down
8 changes: 8 additions & 0 deletions src/util/SequenceEx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ extension Sequence {
func grouped<Group>(by criterion: (_ transforming: Element) -> Group) -> [Group: [Element]] {
Dictionary(grouping: self, by: criterion)
}

var withIndex: [(index: Int, value: Element)] {
var index = -1
return map {
index += 1
return (index, $0)
}
}
}

extension Sequence where Self.Element: Comparable {
Expand Down
12 changes: 12 additions & 0 deletions test/command/ListMonitorsTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import XCTest
import Common
@testable import AeroSpace_Debug

final class ListMonitorsTest: XCTestCase {
override func setUpWithError() throws { setUpWorkspacesForTests() }

func testParseListMonitorsCommand() {
XCTAssertEqual(parseCommand("list-monitors").cmdOrNil?.describe, .listMonitors(args: ListMonitorsCmdArgs()))
XCTAssertEqual(parseCommand("list-monitors --focused").cmdOrNil?.describe, .listMonitors(args: ListMonitorsCmdArgs().copy(\.focused, true)))
}
}
3 changes: 3 additions & 0 deletions test/config/ConfigTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ extension Command {
return .execAndForget(exec.args.bashScript)
} else if let moveNodeToWorkspace = self as? MoveNodeToWorkspaceCommand {
return .moveNodeToWorkspace(args: moveNodeToWorkspace.args)
} else if let listMonitors = self as? ListMonitorsCommand {
return .listMonitors(args: listMonitors.args)
} else if let workspace = self as? WorkspaceCommand {
return .workspace(args: workspace.args)
}
Expand All @@ -361,4 +363,5 @@ enum CommandDescription: Equatable { // todo do I need this class after CmdArgs
case execAndForget(String)
case moveNodeToWorkspace(args: MoveNodeToWorkspaceCmdArgs)
case workspace(args: WorkspaceCmdArgs)
case listMonitors(args: ListMonitorsCmdArgs)
}

0 comments on commit 547c55c

Please sign in to comment.