Skip to content

Commit 974128f

Browse files
committed
Use SwiftBasicFormat to improve formatting of edited manifest
Rather than trying to thread through trivia everywhere, apply BasicFormat to arguments.
1 parent 2f0db3b commit 974128f

File tree

3 files changed

+146
-29
lines changed

3 files changed

+146
-29
lines changed

Sources/PackageModelSyntax/SyntaxEditUtils.swift

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import Basics
1414
import PackageModel
15+
import SwiftBasicFormat
1516
import SwiftSyntax
1617
import SwiftParser
1718

@@ -244,12 +245,22 @@ extension Array<ArrayElementSyntax> {
244245
/// Append a new argument expression.
245246
mutating func append(expression: ExprSyntax) {
246247
// Add a comma on the prior expression, if there is one.
248+
let leadingTrivia: Trivia?
247249
if count > 0 {
248-
self[count - 1].trailingComma = TokenSyntax.commaToken(trailingTrivia: .space)
250+
self[count - 1].trailingComma = TokenSyntax.commaToken()
251+
leadingTrivia = .newline
252+
253+
// Adjust the first element to start with a newline
254+
if count == 1 {
255+
self[0].leadingTrivia = .newline
256+
}
257+
} else {
258+
leadingTrivia = nil
249259
}
250260

251261
append(
252262
ArrayElementSyntax(
263+
leadingTrivia: leadingTrivia,
253264
expression: expression
254265
)
255266
)
@@ -262,12 +273,26 @@ extension Array<LabeledExprSyntax> {
262273
/// Append a potentially labeled argument with the argument expression.
263274
mutating func append(label: String?, expression: ExprSyntax) {
264275
// Add a comma on the prior expression, if there is one.
276+
let leadingTrivia: Trivia
265277
if count > 0 {
266-
self[count - 1].trailingComma = TokenSyntax.commaToken(trailingTrivia: .space)
278+
self[count - 1].trailingComma = TokenSyntax.commaToken()
279+
leadingTrivia = .newline
280+
281+
// Adjust the first element to start with a newline
282+
if count == 1 {
283+
self[0].leadingTrivia = .newline
284+
}
285+
} else {
286+
leadingTrivia = Trivia()
267287
}
268288

269289
// Add the new expression.
270-
append(LabeledExprSyntax(label: label, expression: expression))
290+
append(
291+
LabeledExprSyntax(
292+
label: label,
293+
expression: expression
294+
).with(\.leadingTrivia, leadingTrivia)
295+
)
271296
}
272297

273298
/// Append a potentially labeled argument with a string literal.
@@ -294,7 +319,19 @@ extension Array<LabeledExprSyntax> {
294319
elements.append(expression: element.asSyntax())
295320
}
296321

297-
let array = ArrayExprSyntax(elements: ArrayElementListSyntax(elements))
322+
// When we have more than one element in the array literal, we add
323+
// newlines at the beginning of each element. Do the same for the
324+
// right square bracket.
325+
let rightSquareLeadingTrivia: Trivia = elements.count > 0
326+
? .newline
327+
: Trivia()
328+
329+
let array = ArrayExprSyntax(
330+
elements: ArrayElementListSyntax(elements),
331+
rightSquare: .rightSquareToken(
332+
leadingTrivia: rightSquareLeadingTrivia
333+
)
334+
)
298335
append(label: label, expression: ExprSyntax(array))
299336
}
300337

@@ -350,8 +387,19 @@ extension FunctionCallExprSyntax {
350387
)
351388
}
352389

390+
// Format the element appropriately for the context.
391+
let indentation = Trivia(
392+
pieces: arg.leadingTrivia.filter { $0.isSpaceOrTab }
393+
)
394+
let format = BasicFormat(
395+
indentationWidth: [ defaultIndent ],
396+
initialIndentation: indentation.appending(defaultIndent)
397+
)
398+
let formattedElement = newElement.formatted(using: format)
399+
.cast(ExprSyntax.self)
400+
353401
let updatedArgArray = argArray.appending(
354-
element: newElement,
402+
element: formattedElement,
355403
outerLeadingTrivia: arg.leadingTrivia
356404
)
357405
return [ .replace(argArray, with: updatedArgArray.description) ]
@@ -366,36 +414,40 @@ extension FunctionCallExprSyntax {
366414
let newArguments = arguments.insertingArgument(
367415
at: insertionPos
368416
) { (leadingTrivia, trailingComma) in
369-
// The argument is always [ element ], but if we have any newlines
370-
// in the leading trivia, then we really want to split it across
371-
// multiple lines, like this:
372-
// [
373-
// element
374-
// ]
375-
let newArgument: ExprSyntax
376-
if !leadingTrivia.hasNewlines {
377-
newArgument = " [ \(newElement), ]"
378-
} else {
379-
let innerTrivia = leadingTrivia.appending(defaultIndent)
380-
let arrayExpr = ArrayExprSyntax(
381-
leadingTrivia: .space,
382-
elements: [
417+
// Format the element appropriately for the context.
418+
let indentation = Trivia(pieces: leadingTrivia.filter { $0.isSpaceOrTab })
419+
let format = BasicFormat(
420+
indentationWidth: [ defaultIndent ],
421+
initialIndentation: indentation.appending(defaultIndent)
422+
)
423+
let formattedElement = newElement.formatted(using: format)
424+
.cast(ExprSyntax.self)
425+
426+
// Form the array.
427+
let newArgument = ArrayExprSyntax(
428+
leadingTrivia: .space,
429+
leftSquare: .leftSquareToken(
430+
trailingTrivia: .newline
431+
),
432+
elements: ArrayElementListSyntax(
433+
[
383434
ArrayElementSyntax(
384-
leadingTrivia: innerTrivia,
385-
expression: newElement,
435+
expression: formattedElement,
386436
trailingComma: .commaToken()
387437
)
388-
],
389-
rightSquare: .rightSquareToken(leadingTrivia: leadingTrivia)
438+
]
439+
),
440+
rightSquare: .rightSquareToken(
441+
leadingTrivia: leadingTrivia
390442
)
391-
newArgument = ExprSyntax(arrayExpr)
392-
}
443+
)
393444

445+
// Create the labeled argument for the array.
394446
return LabeledExprSyntax(
395447
leadingTrivia: leadingTrivia,
396448
label: "\(raw: label)",
397449
colon: .colonToken(),
398-
expression: newArgument,
450+
expression: ExprSyntax(newArgument),
399451
trailingComma: trailingComma
400452
)
401453
}

Sources/PackageModelSyntax/TargetDescription+Syntax.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ extension TargetDescription: ManifestSyntaxRepresentable {
7171
// Only for plugins
7272
arguments.appendIf(label: "checksum", stringLiteral: checksum)
7373

74-
return ".\(raw: functionName)(\(LabeledExprListSyntax(arguments)))"
74+
let separateParen: String = arguments.count > 1 ? "\n" : ""
75+
let argumentsSyntax = LabeledExprListSyntax(arguments)
76+
return ".\(raw: functionName)(\(argumentsSyntax)\(raw: separateParen))"
7577
}
7678
}
7779

Tests/PackageModelSyntaxTests/ManifestEditTests.swift

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class ManifestEditTests: XCTestCase {
213213
let package = Package(
214214
name: "packages",
215215
dependencies: [
216-
.package(url: "https://github.com/apple/swift-system.git", "508.0.0"..<"510.0.0"),
216+
.package(url: "https://github.com/apple/swift-system.git", "508.0.0" ..< "510.0.0"),
217217
]
218218
)
219219
""") { manifest in
@@ -375,7 +375,14 @@ class ManifestEditTests: XCTestCase {
375375
let package = Package(
376376
name: "packages",
377377
targets: [
378-
.target(name: "MyLib", dependencies: ["OtherLib", .product(name: "SwiftSyntax", package: "swift-syntax"), .target(name: "TargetLib")]),
378+
.target(
379+
name: "MyLib",
380+
dependencies: [
381+
"OtherLib",
382+
.product(name: "SwiftSyntax", package: "swift-syntax"),
383+
.target(name: "TargetLib")
384+
]
385+
),
379386
]
380387
)
381388
""",
@@ -398,6 +405,62 @@ class ManifestEditTests: XCTestCase {
398405
)
399406
}
400407
}
408+
409+
func testAddExecutableTargetWithDependencies() throws {
410+
try assertManifestRefactor("""
411+
// swift-tools-version: 5.5
412+
let package = Package(
413+
name: "packages",
414+
targets: [
415+
.target(name: "MyLib")
416+
]
417+
)
418+
""",
419+
expectedManifest: """
420+
// swift-tools-version: 5.5
421+
let package = Package(
422+
name: "packages",
423+
targets: [
424+
.target(name: "MyLib"),
425+
.executableTarget(
426+
name: "MyProgram",
427+
dependencies: [
428+
.product(name: "SwiftSyntax", package: "swift-syntax"),
429+
.target(name: "TargetLib"),
430+
"MyLib"
431+
]
432+
),
433+
]
434+
)
435+
""",
436+
expectedAuxiliarySources: [
437+
RelativePath("Sources/MyProgram/MyProgram.swift") : """
438+
import MyLib
439+
import SwiftSyntax
440+
import TargetLib
441+
442+
@main
443+
struct MyProgramMain {
444+
static func main() {
445+
print("Hello, world")
446+
}
447+
}
448+
"""
449+
]) { manifest in
450+
try AddTarget.addTarget(
451+
TargetDescription(
452+
name: "MyProgram",
453+
dependencies: [
454+
.product(name: "SwiftSyntax", package: "swift-syntax"),
455+
.target(name: "TargetLib", condition: nil),
456+
.byName(name: "MyLib", condition: nil)
457+
],
458+
type: .executable
459+
),
460+
to: manifest
461+
)
462+
}
463+
}
401464
}
402465

403466

0 commit comments

Comments
 (0)