Skip to content

Commit

Permalink
Replace double ampersand with comma #315 (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
akantsevoi authored and nicklockwood committed Dec 14, 2018
1 parent 9c00868 commit c000ee5
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 6 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,28 @@ Or for `--wrapcollections beforefirst`:
]
```

***commasInsteadOfAmpersands*** - `--commasinsteadofampersands` replaces `&&` operator to `,` in if and guard constructions.

```diff
- if true && true {
+ if true, true {
```

```diff
- guard true && true
+ guard true, true
```

```diff
- if functionReturnsBool() && true {
+ if functionReturnsBool(), true {
```

```diff
- if functionReturnsBool() && variable
+ if functionReturnsBool(), variable
```


Config
------
Expand Down
1 change: 1 addition & 0 deletions Sources/CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ func printHelp(as type: CLI.OutputType) {
--trimwhitespace trim trailing space. "always" (default) or "nonblank-lines"
--wraparguments wrap function args. "beforefirst", "afterfirst", "preserve"
--wrapcollections wrap array/dict. "beforefirst", "afterfirst", "preserve"
--commasinsteadofampersands replaces `&&` symbols to `,`
""", as: type)
print("")
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public struct FormatOptions: CustomStringConvertible {
public var removeSelf: Bool
public var experimentalRules: Bool
public var fragment: Bool
public var commasInsteadOfAmpersands: Bool
public var importGrouping: ImportGrouping

// Doesn't really belong here, but hard to put elsewhere
Expand Down Expand Up @@ -246,6 +247,7 @@ public struct FormatOptions: CustomStringConvertible {
experimentalRules: Bool = false,
fragment: Bool = false,
ignoreConflictMarkers: Bool = false,
commasInsteadOfAmpersands: Bool = true,
importGrouping: ImportGrouping = .alphabetized) {
self.indent = indent
self.linebreak = linebreak
Expand Down Expand Up @@ -280,6 +282,7 @@ public struct FormatOptions: CustomStringConvertible {
self.experimentalRules = experimentalRules
self.fragment = fragment
self.ignoreConflictMarkers = ignoreConflictMarkers
self.commasInsteadOfAmpersands = commasInsteadOfAmpersands
self.importGrouping = importGrouping
}

Expand Down
10 changes: 9 additions & 1 deletion Sources/OptionsDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ extension FormatOptions.Descriptor {
stripUnusedArguments,
elsePosition,
removeSelf,
commasInsteadOfAmpersands,
importGrouping,

// Deprecated
Expand Down Expand Up @@ -453,7 +454,14 @@ extension FormatOptions.Descriptor {
trueValues: ["remove"],
falseValues: ["insert"]
)

static let commasInsteadOfAmpersands = FormatOptions.Descriptor(
argumentName: "commasinsteadofampersands",
propertyName: "commasInsteadOfAmpersands",
displayName: "Commas Instead Of Ampersands",
keyPath: \.commasInsteadOfAmpersands,
trueValues: ["true", "enabled"],
falseValues: ["false", "disabled"]
)
static let importGrouping = FormatOptions.Descriptor(
argumentName: "importgrouping",
propertyName: "importGrouping",
Expand Down
53 changes: 53 additions & 0 deletions Sources/Rules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3294,6 +3294,59 @@ extension FormatRules {
}
}
}

/// Replace `&&` to `,` in if or guard closure
@objc public class func commasInsteadOfAmpersands(_ formatter: Formatter) {
let keywords = ["if", "guard", "while"]

func noOtherOperators(_ formatter: Formatter, startIndex: Int) -> Bool {
for token in formatter.tokens[startIndex...] {
if case Token.operator("||", .infix) = token { return false }
if case Token.keyword("case") = token { return false }

if case Token.startOfScope("{") = token { return true }
}

return false
}

guard formatter.options.commasInsteadOfAmpersands else { return }

var scopeStarted = false
var openCommasCounter = 0
formatter.forEachToken { index, token in
if case let Token.keyword(keyword) = token,
keywords.contains(keyword),
noOtherOperators(formatter, startIndex: index) {
scopeStarted = true
return
}

guard scopeStarted else { return }
if case Token.startOfScope("{") = token {
scopeStarted = false
return
}

if case Token.startOfScope("(") = token {
openCommasCounter += 1
return
} else if case Token.endOfScope(")") = token {
openCommasCounter -= 1
return
}

guard openCommasCounter == 0 else { return }
guard case Token.operator("&&", .infix) = token else { return }

formatter.replaceToken(at: index, with: .delimiter(","))

guard let previousToken = formatter.token(at: index - 1) else { return }
if case Token.space = previousToken {
formatter.removeToken(at: index - 1)
}
}
}
}

private extension FormatRules {
Expand Down
2 changes: 1 addition & 1 deletion Tests/ArgumentsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class ArgumentsTests: XCTestCase {
}

func testCommandLineArgumentsAreCorrect() {
let output = ["allman": "false", "wraparguments": "preserve", "stripunusedargs": "always", "self": "remove", "header": "ignore", "importgrouping": "alphabetized", "fractiongrouping": "disabled", "binarygrouping": "4,8", "octalgrouping": "4,8", "indentcase": "false", "trimwhitespace": "always", "decimalgrouping": "3,6", "exponentgrouping": "disabled", "patternlet": "hoist", "commas": "always", "wrapcollections": "preserve", "semicolons": "inline", "indent": "4", "exponentcase": "lowercase", "operatorfunc": "spaced", "symlinks": "ignore", "elseposition": "same-line", "empty": "void", "ranges": "spaced", "hexliteralcase": "uppercase", "linebreaks": "lf", "hexgrouping": "4,8", "comments": "indent", "ifdef": "indent", "closingparen": "balanced"]
let output = ["allman": "false", "wraparguments": "preserve", "stripunusedargs": "always", "self": "remove", "header": "ignore", "importgrouping": "alphabetized", "fractiongrouping": "disabled", "binarygrouping": "4,8", "octalgrouping": "4,8", "indentcase": "false", "trimwhitespace": "always", "decimalgrouping": "3,6", "exponentgrouping": "disabled", "patternlet": "hoist", "commas": "always", "wrapcollections": "preserve", "semicolons": "inline", "indent": "4", "exponentcase": "lowercase", "operatorfunc": "spaced", "symlinks": "ignore", "elseposition": "same-line", "empty": "void", "ranges": "spaced", "hexliteralcase": "uppercase", "linebreaks": "lf", "hexgrouping": "4,8", "comments": "indent", "ifdef": "indent", "closingparen": "balanced", "commasinsteadofampersands": "true"]
XCTAssertEqual(argumentsFor(.default), output)
}

Expand Down
183 changes: 179 additions & 4 deletions Tests/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2099,8 +2099,9 @@ class RulesTests: XCTestCase {
func testNestedWrappedIfIndents() {
let input = "if foo {\nif bar &&\n(baz ||\nquux) {\nfoo()\n}\n}"
let output = "if foo {\n if bar &&\n (baz ||\n quux) {\n foo()\n }\n}"
let options = FormatOptions(commasInsteadOfAmpersands: false)
XCTAssertEqual(try format(input, rules: [FormatRules.indent]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default, options: options), output + "\n")
}

func testWrappedEnumThatLooksLikeIf() {
Expand Down Expand Up @@ -3628,8 +3629,9 @@ class RulesTests: XCTestCase {
func testOuterParensRemoved() {
let input = "while ((x || y) && z) {}"
let output = "while (x || y) && z {}"
let options = FormatOptions(commasInsteadOfAmpersands: false)
XCTAssertEqual(try format(input, rules: [FormatRules.redundantParens]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default, options: options), output + "\n")
}

func testOuterParensRemoved2() {
Expand Down Expand Up @@ -3884,15 +3886,17 @@ class RulesTests: XCTestCase {
func testParensNotRemovedBeforeIfBody2() {
let input = "if try foo as Bar && baz() { /* some code */ }"
let output = "if try foo as Bar && baz() { /* some code */ }"
let options = FormatOptions(commasInsteadOfAmpersands: false)
XCTAssertEqual(try format(input, rules: [FormatRules.redundantParens]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default, options: options), output + "\n")
}

func testParensNotRemovedBeforeIfBody3() {
let input = "if #selector(foo(_:)) && bar() { /* some code */ }"
let output = "if #selector(foo(_:)) && bar() { /* some code */ }"
let options = FormatOptions(commasInsteadOfAmpersands: false)
XCTAssertEqual(try format(input, rules: [FormatRules.redundantParens]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default, options: options), output + "\n")
}

func testParensNotRemovedBeforeIfBody4() {
Expand Down Expand Up @@ -7155,4 +7159,175 @@ class RulesTests: XCTestCase {
XCTAssertEqual(try format(input, rules: [FormatRules.emptyBraces], options: options), output)
XCTAssertEqual(try format(input, rules: FormatRules.default, options: options), output)
}

// MARK: replaceDoubleAmpersandWithComma

func testIfAmpersandReplacedBase() {
let input = "if true && true {}"
let output = "if true, true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedBase() {
let input = "guard true && true\nelse { return }"
let output = "guard true, true\nelse { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedBase() {
let input = "while true && true {}"
let output = "while true, true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandReplacedTripple() {
let input = "if true && true && true {}"
let output = "if true, true, true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedTripple() {
let input = "guard true && true && true\nelse { return }"
let output = "guard true, true, true\nelse { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedTripple() {
let input = "while true && true && true {}"
let output = "while true, true, true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandReplacedBrackets() {
let input = "if true && (true && true) {}"
let output = "if true, (true && true) {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedBrackets() {
let input = "guard true && (true && true)\nelse { return }"
let output = "guard true, (true && true)\nelse { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedBrackets() {
let input = "while true && (true && true) {}"
let output = "while true, (true && true) {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandReplacedWithFunction() {
let input = "if functionReturnsBool() && true {}"
let output = "if functionReturnsBool(), true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedWithFunction() {
let input = "if functionReturnsBool() && variable\nelse { return }"
let output = "if functionReturnsBool(), variable\nelse { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedWithFunction() {
let input = "while functionReturnsBool() && true {}"
let output = "while functionReturnsBool(), true {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandReplacedWithOtherOperators() {
let input = "if foo || bar && baz {}"
let output = "if foo || bar && baz {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedWithOtherOperators() {
let input = "guard foo || bar && baz\nelse { return }"
let output = "guard foo || bar && baz\nelse { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedWithOtherOperators() {
let input = "while foo || bar && baz {}"
let output = "while foo || bar && baz {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandReplacedInParentScope() {
let input = "func someFunc() { if bar && baz {} }"
let output = "func someFunc() { if bar, baz {} }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandReplacedInParentScope() {
let input = "func someFunc() { guard bar && baz else { return } }"
let output = "func someFunc() { guard bar, baz else { return } }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandReplacedInParentScope() {
let input = "func someFunc() { while bar && baz {} }"
let output = "func someFunc() { while bar, baz {} }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testIfAmpersandCase() {
let input = "if case let a = foo && bar {}"
let output = "if case let a = foo && bar {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testWhileAmpersandCase() {
let input = "while case let a = foo && bar {}"
let output = "while case let a = foo && bar {}"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

func testGuardAmpersandCase() {
let input = "guard case let a = foo && bar else { return }"
let output = "guard case let a = foo && bar else { return }"

XCTAssertEqual(try format(input, rules: [FormatRules.commasInsteadOfAmpersands]), output)
XCTAssertEqual(try format(input + "\n", rules: FormatRules.default), output + "\n")
}

}

0 comments on commit c000ee5

Please sign in to comment.