Skip to content

Commit 19f139f

Browse files
committed
Make Nested Macro Expansions to work (Tested upto 3rd level)
1 parent 651eadc commit 19f139f

10 files changed

+81
-98
lines changed

Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,7 @@ public struct DocumentURI: Codable, Hashable, Sendable {
7676

7777
public init(_ url: URL) {
7878
self.storage = url
79-
80-
if !self.storage.absoluteString.hasPrefix("@") {
81-
assert(self.storage.scheme != nil, "Received invalid URI without a scheme '\(self.storage.absoluteString)'")
82-
}
79+
assert(self.storage.scheme != nil, "Received invalid URI without a scheme '\(self.storage.absoluteString)'")
8380
}
8481

8582
public init(filePath: String, isDirectory: Bool) {

Sources/SourceKitLSP/Swift/CursorInfo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ extension SwiftLanguageService {
153153
keys.cancelOnSubsequentRequest: 0,
154154
keys.offset: offsetRange.lowerBound,
155155
keys.length: offsetRange.upperBound != offsetRange.lowerBound ? offsetRange.count : nil,
156-
keys.sourceFile: snapshot.uri.actualFile.pseudoPath,
156+
keys.sourceFile: snapshot.uri.actualFilePath,
157157
keys.compilerArgs: await self.buildSettings(for: uri)?.compilerArgs as [SKDRequestValue]?,
158158
])
159159

Sources/SourceKitLSP/Swift/DiagnosticReportManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ actor DiagnosticReportManager {
104104

105105
let skreq = sourcekitd.dictionary([
106106
keys.request: requests.diagnostics,
107-
keys.sourceFile: snapshot.uri.actualFile.pseudoPath,
107+
keys.sourceFile: snapshot.uri.actualFilePath,
108108
keys.compilerArgs: compilerArgs as [SKDRequestValue],
109109
])
110110

Sources/SourceKitLSP/Swift/MacroExpansion.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ extension SwiftLanguageService {
8080
MacroExpansionReferenceDocumentURLData(
8181
macroExpansionEditRange: macroEdit.range,
8282
primaryFileURL: primaryFileURL,
83-
sourceFileURL: expandMacroCommand.textDocument.uri.arbitrarySchemeURL,
83+
parentReferenceDocumentURL: expandMacroCommand.textDocument.uri.arbitrarySchemeURL,
8484
selectionRange: expandMacroCommand.positionRange,
8585
bufferName: bufferName
8686
)
@@ -90,7 +90,7 @@ extension SwiftLanguageService {
9090
MacroExpansionReferenceDocumentURLData(
9191
macroExpansionEditRange: macroEdit.range,
9292
primaryFileURL: primaryFileURL,
93-
sourceFileURL: nil,
93+
parentReferenceDocumentURL: nil,
9494
selectionRange: expandMacroCommand.positionRange,
9595
bufferName: bufferName
9696
)
@@ -203,10 +203,10 @@ extension SwiftLanguageService {
203203

204204
func expandMacro(macroExpansionURLData: MacroExpansionReferenceDocumentURLData) async throws -> String {
205205
var expandMacroCommand: ExpandMacroCommand
206-
if let sourceFileURL = macroExpansionURLData.sourceFileURL {
206+
if let parentReferenceDocumentURL = macroExpansionURLData.parentReferenceDocumentURL {
207207
expandMacroCommand = ExpandMacroCommand(
208208
positionRange: macroExpansionURLData.selectionRange,
209-
textDocument: TextDocumentIdentifier(DocumentURI(sourceFileURL))
209+
textDocument: TextDocumentIdentifier(DocumentURI(parentReferenceDocumentURL)) // usage
210210
)
211211
} else {
212212
expandMacroCommand = ExpandMacroCommand(

Sources/SourceKitLSP/Swift/MacroExpansionReferenceDocumentURLData.swift

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,34 @@ import LanguageServerProtocol
1515
import RegexBuilder
1616

1717
/// Represents url of macro expansion reference document as follows:
18-
/// `sourcekit-lsp://swift-macro-expansion/LaCb-LcCd.swift?primaryFilePath=&sourceFilePath=&fromLine=&fromColumn=&toLine=&toColumn=&bufferName=`
18+
/// `sourcekit-lsp://swift-macro-expansion/LaCb-LcCd.swift?primaryFilePath=&fromLine=&fromColumn=&toLine=&toColumn=&bufferName=&parentReferenceDocumentURL=`
1919
///
2020
/// Here,
2121
/// - `LaCb-LcCd.swift`, the `displayName`, represents where the macro will expand to or
2222
/// replace in the source file (i.e. `macroExpansionEditRange`)
2323
/// - `primaryFilePath` denoting the URL of the source file
2424
/// - `fromLine`, `fromColumn`, `toLine`, `toColumn` represents the cursor's `selectionRange`
2525
/// - `bufferName` denotes the buffer name of the specific macro expansion edit
26+
/// - `parentReferenceDocumentURL` the reference document url of the macro
27+
/// which expands to the current macro (optional)
2628
package struct MacroExpansionReferenceDocumentURLData {
2729
package static let documentType = "swift-macro-expansion"
2830

2931
package var primaryFileURL: URL
30-
package var sourceFileURL: URL?
32+
package var parentReferenceDocumentURL: URL?
3133
package var selectionRange: Range<Position>
3234
package var bufferName: String
3335
package var macroExpansionEditRange: Range<Position>
3436

3537
package init(
3638
macroExpansionEditRange: Range<Position>,
3739
primaryFileURL: URL,
38-
sourceFileURL: URL?,
40+
parentReferenceDocumentURL: URL?,
3941
selectionRange: Range<Position>,
4042
bufferName: String
4143
) {
4244
self.primaryFileURL = primaryFileURL
43-
self.sourceFileURL = sourceFileURL
45+
self.parentReferenceDocumentURL = parentReferenceDocumentURL
4446
self.selectionRange = selectionRange
4547
self.bufferName = bufferName
4648
self.macroExpansionEditRange = macroExpansionEditRange
@@ -51,7 +53,7 @@ package struct MacroExpansionReferenceDocumentURLData {
5153
}
5254

5355
package var queryItems: [URLQueryItem] {
54-
if let sourceFileURL {
56+
if let parentReferenceDocumentURL { // usage
5557
[
5658
URLQueryItem(name: Parameters.fromLine, value: String(selectionRange.lowerBound.line)),
5759
URLQueryItem(name: Parameters.fromColumn, value: String(selectionRange.lowerBound.utf16index)),
@@ -60,8 +62,8 @@ package struct MacroExpansionReferenceDocumentURLData {
6062
URLQueryItem(name: Parameters.bufferName, value: bufferName),
6163
URLQueryItem(name: Parameters.primaryFilePath, value: primaryFileURL.path(percentEncoded: false)),
6264
URLQueryItem(
63-
name: Parameters.sourceFilePath,
64-
value: sourceFileURL.absoluteString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
65+
name: Parameters.parentReferenceDocumentURL,
66+
value: parentReferenceDocumentURL.absoluteString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
6567
),
6668
]
6769
} else {
@@ -93,62 +95,30 @@ package struct MacroExpansionReferenceDocumentURLData {
9395
)
9496
}
9597

96-
var sourceFileURL: URL? = nil
97-
if let sourceFilePath = queryItems.last(where: { $0.name == Parameters.sourceFilePath })?.value?
98-
.removingPercentEncoding
99-
{
100-
guard let url = URL(string: sourceFilePath) else {
98+
var parentReferenceDocumentURL: URL? = nil
99+
if let parentReferenceDocumentURLString = queryItems.last(where: {
100+
$0.name == Parameters.parentReferenceDocumentURL
101+
})?.value?
102+
.removingPercentEncoding {
103+
guard let url = URL(string: parentReferenceDocumentURLString) else {
101104
throw ReferenceDocumentURLError(
102105
description: "Unable to parse source file url"
103106
)
104107
}
105108

106-
sourceFileURL = url
109+
parentReferenceDocumentURL = url
107110
}
108111

109112
self.primaryFileURL = primaryFileURL
110-
self.sourceFileURL = sourceFileURL
113+
self.parentReferenceDocumentURL = parentReferenceDocumentURL
111114
self.selectionRange =
112115
Position(line: fromLine, utf16index: fromColumn)..<Position(line: toLine, utf16index: toColumn)
113116
self.bufferName = bufferName
114117
self.macroExpansionEditRange = try Self.parse(displayName: displayName)
115118
}
116119

117-
package var actualFile: DocumentURI {
118-
get throws {
119-
guard let uri = try? DocumentURI(string: bufferName) else {
120-
throw ReferenceDocumentURLError(
121-
description: "Unable to retrieve actual file uri of macro expansion reference document"
122-
)
123-
}
124-
125-
return uri
126-
}
127-
}
128-
129-
package var sourceFile: DocumentURI {
130-
get throws {
131-
var uri: DocumentURI?
132-
if let sourceFileURL {
133-
let referenceDocumentURL = try ReferenceDocumentURL(from: sourceFileURL)
134-
guard case let .macroExpansion(urlData) = referenceDocumentURL else {
135-
throw ReferenceDocumentURLError(
136-
description: "Unable to retrieve buffer name from source file"
137-
)
138-
}
139-
uri = try? DocumentURI(string: urlData.bufferName)
140-
} else {
141-
uri = try? DocumentURI(string: bufferName)
142-
}
143-
144-
guard let uri else {
145-
throw ReferenceDocumentURLError(
146-
description: "Unable to retrieve source file uri of macro expansion reference document"
147-
)
148-
}
149-
150-
return uri
151-
}
120+
package var actualFilePath: String {
121+
bufferName
152122
}
153123

154124
package var primaryFile: DocumentURI {
@@ -157,7 +127,7 @@ package struct MacroExpansionReferenceDocumentURLData {
157127

158128
private struct Parameters {
159129
static let primaryFilePath = "primaryFilePath"
160-
static let sourceFilePath = "sourceFilePath"
130+
static let parentReferenceDocumentURL = "parentReferenceDocumentURL"
161131
static let fromLine = "fromLine"
162132
static let fromColumn = "fromColumn"
163133
static let toLine = "toLine"
@@ -173,8 +143,8 @@ package struct MacroExpansionReferenceDocumentURLData {
173143
}
174144

175145
var result = URL(string: urlWithoutEncoding)
176-
if urlWithoutEncoding.contains(Parameters.sourceFilePath) {
177-
let location = urlWithoutEncoding.firstRange(of: "&\(Parameters.sourceFilePath)=")
146+
if urlWithoutEncoding.contains(Parameters.parentReferenceDocumentURL) {
147+
let location = urlWithoutEncoding.firstRange(of: "&\(Parameters.parentReferenceDocumentURL)=")
178148
guard let location else {
179149
return nil
180150
}
@@ -184,7 +154,7 @@ package struct MacroExpansionReferenceDocumentURLData {
184154
let range = start..<end
185155

186156
guard
187-
let sourceFileURL = String(urlWithoutEncoding[range]).addingPercentEncoding(
157+
let parentReferenceDocumentURL = String(urlWithoutEncoding[range]).addingPercentEncoding(
188158
withAllowedCharacters: .alphanumerics
189159
)
190160
else {
@@ -195,9 +165,10 @@ package struct MacroExpansionReferenceDocumentURLData {
195165
let firstEnd = location.lowerBound
196166
let firstRange = firstStart..<firstEnd
197167

198-
let urlExceptSourceFileURL = String(urlWithoutEncoding[firstRange])
168+
let urlExceptParentReferenceDocumentURL = String(urlWithoutEncoding[firstRange])
199169

200-
let finalURLString = urlExceptSourceFileURL + "&\(Parameters.sourceFilePath)=" + sourceFileURL
170+
let finalURLString =
171+
urlExceptParentReferenceDocumentURL + "&\(Parameters.parentReferenceDocumentURL)=" + parentReferenceDocumentURL
201172
result = URL(string: finalURLString)
202173
}
203174

Sources/SourceKitLSP/Swift/RefactoringResponse.swift

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,35 +103,63 @@ extension SwiftLanguageService {
103103
let uri = refactorCommand.textDocument.uri
104104
let referenceDocument = try? ReferenceDocumentURL(from: uri)
105105

106-
let snapshot = try self.documentManager.latestSnapshot(referenceDocument?.primaryFile ?? uri)
106+
var snapshot: DocumentSnapshot
107+
var opened = false
108+
109+
if let referenceDocument {
110+
let primaryFileSnapshot = try self.documentManager.latestSnapshot(referenceDocument.primaryFile)
111+
112+
var newSnapshot: DocumentSnapshot
113+
do {
114+
newSnapshot = try self.documentManager.latestSnapshot(uri)
115+
} catch {
116+
newSnapshot = try await documentManager.open(
117+
uri,
118+
language: primaryFileSnapshot.language,
119+
version: primaryFileSnapshot.version,
120+
text: getReferenceDocument(GetReferenceDocumentRequest(uri: refactorCommand.textDocument.uri)).content
121+
)
122+
opened = true
123+
}
124+
snapshot = newSnapshot
125+
} else {
126+
snapshot = try self.documentManager.latestSnapshot(uri)
127+
}
128+
107129
let line = refactorCommand.positionRange.lowerBound.line
108130
let utf16Column = refactorCommand.positionRange.lowerBound.utf16index
109131
let utf8Column = snapshot.lineTable.utf8ColumnAt(line: line, utf16Column: utf16Column)
132+
let length = snapshot.utf8OffsetRange(of: refactorCommand.positionRange).count
133+
let buildSettings =
134+
await self.buildSettings(for: referenceDocument?.primaryFile ?? snapshot.uri)?.compilerArgs as [SKDRequestValue]?
135+
136+
if opened {
137+
try? documentManager.close(snapshot.uri)
138+
}
110139

111140
let skreq = sourcekitd.dictionary([
112141
keys.request: self.requests.semanticRefactoring,
113142
// Preferred name for e.g. an extracted variable.
114143
// Empty string means sourcekitd chooses a name automatically.
115144
keys.name: "",
145+
keys.sourceFile: uri.actualFilePath,
116146
// LSP is zero based, but this request is 1 based.
117147
keys.line: line + 1,
118148
keys.column: utf8Column + 1,
119-
keys.length: snapshot.utf8OffsetRange(of: refactorCommand.positionRange).count,
149+
keys.length: length,
120150
keys.actionUID: self.sourcekitd.api.uid_get_from_cstr(refactorCommand.actionString)!,
121-
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
151+
keys.compilerArgs: buildSettings,
122152
])
123153

124154
if let referenceDocument {
125-
skreq.set(keys.sourceFile, to: try referenceDocument.actualFile.pseudoPath)
126155
skreq.set(keys.primaryFile, to: referenceDocument.primaryFile.pseudoPath)
127-
} else {
128-
skreq.set(keys.sourceFile, to: uri.actualFile.pseudoPath)
129156
}
130157

131158
let dict = try await sendSourcekitdRequest(skreq, fileContents: snapshot.text)
132159
guard let refactor = T.Response(refactorCommand.title, dict, snapshot, self.keys) else {
133160
throw SemanticRefactoringError.noEditsNeeded(uri)
134161
}
162+
135163
return refactor
136164
}
137165
}

Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89,28 +89,17 @@ package enum ReferenceDocumentURL {
8989
}
9090
}
9191

92-
/// The URI of the document which originally contains the contents of the reference document. This is used to
92+
/// The file path of the document which originally contains the contents of the reference document. This is used to
9393
/// communicate with sourcekitd.
94-
var actualFile: DocumentURI {
95-
get throws {
96-
switch self {
97-
case let .macroExpansion(data):
98-
return try data.actualFile
99-
}
100-
}
101-
}
102-
103-
var sourceFile: DocumentURI {
104-
get throws {
105-
switch self {
106-
case let .macroExpansion(data):
107-
return try data.sourceFile
108-
}
94+
var actualFilePath: String {
95+
switch self {
96+
case let .macroExpansion(data):
97+
data.actualFilePath
10998
}
11099
}
111100

112-
/// The URI of the document from which this reference document was derived. This is used to determine the
113-
/// workspace and language service that is used to generate the reference document.
101+
/// The URI of the document from which this reference document and its parent reference document was derived.
102+
/// This is used to determine the workspace and language service that is used to generate the reference document.
114103
var primaryFile: DocumentURI {
115104
switch self {
116105
case let .macroExpansion(data):
@@ -120,13 +109,11 @@ package enum ReferenceDocumentURL {
120109
}
121110

122111
extension DocumentURI {
123-
var actualFile: DocumentURI {
124-
if let referenceDocument = try? ReferenceDocumentURL(from: self),
125-
let actualFile = try? referenceDocument.actualFile
126-
{
127-
actualFile
112+
var actualFilePath: String {
113+
if let referenceDocument = try? ReferenceDocumentURL(from: self) {
114+
referenceDocument.actualFilePath
128115
} else {
129-
self
116+
self.pseudoPath
130117
}
131118
}
132119

Sources/SourceKitLSP/Swift/RelatedIdentifiers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ extension SwiftLanguageService {
6969
keys.request: requests.relatedIdents,
7070
keys.cancelOnSubsequentRequest: 0,
7171
keys.offset: snapshot.utf8Offset(of: position),
72-
keys.sourceFile: snapshot.uri.actualFile.pseudoPath,
72+
keys.sourceFile: snapshot.uri.actualFilePath,
7373
keys.includeNonEditableBaseNames: includeNonEditableBaseNames ? 1 : 0,
7474
keys.compilerArgs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDRequestValue]?,
7575
])

Sources/SourceKitLSP/Swift/SemanticTokens.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension SwiftLanguageService {
2626

2727
let skreq = sourcekitd.dictionary([
2828
keys.request: requests.semanticTokens,
29-
keys.sourceFile: snapshot.uri.actualFile.pseudoPath,
29+
keys.sourceFile: snapshot.uri.actualFilePath,
3030
keys.compilerArgs: buildSettings.compilerArgs as [SKDRequestValue],
3131
])
3232

Sources/SourceKitLSP/Swift/VariableTypeInfo.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ extension SwiftLanguageService {
8787

8888
let skreq = sourcekitd.dictionary([
8989
keys.request: requests.collectVariableType,
90-
keys.sourceFile: snapshot.uri.actualFile.pseudoPath,
90+
keys.sourceFile: snapshot.uri.actualFilePath,
9191
keys.compilerArgs: await self.buildSettings(for: uri)?.compilerArgs as [SKDRequestValue]?,
9292
])
9393

0 commit comments

Comments
 (0)