8000 Merge pull request #38 from dylansturg/annotations_formatting · adevress/swift-syntax@77892b1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 77892b1

Browse files
authored
Merge pull request swiftlang#38 from dylansturg/annotations_formatting
Allow attributes, including `available`, to span multiple lines.
2 parents 022e797 + 5b5ce74 commit 77892b1

File tree

3 files changed

+209
-30
lines changed

3 files changed

+209
-30
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,32 +1016,36 @@ private final class TokenStreamCreator: SyntaxVisitor {
10161016
return .visitChildren
10171017
}
10181018

1019-
func handleAvailabilitySpec(leftParen: TokenSyntax?, spec: Syntax, rightParen: TokenSyntax?) {
1020-
var tokens: [TokenSyntax] = []
1021-
if let leftParen = leftParen {
1022-
tokens.append(leftParen)
1023-
}
1024-
spec.tokens.forEach { tokens.append($0) }
1025-
if let rightParen = rightParen {
1026-
tokens.append(rightParen)
1027-
}
1028-
1029-
for i in 0..<(tokens.count - 1) {
1030-
switch (tokens[i].tokenKind, tokens[i+1].tokenKind) {
1031-
case (.leftParen, _): ()
1032-
case (_, .rightParen): ()
1033-
case (_, .comma): ()
1034-
case (_, .colon): ()
1035-
default:
1036-
after(tokens[i], tokens: .space)
1037-
}
1019+
func visit(_ node: AttributeSyntax) -> SyntaxVisitorContinueKind {
1020+
before(node.firstToken, tokens: .open)
1021+
if node.argument != nil {
1022+
// Wrap the attribute's arguments in their own group, so arguments stay together with a higher
1023+
// affinity than the overall attribute (e.g. allows a break after the opening "(" and then
1024+
// having the entire argument list on 1 line). Necessary spaces and breaks are added inside of
1025+
// the argument, using type specific visitor methods.
1026+
after(node.leftParen, tokens: .break(.open, size: 0), .open(argumentListConsistency()))
1027+
before(node.rightParen, tokens: .break(.close, size: 0), .close)
10381028
}
1029+
after(node.lastToken, tokens: .close)
1030+
return .visitChildren
10391031
}
10401032

1041-
func visit(_ node: AttributeSyntax) -> SyntaxVisitorContinueKind {
1042-
if let argument = node.argument {
1043-
handleAvailabilitySpec(leftParen: node.leftParen, spec: argument, rightParen: node.rightParen)
1044-
}
1033+
func visit(_ node: AvailabilitySpecListSyntax) -> SyntaxVisitorContinueKind {
1034+
insertTokens(.break(.same, size: 1), betweenElementsOf: node)
1035+
return .visitChildren
1036+
}
1037+
1038+
func visit(_ node: AvailabilityLabeledArgumentSyntax) -> SyntaxVisitorContinueKind {
1039+
before(node.label, tokens: .open)
1040+
after(node.colon, tokens: .break(.continue, size: 1))
1041+
after(node.value.lastToken, tokens: .close)
1042+
return .visitChildren
1043+
}
1044+
1045+
func visit(_ node: AvailabilityVersionRestrictionSyntax) -> SyntaxVisitorContinueKind {
1046+
before(node.firstToken, tokens: .open)
1047+
after(node.platform, tokens: .break(.continue, size: 1))
1048+
after(node.lastToken, tokens: .close)
10451049
return .visitChildren
10461050
}
10471051

@@ -1411,10 +1415,6 @@ private final class TokenStreamCreator: SyntaxVisitor {
14111415
}
14121416

14131417
func visit(_ node: AvailabilityConditionSyntax) -> SyntaxVisitorContinueKind {
1414-
handleAvailabilitySpec(
1415-
leftParen: node.leftParen,
1416-
spec: node.availabilitySpec,
1417-
rightParen: node.rightParen)
14181418
return .visitChildren
14191419
}
14201420

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,200 @@
1+
import SwiftFormatConfiguration
2+
13
public class AttributeTests: PrettyPrintTestCase {
24
public func testAttributeParamSpacing() {
35
let input =
6+
"""
7+
@available( iOS 9.0,* )
8+
func f() {}
9+
@available(*, unavailable ,renamed:"MyRenamedProtocol")
10+
func f() {}
11+
@available(iOS 10.0, macOS 10.12, *)
12+
func f() {}
13+
"""
14+
15+
let expected =
416
"""
517
@available(iOS 9.0, *)
618
func f() {}
719
@available(*, unavailable, renamed: "MyRenamedProtocol")
820
func f() {}
921
@available(iOS 10.0, macOS 10.12, *)
1022
func f() {}
23+
24+
"""
25+
26+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 60)
27+
}
28+
29+
public func testAttributeBinPackedWrapping() {
30+
let input =
31+
"""
32+
@available(iOS 9.0, *)
33+
func f() {}
34+
@available(*,unavailable, renamed:"MyRenamedProtocol")
35+
func f() {}
36+
@available(iOS 10.0, macOS 10.12, *)
37+
func f() {}
1138
"""
1239

1340
let expected =
1441
"""
1542
@available(iOS 9.0, *)
1643
func f() {}
17-
@available(*, unavailable, renamed: "MyRenamedProtocol")
44+
@available(
45+
*, unavailable,
46+
renamed: "MyRenamedProtocol"
47+
)
48+
func f() {}
49+
@available(
50+
iOS 10.0, macOS 10.12, *
51+
)
52+
func f() {}
53+
54+
"""
55+
56+
// Attributes should wrap to avoid overflowing the line length, using the following priorities:
57+
// 1. Keep the entire attribute together, on 1 line.
58+
// 2. Otherwise, try to keep the entire attribute argument list together on 1 line.
59+
// 3. Otherwise, use argument list consistency (default: inconsistent) for the arguments.
60+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 32)
61+
}
62+
63+
public func testAttributeArgumentPerLineWrapping() {
64+
let input =
65+
"""
66+
@available(iOS 9.0, *)
67+
func f() {}
68+
@available(*,unavailable, renamed:"MyRenamedProtocol")
1869
func f() {}
1970
@available(iOS 10.0, macOS 10.12, *)
2071
func f() {}
72+
"""
73+
74+
let expected =
75+
"""
76+
@available(iOS 9.0, *)
77+
func f() {}
78+
@available(
79+
*,
80+
unavailable,
81+
renamed: "MyRenamedProtocol"
82+
)
83+
func f() {}
84+
@available(
85+
iOS 10.0,
86+
macOS 10.12,
87+
*
88+
)
89+
func f() {}
90+
91+
"""
92+
93+
let configuration = Configuration()
94+
configuration.lineBreakBeforeEachArgument = true
95+
assertPrettyPrintEqual(
96+
input: input, expected: expected, linelength: 32, configuration: configuration)
97+
}
98+
99+
public func testAttributeFormattingRespectsDiscretionaryLineBreaks() {
100+
let input =
101+
"""
102+
@available(
103+
iOSApplicationExtension,
104+
introduced: 10.0,
105+
deprecated: 11.0,
106+
message:
107+
"Use something else because this is definitely deprecated.")
108+
func f2() {}
109+
"""
110+
111+
let expected =
112+
"""
113+
@available(
114+
iOSApplicationExtension,
115+
introduced: 10.0,
116+
deprecated: 11.0,
117+
message:
118+
"Use something else because this is definitely deprecated."
119+
)
120+
func f2() {}
121+
122+
"""
123+
124+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40)
125+
}
126+
127+
public func testAttributeInterArgumentBinPackedLineBreaking() {
128+
let input =
129+
"""
130+
@available(iOSApplicationExtension, introduced: 10.0, deprecated: 11.0, message: "Use something else because this is definitely deprecated.")
131+
func f2() {}
132+
"""
133+
134+
let expected =
135+
"""
136+
@available(
137+
iOSApplicationExtension,
138+
introduced: 10.0, deprecated: 11.0,
139+
message:
140+
"Use something else because this is definitely deprecated."
141+
)
142+
func f2() {}
143+
144+
"""
145+
146+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40)
147+
}
148+
149+
public func testAttributArgumentPerLineBreaking() {
150+
let input =
151+
"""
152+
@available(iOSApplicationExtension, introduced: 10.0, deprecated: 11.0, message: "Use something else because this is definitely deprecated.")
153+
func f2() {}
154+
"""
155+
156+
let expected =
157+
"""
158+
@available(
159+
iOSApplicationExtension,
160+
introduced: 10.0,
161+
deprecated: 11.0,
162+
message:
163+
"Use something else because this is definitely deprecated."
164+
)
165+
func f2() {}
166+
167+
"""
168+
169+
let configuration = Configuration()
170+
configuration.lineBreakBeforeEachArgument = true
171+
assertPrettyPrintEqual(
172+
input: input, expected: expected, linelength: 40, configuration: configuration)
173+
}
174+
175+
public func testObjCAttributes() {
176+
let input =
177+
"""
178+
@objc func f() {}
179+
@objc(foo:bar:baz)
180+
func f() {}
181+
@objc(thisMethodHasAVeryLongName:andThisArgumentHasANameToo:soDoesThisOne:bar:)
182+
func f() {}
183+
"""
184+
185+
let expected =
186+
"""
187+
@objc func f() {}
188+
@objc(foo:bar:baz)
189+
func f() {}
190+
@objc(
191+
thisMethodHasAVeryLongName:andThisArgumentHasANameToo:soDoesThisOne:bar:
192+
)
193+
func f() {}
21194
22195
"""
23196

24-
// Attributes should not wrap.
25-
assertPrettyPrintEqual(input: input, expected: expected, linelength: 15)
197+
// TODO: Support line breaks between argument names in Objective-C selectors.
198+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40)
26199
}
27200
}

Tests/SwiftFormatPrettyPrintTests/XCTestManifests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ extension AttributeTests {
3939
// `swift test --generate-linuxmain`
4040
// to regenerate.
4141
static let __allTests__AttributeTests = [
42+
("testAttributArgumentPerLineBreaking", testAttributArgumentPerLineBreaking),
43+
("testAttributeArgumentPerLineWrapping", testAttributeArgumentPerLineWrapping),
44+
("testAttributeBinPackedWrapping", testAttributeBinPackedWrapping),
45+
("testAttributeFormattingRespectsDiscretionaryLineBreaks", testAttributeFormattingRespectsDiscretionaryLineBreaks),
46+
("testAttributeInterArgumentBinPackedLineBreaking", testAttributeInterArgumentBinPackedLineBreaking),
4247
("testAttributeParamSpacing", testAttributeParamSpacing),
48+
("testObjCAttributes", testObjCAttributes),
4349
]
4450
}
4551

0 commit comments

Comments
 (0)
0