1010//
1111//===----------------------------------------------------------------------===//
1212
13+ import Crypto
1314import Foundation
1415import LSPLogging
1516import LanguageServerProtocol
@@ -46,17 +47,15 @@ struct MacroExpansion: RefactoringResponse {
4647extension SwiftLanguageService {
4748 /// Handles the `ExpandMacroCommand`.
4849 ///
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 .
5253 ///
5354 /// - Parameters:
5455 /// - expandMacroCommand: The `ExpandMacroCommand` that triggered this request.
55- ///
56- /// - Returns: A `[RefactoringEdit]` with the necessary edits and buffer name as a `LSPAny`
5756 func expandMacro(
5857 _ expandMacroCommand: ExpandMacroCommand
59- ) async throws -> LSPAny {
58+ ) async throws {
6059 guard let sourceKitLSPServer else {
6160 // `SourceKitLSPServer` has been destructed. We are tearing down the
6261 // language server. Nothing left to do.
@@ -69,6 +68,10 @@ extension SwiftLanguageService {
6968
7069 let expansion = try await self . refactoring ( expandMacroCommand)
7170
71+ var completeExpansionFileContent = " "
72+ var completeExpansionDirectoryName = " "
73+
74+ var macroExpansionFilePaths : [ URL ] = [ ]
7275 for macroEdit in expansion. edits {
7376 if let bufferName = macroEdit. bufferName {
7477 // buffer name without ".swift"
@@ -79,6 +82,9 @@ extension SwiftLanguageService {
7982
8083 let macroExpansionBufferDirectoryURL = self . generatedMacroExpansionsPath
8184 . appendingPathComponent ( macroExpansionBufferDirectoryName)
85+
86+ completeExpansionDirectoryName += " \( bufferName) - "
87+
8288 do {
8389 try FileManager . default. createDirectory (
8490 at: macroExpansionBufferDirectoryURL,
@@ -95,7 +101,7 @@ extension SwiftLanguageService {
95101
96102 // github permalink notation for position range
97103 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 ) "
99105
100106 let macroExpansionFilePath =
101107 macroExpansionBufferDirectoryURL
@@ -111,22 +117,95 @@ extension SwiftLanguageService {
111117 )
112118 }
113119
114- Task {
115- let req = ShowDocumentRequest ( uri: DocumentURI ( macroExpansionFilePath) , selection: macroEdit. range)
120+ macroExpansionFilePaths. append ( macroExpansionFilePath)
116121
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)
120126
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
125130 } else if !macroEdit. newText. isEmpty {
126131 logger. fault ( " Unable to retrieve some parts of macro expansion " )
127132 }
128133 }
129134
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+ }
131210 }
132211}
0 commit comments