Skip to content

Commit

Permalink
Merge pull request swiftlang#619 from mlavergn/trailingCommas
Browse files Browse the repository at this point in the history
Add option to disable trailing commas on multi-line collections
  • Loading branch information
allevato committed Sep 14, 2023
1 parent f27c0e8 commit 822d85e
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ top-level keys and values:
* `spacesAroundRangeFormationOperators` _(boolean)_: Determines whether whitespace should be forced
before and after the range formation operators `...` and `..<`.

* `multiElementCollectionTrailingCommas` _(boolean)_: Determines whether multi-element collection literals should have trailing commas.
Defaults to `true`.

> TODO: Add support for enabling/disabling specific syntax transformations in
> the pipeline.
Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftFormat/API/Configuration+Default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ extension Configuration {
self.indentSwitchCaseLabels = false
self.spacesAroundRangeFormationOperators = false
self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
self.multiElementCollectionTrailingCommas = true
}
}
29 changes: 29 additions & 0 deletions Sources/SwiftFormat/API/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public struct Configuration: Codable, Equatable {
case rules
case spacesAroundRangeFormationOperators
case noAssignmentInExpressions
case multiElementCollectionTrailingCommas
}

/// A dictionary containing the default enabled/disabled states of rules, keyed by the rules'
Expand Down Expand Up @@ -162,6 +163,29 @@ public struct Configuration: Codable, Equatable {
/// Contains exceptions for the `NoAssignmentInExpressions` rule.
public var noAssignmentInExpressions: NoAssignmentInExpressionsConfiguration

/// Determines if multi-element collection literals should have trailing commas.
///
/// When `true` (default), the correct form is:
/// ```swift
/// let MyCollection = [1, 2,]
/// ...
/// let MyCollection = [
/// "a": 1,
/// "b": 2,
/// ]
/// ```
///
/// When `false`, the correct form is:
/// ```swift
/// let MyCollection = [1, 2]
/// ...
/// let MyCollection = [
/// "a": 1,
/// "b": 2
/// ]
/// ```
public var multiElementCollectionTrailingCommas: Bool

/// Constructs a Configuration by loading it from a configuration file.
public init(contentsOf url: URL) throws {
let data = try Data(contentsOf: url)
Expand Down Expand Up @@ -239,6 +263,10 @@ public struct Configuration: Codable, Equatable {
try container.decodeIfPresent(
NoAssignmentInExpressionsConfiguration.self, forKey: .noAssignmentInExpressions)
?? defaults.noAssignmentInExpressions
self.multiElementCollectionTrailingCommas =
try container.decodeIfPresent(
Bool.self, forKey: .multiElementCollectionTrailingCommas)
?? defaults.multiElementCollectionTrailingCommas

// If the `rules` key is not present at all, default it to the built-in set
// so that the behavior is the same as if the configuration had been
Expand Down Expand Up @@ -271,6 +299,7 @@ public struct Configuration: Codable, Equatable {
try container.encode(fileScopedDeclarationPrivacy, forKey: .fileScopedDeclarationPrivacy)
try container.encode(indentSwitchCaseLabels, forKey: .indentSwitchCaseLabels)
try container.encode(noAssignmentInExpressions, forKey: .noAssignmentInExpressions)
try container.encode(multiElementCollectionTrailingCommas, forKey: .multiElementCollectionTrailingCommas)
try container.encode(rules, forKey: .rules)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ public class PrettyPrinter {
// We never want to add a trailing comma in an initializer so we disable trailing commas on
// single element collections.
let shouldHaveTrailingComma =
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement && configuration.multiElementCollectionTrailingCommas
if shouldHaveTrailingComma && !hasTrailingComma {
diagnose(.addTrailingComma, category: .trailingComma)
} else if !shouldHaveTrailingComma && hasTrailingComma {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extension Configuration {
config.indentSwitchCaseLabels = false
config.spacesAroundRangeFormationOperators = false
config.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
config.multiElementCollectionTrailingCommas = true
return config
}
}
289 changes: 289 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/CommaTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
import SwiftFormat

final class CommaTests: PrettyPrintTestCase {
func testArrayCommasAbsentEnabled() {
let input =
"""
let MyCollection = [
1,
2,
3
]
"""

let expected =
"""
let MyCollection = [
1,
2,
3,
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testArrayCommasAbsentDisabled() {
let input =
"""
let MyCollection = [
1,
2,
3
]
"""

let expected =
"""
let MyCollection = [
1,
2,
3
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testArrayCommasPresentEnabled() {
let input =
"""
let MyCollection = [
1,
2,
3,
]
"""

let expected =
"""
let MyCollection = [
1,
2,
3,
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testArrayCommasPresentDisabled() {
let input =
"""
let MyCollection = [
1,
2,
3,
]
"""

let expected =
"""
let MyCollection = [
1,
2,
3
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testArraySingleLineCommasPresentDisabled() {
let input =
"""
let MyCollection = [1, 2, 3,]
"""

// no effect expected
let expected =
"""
let MyCollection = [1, 2, 3]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
}

func testArraySingleLineCommasPresentEnabled() {
let input =
"""
let MyCollection = [1, 2, 3,]
"""

// no effect expected
let expected =
"""
let MyCollection = [1, 2, 3]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
}

func testDictionaryCommasAbsentEnabled() {
let input =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3
]
"""

let expected =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3,
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testDictionaryCommasAbsentDisabled() {
let input =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3
]
"""

let expected =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testDictionaryCommasPresentEnabled() {
let input =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3,
]
"""

let expected =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3,
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testDictionaryCommasPresentDisabled() {
let input =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3,
]
"""

let expected =
"""
let MyCollection = [
"a": 1,
"b": 2,
"c": 3
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
}

func testDictionarySingleLineCommasPresentDisabled() {
let input =
"""
let MyCollection = ["a": 1, "b": 2, "c": 3,]
"""

let expected =
"""
let MyCollection = [
"a": 1, "b": 2, "c": 3,
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
}

func testDictionarySingleLineCommasPresentEnabled() {
let input =
"""
let MyCollection = ["a": 1, "b": 2, "c": 3,]
"""

let expected =
"""
let MyCollection = [
"a": 1, "b": 2, "c": 3
]
"""

var configuration = Configuration.forTesting
configuration.multiElementCollectionTrailingCommas = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
}
}

0 comments on commit 822d85e

Please sign in to comment.