Skip to content

Commit 7f495e1

Browse files
committed
Allow macro expansions to be viewed through GetReferenceDocumentRequest instead of storing in temporary files
1 parent 8fea9fa commit 7f495e1

16 files changed

+493
-137
lines changed

Documentation/LSP Extensions.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,28 @@ export interface PeekDocumentsResult {
473473
success: boolean;
474474
}
475475
```
476+
477+
## `workspace/getReferenceDocument`
478+
479+
Request from the client to the server asking for contents of a URI having a custom scheme.
480+
For example: "sourcekit-lsp:"
481+
482+
- params: `GetReferenceDocumentParams`
483+
484+
- result: `GetReferenceDocumentResponse`
485+
486+
```ts
487+
export interface GetReferenceDocumentParams {
488+
/**
489+
* The `DocumentUri` of the custom scheme url for which content is required
490+
*/
491+
uri: DocumentUri;
492+
}
493+
494+
/**
495+
* Response containing `content` of `GetReferenceDocumentRequest`
496+
*/
497+
export interface GetReferenceDocumentResult {
498+
content: string;
499+
}
500+
```

Sources/LanguageServerProtocol/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ add_library(LanguageServerProtocol STATIC
5555
Requests/ExecuteCommandRequest.swift
5656
Requests/FoldingRangeRequest.swift
5757
Requests/FormattingRequests.swift
58+
Requests/GetReferenceDocumentRequest.swift
5859
Requests/HoverRequest.swift
5960
Requests/ImplementationRequest.swift
6061
Requests/IndexedRenameRequest.swift

Sources/LanguageServerProtocol/Messages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public let builtinRequests: [_RequestType.Type] = [
4848
DocumentTestsRequest.self,
4949
ExecuteCommandRequest.self,
5050
FoldingRangeRequest.self,
51+
GetReferenceDocumentRequest.self,
5152
HoverRequest.self,
5253
ImplementationRequest.self,
5354
InitializeRequest.self,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Request from the client to the server asking for contents of a URI having a custom scheme **(LSP Extension)**
14+
/// For example: "sourcekit-lsp:"
15+
///
16+
/// - Parameters:
17+
/// - uri: The `DocumentUri` of the custom scheme url for which content is required
18+
///
19+
/// - Returns: `GetReferenceDocumentResponse` which contains the `content` to be displayed.
20+
///
21+
/// ### LSP Extension
22+
///
23+
/// This request is an extension to LSP supported by SourceKit-LSP.
24+
public struct GetReferenceDocumentRequest: RequestType {
25+
public static let method: String = "workspace/getReferenceDocument"
26+
public typealias Response = GetReferenceDocumentResponse
27+
28+
public var uri: DocumentURI
29+
30+
public init(uri: DocumentURI) {
31+
self.uri = uri
32+
}
33+
}
34+
35+
/// Response containing `content` of `GetReferenceDocumentRequest`
36+
public struct GetReferenceDocumentResponse: ResponseType {
37+
public var content: String
38+
39+
public init(content: String) {
40+
self.content = content
41+
}
42+
}

Sources/SourceKitLSP/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ target_sources(SourceKitLSP PRIVATE
4747
Swift/ExpandMacroCommand.swift
4848
Swift/FoldingRange.swift
4949
Swift/MacroExpansion.swift
50+
Swift/MacroExpansionReferenceDocumentURLData.swift
5051
Swift/OpenInterface.swift
5152
Swift/RefactoringResponse.swift
5253
Swift/RefactoringEdit.swift
5354
Swift/RefactorCommand.swift
55+
Swift/ReferenceDocumentURL.swift
5456
Swift/RelatedIdentifiers.swift
5557
Swift/RewriteSourceKitPlaceholders.swift
5658
Swift/SemanticRefactorCommand.swift

Sources/SourceKitLSP/Clang/ClangLanguageService.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,10 @@ extension ClangLanguageService {
661661
func executeCommand(_ req: ExecuteCommandRequest) async throws -> LSPAny? {
662662
return try await forwardRequestToClangd(req)
663663
}
664+
665+
func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse {
666+
throw ResponseError.unknown("unsupported method")
667+
}
664668
}
665669

666670
/// Clang build settings derived from a `FileBuildSettingsChange`.

Sources/SourceKitLSP/LanguageService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ package protocol LanguageService: AnyObject, Sendable {
251251

252252
func executeCommand(_ req: ExecuteCommandRequest) async throws -> LSPAny?
253253

254+
func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse
255+
254256
/// Perform a syntactic scan of the file at the given URI for test cases and test classes.
255257
///
256258
/// This is used as a fallback to show the test cases in a file if the index for a given file is not up-to-date.

Sources/SourceKitLSP/Rename.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ extension SourceKitLSPServer {
687687
guard let workspace = await workspaceForDocument(uri: uri) else {
688688
throw ResponseError.workspaceNotOpen(uri)
689689
}
690-
guard let primaryFileLanguageService = workspace.documentService.value[uri] else {
690+
guard let primaryFileLanguageService = workspace.documentService(for: uri) else {
691691
return nil
692692
}
693693

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ package actor SourceKitLSPServer {
356356

357357
// This should be created as soon as we receive an open call, even if the document
358358
// isn't yet ready.
359-
guard let languageService = workspace.documentService.value[doc] else {
359+
guard let languageService = workspace.documentService(for: doc) else {
360360
return
361361
}
362362

@@ -376,7 +376,7 @@ package actor SourceKitLSPServer {
376376
guard let workspace = await self.workspaceForDocument(uri: request.textDocument.uri) else {
377377
throw ResponseError.workspaceNotOpen(request.textDocument.uri)
378378
}
379-
guard let languageService = workspace.documentService.value[doc] else {
379+
guard let languageService = workspace.documentService(for: doc) else {
380380
throw ResponseError.unknown("No language service for '\(request.textDocument.uri)' found")
381381
}
382382
return try await requestHandler(request, workspace, languageService)
@@ -399,7 +399,7 @@ package actor SourceKitLSPServer {
399399
guard let workspace = await self.workspaceForDocument(uri: documentUri) else {
400400
continue
401401
}
402-
guard workspace.documentService.value[documentUri] === languageService else {
402+
guard workspace.documentService(for: documentUri) === languageService else {
403403
continue
404404
}
405405
guard let snapshot = try? self.documentManager.latestSnapshot(documentUri) else {
@@ -517,7 +517,7 @@ package actor SourceKitLSPServer {
517517
_ language: Language,
518518
in workspace: Workspace
519519
) async -> LanguageService? {
520-
if let service = workspace.documentService.value[uri] {
520+
if let service = workspace.documentService(for: uri) {
521521
return service
522522
}
523523

@@ -535,17 +535,7 @@ package actor SourceKitLSPServer {
535535
"""
536536
)
537537

538-
return workspace.documentService.withLock { documentService in
539-
if let concurrentlySetService = documentService[uri] {
540-
// Since we await the construction of `service`, another call to this
541-
// function might have happened and raced us, setting
542-
// `workspace.documentServices[uri]`. If this is the case, return the
543-
// existing value and discard the service that we just retrieved.
544-
return concurrentlySetService
545-
}
546-
documentService[uri] = service
547-
return service
548-
}
538+
return workspace.documentService(for: uri, newLanguageService: service)
549539
}
550540
}
551541

@@ -732,6 +722,8 @@ extension SourceKitLSPServer: MessageHandler {
732722
await request.reply { try await executeCommand(request.params) }
733723
case let request as RequestAndReply<FoldingRangeRequest>:
734724
await self.handleRequest(for: request, requestHandler: self.foldingRange)
725+
case let request as RequestAndReply<GetReferenceDocumentRequest>:
726+
await request.reply { try await getReferenceDocument(request.params) }
735727
case let request as RequestAndReply<HoverRequest>:
736728
await self.handleRequest(for: request, requestHandler: self.hover)
737729
case let request as RequestAndReply<ImplementationRequest>:
@@ -803,7 +795,7 @@ extension SourceKitLSPServer: BuildSystemDelegate {
803795
continue
804796
}
805797

806-
guard let service = await self.workspaceForDocument(uri: uri)?.documentService.value[uri] else {
798+
guard let service = await self.workspaceForDocument(uri: uri)?.documentService(for: uri) else {
807799
continue
808800
}
809801

@@ -827,7 +819,7 @@ extension SourceKitLSPServer: BuildSystemDelegate {
827819
}
828820
for uri in self.affectedOpenDocumentsForChangeSet(changedFilesForWorkspace, self.documentManager) {
829821
logger.log("Dependencies updated for opened file \(uri.forLogging)")
830-
if let service = workspace.documentService.value[uri] {
822+
if let service = workspace.documentService(for: uri) {
831823
await service.documentDependenciesUpdated(uri)
832824
}
833825
}
@@ -962,6 +954,8 @@ extension SourceKitLSPServer {
962954
//
963955
// The below is a workaround for the vscode-swift extension since it cannot set client capabilities.
964956
// It passes "workspace/peekDocuments" through the `initializationOptions`.
957+
//
958+
// Similarly, for "workspace/getReferenceDocument".
965959
var clientCapabilities = req.capabilities
966960
if case .dictionary(let initializationOptions) = req.initializationOptions {
967961
if let peekDocuments = initializationOptions["workspace/peekDocuments"] {
@@ -973,6 +967,15 @@ extension SourceKitLSPServer {
973967
}
974968
}
975969

970+
if let getReferenceDocument = initializationOptions["workspace/getReferenceDocument"] {
971+
if case .dictionary(var experimentalCapabilities) = clientCapabilities.experimental {
972+
experimentalCapabilities["workspace/getReferenceDocument"] = getReferenceDocument
973+
clientCapabilities.experimental = .dictionary(experimentalCapabilities)
974+
} else {
975+
clientCapabilities.experimental = .dictionary(["workspace/getReferenceDocument": getReferenceDocument])
976+
}
977+
}
978+
976979
// The client announces what CodeLenses it supports, and the LSP will only return
977980
// ones found in the supportedCommands dictionary.
978981
if let codeLens = initializationOptions["textDocument/codeLens"],
@@ -1139,6 +1142,7 @@ extension SourceKitLSPServer {
11391142
"workspace/tests": .dictionary(["version": .int(2)]),
11401143
"textDocument/tests": .dictionary(["version": .int(2)]),
11411144
"workspace/triggerReindex": .dictionary(["version": .int(1)]),
1145+
"workspace/getReferenceDocument": .dictionary(["version": .int(1)]),
11421146
])
11431147
)
11441148
}
@@ -1338,7 +1342,7 @@ extension SourceKitLSPServer {
13381342
)
13391343
return
13401344
}
1341-
await workspace.documentService.value[uri]?.reopenDocument(notification)
1345+
await workspace.documentService(for: uri)?.reopenDocument(notification)
13421346
}
13431347

13441348
func closeDocument(_ notification: DidCloseTextDocumentNotification, workspace: Workspace) async {
@@ -1352,7 +1356,7 @@ extension SourceKitLSPServer {
13521356

13531357
await workspace.buildSystemManager.unregisterForChangeNotifications(for: uri)
13541358

1355-
await workspace.documentService.value[uri]?.closeDocument(notification)
1359+
await workspace.documentService(for: uri)?.closeDocument(notification)
13561360
}
13571361

13581362
func changeDocument(_ notification: DidChangeTextDocumentNotification) async {
@@ -1378,7 +1382,7 @@ extension SourceKitLSPServer {
13781382
// Already logged failure
13791383
return
13801384
}
1381-
await workspace.documentService.value[uri]?.changeDocument(
1385+
await workspace.documentService(for: uri)?.changeDocument(
13821386
notification,
13831387
preEditSnapshot: preEditSnapshot,
13841388
postEditSnapshot: postEditSnapshot,
@@ -1641,7 +1645,7 @@ extension SourceKitLSPServer {
16411645
guard let workspace = await workspaceForDocument(uri: uri) else {
16421646
throw ResponseError.workspaceNotOpen(uri)
16431647
}
1644-
guard let languageService = workspace.documentService.value[uri] else {
1648+
guard let languageService = workspace.documentService(for: uri) else {
16451649
return nil
16461650
}
16471651

@@ -1652,6 +1656,21 @@ extension SourceKitLSPServer {
16521656
return try await languageService.executeCommand(executeCommand)
16531657
}
16541658

1659+
func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse {
1660+
let referenceDocumentURL = try ReferenceDocumentURL(from: req.uri)
1661+
let sourceFileURI = referenceDocumentURL.sourceDocument()
1662+
1663+
guard let workspace = await workspaceForDocument(uri: sourceFileURI) else {
1664+
throw ResponseError.workspaceNotOpen(sourceFileURI)
1665+
}
1666+
1667+
guard let languageService = workspace.documentService(for: sourceFileURI) else {
1668+
throw ResponseError.unknown("No Language Service for URI: \(sourceFileURI)")
1669+
}
1670+
1671+
return try await languageService.getReferenceDocument(req)
1672+
}
1673+
16551674
func codeAction(
16561675
_ req: CodeActionRequest,
16571676
workspace: Workspace,

0 commit comments

Comments
 (0)