@@ -40,7 +40,9 @@ import SwiftSyntaxBuilder
4040/// `type-for-expansion-string`), is parsed into a syntax node. If that node is
4141/// a `FunctionTypeSyntax` then the placeholder is expanded into a
4242/// `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.
4446///
4547/// ## Function Typed Placeholder
4648/// ### Before
@@ -78,12 +80,15 @@ import SwiftSyntaxBuilder
7880/// ```
7981struct ExpandSingleEditorPlaceholder : EditRefactoringProvider {
8082 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
8792 }
8893 }
8994
@@ -94,16 +99,17 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
9499
95100 let expanded : String
96101 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
102105 // Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
103106 // format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
104107 // 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)
107113 }
108114 expanded = formattedExpansion
109115 } else {
@@ -161,20 +167,24 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
161167 let arg = placeholder. parent? . as ( LabeledExprSyntax . self) ,
162168 let argList = arg. parent? . as ( LabeledExprListSyntax . self) ,
163169 let call = argList. parent? . as ( FunctionCallExprSyntax . self) ,
164- let expandedTrailingClosures = ExpandEditorPlaceholdersToTrailingClosures . expandTrailingClosurePlaceholders (
170+ let expandedClosures = ExpandEditorPlaceholdersToLiteralClosures . expandClosurePlaceholders (
165171 in: call,
166172 ifIncluded: arg,
167- indentationWidth: context. indentationWidth
173+ context: ExpandEditorPlaceholdersToLiteralClosures . Context (
174+ format: . trailing( indentationWidth: context. indentationWidth)
175+ )
168176 )
169177 else {
170178 return ExpandSingleEditorPlaceholder . textRefactor ( syntax: token)
171179 }
172180
173- return [ SourceEdit . replace ( call, with: expandedTrailingClosures . description) ]
181+ return [ SourceEdit . replace ( call, with: expandedClosures . description) ]
174182 }
175183}
176184
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.
178188///
179189/// ## Before
180190/// ```swift
@@ -195,45 +205,72 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
195205/// <#T##String#>
196206/// }
197207/// ```
198- public struct ExpandEditorPlaceholdersToTrailingClosures : SyntaxRefactoringProvider {
208+ public struct ExpandEditorPlaceholdersToLiteralClosures : SyntaxRefactoringProvider {
199209 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+ }
201223
202224 public init ( indentationWidth: Trivia ? = nil ) {
203- self . indentationWidth = indentationWidth
225+ self . init ( format : . trailing ( indentationWidth: indentationWidth) )
204226 }
205227 }
206228
207229 public static func refactor(
208230 syntax call: FunctionCallExprSyntax ,
209231 in context: Context = Context ( )
210232 ) -> 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+ )
212238 }
213239
214240 /// If the given argument is `nil` or one of the last arguments that are all
215241 /// function-typed placeholders and this call doesn't have a trailing
216242 /// closure, then return a replacement of this call with one that uses
217243 /// closures based on the function types provided by each editor placeholder.
218244 /// Otherwise return nil.
219- fileprivate static func expandTrailingClosurePlaceholders (
245+ fileprivate static func expandClosurePlaceholders (
220246 in call: FunctionCallExprSyntax ,
221247 ifIncluded arg: LabeledExprSyntax ? ,
222- indentationWidth : Trivia ?
248+ context : Context
223249 ) -> 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
228258
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+ }
235271
236- return trailing
272+ return trailing
273+ }
237274 }
238275}
239276
@@ -311,9 +348,11 @@ extension FunctionCallExprSyntax {
311348 /// closure, then return a replacement of this call with one that uses
312349 /// closures based on the function types provided by each editor placeholder.
313350 /// Otherwise return nil.
314- fileprivate func expandTrailingClosurePlaceholders (
351+ fileprivate func expandClosurePlaceholders (
315352 ifIncluded: LabeledExprSyntax ? ,
316- indentationWidth: Trivia ?
353+ indentationWidth: Trivia ? = nil ,
354+ customFormat: BasicFormat ? = nil ,
355+ allowNestedPlaceholders: Bool = false
317356 ) -> ( expr: FunctionCallExprSyntax , numClosures: Int ) ? {
318357 var includedArg = false
319358 var argsToExpand = 0
@@ -343,8 +382,12 @@ extension FunctionCallExprSyntax {
343382 let edits = ExpandSingleEditorPlaceholder . textRefactor (
344383 syntax: arg. expression. cast ( DeclReferenceExprSyntax . self) . baseName,
345384 in: ExpandSingleEditorPlaceholder . Context (
346- indentationWidth: indentationWidth,
347- initialIndentation: lineIndentation
385+ closureLiteralFormat: customFormat
386+ ?? BasicFormat (
387+ indentationWidth: indentationWidth,
388+ initialIndentation: lineIndentation
389+ ) ,
390+ allowNestedPlaceholders: allowNestedPlaceholders
348391 )
349392 )
350393 guard edits. count == 1 , let edit = edits. first, !edit. replacement. isEmpty else {
0 commit comments