@@ -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,29 @@ 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+     /// 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
87106    } 
88107  } 
89108
@@ -94,16 +113,17 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
94113
95114    let  expanded :  String 
96115    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
102119      // Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
103120      // format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
104121      // 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) 
107127      } 
108128      expanded =  formattedExpansion
109129    }  else  { 
@@ -161,20 +181,24 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
161181      let  arg =  placeholder. parent? . as ( LabeledExprSyntax . self) , 
162182      let  argList =  arg. parent? . as ( LabeledExprListSyntax . self) , 
163183      let  call =  argList. parent? . as ( FunctionCallExprSyntax . self) , 
164-       let  expandedTrailingClosures  =  ExpandEditorPlaceholdersToTrailingClosures . expandTrailingClosurePlaceholders ( 
184+       let  expandedClosures  =  ExpandEditorPlaceholdersToLiteralClosures . expandClosurePlaceholders ( 
165185        in:  call, 
166186        ifIncluded:  arg, 
167-         indentationWidth:  context. indentationWidth
187+         context:  ExpandEditorPlaceholdersToLiteralClosures . Context ( 
188+           format:  . trailing( indentationWidth:  context. indentationWidth) 
189+         ) 
168190      ) 
169191    else  { 
170192      return  ExpandSingleEditorPlaceholder . textRefactor ( syntax:  token) 
171193    } 
172194
173-     return  [ SourceEdit . replace ( call,  with:  expandedTrailingClosures . description) ] 
195+     return  [ SourceEdit . replace ( call,  with:  expandedClosures . description) ] 
174196  } 
175197} 
176198
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.
178202///
179203/// ## Before
180204/// ```swift
@@ -185,7 +209,7 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
185209/// )
186210/// ```
187211///
188- /// ## Expansion of `foo`
212+ /// ## Expansion of `foo`, default behavior 
189213/// ```swift
190214/// foo(
191215///   arg: <#T##Int#>,
@@ -195,45 +219,98 @@ public struct ExpandEditorPlaceholder: EditRefactoringProvider {
195219///   <#T##String#>
196220/// }
197221/// ```
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  { 
199249  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+     } 
201263
202264    public  init ( indentationWidth:  Trivia ? =  nil )  { 
203-       self . indentationWidth  =   indentationWidth
265+       self . init ( format :   . trailing ( indentationWidth:   indentationWidth) ) 
204266    } 
205267  } 
206268
207269  public  static  func  refactor( 
208270    syntax call:  FunctionCallExprSyntax , 
209271    in context:  Context  =  Context ( ) 
210272  )  ->  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+     ) 
212278  } 
213279
214280  /// If the given argument is `nil` or one of the last arguments that are all
215281  /// function-typed placeholders and this call doesn't have a trailing
216282  /// closure, then return a replacement of this call with one that uses
217283  /// closures based on the function types provided by each editor placeholder.
218284  /// Otherwise return nil.
219-   fileprivate  static  func  expandTrailingClosurePlaceholders ( 
285+   fileprivate  static  func  expandClosurePlaceholders ( 
220286    in call:  FunctionCallExprSyntax , 
221287    ifIncluded arg:  LabeledExprSyntax ? , 
222-     indentationWidth :   Trivia ? 
288+     context :   Context 
223289  )  ->  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
228298
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+       } 
235304
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+     } 
237314  } 
238315} 
239316
@@ -311,9 +388,11 @@ extension FunctionCallExprSyntax {
311388  /// closure, then return a replacement of this call with one that uses
312389  /// closures based on the function types provided by each editor placeholder.
313390  /// Otherwise return nil.
314-   fileprivate  func  expandTrailingClosurePlaceholders ( 
391+   fileprivate  func  expandClosurePlaceholders ( 
315392    ifIncluded:  LabeledExprSyntax ? , 
316-     indentationWidth:  Trivia ? 
393+     indentationWidth:  Trivia ? =  nil , 
394+     customFormat:  BasicFormat ? =  nil , 
395+     allowNestedPlaceholders:  Bool  =  false 
317396  )  ->  ( expr:  FunctionCallExprSyntax ,  numClosures:  Int ) ? { 
318397    var  includedArg  =  false 
319398    var  argsToExpand  =  0 
@@ -343,8 +422,12 @@ extension FunctionCallExprSyntax {
343422      let  edits  =  ExpandSingleEditorPlaceholder . textRefactor ( 
344423        syntax:  arg. expression. cast ( DeclReferenceExprSyntax . self) . baseName, 
345424        in:  ExpandSingleEditorPlaceholder . Context ( 
346-           indentationWidth:  indentationWidth, 
347-           initialIndentation:  lineIndentation
425+           closureLiteralFormat:  customFormat
426+             ??  BasicFormat ( 
427+               indentationWidth:  indentationWidth, 
428+               initialIndentation:  lineIndentation
429+             ) , 
430+           allowNestedPlaceholders:  allowNestedPlaceholders
348431        ) 
349432      ) 
350433      guard  edits. count ==  1 ,  let  edit =  edits. first,  !edit. replacement. isEmpty else  { 
0 commit comments