Skip to content

Commit 5bc2714

Browse files
authored
Merge pull request swiftlang#77 from allevato/fix-props-with-defaults-and-accessors
Fix formatting for vars with default values and accessors.
2 parents 53386ee + dfe56df commit 5bc2714

File tree

3 files changed

+175
-12
lines changed

3 files changed

+175
-12
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,21 +1333,37 @@ private final class TokenStreamCreator: SyntaxVisitor {
13331333
}
13341334

13351335
func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind {
1336+
// If the type annotation and/or the initializer clause need to wrap, we want those
1337+
// continuations to stack to improve readability. So, we need to keep track of how many open
1338+
// breaks we create (so we can close them at the end of the binding) and also keep track of the
1339+
// right-most token that will anchor the close breaks.
1340+
var closesNeeded: Int = 0
1341+
var closeAfterToken: TokenSyntax? = nil
1342+
1343+
if let typeAnnotation = node.typeAnnotation {
1344+
after(typeAnnotation.colon, tokens: .break(.open(kind: .continuation)))
1345+
closesNeeded += 1
1346+
closeAfterToken = typeAnnotation.lastToken
1347+
}
1348+
if let initializer = node.initializer {
1349+
after(initializer.equal, tokens: .break(.open(kind: .continuation)))
1350+
closesNeeded += 1
1351+
closeAfterToken = initializer.lastToken
1352+
}
1353+
13361354
if let accessorOrCodeBlock = node.accessor {
1337-
if let typeAnnotation = node.typeAnnotation {
1338-
after(typeAnnotation.colon, tokens: .break)
1339-
}
13401355
arrangeAccessorOrCodeBlock(accessorOrCodeBlock)
1341-
} else {
1342-
if let typeAnnotation = node.typeAnnotation {
1343-
after(typeAnnotation.colon, tokens: .break(.open(kind: .continuation)))
1344-
after(node.lastToken, tokens: .break(.close, size: 0))
1345-
}
1346-
if let initializer = node.initializer {
1347-
after(initializer.equal, tokens: .break(.open(kind: .continuation)))
1348-
after(node.lastToken, tokens: .break(.close, size: 0))
1349-
}
1356+
} else if let trailingComma = node.trailingComma {
1357+
// If this is one of multiple comma-delimited bindings, move any pending close breaks to
1358+
// follow the comma so that it doesn't get separated from the tokens before it.
1359+
closeAfterToken = trailingComma
13501360
}
1361+
1362+
if closeAfterToken != nil && closesNeeded > 0 {
1363+
let closeTokens = [Token](repeatElement(.break(.close, size: 0), count: closesNeeded))
1364+
after(closeAfterToken, tokens: closeTokens)
1365+
}
1366+
13511367
return .visitChildren
13521368
}
13531369

Tests/SwiftFormatPrettyPrintTests/AccessorTests.swift

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,148 @@ public class AccessorTests: PrettyPrintTestCase {
151151

152152
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50)
153153
}
154+
155+
public func testDefaultValueAndAccessor() {
156+
let input =
157+
"""
158+
var property = defaultValue {
159+
didSet {
160+
foo()
161+
bar()
162+
}
163+
}
164+
"""
165+
166+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 80)
167+
168+
let expected20 =
169+
"""
170+
var property =
171+
defaultValue
172+
{
173+
didSet {
174+
foo()
175+
bar()
176+
}
177+
}
178+
179+
"""
180+
181+
assertPrettyPrintEqual(input: input, expected: expected20, linelength: 20)
182+
}
183+
184+
public func testTypeDefaultValueAndAccessor() {
185+
let input =
186+
"""
187+
var property: SomeType = defaultValue {
188+
didSet {
189+
foo()
190+
bar()
191+
}
192+
}
193+
"""
194+
195+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 80)
196+
197+
let expected25 =
198+
"""
199+
var property: SomeType =
200+
defaultValue
201+
{
202+
didSet {
203+
foo()
204+
bar()
205+
}
206+
}
207+
208+
"""
209+
210+
assertPrettyPrintEqual(input: input, expected: expected25, linelength: 25)
211+
212+
let expected20 =
213+
"""
214+
var property:
215+
SomeType =
216+
defaultValue
217+
{
218+
didSet {
219+
foo()
220+
bar()
221+
}
222+
}
223+
224+
"""
225+
226+
assertPrettyPrintEqual(input: input, expected: expected20, linelength: 20)
227+
}
228+
229+
public func testMultipleBindingsWithAccessors() {
230+
// NOTE: These examples are not actually valid Swift! The syntax parser will allow a variable
231+
// declaration that has multiple comma-separated bindings that have accessors, but the compiler
232+
// rejects these at a later stage ("error: 'var' declarations with multiple variables cannot
233+
// have explicit getters/setters"). But since the parser allows it, we make an attempt to format
234+
// them correctly, rather than bail out and potentially leave the source code in a worse state
235+
// than the original.
236+
237+
let input =
238+
"""
239+
var property1: SomeType = defaultValue {
240+
didSet {
241+
foo()
242+
bar()
243+
}
244+
}, property2: SomeType = defaultValue {
245+
didSet {
246+
foo()
247+
bar()
248+
}
249+
}
250+
"""
251+
252+
let expected =
253+
"""
254+
var
255+
property1: SomeType = defaultValue {
256+
didSet {
257+
foo()
258+
bar()
259+
}
260+
},
261+
property2: SomeType = defaultValue {
262+
didSet {
263+
foo()
264+
bar()
265+
}
266+
}
267+
268+
"""
269+
270+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
271+
272+
let expected20 =
273+
"""
274+
var
275+
property1:
276+
SomeType =
277+
defaultValue
278+
{
279+
didSet {
280+
foo()
281+
bar()
282+
}
283+
},
284+
property2:
285+
SomeType =
286+
defaultValue
287+
{
288+
didSet {
289+
foo()
290+
bar()
291+
}
292+
}
293+
294+
"""
295+
296+
assertPrettyPrintEqual(input: input, expected: expected20, linelength: 20)
297+
}
154298
}

Tests/SwiftFormatPrettyPrintTests/XCTestManifests.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ extension AccessorTests {
77
// to regenerate.
88
static let __allTests__AccessorTests = [
99
("testBasicAccessors", testBasicAccessors),
10+
("testDefaultValueAndAccessor", testDefaultValueAndAccessor),
1011
("testEmptyAccessorBody", testEmptyAccessorBody),
1112
("testEmptyAccessorBodyWithComment", testEmptyAccessorBodyWithComment),
1213
("testEmptyAccessorList", testEmptyAccessorList),
14+
("testMultipleBindingsWithAccessors", testMultipleBindingsWithAccessors),
1315
("testSetModifier", testSetModifier),
16+
("testTypeDefaultValueAndAccessor", testTypeDefaultValueAndAccessor),
1417
]
1518
}
1619

0 commit comments

Comments
 (0)