Skip to content

Commit

Permalink
resize command: support absolute values
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Oct 31, 2023
1 parent e8e0f2c commit d9bac2d
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 31 deletions.
8 changes: 8 additions & 0 deletions AeroSpace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
1311398A83B998908773C54D /* FocusCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EAADE8D2FB5D05FA5456B0 /* FocusCommandTest.swift */; };
1C46EBB55D401C0D1AFD50F0 /* CollectionEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CE37C1B8D858C81A396F40 /* CollectionEx.swift */; };
1D408CDF1A489E527327EB15 /* CompositeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82CD9670B7A6050073E0F76 /* CompositeCommand.swift */; };
22175400298B985658E774EE /* ResizeCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */; };
238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */ = {isa = PBXBuildFile; fileRef = 8FE45A887100EB70912B07F0 /* default-config.toml */; };
29AC28A25E1A66C608EBD7ED /* WorkspaceCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DD32B1711B8EFCC834B68E /* WorkspaceCommand.swift */; };
2C73CDB1A933FCA5616CDFB3 /* WorkspaceEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B60F7A58DAD4D3AF2788E4 /* WorkspaceEx.swift */; };
Expand All @@ -36,6 +37,7 @@
635733FDDF37E44364372B74 /* MruStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954A434EE57D76F5A9D4140D /* MruStack.swift */; };
64A058E536F1EEF7F01043AF /* TOMLKit in Frameworks */ = {isa = PBXBuildFile; productRef = EC8E4F2CA4FF8884F9F59975 /* TOMLKit */; };
66E6CDA75DDD5E4B9647EDE2 /* AeroSpaceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E81623E8954701269A22322 /* AeroSpaceApp.swift */; };
6A16BC0B4B67A45EF90F9DFE /* ResultEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A342893BEB5525BA1F8B74 /* ResultEx.swift */; };
6E4E235FDA41307B19F16182 /* ModeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CD3C2A0E86CDB9DF312AB /* ModeCommand.swift */; };
70A82A4A9DFC89286C4F7696 /* TilingContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00ECDFE176777828D560A737 /* TilingContainer.swift */; };
77FA83225024151CD556E1ED /* CloseAllWindowsButCurrentCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99853C505D93E41F6531C324 /* CloseAllWindowsButCurrentCommand.swift */; };
Expand Down Expand Up @@ -96,6 +98,7 @@

/* Begin PBXFileReference section */
00ECDFE176777828D560A737 /* TilingContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TilingContainer.swift; sourceTree = "<group>"; };
07A342893BEB5525BA1F8B74 /* ResultEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultEx.swift; sourceTree = "<group>"; };
09685297933511208058F7CF /* AeroSpace.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AeroSpace.app; sourceTree = BUILT_PRODUCTS_DIR; };
0AEE5470AF418906B180A593 /* mouse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mouse.swift; sourceTree = "<group>"; };
0D9301F99737BE4888DBC2A3 /* FocusedWorkspaceSourceOfTruth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedWorkspaceSourceOfTruth.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -128,6 +131,7 @@
6E330182A6FACBABABB6FC51 /* MoveInCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveInCommand.swift; sourceTree = "<group>"; };
6F1905935B0C61590A96EFEF /* TestWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestWindow.swift; sourceTree = "<group>"; };
7A247B616D1951777C565D02 /* MoveThroughCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveThroughCommandTest.swift; sourceTree = "<group>"; };
7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeCommandTest.swift; sourceTree = "<group>"; };
7DBD002C4A68AED07BB63EFA /* MoveContainerToWorkspaceCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveContainerToWorkspaceCommandTest.swift; sourceTree = "<group>"; };
82476B9BEBAC00EB9E32256F /* AeroAny.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AeroAny.swift; sourceTree = "<group>"; };
883D7F7F87FBE7D0BDE4E87F /* ArrayEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayEx.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -204,6 +208,7 @@
7DBD002C4A68AED07BB63EFA /* MoveContainerToWorkspaceCommandTest.swift */,
13E5ED8E885494CAEEC70A90 /* MoveInCommandTest.swift */,
7A247B616D1951777C565D02 /* MoveThroughCommandTest.swift */,
7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */,
);
path = command;
sourceTree = "<group>";
Expand Down Expand Up @@ -358,6 +363,7 @@
954A434EE57D76F5A9D4140D /* MruStack.swift */,
A9EDFD4A9F45182CA6E0BD7B /* OptionalEx.swift */,
28B788A95DD3C267878E05B5 /* Rect.swift */,
07A342893BEB5525BA1F8B74 /* ResultEx.swift */,
B1316FFED5D1044CB693EA45 /* SelectorComparator.swift */,
AAE5DCAEC5EE619CE33859E7 /* SequenceEx.swift */,
14FABE2521026C8394254D97 /* showMessageToUser.swift */,
Expand Down Expand Up @@ -466,6 +472,7 @@
A55F31B0CC357B37108B8F54 /* MoveContainerToWorkspaceCommandTest.swift in Sources */,
3BE358D5C209F279BB71AC95 /* MoveInCommandTest.swift in Sources */,
B1E527CF4941A4E9B8D9C3D0 /* MoveThroughCommandTest.swift in Sources */,
22175400298B985658E774EE /* ResizeCommandTest.swift in Sources */,
7ED8C2A66DD6F903796F090C /* TestApp.swift in Sources */,
E22ACB36C90695FBAC78226E /* TestWindow.swift in Sources */,
70A82A4A9DFC89286C4F7696 /* TilingContainer.swift in Sources */,
Expand Down Expand Up @@ -510,6 +517,7 @@
78EE0CEF814ABDBA67941B84 /* Rect.swift in Sources */,
FC35D6D0A678CC802972C6FE /* ReloadConfigCommand.swift in Sources */,
D24D02B1FD87424B908986AF /* ResizeCommand.swift in Sources */,
6A16BC0B4B67A45EF90F9DFE /* ResultEx.swift in Sources */,
991943D50DF9EDBF321A66F1 /* SelectorComparator.swift in Sources */,
AE76A183D0454E4C8ADCE380 /* SequenceEx.swift in Sources */,
E5682579AEC6B84CF6FCE90D /* TilingContainer.swift in Sources */,
Expand Down
17 changes: 15 additions & 2 deletions src/command/ResizeCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ struct ResizeCommand: Command { // todo cover with tests
case width, height, smart
}

enum ResizeMode: String {
case set, add, subtract
}

let dimension: Dimension
let diff: Int
let mode: ResizeMode
let unit: UInt

func runWithoutLayout() { // todo support key repeat
check(Thread.current.isMainThread)
Expand All @@ -29,7 +34,15 @@ struct ResizeCommand: Command { // todo cover with tests
parent = directParent
orientation = parent.orientation
}
let diff = CGFloat(diff)
let diff: CGFloat
switch mode {
case .set:
diff = CGFloat(unit) - window.getWeight(orientation)
case .add:
diff = CGFloat(unit)
case .subtract:
diff = -CGFloat(unit)
}

guard let childDiff = diff.div(parent.children.count - 1) else { return }
parent.children.lazy
Expand Down
29 changes: 22 additions & 7 deletions src/command/parseCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func parseCommand(_ raw: TOMLValueConvertible) -> ParsedCommand<Command> {
}
}

private func parseSingleCommand(_ raw: String) -> ParsedCommand<Command> {
func parseSingleCommand(_ raw: String) -> ParsedCommand<Command> {
let words = raw.split(separator: " ")
let args = words[1...].map { String($0) }
let firstWord = String(words.first ?? "")
Expand All @@ -36,13 +36,28 @@ private func parseSingleCommand(_ raw: String) -> ParsedCommand<Command> {
.flatMap { MoveWorkspaceToDisplayCommand.DisplayTarget(rawValue: $0).orFailure("Can't parse '\(firstWord)' display target") }
.map { MoveWorkspaceToDisplayCommand(displayTarget: $0) }
} else if firstWord == "resize" {
let arg1 = args.getOrNil(atIndex: 0).orFailure("''\(firstWord)' must have two parameters")
let mustHaveTwoArgsMessage = "''\(firstWord)' command must have two parameters"
let dimension = args.getOrNil(atIndex: 0).orFailure(mustHaveTwoArgsMessage)
.flatMap { ResizeCommand.Dimension(rawValue: String($0)).orFailure("Can't parse '\(firstWord)' first arg") }
let arg2 = args.getOrNil(atIndex: 1).orFailure("''\(firstWord)' must have two parameters")
.flatMap { Int($0).orFailure("Can't parse '\(firstWord)' second arg") }
return arg1.flatMap { arg1 in
arg2.map { arg2 in
ResizeCommand(dimension: arg1, diff: arg2)
let secondArg: Result<String, String> = args.getOrNil(atIndex: 1).orFailure(mustHaveTwoArgsMessage)
let mode = secondArg.map { (secondArg: String) in
if secondArg.starts(with: "+") {
return ResizeCommand.ResizeMode.add
} else if secondArg.starts(with: "-") {
return ResizeCommand.ResizeMode.subtract
} else {
return ResizeCommand.ResizeMode.set
}
}
let unit = secondArg.flatMap {
UInt($0.removePrefix("+").removePrefix("-"))
.orFailure("'\(firstWord)' command: Second arg must be a number")
}
return dimension.flatMap { dimension in
mode.flatMap { mode in
unit.map { unit in
ResizeCommand(dimension: dimension, mode: mode, unit: unit)
}
}
}
} else if firstWord == "exec-and-wait" {
Expand Down
23 changes: 1 addition & 22 deletions src/config/parseConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,33 +51,12 @@ enum TomlParseError: Error, CustomStringConvertible {

private typealias ParsedTomlResult<T> = Result<T, TomlParseError>

private extension Result {
func unwrapAndAppendErrors(_ errors: inout [Failure]) -> Success? {
switch self {
case .success(let success):
return success
case .failure(let error):
errors += [error]
return nil
}
}

func getOrNils() -> (Success?, Failure?) {
switch self {
case .success(let success):
return (success, nil)
case .failure(let failure):
return (nil, failure)
}
}
}

private extension ParserProtocol {
func transformRawConfig(_ raw: RawConfig,
_ value: TOMLValueConvertible,
_ backtrace: TomlBacktrace,
_ errors: inout [TomlParseError]) -> RawConfig {
raw.copy(keyPath, parse(value, backtrace).unwrapAndAppendErrors(&errors))
raw.copy(keyPath, parse(value, backtrace).getOrNil(appendErrorTo: &errors))
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/util/ResultEx.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
extension Result {
func getOrNil(appendErrorTo errors: inout [Failure]) -> Success? {
switch self {
case .success(let success):
return success
case .failure(let error):
errors += [error]
return nil
}
}

func getOrNils() -> (Success?, Failure?) {
switch self {
case .success(let success):
return (success, nil)
case .failure(let failure):
return (nil, failure)
}
}
}
38 changes: 38 additions & 0 deletions test/command/ResizeCommandTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import XCTest
@testable import AeroSpace_Debug

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

func testParseCommand() {
testParseCommandSucc("resize smart +10", .resizeCommand(dimension: .smart, mode: .add, unit: 10))
testParseCommandSucc("resize smart -10", .resizeCommand(dimension: .smart, mode: .subtract, unit: 10))
testParseCommandSucc("resize smart 10", .resizeCommand(dimension: .smart, mode: .set, unit: 10))

testParseCommandSucc("resize height 10", .resizeCommand(dimension: .height, mode: .set, unit: 10))
testParseCommandSucc("resize width 10", .resizeCommand(dimension: .width, mode: .set, unit: 10))

testParseCommandFail("resize s 10", msg: "Can't parse 'resize' first arg")
testParseCommandFail("resize smart foo", msg: "'resize' command: Second arg must be a number")
}
}

private func testParseCommandSucc(_ command: String, _ expected: CommandDescription) {
let parsed = parseSingleCommand(command)
switch parsed {
case .success(let command):
XCTAssertEqual(command.describe, expected)
case .failure(let msg):
XCTFail(msg)
}
}

private func testParseCommandFail(_ command: String, msg expected: String) {
let parsed = parseSingleCommand(command)
switch parsed {
case .success(let command):
XCTFail("\(command) isn't supposed to be parcelable")
case .failure(let msg):
XCTAssertEqual(msg, expected)
}
}
3 changes: 3 additions & 0 deletions test/config/ConfigTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,14 @@ extension Command {
var describe: CommandDescription {
if let focus = self as? FocusCommand {
return .focusCommand(focus.direction)
} else if let resize = self as? ResizeCommand {
return .resizeCommand(dimension: resize.dimension, mode: resize.mode, unit: resize.unit)
}
error("Unsupported command: \(self)")
}
}

enum CommandDescription: Equatable {
case focusCommand(CardinalDirection)
case resizeCommand(dimension: ResizeCommand.Dimension, mode: ResizeCommand.ResizeMode, unit: UInt)
}

0 comments on commit d9bac2d

Please sign in to comment.