Skip to content

Commit 822d85e

Browse files
committed
Merge pull request swiftlang#619 from mlavergn/trailingCommas
Add option to disable trailing commas on multi-line collections
1 parent f27c0e8 commit 822d85e

File tree

6 files changed

+324
-1
lines changed

6 files changed

+324
-1
lines changed

Documentation/Configuration.md

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ top-level keys and values:
8282
* `spacesAroundRangeFormationOperators` _(boolean)_: Determines whether whitespace should be forced
8383
before and after the range formation operators `...` and `..<`.
8484

85+
* `multiElementCollectionTrailingCommas` _(boolean)_: Determines whether multi-element collection literals should have trailing commas.
86+
Defaults to `true`.
87+
8588
> TODO: Add support for enabling/disabling specific syntax transformations in
8689
> the pipeline.
8790

Sources/SwiftFormat/API/Configuration+Default.swift

+1
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ extension Configuration {
3737
self.indentSwitchCaseLabels = false
3838
self.spacesAroundRangeFormationOperators = false
3939
self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
40+
self.multiElementCollectionTrailingCommas = true
4041
}
4142
}

Sources/SwiftFormat/API/Configuration.swift

+29
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public struct Configuration: Codable, Equatable {
4242
case rules
4343
case spacesAroundRangeFormationOperators
4444
case noAssignmentInExpressions
45+
case multiElementCollectionTrailingCommas
4546
}
4647

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

166+
/// Determines if multi-element collection literals should have trailing commas.
167+
///
168+
/// When `true` (default), the correct form is:
169+
/// ```swift
170+
/// let MyCollection = [1, 2,]
171+
/// ...
172+
/// let MyCollection = [
173+
/// "a": 1,
174+
/// "b": 2,
175+
/// ]
176+
/// ```
177+
///
178+
/// When `false`, the correct form is:
179+
/// ```swift
180+
/// let MyCollection = [1, 2]
181+
/// ...
182+
/// let MyCollection = [
183+
/// "a": 1,
184+
/// "b": 2
185+
/// ]
186+
/// ```
187+
public var multiElementCollectionTrailingCommas: Bool
188+
165189
/// Constructs a Configuration by loading it from a configuration file.
166190
public init(contentsOf url: URL) throws {
167191
let data = try Data(contentsOf: url)
@@ -239,6 +263,10 @@ public struct Configuration: Codable, Equatable {
239263
try container.decodeIfPresent(
240264
NoAssignmentInExpressionsConfiguration.self, forKey: .noAssignmentInExpressions)
241265
?? defaults.noAssignmentInExpressions
266+
self.multiElementCollectionTrailingCommas =
267+
try container.decodeIfPresent(
268+
Bool.self, forKey: .multiElementCollectionTrailingCommas)
269+
?? defaults.multiElementCollectionTrailingCommas
242270

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

Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ public class PrettyPrinter {
557557
// We never want to add a trailing comma in an initializer so we disable trailing commas on
558558
// single element collections.
559559
let shouldHaveTrailingComma =
560-
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement
560+
startLineNumber != openCloseBreakCompensatingLineNumber && !isSingleElement && configuration.multiElementCollectionTrailingCommas
561561
if shouldHaveTrailingComma && !hasTrailingComma {
562562
diagnose(.addTrailingComma, category: .trailingComma)
563563
} else if !shouldHaveTrailingComma && hasTrailingComma {

Sources/_SwiftFormatTestSupport/Configuration+Testing.swift

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ extension Configuration {
4040
config.indentSwitchCaseLabels = false
4141
config.spacesAroundRangeFormationOperators = false
4242
config.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
43+
config.multiElementCollectionTrailingCommas = true
4344
return config
4445
}
4546
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import SwiftFormat
2+
3+
final class CommaTests: PrettyPrintTestCase {
4+
func testArrayCommasAbsentEnabled() {
5+
let input =
6+
"""
7+
let MyCollection = [
8+
1,
9+
2,
10+
3
11+
]
12+
13+
"""
14+
15+
let expected =
16+
"""
17+
let MyCollection = [
18+
1,
19+
2,
20+
3,
21+
]
22+
23+
"""
24+
25+
var configuration = Configuration.forTesting
26+
configuration.multiElementCollectionTrailingCommas = true
27+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
28+
}
29+
30+
func testArrayCommasAbsentDisabled() {
31+
let input =
32+
"""
33+
let MyCollection = [
34+
1,
35+
2,
36+
3
37+
]
38+
39+
"""
40+
41+
let expected =
42+
"""
43+
let MyCollection = [
44+
1,
45+
2,
46+
3
47+
]
48+
49+
"""
50+
51+
var configuration = Configuration.forTesting
52+
configuration.multiElementCollectionTrailingCommas = false
53+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
54+
}
55+
56+
func testArrayCommasPresentEnabled() {
57+
let input =
58+
"""
59+
let MyCollection = [
60+
1,
61+
2,
62+
3,
63+
]
64+
65+
"""
66+
67+
let expected =
68+
"""
69+
let MyCollection = [
70+
1,
71+
2,
72+
3,
73+
]
74+
75+
"""
76+
77+
var configuration = Configuration.forTesting
78+
configuration.multiElementCollectionTrailingCommas = true
79+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
80+
}
81+
82+
func testArrayCommasPresentDisabled() {
83+
let input =
84+
"""
85+
let MyCollection = [
86+
1,
87+
2,
88+
3,
89+
]
90+
91+
"""
92+
93+
let expected =
94+
"""
95+
let MyCollection = [
96+
1,
97+
2,
98+
3
99+
]
100+
101+
"""
102+
103+
var configuration = Configuration.forTesting
104+
configuration.multiElementCollectionTrailingCommas = false
105+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
106+
}
107+
108+
func testArraySingleLineCommasPresentDisabled() {
109+
let input =
110+
"""
111+
let MyCollection = [1, 2, 3,]
112+
113+
"""
114+
115+
// no effect expected
116+
let expected =
117+
"""
118+
let MyCollection = [1, 2, 3]
119+
120+
"""
121+
122+
var configuration = Configuration.forTesting
123+
configuration.multiElementCollectionTrailingCommas = true
124+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
125+
}
126+
127+
func testArraySingleLineCommasPresentEnabled() {
128+
let input =
129+
"""
130+
let MyCollection = [1, 2, 3,]
131+
132+
"""
133+
134+
// no effect expected
135+
let expected =
136+
"""
137+
let MyCollection = [1, 2, 3]
138+
139+
"""
140+
141+
var configuration = Configuration.forTesting
142+
configuration.multiElementCollectionTrailingCommas = false
143+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
144+
}
145+
146+
func testDictionaryCommasAbsentEnabled() {
147+
let input =
148+
"""
149+
let MyCollection = [
150+
"a": 1,
151+
"b": 2,
152+
"c": 3
153+
]
154+
155+
"""
156+
157+
let expected =
158+
"""
159+
let MyCollection = [
160+
"a": 1,
161+
"b": 2,
162+
"c": 3,
163+
]
164+
165+
"""
166+
167+
var configuration = Configuration.forTesting
168+
configuration.multiElementCollectionTrailingCommas = true
169+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
170+
}
171+
172+
func testDictionaryCommasAbsentDisabled() {
173+
let input =
174+
"""
175+
let MyCollection = [
176+
"a": 1,
177+
"b": 2,
178+
"c": 3
179+
]
180+
181+
"""
182+
183+
let expected =
184+
"""
185+
let MyCollection = [
186+
"a": 1,
187+
"b": 2,
188+
"c": 3
189+
]
190+
191+
"""
192+
193+
var configuration = Configuration.forTesting
194+
configuration.multiElementCollectionTrailingCommas = false
195+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
196+
}
197+
198+
func testDictionaryCommasPresentEnabled() {
199+
let input =
200+
"""
201+
let MyCollection = [
202+
"a": 1,
203+
"b": 2,
204+
"c": 3,
205+
]
206+
207+
"""
208+
209+
let expected =
210+
"""
211+
let MyCollection = [
212+
"a": 1,
213+
"b": 2,
214+
"c": 3,
215+
]
216+
217+
"""
218+
219+
var configuration = Configuration.forTesting
220+
configuration.multiElementCollectionTrailingCommas = true
221+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
222+
}
223+
224+
func testDictionaryCommasPresentDisabled() {
225+
let input =
226+
"""
227+
let MyCollection = [
228+
"a": 1,
229+
"b": 2,
230+
"c": 3,
231+
]
232+
233+
"""
234+
235+
let expected =
236+
"""
237+
let MyCollection = [
238+
"a": 1,
239+
"b": 2,
240+
"c": 3
241+
]
242+
243+
"""
244+
245+
var configuration = Configuration.forTesting
246+
configuration.multiElementCollectionTrailingCommas = false
247+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 20, configuration: configuration)
248+
}
249+
250+
func testDictionarySingleLineCommasPresentDisabled() {
251+
let input =
252+
"""
253+
let MyCollection = ["a": 1, "b": 2, "c": 3,]
254+
255+
"""
256+
257+
let expected =
258+
"""
259+
let MyCollection = [
260+
"a": 1, "b": 2, "c": 3,
261+
]
262+
263+
"""
264+
265+
var configuration = Configuration.forTesting
266+
configuration.multiElementCollectionTrailingCommas = true
267+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
268+
}
269+
270+
func testDictionarySingleLineCommasPresentEnabled() {
271+
let input =
272+
"""
273+
let MyCollection = ["a": 1, "b": 2, "c": 3,]
274+
275+
"""
276+
277+
let expected =
278+
"""
279+
let MyCollection = [
280+
"a": 1, "b": 2, "c": 3
281+
]
282+
283+
"""
284+
285+
var configuration = Configuration.forTesting
286+
configuration.multiElementCollectionTrailingCommas = false
287+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40, configuration: configuration)
288+
}
289+
}

0 commit comments

Comments
 (0)