13
13
#if swift(>=6)
14
14
import SwiftBasicFormat
15
15
import SwiftParser
16
- public import SwiftSyntax
16
+ @ _spi ( RawSyntax ) public import SwiftSyntax
17
17
import SwiftSyntaxBuilder
18
18
#else
19
19
import SwiftBasicFormat
20
20
import SwiftParser
21
- import SwiftSyntax
21
+ @ _spi ( RawSyntax ) import SwiftSyntax
22
22
import SwiftSyntaxBuilder
23
23
#endif
24
24
@@ -88,31 +88,26 @@ struct ExpandSingleEditorPlaceholder: EditRefactoringProvider {
88
88
}
89
89
90
90
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 {
92
92
return [ ]
93
93
}
94
94
95
95
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) )
115
107
}
108
+ expanded = formattedExpansion
109
+ } else {
110
+ expanded = placeholder. displayText
116
111
}
117
112
118
113
return [
@@ -325,8 +320,8 @@ extension FunctionCallExprSyntax {
325
320
for arg in arguments. reversed ( ) {
326
321
guard let expr = arg. expression. as ( DeclReferenceExprSyntax . self) ,
327
322
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 ,
330
325
type. is ( FunctionTypeSyntax . self)
331
326
else {
332
327
break
@@ -371,87 +366,27 @@ extension FunctionCallExprSyntax {
371
366
}
372
367
}
373
368
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 ?
389
372
390
373
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 {
397
375
return nil
398
376
}
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 )
409
377
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
444
383
} else {
445
- self = . typed( text: typeText, type: type)
384
+ self . displayText = String ( syntaxText: rawData. displayText)
385
+ self . typeForExpansion = nil
446
386
}
447
387
}
448
388
}
449
389
450
- @_spi ( Testing)
451
- public func isPlaceholder( _ str: String ) -> Bool {
452
- return str. hasPrefix ( placeholderStart) && str. hasSuffix ( placeholderEnd)
453
- }
454
-
455
390
@_spi ( Testing)
456
391
public func wrapInPlaceholder( _ str: String ) -> String {
457
392
return placeholderStart + str + placeholderEnd
@@ -462,16 +397,5 @@ public func wrapInTypePlaceholder(_ str: String, type: String) -> String {
462
397
return wrapInPlaceholder ( " T## " + str + " ## " + type)
463
398
}
464
399
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
-
476
400
fileprivate let placeholderStart : String = " <# "
477
401
fileprivate let placeholderEnd : String = " #> "
0 commit comments