10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ import Crypto
13
14
import Foundation
14
15
import LSPLogging
15
16
import LanguageServerProtocol
@@ -46,17 +47,15 @@ struct MacroExpansion: RefactoringResponse {
46
47
extension SwiftLanguageService {
47
48
/// Handles the `ExpandMacroCommand`.
48
49
///
49
- /// Makes a request to sourcekitd and wraps the result into a `MacroExpansion`
50
- /// and then makes a `ShowDocumentRequest` to the client side for each
51
- /// expansion to be displayed .
50
+ /// Makes a `PeekDocumentsRequest` or `ShowDocumentRequest`, containing the
51
+ /// location of each macro expansion, to the client depending on whether the
52
+ /// client supports the `experimental["workspace/peekDocuments"]` capability .
52
53
///
53
54
/// - Parameters:
54
55
/// - expandMacroCommand: The `ExpandMacroCommand` that triggered this request.
55
- ///
56
- /// - Returns: A `[RefactoringEdit]` with the necessary edits and buffer name as a `LSPAny`
57
56
func expandMacro(
58
57
_ expandMacroCommand: ExpandMacroCommand
59
- ) async throws -> LSPAny {
58
+ ) async throws {
60
59
guard let sourceKitLSPServer else {
61
60
// `SourceKitLSPServer` has been destructed. We are tearing down the
62
61
// language server. Nothing left to do.
@@ -69,6 +68,10 @@ extension SwiftLanguageService {
69
68
70
69
let expansion = try await self . refactoring ( expandMacroCommand)
71
70
71
+ var completeExpansionFileContent = " "
72
+ var completeExpansionDirectoryName = " "
73
+
74
+ var macroExpansionFilePaths : [ URL ] = [ ]
72
75
for macroEdit in expansion. edits {
73
76
if let bufferName = macroEdit. bufferName {
74
77
// buffer name without ".swift"
@@ -79,6 +82,9 @@ extension SwiftLanguageService {
79
82
80
83
let macroExpansionBufferDirectoryURL = self . generatedMacroExpansionsPath
81
84
. appendingPathComponent ( macroExpansionBufferDirectoryName)
85
+
86
+ completeExpansionDirectoryName += " \( bufferName) - "
87
+
82
88
do {
83
89
try FileManager . default. createDirectory (
84
90
at: macroExpansionBufferDirectoryURL,
@@ -95,7 +101,7 @@ extension SwiftLanguageService {
95
101
96
102
// github permalink notation for position range
97
103
let macroExpansionPositionRangeIndicator =
98
- " L \( macroEdit. range. lowerBound. line) C \( macroEdit. range. lowerBound. utf16index) -L \( macroEdit. range. upperBound. line) C \( macroEdit. range. upperBound. utf16index) "
104
+ " L \( macroEdit. range. lowerBound. line + 1 ) C \( macroEdit. range. lowerBound. utf16index + 1 ) -L \( macroEdit. range. upperBound. line + 1 ) C \( macroEdit. range. upperBound. utf16index + 1 ) "
99
105
100
106
let macroExpansionFilePath =
101
107
macroExpansionBufferDirectoryURL
@@ -111,22 +117,95 @@ extension SwiftLanguageService {
111
117
)
112
118
}
113
119
114
- Task {
115
- let req = ShowDocumentRequest ( uri: DocumentURI ( macroExpansionFilePath) , selection: macroEdit. range)
120
+ macroExpansionFilePaths. append ( macroExpansionFilePath)
116
121
117
- let response = await orLog ( " Sending ShowDocumentRequest to Client " ) {
118
- try await sourceKitLSPServer. sendRequestToClient ( req)
119
- }
122
+ let editContent =
123
+ """
124
+ // \( sourceFileURL. lastPathComponent) @ \( macroEdit. range. lowerBound. line + 1 ) : \( macroEdit. range. lowerBound. utf16index + 1 ) - \( macroEdit. range. upperBound. line + 1 ) : \( macroEdit. range. upperBound. utf16index + 1 )
125
+ \( macroEdit. newText)
120
126
121
- if let response, !response. success {
122
- logger. error ( " client refused to show document for \( expansion. title, privacy: . public) " )
123
- }
124
- }
127
+
128
+ """
129
+ completeExpansionFileContent += editContent
125
130
} else if !macroEdit. newText. isEmpty {
126
131
logger. fault ( " Unable to retrieve some parts of macro expansion " )
127
132
}
128
133
}
129
134
130
- return expansion. edits. encodeToLSPAny ( )
135
+ // removes superfluous newline
136
+ if completeExpansionFileContent. hasSuffix ( " \n \n " ) {
137
+ completeExpansionFileContent. removeLast ( )
138
+ }
139
+
140
+ if completeExpansionDirectoryName. hasSuffix ( " - " ) {
141
+ completeExpansionDirectoryName. removeLast ( )
142
+ }
143
+
144
+ var completeExpansionFilePath =
145
+ self . generatedMacroExpansionsPath. appendingPathComponent (
146
+ Insecure . MD5. hash (
147
+ data: Data ( completeExpansionDirectoryName. utf8)
148
+ )
149
+ . map { String ( format: " %02hhx " , $0) } // maps each byte of the hash to its hex equivalent `String`
150
+ . joined ( )
151
+ )
152
+
153
+ do {
154
+ try FileManager . default. createDirectory (
155
+ at: completeExpansionFilePath,
156
+ withIntermediateDirectories: true
157
+ )
158
+ } catch {
159
+ throw ResponseError . unknown (
160
+ " Failed to create directory for complete macro expansion at path: \( completeExpansionFilePath. path) "
161
+ )
162
+ }
163
+
164
+ completeExpansionFilePath =
165
+ completeExpansionFilePath. appendingPathComponent ( sourceFileURL. lastPathComponent)
166
+ do {
167
+ try completeExpansionFileContent. write ( to: completeExpansionFilePath, atomically: true , encoding: . utf8)
168
+ } catch {
169
+ throw ResponseError . unknown (
170
+ " Unable to write complete macro expansion to file path: \" \( completeExpansionFilePath. path) \" "
171
+ )
172
+ }
173
+
174
+ let completeMacroExpansionFilePath = completeExpansionFilePath
175
+ let expansionURIs = macroExpansionFilePaths. map {
176
+ return DocumentURI ( $0)
177
+ }
178
+
179
+ if case . dictionary( let experimentalCapabilities) = self . capabilityRegistry. clientCapabilities. experimental,
180
+ case . bool( true ) = experimentalCapabilities [ " workspace/peekDocuments " ]
181
+ {
182
+ Task {
183
+ let req = PeekDocumentsRequest (
184
+ uri: expandMacroCommand. textDocument. uri,
185
+ position: expandMacroCommand. positionRange. lowerBound,
186
+ locations: expansionURIs
187
+ )
188
+
189
+ let response = await orLog ( " Sending PeekDocumentsRequest to Client " ) {
190
+ try await sourceKitLSPServer. sendRequestToClient ( req)
191
+ }
192
+
193
+ if let response, !response. success {
194
+ logger. error ( " client refused to peek macro " )
195
+ }
196
+ }
197
+ } else {
198
+ Task {
199
+ let req = ShowDocumentRequest ( uri: DocumentURI ( completeMacroExpansionFilePath) )
200
+
201
+ let response = await orLog ( " Sending ShowDocumentRequest to Client " ) {
202
+ try await sourceKitLSPServer. sendRequestToClient ( req)
203
+ }
204
+
205
+ if let response, !response. success {
206
+ logger. error ( " client refused to show document for macro expansion " )
207
+ }
208
+ }
209
+ }
131
210
}
132
211
}
0 commit comments