@@ -14,166 +14,34 @@ import Foundation
1414import SKLogging
1515@_spi ( RawSyntax) import SwiftSyntax
1616
17- /// Translate SourceKit placeholder syntax — `<#foo#>` — in `input` to LSP
18- /// placeholder syntax: `${n:foo}`.
19- ///
20- /// If `clientSupportsSnippets` is `false`, the placeholder is rendered as an
21- /// empty string, to prevent the client from inserting special placeholder
22- /// characters as if they were literal text.
23- @_spi ( Testing)
24- public func rewriteSourceKitPlaceholders( in input: String , clientSupportsSnippets: Bool ) -> String {
25- var result = " "
26- var nextPlaceholderNumber = 1
27- // Current stack of nested placeholders, most nested last. Each element needs
28- // to be rendered inside the element before it.
29- var placeholders : [ ( number: Int , contents: String ) ] = [ ]
30- let tokens = tokenize ( input)
31- for token in tokens {
32- switch token {
33- case let . text( text) :
34- if placeholders. isEmpty {
35- result += text
36- } else {
37- placeholders. latest. contents += text
38- }
39-
40- case let . curlyBrace( brace) :
41- if placeholders. isEmpty {
42- result. append ( brace)
43- } else {
44- // Braces are only escaped _inside_ a placeholder; otherwise the client
45- // would include the backslashes literally.
46- placeholders. latest. contents. append ( contentsOf: [ " \\ " , brace] )
47- }
48-
49- case . placeholderOpen:
50- placeholders. append ( ( number: nextPlaceholderNumber, contents: " " ) )
51- nextPlaceholderNumber += 1
52-
53- case . placeholderClose:
54- guard let ( number, placeholderBody) = placeholders. popLast ( ) else {
55- logger. fault ( " Invalid placeholder in \( input) " )
56- return input
57- }
58- guard let displayName = nameForSnippet ( placeholderBody) else {
59- logger. fault ( " Failed to decode placeholder \( placeholderBody) in \( input) " )
60- return input
61- }
62- let placeholder =
63- clientSupportsSnippets
64- ? formatLSPPlaceholder ( displayName, number: number)
65- : " "
66- if placeholders. isEmpty {
67- result += placeholder
68- } else {
69- placeholders. latest. contents += placeholder
70- }
71- }
72- }
73-
74- return result
75- }
76-
77- /// Scan `input` to identify special elements within: curly braces, which may
78- /// need to be escaped; and SourceKit placeholder open/close delimiters.
79- private func tokenize( _ input: String ) -> [ SnippetToken ] {
80- var index = input. startIndex
81- var isAtEnd : Bool { index == input. endIndex }
82- func match( _ char: Character ) -> Bool {
83- if isAtEnd || input [ index] != char {
84- return false
85- } else {
86- input. formIndex ( after: & index)
87- return true
17+ func rewriteSourceKitPlaceholders( in string: String , clientSupportsSnippets: Bool ) -> String {
18+ var result = string
19+ var index = 1
20+ while let start = result. range ( of: " <# " ) {
21+ guard let end = result [ start. upperBound... ] . range ( of: " #> " ) else {
22+ logger. fault ( " Invalid placeholder in \( string) " )
23+ return string
8824 }
89- }
90- func next( ) -> Character ? {
91- guard !isAtEnd else { return nil }
92- defer { input. formIndex ( after: & index) }
93- return input [ index]
94- }
95-
96- var tokens : [ SnippetToken ] = [ ]
97- var text = " "
98- while let char = next ( ) {
99- switch char {
100- case " < " :
101- if match ( " # " ) {
102- tokens. append ( . text( text) )
103- text. removeAll ( )
104- tokens. append ( . placeholderOpen)
105- } else {
106- text. append ( char)
107- }
108-
109- case " # " :
110- if match ( " > " ) {
111- tokens. append ( . text( text) )
112- text. removeAll ( )
113- tokens. append ( . placeholderClose)
114- } else {
115- text. append ( char)
116- }
117-
118- case " { " , " } " :
119- tokens. append ( . text( text) )
120- text. removeAll ( )
121- tokens. append ( . curlyBrace( char) )
122-
123- case let c:
124- text. append ( c)
25+ let rawPlaceholder = String ( result [ start. lowerBound..< end. upperBound] )
26+ guard let displayName = nameForSnippet ( rawPlaceholder) else {
27+ logger. fault ( " Failed to decode placeholder \( rawPlaceholder) in \( string) " )
28+ return string
12529 }
30+ let snippet = clientSupportsSnippets ? " ${ \( index) : \( displayName) } " : " "
31+ result. replaceSubrange ( start. lowerBound..< end. upperBound, with: snippet)
32+ index += 1
12633 }
127-
128- tokens. append ( . text( text) )
129-
130- return tokens
131- }
132-
133- /// A syntactical element inside a SourceKit snippet.
134- private enum SnippetToken {
135- /// A placeholder delimiter.
136- case placeholderOpen, placeholderClose
137- /// One of '{' or '}', which may need to be escaped in the output.
138- case curlyBrace( Character )
139- /// Any other consecutive run of characters from the input, which needs no
140- /// special treatment.
141- case text( String )
34+ return result
14235}
14336
144- /// Given the interior text of a SourceKit placeholder, extract a display name
145- /// suitable for a LSP snippet.
146- private func nameForSnippet( _ body : String ) -> String ? {
147- var text = rewrappedAsPlaceholder ( body )
37+ /// Parse a SourceKit placeholder and extract the display name suitable for a
38+ /// LSP snippet.
39+ fileprivate func nameForSnippet( _ text : String ) -> String ? {
40+ var text = text
14841 return text. withSyntaxText {
14942 guard let data = RawEditorPlaceholderData ( syntaxText: $0) else {
15043 return nil
15144 }
15245 return String ( syntaxText: data. typeForExpansionText ?? data. displayText)
15346 }
15447}
155-
156- private let placeholderStart = " <# "
157- private let placeholderEnd = " #> "
158- private func rewrappedAsPlaceholder( _ body: String ) -> String {
159- return placeholderStart + body + placeholderEnd
160- }
161-
162- /// Wrap `body` in LSP snippet placeholder syntax, using `number` as the
163- /// placeholder's index in the snippet.
164- private func formatLSPPlaceholder( _ body: String , number: Int ) -> String {
165- " ${ \( number) : \( body) } "
166- }
167-
168- private extension Array {
169- /// Mutable access to the final element of an array.
170- ///
171- /// - precondition: The array must not be empty.
172- var latest : Element {
173- get { self . last! }
174- _modify {
175- let index = self . index ( before: self . endIndex)
176- yield & self [ index]
177- }
178- }
179- }
0 commit comments