1313#if swift(>=6)
1414import SwiftBasicFormat
1515import SwiftParser
16- public import SwiftSyntax
16+ @ _spi ( RawSyntax ) public import SwiftSyntax
1717import SwiftSyntaxBuilder
1818#else
1919import SwiftBasicFormat
2020import SwiftParser
21- import SwiftSyntax
21+ @ _spi ( RawSyntax ) import SwiftSyntax
2222import SwiftSyntaxBuilder
2323#endif
2424
@@ -88,31 +88,26 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
8888 }
8989
9090 static func textRefactor( syntax token: TokenSyntax , in context: Context = Context ( ) ) -> [ SourceEdit ] {
91- guard let placeholder = EditorPlaceholderData ( token: token) else {
91+ guard let placeholder = EditorPlaceholderExpansionData ( token: token) else {
9292 return [ ]
9393 }
9494
9595 let expanded : String
96- switch placeholder {
97- case let . basic( text) :
98- expanded = String ( text)
99- case let . typed( text, type) :
100- if let functionType = type. as ( FunctionTypeSyntax . self) {
101- let basicFormat = BasicFormat (
102- indentationWidth: context. indentationWidth,
103- initialIndentation: context. initialIndentation
104- )
105- var formattedExpansion = functionType. closureExpansion. formatted ( using: basicFormat) . description
106- // Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
107- // format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
108- // that might be in the middle of a line.
109- if formattedExpansion. hasPrefix ( context. initialIndentation. description) {
110- formattedExpansion = String ( formattedExpansion. dropFirst ( context. initialIndentation. description. count) )
111- }
112- expanded = formattedExpansion
113- } else {
114- expanded = String ( text)
96+ 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+ // Strip the initial indentation from the placeholder itself. We only introduced the initial indentation to
103+ // format consecutive lines. We don't want it at the front of the initial line because it replaces an expression
104+ // 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) )
115107 }
108+ expanded = formattedExpansion
109+ } else {
110+ expanded = placeholder. displayText
116111 }
117112
118113 return [
@@ -325,8 +320,8 @@ extension FunctionCallExprSyntax {
325320 for arg in arguments. reversed ( ) {
326321 guard let expr = arg. expression. as ( DeclReferenceExprSyntax . self) ,
327322 expr. baseName. isEditorPlaceholder,
328- let data = EditorPlaceholderData ( token: expr. baseName) ,
329- case let . typed ( _ , type) = data,
323+ let data = EditorPlaceholderExpansionData ( token: expr. baseName) ,
324+ let type = data. typeForExpansion ,
330325 type. is ( FunctionTypeSyntax . self)
331326 else {
332327 break
@@ -371,87 +366,27 @@ extension FunctionCallExprSyntax {
371366 }
372367}
373368
374- /// Placeholder text must start with '<#' and end with
375- /// '#>'. Placeholders can be one of the following formats:
376- /// ```
377- /// 'T##' display-string '##' type-string ('##' type-for-expansion-string)?
378- /// 'T##' display-and-type-string
379- /// display-string
380- /// ```
381- ///
382- /// NOTE: It is required that '##' is not a valid substring of display-string
383- /// or type-string. If this ends up not the case for some reason, we can consider
384- /// adding escaping for '##'.
385- @_spi ( SourceKitLSP)
386- public enum EditorPlaceholderData {
387- case basic( text: Substring )
388- case typed( text: Substring , type: TypeSyntax )
369+ private struct EditorPlaceholderExpansionData {
370+ let displayText : String
371+ let typeForExpansion : TypeSyntax ?
389372
390373 init ? ( token: TokenSyntax ) {
391- self . init ( text: token. text)
392- }
393-
394- @_spi ( SourceKitLSP)
395- public init ? ( text: String ) {
396- guard isPlaceholder ( text) else {
374+ guard let rawData = token. rawEditorPlaceHolderData else {
397375 return nil
398376 }
399- var text = text. dropFirst ( 2 ) . dropLast ( 2 )
400-
401- if !text. hasPrefix ( " T## " ) {
402- // No type information
403- self = . basic( text: text)
404- return
405- }
406-
407- // Drop 'T##'
408- text = text. dropFirst ( 3 )
409377
410- var typeText : Substring
411- ( text, typeText) = split ( text, separatedBy: " ## " )
412- if typeText. isEmpty {
413- // Only type information present
414- self . init ( typeText: text)
415- return
416- }
417-
418- // Have type text, see if we also have expansion text
419-
420- let expansionText : Substring
421- ( typeText, expansionText) = split ( typeText, separatedBy: " ## " )
422- if expansionText. isEmpty {
423- if typeText. isEmpty {
424- // No type information
425- self = . basic( text: text)
426- } else {
427- // Only have type text, use it for the placeholder expansion
428- self . init ( typeText: typeText)
429- }
430-
431- return
432- }
433-
434- // Have expansion type text, use it for the placeholder expansion
435- self . init ( typeText: expansionText)
436- }
437-
438- init ( typeText: Substring ) {
439- var parser = Parser ( String ( typeText) )
440-
441- let type : TypeSyntax = TypeSyntax . parse ( from: & parser)
442- if type. hasError {
443- self = . basic( text: typeText)
378+ if let typeText = rawData. typeForExpansionText, !typeText. isEmpty {
379+ self . displayText = String ( syntaxText: typeText)
380+ var parser = Parser ( UnsafeBufferPointer ( start: typeText. baseAddress, count: typeText. count) )
381+ let type : TypeSyntax = TypeSyntax . parse ( from: & parser)
382+ self . typeForExpansion = type. hasError ? nil : type
444383 } else {
445- self = . typed( text: typeText, type: type)
384+ self . displayText = String ( syntaxText: rawData. displayText)
385+ self . typeForExpansion = nil
446386 }
447387 }
448388}
449389
450- @_spi ( Testing)
451- public func isPlaceholder( _ str: String ) -> Bool {
452- return str. hasPrefix ( placeholderStart) && str. hasSuffix ( placeholderEnd)
453- }
454-
455390@_spi ( Testing)
456391public func wrapInPlaceholder( _ str: String ) -> String {
457392 return placeholderStart + str + placeholderEnd
@@ -462,16 +397,5 @@ public func wrapInTypePlaceholder(_ str: String, type: String) -> String {
462397 return wrapInPlaceholder ( " T## " + str + " ## " + type)
463398}
464399
465- /// Split the given string into two components on the first instance of
466- /// `separatedBy`. The second element is empty if `separatedBy` is missing
467- /// from the initial string.
468- fileprivate func split( _ text: Substring , separatedBy separator: String ) -> ( Substring , Substring ) {
469- var rest = text
470- while !rest. isEmpty && !rest. hasPrefix ( separator) {
471- rest = rest. dropFirst ( )
472- }
473- return ( text. dropLast ( rest. count) , rest. dropFirst ( 2 ) )
474- }
475-
476400fileprivate let placeholderStart : String = " <# "
477401fileprivate let placeholderEnd : String = " #> "
0 commit comments