@@ -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