Skip to content

Commit e6d8220

Browse files
authored
Merge pull request swiftlang#189 from google/format-factor-out-func-bodies
Refactor function-like decls, remove spaces in empty bodies.
2 parents 28395f9 + f2ed419 commit e6d8220

File tree

5 files changed

+192
-122
lines changed

5 files changed

+192
-122
lines changed

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

Lines changed: 118 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,124 @@ private final class TokenStreamCreator: SyntaxVisitor {
184184
}
185185
}
186186

187+
// MARK: - Function and function-like declaration nodes (initializers, deinitializers, subscripts)
188+
189+
override func visit(_ node: FunctionDeclSyntax) {
190+
arrangeFunctionLikeDecl(
191+
node,
192+
attributes: node.attributes,
193+
genericWhereClause: node.genericWhereClause,
194+
body: node.body,
195+
bodyContentsAreEmpty: node.body?.statements.isEmpty ?? true)
196+
197+
after(node.funcKeyword, tokens: .break)
198+
super.visit(node)
199+
}
200+
201+
override func visit(_ node: InitializerDeclSyntax) {
202+
arrangeFunctionLikeDecl(
203+
node,
204+
attributes: node.attributes,
205+
genericWhereClause: node.genericWhereClause,
206+
body: node.body,
207+
bodyContentsAreEmpty: node.body?.statements.isEmpty ?? true)
208+
209+
before(node.throwsOrRethrowsKeyword, tokens: .break)
210+
super.visit(node)
211+
}
212+
213+
override func visit(_ node: DeinitializerDeclSyntax) {
214+
arrangeFunctionLikeDecl(
215+
node,
216+
attributes: node.attributes,
217+
genericWhereClause: nil,
218+
body: node.body,
219+
bodyContentsAreEmpty: node.body.statements.isEmpty)
220+
super.visit(node)
221+
}
222+
223+
override func visit(_ node: SubscriptDeclSyntax) {
224+
let bodyContentsAreEmpty: Bool
225+
if let accessor = node.accessor {
226+
if let accessorList = accessor.accessorListOrStmtList as? AccessorListSyntax {
227+
bodyContentsAreEmpty = accessorList.isEmpty
228+
} else if let stmtList = accessor.accessorListOrStmtList as? CodeBlockItemListSyntax {
229+
bodyContentsAreEmpty = stmtList.isEmpty
230+
} else {
231+
// We shouldn't get here because it should be one of the two above, but to be future-proof,
232+
// we'll use false if we see something else.
233+
bodyContentsAreEmpty = false
234+
}
235+
} else {
236+
bodyContentsAreEmpty = true
237+
}
238+
239+
arrangeFunctionLikeDecl(
240+
node,
241+
attributes: node.attributes,
242+
genericWhereClause: node.genericWhereClause,
243+
body: node.accessor,
244+
bodyContentsAreEmpty: bodyContentsAreEmpty)
245+
246+
before(node.result.firstToken, tokens: .break)
247+
super.visit(node)
248+
}
249+
250+
/// Applies formatting tokens to the tokens in the given function or function-like declaration
251+
/// node (e.g., initializers, deinitiailizers, and subscripts).
252+
private func arrangeFunctionLikeDecl(
253+
_ node: Syntax,
254+
attributes: AttributeListSyntax?,
255+
genericWhereClause: GenericWhereClauseSyntax?,
256+
body: BracedSyntax?,
257+
bodyContentsAreEmpty: Bool
258+
) {
259+
before(node.firstToken, tokens: .open(.inconsistent, 0))
260+
261+
if let attributes = attributes {
262+
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0))
263+
after(attributes.lastToken, tokens: .open)
264+
} else {
265+
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0), .open)
266+
}
267+
268+
if let genericWhereClause = genericWhereClause {
269+
before(
270+
genericWhereClause.firstToken,
271+
tokens: .break, .open(.inconsistent, 0), .break(size: 0), .open(.consistent, 0)
272+
)
273+
if body?.leftBrace != nil {
274+
after(genericWhereClause.lastToken, tokens: .break, .close, .close)
275+
} else {
276+
after(genericWhereClause.lastToken, tokens: .close, .close)
277+
}
278+
} else {
279+
before(body?.leftBrace, tokens: .break)
280+
}
281+
282+
if let body = body {
283+
// The body may be free of other syntax nodes, but we still need to insert the breaks if it
284+
// contains a comment (which will be in the leading trivia of the right brace).
285+
let commentPrecedesRightBrace = body.rightBrace.leadingTrivia.numberOfComments > 0
286+
let isBodyCompletelyEmpty = bodyContentsAreEmpty && !commentPrecedesRightBrace
287+
288+
if !isBodyCompletelyEmpty {
289+
after(body.leftBrace, tokens: .close, .close, .break(offset: 2), .open(.consistent, 0))
290+
before(body.rightBrace, tokens: .break(offset: -2), .close)
291+
} else {
292+
// The size-0 break in the empty case allows for a break between the braces in the rare
293+
// event that the declaration would be exactly the column limit + 1.
294+
after(body.leftBrace, tokens: .close, .close, .break(size: 0))
295+
}
296+
} else {
297+
// Function-like declarations in protocols won't have bodies, so make sure we close the
298+
// correct number of groups in that case as well.
299+
after(node.lastToken, tokens: .close, .close)
300+
}
301+
302+
after(node.lastToken, tokens: .close)
303+
}
304+
187305
// TODO: - Other nodes (yet to be organized)
188306

189307
override func visit(_ node: DeclNameArgumentsSyntax) {
@@ -837,128 +955,6 @@ private final class TokenStreamCreator: SyntaxVisitor {
837955
super.visit(node)
838956
}
839957

840-
override func visit(_ node: FunctionDeclSyntax) {
841-
before(node.firstToken, tokens: .open(.inconsistent, 0))
842-
843-
if let attributes = node.attributes {
844-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0))
845-
after(attributes.lastToken, tokens: .open)
846-
} else {
847-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0), .open)
848-
}
849-
850-
after(node.funcKeyword, tokens: .break)
851-
852-
before(
853-
node.genericWhereClause?.firstToken,
854-
tokens: .break, .open(.inconsistent, 0), .break(size: 0), .open(.consistent, 0)
855-
)
856-
if node.body != nil {
857-
after(node.genericWhereClause?.lastToken, tokens: .break, .close, .close)
858-
} else {
859-
after(node.genericWhereClause?.lastToken, tokens: .close, .close)
860-
}
861-
862-
if let body = node.body {
863-
if node.genericWhereClause == nil {
864-
before(body.leftBrace, tokens: .break)
865-
}
866-
after(body.leftBrace, tokens: .close, .close, .break(offset: 2), .open(.consistent, 0))
867-
before(body.rightBrace, tokens: .break(offset: -2), .close)
868-
} else {
869-
// FunctionDecls in protocols won't have bodies, so make sure we close the correct number of
870-
// groups in that case as well.
871-
after(node.lastToken, tokens: .close, .close)
872-
}
873-
874-
after(node.lastToken, tokens: .close)
875-
super.visit(node)
876-
}
877-
878-
override func visit(_ node: InitializerDeclSyntax) {
879-
before(node.firstToken, tokens: .open(.inconsistent, 0))
880-
881-
if let attributes = node.attributes {
882-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0))
883-
after(attributes.lastToken, tokens: .open)
884-
} else {
885-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0), .open)
886-
}
887-
888-
before(
889-
node.genericWhereClause?.firstToken,
890-
tokens: .break, .open(.inconsistent, 0), .break(size: 0), .open(.consistent, 0)
891-
)
892-
after(node.genericWhereClause?.lastToken, tokens: .break, .close, .close)
893-
894-
before(node.throwsOrRethrowsKeyword, tokens: .break)
895-
896-
if let body = node.body {
897-
if node.genericWhereClause == nil {
898-
before(body.leftBrace, tokens: .break)
899-
}
900-
after(body.leftBrace, tokens: .close, .close, .break(offset: 2), .open(.consistent, 0))
901-
before(body.rightBrace, tokens: .break(offset: -2), .close)
902-
} else {
903-
// FunctionDecls in protocols won't have bodies, so make sure we close the correct number of
904-
// groups in that case as well.
905-
after(node.lastToken, tokens: .close, .close)
906-
}
907-
908-
after(node.lastToken, tokens: .close)
909-
super.visit(node)
910-
}
911-
912-
override func visit(_ node: DeinitializerDeclSyntax) {
913-
before(node.firstToken, tokens: .open(.inconsistent, 0))
914-
915-
if let attributes = node.attributes {
916-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0))
917-
after(attributes.lastToken, tokens: .open)
918-
} else {
919-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0), .open)
920-
}
921-
922-
before(node.body.leftBrace, tokens: .break)
923-
after(node.body.leftBrace, tokens: .close, .close, .break(offset: 2), .open(.consistent, 0))
924-
before(node.body.rightBrace, tokens: .break(offset: -2), .close)
925-
926-
after(node.lastToken, tokens: .close)
927-
super.visit(node)
928-
}
929-
930-
override func visit(_ node: SubscriptDeclSyntax) {
931-
before(node.firstToken, tokens: .open(.inconsistent, 0))
932-
933-
if let attributes = node.attributes {
934-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0))
935-
after(attributes.lastToken, tokens: .open)
936-
} else {
937-
before(node.firstToken, tokens: .space(size: 0), .open(.consistent, 0), .open)
938-
}
939-
940-
before(node.result.firstToken, tokens: .break)
941-
942-
before(
943-
node.genericWhereClause?.firstToken,
944-
tokens: .break, .open(.inconsistent, 0), .break(size: 0), .open(.consistent, 0)
945-
)
946-
after(node.genericWhereClause?.lastToken, tokens: .break, .close, .close)
947-
948-
if let accessorBlock = node.accessor {
949-
if node.genericWhereClause == nil {
950-
before(accessorBlock.leftBrace, tokens: .break)
951-
}
952-
after(accessorBlock.leftBrace, tokens: .close, .close, .break(offset: 2), .open(.consistent, 0))
953-
before(accessorBlock.rightBrace, tokens: .break(offset: -2), .close)
954-
} else {
955-
after(node.lastToken, tokens: .close, .close)
956-
}
957-
958-
after(node.lastToken, tokens: .close)
959-
super.visit(node)
960-
}
961-
962958
override func visit(_ node: FunctionSignatureSyntax) {
963959
before(node.throwsOrRethrowsKeyword, tokens: .break)
964960
before(node.output?.firstToken, tokens: .break)

Tests/SwiftFormatPrettyPrintTests/DeinitializerDeclTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,25 @@ public class DeinitializerDeclTests: PrettyPrintTestCase {
7676

7777
assertPrettyPrintEqual(input: input, expected: expected, linelength: 25)
7878
}
79+
80+
public func testEmptyDeinitializer() {
81+
// The comment inside the class prevents it from *also* being collapsed onto a single line.
82+
let input = """
83+
class X {
84+
//
85+
deinit {}
86+
}
87+
"""
88+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50)
89+
90+
let wrapped = """
91+
class X {
92+
//
93+
deinit {
94+
}
95+
}
96+
97+
"""
98+
assertPrettyPrintEqual(input: input, expected: wrapped, linelength: 10)
99+
}
79100
}

Tests/SwiftFormatPrettyPrintTests/FunctionDeclTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,16 @@ public class FunctionDeclTests: PrettyPrintTestCase {
365365

366366
assertPrettyPrintEqual(input: input, expected: expected, linelength: 30)
367367
}
368+
369+
public func testEmptyFunction() {
370+
let input = "func foo() {}"
371+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50)
372+
373+
let wrapped = """
374+
func foo() {
375+
}
376+
377+
"""
378+
assertPrettyPrintEqual(input: input, expected: wrapped, linelength: 12)
379+
}
368380
}

Tests/SwiftFormatPrettyPrintTests/InitializerDeclTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,25 @@ public class InitializerDeclTests: PrettyPrintTestCase {
295295

296296
assertPrettyPrintEqual(input: input, expected: expected, linelength: 40)
297297
}
298+
299+
public func testEmptyInitializer() {
300+
// The comment inside the struct prevents it from *also* being collapsed onto a single line.
301+
let input = """
302+
struct X {
303+
//
304+
init() {}
305+
}
306+
"""
307+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50)
308+
309+
let wrapped = """
310+
struct X {
311+
//
312+
init() {
313+
}
314+
}
315+
316+
"""
317+
assertPrettyPrintEqual(input: input, expected: wrapped, linelength: 10)
318+
}
298319
}

Tests/SwiftFormatPrettyPrintTests/SubscriptDeclTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,24 @@ public class SubscriptDeclTests: PrettyPrintTestCase {
233233

234234
assertPrettyPrintEqual(input: input, expected: expected, linelength: 34)
235235
}
236+
237+
public func testEmptySubscript() {
238+
// The comment inside the struct prevents it from *also* being collapsed onto a single line.
239+
let input = """
240+
struct X {
241+
//
242+
subscript(i: Int) {}
243+
}
244+
"""
245+
assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50)
246+
247+
let wrapped = """
248+
struct X {
249+
//
250+
subscript(i: Int) {}
251+
}
252+
253+
"""
254+
assertPrettyPrintEqual(input: input, expected: wrapped, linelength: 21)
255+
}
236256
}

0 commit comments

Comments
 (0)