-
Notifications
You must be signed in to change notification settings - Fork 307
Add Semantic Functionality to Macro Expansion Reference Documents (including Nested Macro Expansion) 🚦 #1610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,21 @@ struct MacroExpansion: RefactoringResponse { | |
} | ||
} | ||
|
||
actor GeneratedMacroExpansionsStorage { | ||
static var shared = GeneratedMacroExpansionsStorage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of having a static shared instance, can this be a member on |
||
private init() {} | ||
|
||
private var generatedMacroExpansions: [String: String] = [:] | ||
|
||
func addOrUpdateMacroExpansion(havingBufferName bufferName: String, withContents content: String) { | ||
generatedMacroExpansions[bufferName] = content | ||
} | ||
|
||
func retrieveAndDeleteMacroExpansion(havingBufferName bufferName: String) -> String? { | ||
return generatedMacroExpansions.removeValue(forKey: bufferName) | ||
} | ||
} | ||
|
||
extension SwiftLanguageService { | ||
/// Handles the `ExpandMacroCommand`. | ||
/// | ||
|
@@ -62,9 +77,9 @@ extension SwiftLanguageService { | |
throw ResponseError.unknown("Connection to the editor closed") | ||
} | ||
|
||
guard let primaryFileURL = expandMacroCommand.textDocument.uri.fileURL else { | ||
throw ResponseError.unknown("Given URI is not a file URL") | ||
} | ||
let referenceDocumentURL = try? ReferenceDocumentURL(from: expandMacroCommand.textDocument.uri) | ||
let primaryFileURL = | ||
referenceDocumentURL?.primaryFile.arbitrarySchemeURL ?? expandMacroCommand.textDocument.uri.arbitrarySchemeURL | ||
|
||
let expansion = try await self.refactoring(expandMacroCommand) | ||
|
||
|
@@ -74,6 +89,11 @@ extension SwiftLanguageService { | |
var macroExpansionReferenceDocumentURLs: [ReferenceDocumentURL] = [] | ||
for macroEdit in expansion.edits { | ||
if let bufferName = macroEdit.bufferName { | ||
await GeneratedMacroExpansionsStorage.shared.addOrUpdateMacroExpansion( | ||
havingBufferName: bufferName, | ||
withContents: macroEdit.newText | ||
) | ||
|
||
let macroExpansionReferenceDocumentURLData = | ||
ReferenceDocumentURL.macroExpansion( | ||
MacroExpansionReferenceDocumentURLData( | ||
|
@@ -108,10 +128,24 @@ extension SwiftLanguageService { | |
let expansionURIs = try macroExpansionReferenceDocumentURLs.map { | ||
return DocumentURI(try $0.url) | ||
} | ||
|
||
Task { | ||
let (uri, position) = | ||
if let referenceDocumentURL, case let .macroExpansion(referenceDocumentURLData) = referenceDocumentURL { | ||
( | ||
referenceDocumentURL.primaryFile, | ||
referenceDocumentURLData.macroExpansionEditRange.lowerBound | ||
) | ||
} else { | ||
( | ||
expandMacroCommand.textDocument.uri, | ||
expandMacroCommand.positionRange.lowerBound | ||
) | ||
} | ||
|
||
let req = PeekDocumentsRequest( | ||
uri: expandMacroCommand.textDocument.uri, | ||
position: expandMacroCommand.positionRange.lowerBound, | ||
uri: uri, | ||
position: position, | ||
locations: expansionURIs | ||
) | ||
|
||
|
@@ -179,22 +213,15 @@ extension SwiftLanguageService { | |
} | ||
} | ||
|
||
func expandMacro(macroExpansionURLData: MacroExpansionReferenceDocumentURLData) async throws -> String { | ||
let expandMacroCommand = ExpandMacroCommand( | ||
positionRange: macroExpansionURLData.selectionRange, | ||
textDocument: TextDocumentIdentifier(macroExpansionURLData.primaryFile) | ||
) | ||
|
||
let expansion = try await self.refactoring(expandMacroCommand) | ||
|
||
func getMacroExpansion(macroExpansionURLData: MacroExpansionReferenceDocumentURLData) async throws -> String { | ||
guard | ||
let macroExpansionEdit = expansion.edits.filter({ | ||
$0.bufferName == macroExpansionURLData.bufferName | ||
}).only | ||
let content = await GeneratedMacroExpansionsStorage.shared.retrieveAndDeleteMacroExpansion( | ||
havingBufferName: macroExpansionURLData.bufferName | ||
) | ||
else { | ||
throw ResponseError.unknown("Macro expansion edit doesn't exist") | ||
} | ||
|
||
return macroExpansionEdit.newText | ||
return content | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,10 @@ package enum ReferenceDocumentURL { | |
|
||
switch documentType { | ||
case MacroExpansionReferenceDocumentURLData.documentType: | ||
guard let queryItems = URLComponents(string: url.absoluteString)?.queryItems else { | ||
guard | ||
let queryItems = URLComponents(string: url.absoluteString.removingPercentEncoding ?? url.absoluteString)? | ||
.queryItems | ||
Comment on lines
+67
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to remove the percent encoding here? Do you have a log of the requests and replies sent between SourceKit-LSP and VS Code to see where the additional percent encoding gets added? |
||
else { | ||
throw ReferenceDocumentURLError( | ||
description: "No queryItems passed for macro expansion reference document: \(url)" | ||
) | ||
|
@@ -85,8 +88,17 @@ package enum ReferenceDocumentURL { | |
} | ||
} | ||
|
||
/// The URI of the document from which this reference document was derived. This is used to determine the | ||
/// workspace and language service that is used to generate the reference document. | ||
/// The file path of the document that originally contains the contents of this reference document. | ||
/// This is used since sourcekitd cannot understand reference document urls. | ||
var actualFilePath: String { | ||
switch self { | ||
case let .macroExpansion(data): | ||
data.actualFilePath | ||
} | ||
} | ||
|
||
/// The URI of the source file from which this reference document was derived. | ||
/// This is used to determine the workspace and language service that is used to generate the reference document. | ||
var primaryFile: DocumentURI { | ||
switch self { | ||
case let .macroExpansion(data): | ||
|
@@ -95,6 +107,20 @@ package enum ReferenceDocumentURL { | |
} | ||
} | ||
|
||
extension DocumentURI { | ||
/// The file path of the document that originally contains the contents of the reference document | ||
/// of this `DocumentURI`. This is used since sourcekitd cannot understand reference document urls. | ||
/// | ||
/// If the `DocumentURI` is not a reference document, this gives the `pseudoPath` | ||
var actualFilePath: String { | ||
lokesh-tr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if let referenceDocument = try? ReferenceDocumentURL(from: self) { | ||
referenceDocument.actualFilePath | ||
} else { | ||
self.pseudoPath | ||
} | ||
} | ||
} | ||
|
||
package struct ReferenceDocumentURLError: Error, CustomStringConvertible { | ||
package var description: String | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having an
if
expression inside aguard
condition looks a little confusing to me. Can you check if there’s a way to refactor this, maybe splitting it into twoguard
s?