@@ -40,7 +40,9 @@ import SwiftSyntaxBuilder
40
40
/// `type-for-expansion-string`), is parsed into a syntax node. If that node is
41
41
/// a `FunctionTypeSyntax` then the placeholder is expanded into a
42
42
/// `ClosureExprSyntax`. Otherwise it is expanded as is, which is also the case
43
- /// for when only a display string is provided.
43
+ /// for when only a display string is provided. You may customize the formatting
44
+ /// of a closure expansion via ``Context/closureLiteralFormat``, for example to
45
+ /// change whether it is split onto multiple lines.
44
46
///
45
47
/// ## Function Typed Placeholder
46
48
/// ### Before
@@ -78,12 +80,15 @@ import SwiftSyntaxBuilder
78
80
/// ```
79
81
struct ExpandSingleEditorPlaceholder : EditRefactoringProvider {
80
82
struct Context {
81
- let indentationWidth : Trivia ?
82
- let initialIndentation : Trivia
83
-
84
- init ( indentationWidth: Trivia ? = nil , initialIndentation: Trivia = [ ] ) {
85
- self . indentationWidth = indentationWidth
86
- self . initialIndentation = initialIndentation
83
+ let closureLiteralFormat : BasicFormat
84
+ let allowNestedPlaceholders : Bool
85
+
86
+ init (
87
+ closureLiteralFormat: BasicFormat = BasicFormat ( ) ,
88
+ allowNestedPlaceholders: Bool = false
89
+ ) {
90
+ self . closureLiteralFormat = closureLiteralFormat
91
+ self . allowNestedPlaceholders = allowNestedPlaceholders
87
92
}
88
93
}
89
94
@@ -94,16 +99,17 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
94
99
95
100
let expanded : String
96
101
if let functionType = placeholder. typeForExpansion? . as ( FunctionTypeSyntax . self) {
97
- let basicFormat = BasicFormat (
98
- indentationWidth: context. indentationWidth,
99
- initialIndentation: context. initialIndentation
100
- )
101
- var formattedExpansion = functionType. closureExpansion. formatted ( using: basicFormat) . description
102
+ let format = context. closureLiteralFormat
103
+ let initialIndentation = format. currentIndentationLevel
104
+ var formattedExpansion = functionType. closureExpansion. formatted ( using: format) . description
102
105
// Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
103
106
// format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
104
107
// that might be in the middle of a line.
105
- if formattedExpansion. hasPrefix ( context. initialIndentation. description) {
106
- formattedExpansion = String ( formattedExpansion. dropFirst ( context. initialIndentation. description. count) )
108
+ if formattedExpansion. hasPrefix ( initialIndentation. description) {
109
+ formattedExpansion = String ( formattedExpansion. dropFirst ( initialIndentation. description. count) )
110
+ }
111
+ if context. allowNestedPlaceholders {
112
+ formattedExpansion = wrapInPlaceholder ( formattedExpansion)
107
113
}
108
114
expanded = formattedExpansion
109
115
} else {
@@ -161,20 +167,24 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
161
167
let arg = placeholder. parent? . as ( LabeledExprSyntax . self) ,
162
168
let argList = arg. parent? . as ( LabeledExprListSyntax . self) ,
163
169
let call = argList. parent? . as ( FunctionCallExprSyntax . self) ,
164
- let expandedTrailingClosures = ExpandEditorPlaceholdersToTrailingClosures . expandTrailingClosurePlaceholders (
170
+ let expandedClosures = ExpandEditorPlaceholdersToLiteralClosures . expandClosurePlaceholders (
165
171
in: call,
166
172
ifIncluded: arg,
167
- indentationWidth: context. indentationWidth
173
+ context: ExpandEditorPlaceholdersToLiteralClosures . Context (
174
+ format: . trailing( indentationWidth: context. indentationWidth)
175
+ )
168
176
)
169
177
else {
170
178
return ExpandSingleEditorPlaceholder . textRefactor ( syntax: token)
171
179
}
172
180
173
- return [ SourceEdit . replace ( call, with: expandedTrailingClosures . description) ]
181
+ return [ SourceEdit . replace ( call, with: expandedClosures . description) ]
174
182
}
175
183
}
176
184
177
- /// Expand all the editor placeholders in the function call that can be converted to trailing closures.
185
+ /// Expand all the editor placeholders in the function call to literal closures.
186
+ /// By default they will be expanded to trailing form; if you provide your own
187
+ /// formatter via ``Context/format`` they will be expanded inline.
178
188
///
179
189
/// ## Before
180
190
/// ```swift
@@ -195,45 +205,72 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
195
205
/// <#T##String#>
196
206
/// }
197
207
/// ```
198
- public struct ExpandEditorPlaceholdersToTrailingClosures : SyntaxRefactoringProvider {
208
+ public struct ExpandEditorPlaceholdersToLiteralClosures : SyntaxRefactoringProvider {
199
209
public struct Context {
200
- public let indentationWidth : Trivia ?
210
+ public enum Format {
211
+ /// Default formatting behavior: expand to trailing closures.
212
+ case trailing( indentationWidth: Trivia ? )
213
+ /// Use the given formatter and expand the placeholder inline, without
214
+ /// moving it to trailing position. If `allowNestedPlaceholders` is true,
215
+ /// the entire closure will also be wrapped as a placeholder.
216
+ case custom( BasicFormat , allowNestedPlaceholders: Bool )
217
+ }
218
+ public let format : Format
219
+
220
+ public init ( format: Format ) {
221
+ self . format = format
222
+ }
201
223
202
224
public init ( indentationWidth: Trivia ? = nil ) {
203
- self . indentationWidth = indentationWidth
225
+ self . init ( format : . trailing ( indentationWidth: indentationWidth) )
204
226
}
205
227
}
206
228
207
229
public static func refactor(
208
230
syntax call: FunctionCallExprSyntax ,
209
231
in context: Context = Context ( )
210
232
) -> FunctionCallExprSyntax ? {
211
- return Self . expandTrailingClosurePlaceholders ( in: call, ifIncluded: nil , indentationWidth: context. indentationWidth)
233
+ return Self . expandClosurePlaceholders (
234
+ in: call,
235
+ ifIncluded: nil ,
236
+ context: context
237
+ )
212
238
}
213
239
214
240
/// If the given argument is `nil` or one of the last arguments that are all
215
241
/// function-typed placeholders and this call doesn't have a trailing
216
242
/// closure, then return a replacement of this call with one that uses
217
243
/// closures based on the function types provided by each editor placeholder.
218
244
/// Otherwise return nil.
219
- fileprivate static func expandTrailingClosurePlaceholders (
245
+ fileprivate static func expandClosurePlaceholders (
220
246
in call: FunctionCallExprSyntax ,
221
247
ifIncluded arg: LabeledExprSyntax ? ,
222
- indentationWidth : Trivia ?
248
+ context : Context
223
249
) -> FunctionCallExprSyntax ? {
224
- guard let expanded = call. expandTrailingClosurePlaceholders ( ifIncluded: arg, indentationWidth: indentationWidth)
225
- else {
226
- return nil
227
- }
250
+ switch context. format {
251
+ case let . custom( formatter, allowNestedPlaceholders: allowNesting) :
252
+ let expanded = call. expandClosurePlaceholders (
253
+ ifIncluded: arg,
254
+ customFormat: formatter,
255
+ allowNestedPlaceholders: allowNesting
256
+ )
257
+ return expanded? . expr
228
258
229
- let callToTrailingContext = CallToTrailingClosures . Context (
230
- startAtArgument: call. arguments. count - expanded. numClosures
231
- )
232
- guard let trailing = CallToTrailingClosures . refactor ( syntax: expanded. expr, in: callToTrailingContext) else {
233
- return nil
234
- }
259
+ case let . trailing( indentationWidth) :
260
+ guard let expanded = call. expandClosurePlaceholders ( ifIncluded: arg, indentationWidth: indentationWidth)
261
+ else {
262
+ return nil
263
+ }
264
+
265
+ let callToTrailingContext = CallToTrailingClosures . Context (
266
+ startAtArgument: call. arguments. count - expanded. numClosures
267
+ )
268
+ guard let trailing = CallToTrailingClosures . refactor ( syntax: expanded. expr, in: callToTrailingContext) else {
269
+ return nil
270
+ }
235
271
236
- return trailing
272
+ return trailing
273
+ }
237
274
}
238
275
}
239
276
@@ -311,9 +348,11 @@ extension FunctionCallExprSyntax {
311
348
/// closure, then return a replacement of this call with one that uses
312
349
/// closures based on the function types provided by each editor placeholder.
313
350
/// Otherwise return nil.
314
- fileprivate func expandTrailingClosurePlaceholders (
351
+ fileprivate func expandClosurePlaceholders (
315
352
ifIncluded: LabeledExprSyntax ? ,
316
- indentationWidth: Trivia ?
353
+ indentationWidth: Trivia ? = nil ,
354
+ customFormat: BasicFormat ? = nil ,
355
+ allowNestedPlaceholders: Bool = false
317
356
) -> ( expr: FunctionCallExprSyntax , numClosures: Int ) ? {
318
357
var includedArg = false
319
358
var argsToExpand = 0
@@ -343,8 +382,12 @@ extension FunctionCallExprSyntax {
343
382
let edits = ExpandSingleEditorPlaceholder . textRefactor (
344
383
syntax: arg. expression. cast ( DeclReferenceExprSyntax . self) . baseName,
345
384
in: ExpandSingleEditorPlaceholder . Context (
346
- indentationWidth: indentationWidth,
347
- initialIndentation: lineIndentation
385
+ closureLiteralFormat: customFormat
386
+ ?? BasicFormat (
387
+ indentationWidth: indentationWidth,
388
+ initialIndentation: lineIndentation
389
+ ) ,
390
+ allowNestedPlaceholders: allowNestedPlaceholders
348
391
)
349
392
)
350
393
guard edits. count == 1 , let edit = edits. first, !edit. replacement. isEmpty else {
0 commit comments