@@ -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,29 @@ 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
+ /// The formatter to use when expanding a function-typed placeholder.
84
+ let closureLiteralFormat : BasicFormat
85
+ /// When true, the expansion will wrap a function-typed placeholder's entire
86
+ /// expansion in placeholder delimiters, in addition to any placeholders
87
+ /// inside the expanded closure literal.
88
+ ///
89
+ /// With `allowNestedPlaceholders = false`
90
+ /// ```swift
91
+ /// { someInt in <#String#> }
92
+ /// ```
93
+ ///
94
+ /// With `allowNestedPlaceholders = true`
95
+ /// ```swift
96
+ /// <#{ someInt in <#String#> }#>
97
+ /// ```
98
+ let allowNestedPlaceholders : Bool
99
+
100
+ init (
101
+ closureLiteralFormat: BasicFormat = BasicFormat ( ) ,
102
+ allowNestedPlaceholders: Bool = false
103
+ ) {
104
+ self . closureLiteralFormat = closureLiteralFormat
105
+ self . allowNestedPlaceholders = allowNestedPlaceholders
87
106
}
88
107
}
89
108
@@ -94,16 +113,17 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
94
113
95
114
let expanded : String
96
115
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
116
+ let format = context. closureLiteralFormat
117
+ let initialIndentation = format. currentIndentationLevel
118
+ var formattedExpansion = functionType. closureExpansion. formatted ( using: format) . description
102
119
// Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
103
120
// format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
104
121
// 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) )
122
+ if formattedExpansion. hasPrefix ( initialIndentation. description) {
123
+ formattedExpansion = String ( formattedExpansion. dropFirst ( initialIndentation. description. count) )
124
+ }
125
+ if context. allowNestedPlaceholders {
126
+ formattedExpansion = wrapInPlaceholder ( formattedExpansion)
107
127
}
108
128
expanded = formattedExpansion
109
129
} else {
@@ -161,20 +181,24 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
161
181
let arg = placeholder. parent? . as ( LabeledExprSyntax . self) ,
162
182
let argList = arg. parent? . as ( LabeledExprListSyntax . self) ,
163
183
let call = argList. parent? . as ( FunctionCallExprSyntax . self) ,
164
- let expandedTrailingClosures = ExpandEditorPlaceholdersToTrailingClosures . expandTrailingClosurePlaceholders (
184
+ let expandedClosures = ExpandEditorPlaceholdersToLiteralClosures . expandClosurePlaceholders (
165
185
in: call,
166
186
ifIncluded: arg,
167
- indentationWidth: context. indentationWidth
187
+ context: ExpandEditorPlaceholdersToLiteralClosures . Context (
188
+ format: . trailing( indentationWidth: context. indentationWidth)
189
+ )
168
190
)
169
191
else {
170
192
return ExpandSingleEditorPlaceholder . textRefactor ( syntax: token)
171
193
}
172
194
173
- return [ SourceEdit . replace ( call, with: expandedTrailingClosures . description) ]
195
+ return [ SourceEdit . replace ( call, with: expandedClosures . description) ]
174
196
}
175
197
}
176
198
177
- /// Expand all the editor placeholders in the function call that can be converted to trailing closures.
199
+ /// Expand all the editor placeholders in the function call to literal closures.
200
+ /// By default they will be expanded to trailing form; if you provide your own
201
+ /// formatter via ``Context/format`` they will be expanded inline.
178
202
///
179
203
/// ## Before
180
204
/// ```swift
@@ -185,7 +209,7 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
185
209
/// )
186
210
/// ```
187
211
///
188
- /// ## Expansion of `foo`
212
+ /// ## Expansion of `foo`, default behavior
189
213
/// ```swift
190
214
/// foo(
191
215
/// arg: <#T##Int#>,
@@ -195,45 +219,98 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
195
219
/// <#T##String#>
196
220
/// }
197
221
/// ```
198
- public struct ExpandEditorPlaceholdersToTrailingClosures : SyntaxRefactoringProvider {
222
+ ///
223
+ /// ## Expansion of `foo` with a basic custom formatter
224
+ /// ```swift
225
+ /// foo(
226
+ /// arg: <#T##Int#>,
227
+ /// firstClosure: { someInt in
228
+ /// <#T##String#>
229
+ /// },
230
+ /// secondClosure: { someInt in
231
+ /// <#T##String#>
232
+ /// }
233
+ /// )
234
+ /// ```
235
+ ///
236
+ /// ## Expansion of `foo`, custom formatter with `allowNestedPlaceholders: true`
237
+ /// ```swift
238
+ /// foo(
239
+ /// arg: <#T##Int#>,
240
+ /// firstClosure: <#{ someInt in
241
+ /// <#T##String#>
242
+ /// }#>,
243
+ /// secondClosure: <#{ someInt in
244
+ /// <#T##String#>
245
+ /// }#>
246
+ /// )
247
+ /// ```
248
+ public struct ExpandEditorPlaceholdersToLiteralClosures : SyntaxRefactoringProvider {
199
249
public struct Context {
200
- public let indentationWidth : Trivia ?
250
+ public enum Format {
251
+ /// Default formatting behavior: expand to trailing closures.
252
+ case trailing( indentationWidth: Trivia ? )
253
+ /// Use the given formatter and expand the placeholder inline, without
254
+ /// moving it to trailing position. If `allowNestedPlaceholders` is true,
255
+ /// the entire closure will also be wrapped as a placeholder.
256
+ case custom( BasicFormat , allowNestedPlaceholders: Bool )
257
+ }
258
+ public let format : Format
259
+
260
+ public init ( format: Format ) {
261
+ self . format = format
262
+ }
201
263
202
264
public init ( indentationWidth: Trivia ? = nil ) {
203
- self . indentationWidth = indentationWidth
265
+ self . init ( format : . trailing ( indentationWidth: indentationWidth) )
204
266
}
205
267
}
206
268
207
269
public static func refactor(
208
270
syntax call: FunctionCallExprSyntax ,
209
271
in context: Context = Context ( )
210
272
) -> FunctionCallExprSyntax ? {
211
- return Self . expandTrailingClosurePlaceholders ( in: call, ifIncluded: nil , indentationWidth: context. indentationWidth)
273
+ return Self . expandClosurePlaceholders (
274
+ in: call,
275
+ ifIncluded: nil ,
276
+ context: context
277
+ )
212
278
}
213
279
214
280
/// If the given argument is `nil` or one of the last arguments that are all
215
281
/// function-typed placeholders and this call doesn't have a trailing
216
282
/// closure, then return a replacement of this call with one that uses
217
283
/// closures based on the function types provided by each editor placeholder.
218
284
/// Otherwise return nil.
219
- fileprivate static func expandTrailingClosurePlaceholders (
285
+ fileprivate static func expandClosurePlaceholders (
220
286
in call: FunctionCallExprSyntax ,
221
287
ifIncluded arg: LabeledExprSyntax ? ,
222
- indentationWidth : Trivia ?
288
+ context : Context
223
289
) -> FunctionCallExprSyntax ? {
224
- guard let expanded = call. expandTrailingClosurePlaceholders ( ifIncluded: arg, indentationWidth: indentationWidth)
225
- else {
226
- return nil
227
- }
290
+ switch context. format {
291
+ case let . custom( formatter, allowNestedPlaceholders: allowNesting) :
292
+ let expanded = call. expandClosurePlaceholders (
293
+ ifIncluded: arg,
294
+ customFormat: formatter,
295
+ allowNestedPlaceholders: allowNesting
296
+ )
297
+ return expanded? . expr
228
298
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
- }
299
+ case let . trailing( indentationWidth) :
300
+ guard let expanded = call. expandClosurePlaceholders ( ifIncluded: arg, indentationWidth: indentationWidth)
301
+ else {
302
+ return nil
303
+ }
235
304
236
- return trailing
305
+ let callToTrailingContext = CallToTrailingClosures . Context (
306
+ startAtArgument: call. arguments. count - expanded. numClosures
307
+ )
308
+ guard let trailing = CallToTrailingClosures . refactor ( syntax: expanded. expr, in: callToTrailingContext) else {
309
+ return nil
310
+ }
311
+
312
+ return trailing
313
+ }
237
314
}
238
315
}
239
316
@@ -311,9 +388,11 @@ extension FunctionCallExprSyntax {
311
388
/// closure, then return a replacement of this call with one that uses
312
389
/// closures based on the function types provided by each editor placeholder.
313
390
/// Otherwise return nil.
314
- fileprivate func expandTrailingClosurePlaceholders (
391
+ fileprivate func expandClosurePlaceholders (
315
392
ifIncluded: LabeledExprSyntax ? ,
316
- indentationWidth: Trivia ?
393
+ indentationWidth: Trivia ? = nil ,
394
+ customFormat: BasicFormat ? = nil ,
395
+ allowNestedPlaceholders: Bool = false
317
396
) -> ( expr: FunctionCallExprSyntax , numClosures: Int ) ? {
318
397
var includedArg = false
319
398
var argsToExpand = 0
@@ -343,8 +422,12 @@ extension FunctionCallExprSyntax {
343
422
let edits = ExpandSingleEditorPlaceholder . textRefactor (
344
423
syntax: arg. expression. cast ( DeclReferenceExprSyntax . self) . baseName,
345
424
in: ExpandSingleEditorPlaceholder . Context (
346
- indentationWidth: indentationWidth,
347
- initialIndentation: lineIndentation
425
+ closureLiteralFormat: customFormat
426
+ ?? BasicFormat (
427
+ indentationWidth: indentationWidth,
428
+ initialIndentation: lineIndentation
429
+ ) ,
430
+ allowNestedPlaceholders: allowNestedPlaceholders
348
431
)
349
432
)
350
433
guard edits. count == 1 , let edit = edits. first, !edit. replacement. isEmpty else {
0 commit comments