From 5db1179e51af8912c7e1492d5d4cb7b7a4161922 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 22 Aug 2024 18:02:57 +0200 Subject: [PATCH 01/13] Migrate getReferenceDocument to upstream textDocumentContent --- Contributor Documentation/LSP Extensions.md | 27 ------------------- Sources/LanguageServerProtocol/CMakeLists.txt | 2 +- Sources/LanguageServerProtocol/Messages.swift | 2 +- ...swift => TextDocumentContentRequest.swift} | 22 +++++++-------- .../Clang/ClangLanguageService.swift | 2 +- Sources/SourceKitLSP/LanguageService.swift | 2 +- .../MessageHandlingDependencyTracker.swift | 2 +- Sources/SourceKitLSP/SourceKitLSPServer.swift | 20 +++----------- .../SourceKitLSP/Swift/MacroExpansion.swift | 3 +-- .../Swift/ReferenceDocumentURL.swift | 2 +- .../Swift/SwiftLanguageService.swift | 6 ++--- .../SourceKitLSPTests/ExpandMacroTests.swift | 24 ++++++++--------- 12 files changed, 37 insertions(+), 77 deletions(-) rename Sources/LanguageServerProtocol/Requests/{GetReferenceDocumentRequest.swift => TextDocumentContentRequest.swift} (60%) diff --git a/Contributor Documentation/LSP Extensions.md b/Contributor Documentation/LSP Extensions.md index d7b34bb58..558901c3b 100644 --- a/Contributor Documentation/LSP Extensions.md +++ b/Contributor Documentation/LSP Extensions.md @@ -595,30 +595,3 @@ export interface PeekDocumentsResult { success: boolean; } ``` - -## `workspace/getReferenceDocument` - -Request from the client to the server asking for contents of a URI having a custom scheme. -For example: "sourcekit-lsp:" - -Enable the experimental client capability `"workspace/getReferenceDocument"` so that the server responds with reference document URLs for certain requests or commands whenever possible. - -- params: `GetReferenceDocumentParams` - -- result: `GetReferenceDocumentResponse` - -```ts -export interface GetReferenceDocumentParams { - /** - * The `DocumentUri` of the custom scheme url for which content is required - */ - uri: DocumentUri; -} - -/** - * Response containing `content` of `GetReferenceDocumentRequest` - */ -export interface GetReferenceDocumentResult { - content: string; -} -``` diff --git a/Sources/LanguageServerProtocol/CMakeLists.txt b/Sources/LanguageServerProtocol/CMakeLists.txt index 3b6483686..9d56c8599 100644 --- a/Sources/LanguageServerProtocol/CMakeLists.txt +++ b/Sources/LanguageServerProtocol/CMakeLists.txt @@ -55,7 +55,6 @@ add_library(LanguageServerProtocol STATIC Requests/ExecuteCommandRequest.swift Requests/FoldingRangeRequest.swift Requests/FormattingRequests.swift - Requests/GetReferenceDocumentRequest.swift Requests/HoverRequest.swift Requests/ImplementationRequest.swift Requests/IndexedRenameRequest.swift @@ -79,6 +78,7 @@ add_library(LanguageServerProtocol STATIC Requests/ShutdownRequest.swift Requests/SignatureHelpRequest.swift Requests/SymbolInfoRequest.swift + Requests/TextDocumentContentRequest.swift Requests/TriggerReindexRequest.swift Requests/TypeDefinitionRequest.swift Requests/TypeHierarchyPrepareRequest.swift diff --git a/Sources/LanguageServerProtocol/Messages.swift b/Sources/LanguageServerProtocol/Messages.swift index e0bd03a27..b32e370fa 100644 --- a/Sources/LanguageServerProtocol/Messages.swift +++ b/Sources/LanguageServerProtocol/Messages.swift @@ -48,7 +48,6 @@ public let builtinRequests: [_RequestType.Type] = [ DocumentTestsRequest.self, ExecuteCommandRequest.self, FoldingRangeRequest.self, - GetReferenceDocumentRequest.self, HoverRequest.self, ImplementationRequest.self, InitializeRequest.self, @@ -71,6 +70,7 @@ public let builtinRequests: [_RequestType.Type] = [ ShutdownRequest.self, SignatureHelpRequest.self, SymbolInfoRequest.self, + TextDocumentContentRequest.self, TriggerReindexRequest.self, TypeDefinitionRequest.self, TypeHierarchyPrepareRequest.self, diff --git a/Sources/LanguageServerProtocol/Requests/GetReferenceDocumentRequest.swift b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift similarity index 60% rename from Sources/LanguageServerProtocol/Requests/GetReferenceDocumentRequest.swift rename to Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift index eb3a233f1..cc53042a7 100644 --- a/Sources/LanguageServerProtocol/Requests/GetReferenceDocumentRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift @@ -10,22 +10,22 @@ // //===----------------------------------------------------------------------===// -/// Request from the client to the server asking for contents of a URI having a custom scheme **(LSP Extension)** +/// Request from the client to the server asking for contents of a URI having a custom scheme /// For example: "sourcekit-lsp:" /// /// - Parameters: /// - uri: The `DocumentUri` of the custom scheme url for which content is required /// -/// - Returns: `GetReferenceDocumentResponse` which contains the `content` to be displayed. +/// - Returns: `TextDocumentContentResponse` which contains the `content` to be displayed. /// /// ### LSP Extension /// /// This request is an extension to LSP supported by SourceKit-LSP. -/// Enable the experimental client capability `"workspace/getReferenceDocument"` so that the server responds with +/// Enable the experimental client capability `"workspace/textDocumentContent"` so that the server responds with /// reference document URLs for certain requests or commands whenever possible. -public struct GetReferenceDocumentRequest: RequestType { - public static let method: String = "workspace/getReferenceDocument" - public typealias Response = GetReferenceDocumentResponse +public struct TextDocumentContentRequest: RequestType { + public static let method: String = "workspace/textDocumentContent" + public typealias Response = TextDocumentContentResponse public var uri: DocumentURI @@ -34,11 +34,11 @@ public struct GetReferenceDocumentRequest: RequestType { } } -/// Response containing `content` of `GetReferenceDocumentRequest` -public struct GetReferenceDocumentResponse: ResponseType { - public var content: String +/// Response containing `text` of `TextDocumentContentRequest` +public struct TextDocumentContentResponse: ResponseType { + public var text: String - public init(content: String) { - self.content = content + public init(text: String) { + self.text = text } } diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift index 48dcd0249..4639744f2 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift @@ -648,7 +648,7 @@ extension ClangLanguageService { return try await forwardRequestToClangd(req) } - func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse { + func textDocumentContent(_ req: TextDocumentContentRequest) async throws -> TextDocumentContentResponse { throw ResponseError.unknown("unsupported method") } } diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index bc32fce75..47a0e952e 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -252,7 +252,7 @@ package protocol LanguageService: AnyObject, Sendable { func executeCommand(_ req: ExecuteCommandRequest) async throws -> LSPAny? - func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse + func textDocumentContent(_ req: TextDocumentContentRequest) async throws -> TextDocumentContentResponse /// Perform a syntactic scan of the file at the given URI for test cases and test classes. /// diff --git a/Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift b/Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift index 8262403f5..35a7a4dd4 100644 --- a/Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift +++ b/Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift @@ -179,7 +179,7 @@ enum MessageHandlingDependencyTracker: DependencyTracker { } else { self = .freestanding } - case let request as GetReferenceDocumentRequest: + case let request as TextDocumentContentRequest: self = .documentRequest(request.uri) case is InitializeRequest: self = .globalConfigurationChange diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 7576802cc..ab32c7a92 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -746,8 +746,8 @@ extension SourceKitLSPServer: MessageHandler { await request.reply { try await executeCommand(request.params) } case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.foldingRange) - case let request as RequestAndReply: - await request.reply { try await getReferenceDocument(request.params) } + case let request as RequestAndReply: + await request.reply { try await textDocumentContent(request.params) } case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.hover) case let request as RequestAndReply: @@ -986,8 +986,6 @@ extension SourceKitLSPServer { // // The below is a workaround for the vscode-swift extension since it cannot set client capabilities. // It passes "workspace/peekDocuments" through the `initializationOptions`. - // - // Similarly, for "workspace/getReferenceDocument". var clientCapabilities = req.capabilities if case .dictionary(let initializationOptions) = req.initializationOptions { if let peekDocuments = initializationOptions["workspace/peekDocuments"] { @@ -999,15 +997,6 @@ extension SourceKitLSPServer { } } - if let getReferenceDocument = initializationOptions["workspace/getReferenceDocument"] { - if case .dictionary(var experimentalCapabilities) = clientCapabilities.experimental { - experimentalCapabilities["workspace/getReferenceDocument"] = getReferenceDocument - clientCapabilities.experimental = .dictionary(experimentalCapabilities) - } else { - clientCapabilities.experimental = .dictionary(["workspace/getReferenceDocument": getReferenceDocument]) - } - } - // The client announces what CodeLenses it supports, and the LSP will only return // ones found in the supportedCommands dictionary. if let codeLens = initializationOptions["textDocument/codeLens"], @@ -1176,7 +1165,6 @@ extension SourceKitLSPServer { "workspace/tests": .dictionary(["version": .int(2)]), "textDocument/tests": .dictionary(["version": .int(2)]), "workspace/triggerReindex": .dictionary(["version": .int(1)]), - "workspace/getReferenceDocument": .dictionary(["version": .int(1)]), ]) ) } @@ -1744,7 +1732,7 @@ extension SourceKitLSPServer { return try await languageService.executeCommand(executeCommand) } - func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse { + func textDocumentContent(_ req: TextDocumentContentRequest) async throws -> TextDocumentContentResponse { let primaryFileURI = try ReferenceDocumentURL(from: req.uri).primaryFile guard let workspace = await workspaceForDocument(uri: primaryFileURI) else { @@ -1755,7 +1743,7 @@ extension SourceKitLSPServer { throw ResponseError.unknown("No Language Service for URI: \(primaryFileURI)") } - return try await languageService.getReferenceDocument(req) + return try await languageService.textDocumentContent(req) } func codeAction( diff --git a/Sources/SourceKitLSP/Swift/MacroExpansion.swift b/Sources/SourceKitLSP/Swift/MacroExpansion.swift index 6ff5c9cac..86afc8f58 100644 --- a/Sources/SourceKitLSP/Swift/MacroExpansion.swift +++ b/Sources/SourceKitLSP/Swift/MacroExpansion.swift @@ -218,8 +218,7 @@ extension SwiftLanguageService { } if case .dictionary(let experimentalCapabilities) = self.capabilityRegistry.clientCapabilities.experimental, - case .bool(true) = experimentalCapabilities["workspace/peekDocuments"], - case .bool(true) = experimentalCapabilities["workspace/getReferenceDocument"] + case .bool(true) = experimentalCapabilities["workspace/peekDocuments"] { let expansionURIs = try macroExpansionReferenceDocumentURLs.map { return DocumentURI(try $0.url) diff --git a/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift b/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift index 04d332322..924262613 100644 --- a/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift +++ b/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift @@ -14,7 +14,7 @@ import Foundation import LanguageServerProtocol /// A Reference Document is a document whose url scheme is `sourcekit-lsp:` and whose content can only be retrieved -/// using `GetReferenceDocumentRequest`. The enum represents a specific type of reference document and its +/// using `TextDocumentContentRequest`. The enum represents a specific type of reference document and its /// associated value represents the data necessary to generate the document's contents and its url /// /// The `url` will be of the form: `sourcekit-lsp:///?` diff --git a/Sources/SourceKitLSP/Swift/SwiftLanguageService.swift b/Sources/SourceKitLSP/Swift/SwiftLanguageService.swift index 0151fe01e..799903fb0 100644 --- a/Sources/SourceKitLSP/Swift/SwiftLanguageService.swift +++ b/Sources/SourceKitLSP/Swift/SwiftLanguageService.swift @@ -1002,13 +1002,13 @@ extension SwiftLanguageService { return nil } - package func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse { + package func textDocumentContent(_ req: TextDocumentContentRequest) async throws -> TextDocumentContentResponse { let referenceDocumentURL = try ReferenceDocumentURL(from: req.uri) switch referenceDocumentURL { case let .macroExpansion(data): - return GetReferenceDocumentResponse( - content: try await macroExpansionManager.macroExpansion(for: data) + return TextDocumentContentResponse( + text: try await macroExpansionManager.macroExpansion(for: data) ) } } diff --git a/Tests/SourceKitLSPTests/ExpandMacroTests.swift b/Tests/SourceKitLSPTests/ExpandMacroTests.swift index d379a73f9..83b4d2a05 100644 --- a/Tests/SourceKitLSPTests/ExpandMacroTests.swift +++ b/Tests/SourceKitLSPTests/ExpandMacroTests.swift @@ -114,9 +114,9 @@ final class ExpandMacroTests: XCTestCase { var filesContents = [String]() for uri in uris { - let result = try await project.testClient.send(GetReferenceDocumentRequest(uri: uri)) + let result = try await project.testClient.send(TextDocumentContentRequest(uri: uri)) - filesContents.append(result.content) + filesContents.append(result.text) } XCTAssertEqual( @@ -290,9 +290,9 @@ final class ExpandMacroTests: XCTestCase { var filesContents = [String]() for uri in uris { - let result = try await project.testClient.send(GetReferenceDocumentRequest(uri: uri)) + let result = try await project.testClient.send(TextDocumentContentRequest(uri: uri)) - filesContents.append(result.content) + filesContents.append(result.text) } XCTAssertEqual( @@ -473,10 +473,10 @@ final class ExpandMacroTests: XCTestCase { try await fulfillmentOfOrThrow([outerPeekDocumentRequestReceived]) let outerPeekDocumentURI = try XCTUnwrap(outerPeekDocumentsRequestURIs.value?.only) - let outerMacroExpansion = try await project.testClient.send(GetReferenceDocumentRequest(uri: outerPeekDocumentURI)) + let outerMacroExpansion = try await project.testClient.send(TextDocumentContentRequest(uri: outerPeekDocumentURI)) - guard outerMacroExpansion.content == "/* padding */ #intermediate" else { - XCTFail("Received unexpected macro expansion content: \(outerMacroExpansion.content)") + guard outerMacroExpansion.text == "/* padding */ #intermediate" else { + XCTFail("Received unexpected macro expansion content: \(outerMacroExpansion.text)") return } @@ -509,11 +509,11 @@ final class ExpandMacroTests: XCTestCase { let intermediatePeekDocumentURI = try XCTUnwrap(intermediatePeekDocumentsRequestURIs.value?.only) let intermediateMacroExpansion = try await project.testClient.send( - GetReferenceDocumentRequest(uri: intermediatePeekDocumentURI) + TextDocumentContentRequest(uri: intermediatePeekDocumentURI) ) - guard intermediateMacroExpansion.content == "#stringify(1 + 2)" else { - XCTFail("Received unexpected macro expansion content: \(intermediateMacroExpansion.content)") + guard intermediateMacroExpansion.text == "#stringify(1 + 2)" else { + XCTFail("Received unexpected macro expansion content: \(intermediateMacroExpansion.text)") return } @@ -545,8 +545,8 @@ final class ExpandMacroTests: XCTestCase { try await fulfillmentOfOrThrow([innerPeekDocumentRequestReceived]) let innerPeekDocumentURI = try XCTUnwrap(innerPeekDocumentsRequestURIs.value?.only) - let innerMacroExpansion = try await project.testClient.send(GetReferenceDocumentRequest(uri: innerPeekDocumentURI)) + let innerMacroExpansion = try await project.testClient.send(TextDocumentContentRequest(uri: innerPeekDocumentURI)) - XCTAssertEqual(innerMacroExpansion.content, #"(1 + 2, "1 + 2")"#) + XCTAssertEqual(innerMacroExpansion.text, #"(1 + 2, "1 + 2")"#) } } From 49d51344ddacb6e18da18f6ee78de9e181b1be1c Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 22 Aug 2024 18:24:21 +0200 Subject: [PATCH 02/13] Remove getReferenceDocument capability --- Tests/SourceKitLSPTests/ExpandMacroTests.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Tests/SourceKitLSPTests/ExpandMacroTests.swift b/Tests/SourceKitLSPTests/ExpandMacroTests.swift index 83b4d2a05..d53657d8e 100644 --- a/Tests/SourceKitLSPTests/ExpandMacroTests.swift +++ b/Tests/SourceKitLSPTests/ExpandMacroTests.swift @@ -59,13 +59,12 @@ final class ExpandMacroTests: XCTestCase { """, ] - for (getReferenceDocument, peekDocuments) in cartesianProduct([true], [true]) { + for peekDocuments in [true] { let project = try await SwiftPMTestProject( files: files, manifest: SwiftPMTestProject.macroPackageManifest, capabilities: ClientCapabilities(experimental: [ "workspace/peekDocuments": .bool(peekDocuments), - "workspace/getReferenceDocument": .bool(getReferenceDocument), ]), options: SourceKitLSPOptions.testDefault(), enableBackgroundIndexing: true @@ -93,7 +92,7 @@ final class ExpandMacroTests: XCTestCase { let request = ExecuteCommandRequest(command: command.command, arguments: command.arguments) - if peekDocuments && getReferenceDocument { + if peekDocuments { let expectation = self.expectation(description: "Handle Peek Documents Request") let peekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) @@ -234,13 +233,12 @@ final class ExpandMacroTests: XCTestCase { """#, ] - for (getReferenceDocument, peekDocuments) in cartesianProduct([true, false], [true, false]) { + for peekDocuments in [true, false] { let project = try await SwiftPMTestProject( files: files, manifest: SwiftPMTestProject.macroPackageManifest, capabilities: ClientCapabilities(experimental: [ "workspace/peekDocuments": .bool(peekDocuments), - "workspace/getReferenceDocument": .bool(getReferenceDocument), ]), options: SourceKitLSPOptions.testDefault(), enableBackgroundIndexing: true @@ -268,7 +266,7 @@ final class ExpandMacroTests: XCTestCase { let request = ExecuteCommandRequest(command: command.command, arguments: command.arguments) - if peekDocuments && getReferenceDocument { + if peekDocuments { let expectation = self.expectation(description: "Handle Peek Documents Request") let peekDocumentsRequestURIs = ThreadSafeBox<[DocumentURI]?>(initialValue: nil) @@ -437,7 +435,6 @@ final class ExpandMacroTests: XCTestCase { manifest: SwiftPMTestProject.macroPackageManifest, capabilities: ClientCapabilities(experimental: [ "workspace/peekDocuments": .bool(true), - "workspace/getReferenceDocument": .bool(true), ]), options: SourceKitLSPOptions.testDefault(), enableBackgroundIndexing: true From fd417c76fc3752bd99eb4b4a4e4fa8a41fba869a Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 22 Aug 2024 18:38:22 +0200 Subject: [PATCH 03/13] Add a todo note --- Sources/SourceKitLSP/Swift/MacroExpansion.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SourceKitLSP/Swift/MacroExpansion.swift b/Sources/SourceKitLSP/Swift/MacroExpansion.swift index 86afc8f58..660c2ec90 100644 --- a/Sources/SourceKitLSP/Swift/MacroExpansion.swift +++ b/Sources/SourceKitLSP/Swift/MacroExpansion.swift @@ -219,6 +219,7 @@ extension SwiftLanguageService { if case .dictionary(let experimentalCapabilities) = self.capabilityRegistry.clientCapabilities.experimental, case .bool(true) = experimentalCapabilities["workspace/peekDocuments"] + // TODO: Check if client supports LSP 3.18's workspace/textDocumentContent { let expansionURIs = try macroExpansionReferenceDocumentURLs.map { return DocumentURI(try $0.url) From 066c3854aeb8b0b5e431672cc8b02a0d40259833 Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 23 Aug 2024 03:23:19 +0200 Subject: [PATCH 04/13] Add TextDocumentContentOptions to WorkspaceServerCapabilities --- .../SupportTypes/ServerCapabilities.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift index 36e4e03d8..3874a9fc5 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift @@ -1219,10 +1219,27 @@ public struct WorkspaceServerCapabilities: Codable, Hashable, Sendable { public var willDelete: FileOperationRegistrationOptions? } + /// Text document content provider options. + public struct TextDocumentContentOptions: Codable, Hashable, Sendable { + /// The schemes for which the server provides content. + public var schemes: [String] + + public init(schemes: [String] = []) { + self.schemes = schemes + } + } + /// The server supports workspace folder. public var workspaceFolders: WorkspaceFolders? - public init(workspaceFolders: WorkspaceFolders? = nil) { + /// The server supports the `workspace/textDocumentContent` request`. + public var textDocumentContent: TextDocumentContentOptions? + + public init( + workspaceFolders: WorkspaceFolders? = nil, + textDocumentContent: TextDocumentContentOptions? = nil + ) { self.workspaceFolders = workspaceFolders + self.textDocumentContent = textDocumentContent } } From a7f9382a11c1d6c9f929098e37adc6112fd84cda Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 23 Aug 2024 03:27:40 +0200 Subject: [PATCH 05/13] Declare textDocumentContent server capabilities This includes advertising our custom "sourcekit-lsp" scheme to the client. --- Sources/SourceKitLSP/SourceKitLSPServer.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index ab32c7a92..0207711c7 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -1155,6 +1155,9 @@ extension SourceKitLSPServer { workspaceFolders: .init( supported: true, changeNotifications: .bool(true) + ), + textDocumentContent: .init( + schemes: [ReferenceDocumentURL.scheme] ) ), callHierarchyProvider: .bool(true), From 1dc0213bf48b3c7c7a688a99d802140ead7666b9 Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 23 Aug 2024 03:48:07 +0200 Subject: [PATCH 06/13] Add WorkspaceClientCapabilities.textDocumentContent --- .../SupportTypes/ClientCapabilities.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift b/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift index 95c6af3ea..99159bd92 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift @@ -188,6 +188,8 @@ public struct WorkspaceClientCapabilities: Hashable, Codable, Sendable { public var diagnostics: RefreshRegistrationCapability? = nil + public var textDocumentContent: DynamicRegistrationCapability? = nil + public init( applyEdit: Bool? = nil, workspaceEdit: WorkspaceEdit? = nil, @@ -202,7 +204,8 @@ public struct WorkspaceClientCapabilities: Hashable, Codable, Sendable { fileOperations: FileOperations? = nil, inlineValue: RefreshRegistrationCapability? = nil, inlayHint: RefreshRegistrationCapability? = nil, - diagnostics: RefreshRegistrationCapability? = nil + diagnostics: RefreshRegistrationCapability? = nil, + textDocumentContent: DynamicRegistrationCapability? = nil ) { self.applyEdit = applyEdit self.workspaceEdit = workspaceEdit @@ -218,6 +221,7 @@ public struct WorkspaceClientCapabilities: Hashable, Codable, Sendable { self.inlineValue = inlineValue self.inlayHint = inlayHint self.diagnostics = diagnostics + self.textDocumentContent = textDocumentContent } } From 6e07e52483a1c6b23114ac050e7686f3f4b4bcb3 Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 23 Aug 2024 04:15:32 +0200 Subject: [PATCH 07/13] Fix typo --- .../SupportTypes/ServerCapabilities.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift index 3874a9fc5..cc7112681 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift @@ -1232,7 +1232,7 @@ public struct WorkspaceServerCapabilities: Codable, Hashable, Sendable { /// The server supports workspace folder. public var workspaceFolders: WorkspaceFolders? - /// The server supports the `workspace/textDocumentContent` request`. + /// The server supports the `workspace/textDocumentContent` request. public var textDocumentContent: TextDocumentContentOptions? public init( From af99d89605810c0c5ce580c6883b1f633387badb Mon Sep 17 00:00:00 2001 From: fwcd Date: Fri, 23 Aug 2024 17:36:05 +0200 Subject: [PATCH 08/13] Remove outdated LSP extension comment --- .../Requests/TextDocumentContentRequest.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift index cc53042a7..85af50eb3 100644 --- a/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift @@ -17,12 +17,6 @@ /// - uri: The `DocumentUri` of the custom scheme url for which content is required /// /// - Returns: `TextDocumentContentResponse` which contains the `content` to be displayed. -/// -/// ### LSP Extension -/// -/// This request is an extension to LSP supported by SourceKit-LSP. -/// Enable the experimental client capability `"workspace/textDocumentContent"` so that the server responds with -/// reference document URLs for certain requests or commands whenever possible. public struct TextDocumentContentRequest: RequestType { public static let method: String = "workspace/textDocumentContent" public typealias Response = TextDocumentContentResponse From 6ca4d95ee5fb5a424cfdab9a620935eadb6d312e Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2024 23:21:06 +0200 Subject: [PATCH 09/13] Re-add check for textDocumentContent capability Once the request is upstreamed to LSP 3.18 we should relax this condition to support clients that don't declare it as an experimental capability (since it will become a standard LSP request). --- Sources/SourceKitLSP/Swift/MacroExpansion.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SourceKitLSP/Swift/MacroExpansion.swift b/Sources/SourceKitLSP/Swift/MacroExpansion.swift index 660c2ec90..d38a235ad 100644 --- a/Sources/SourceKitLSP/Swift/MacroExpansion.swift +++ b/Sources/SourceKitLSP/Swift/MacroExpansion.swift @@ -218,8 +218,8 @@ extension SwiftLanguageService { } if case .dictionary(let experimentalCapabilities) = self.capabilityRegistry.clientCapabilities.experimental, - case .bool(true) = experimentalCapabilities["workspace/peekDocuments"] - // TODO: Check if client supports LSP 3.18's workspace/textDocumentContent + case .bool(true) = experimentalCapabilities["workspace/peekDocuments"], + case .bool(true) = experimentalCapabilities["workspace/textDocumentContent"] { let expansionURIs = try macroExpansionReferenceDocumentURLs.map { return DocumentURI(try $0.url) From 88ca0c6d8f041193beb77241653fd23acf58d919 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2024 23:24:32 +0200 Subject: [PATCH 10/13] Re-add documentation for textDocumentContent request Since we 'vendor' it as an LSP extension for now, we'll document it here until it is fully upstreamed to LSP 3.18. --- Contributor Documentation/LSP Extensions.md | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Contributor Documentation/LSP Extensions.md b/Contributor Documentation/LSP Extensions.md index 558901c3b..d954297ff 100644 --- a/Contributor Documentation/LSP Extensions.md +++ b/Contributor Documentation/LSP Extensions.md @@ -595,3 +595,30 @@ export interface PeekDocumentsResult { success: boolean; } ``` + +## `workspace/textDocumentContent` + +Request from the client to the server asking for contents of a URI having a custom scheme. +For example: "sourcekit-lsp:" + +Enable the experimental client capability `"workspace/textDocumentContent"` so that the server responds with reference document URLs for certain requests or commands whenever possible. + +- params: `TextDocumentContentParams` + +- result: `TextDocumentContentResponse` + +```ts +export interface TextDocumentContentParams { + /** + * The `DocumentUri` of the custom scheme url for which content is required + */ + uri: DocumentUri; +} + +/** + * Response containing the content of the requested document + */ +export interface TextDocumentContentResult { + text: string; +} +``` From 9702a2855df20acff713081fb9db64d36d5a98e1 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2024 23:26:47 +0200 Subject: [PATCH 11/13] Add doc comment from LSP to TextDocumentContentResponse --- .../Requests/TextDocumentContentRequest.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift index 85af50eb3..180cce2a2 100644 --- a/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/TextDocumentContentRequest.swift @@ -28,7 +28,11 @@ public struct TextDocumentContentRequest: RequestType { } } -/// Response containing `text` of `TextDocumentContentRequest` +/// Response containing the content of the requested text document. +/// +/// Please note, that the content of any subsequent open notifications for the +/// text document might differ from the returned content due to whitespace and +/// line ending normalizations done on the client. public struct TextDocumentContentResponse: ResponseType { public var text: String From dd7669a85462f6afeaa2e4e999ceaec4a303e7ab Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2024 23:33:55 +0200 Subject: [PATCH 12/13] Update documentation on workspace/textDocumentContent --- Contributor Documentation/LSP Extensions.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Contributor Documentation/LSP Extensions.md b/Contributor Documentation/LSP Extensions.md index d954297ff..d5a9be50e 100644 --- a/Contributor Documentation/LSP Extensions.md +++ b/Contributor Documentation/LSP Extensions.md @@ -598,10 +598,9 @@ export interface PeekDocumentsResult { ## `workspace/textDocumentContent` -Request from the client to the server asking for contents of a URI having a custom scheme. -For example: "sourcekit-lsp:" +Request from the client to the server for querying the contents of a document, potentially using a custom URI scheme (such as `sourcekit-lsp:`). This closely models the proposed LSP 3.18 request of the same name, thus can be removed once LSP 3.18 has been stabilized. -Enable the experimental client capability `"workspace/textDocumentContent"` so that the server responds with reference document URLs for certain requests or commands whenever possible. +Currently requires enabling the experimental client capability `"workspace/textDocumentContent"` to have the server respond with custom `sourcekit-lsp:` URIs (e.g. in macro expansion requests). - params: `TextDocumentContentParams` From 99836e08c3e362cae6876e6e678a531fc73fce93 Mon Sep 17 00:00:00 2001 From: fwcd Date: Wed, 4 Sep 2024 23:39:50 +0200 Subject: [PATCH 13/13] Re-parse experimental client capability for textDocumentContent --- Sources/SourceKitLSP/SourceKitLSPServer.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 0207711c7..18215f3a6 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -986,6 +986,8 @@ extension SourceKitLSPServer { // // The below is a workaround for the vscode-swift extension since it cannot set client capabilities. // It passes "workspace/peekDocuments" through the `initializationOptions`. + // + // Similarly for "workspace/textDocumentContent". var clientCapabilities = req.capabilities if case .dictionary(let initializationOptions) = req.initializationOptions { if let peekDocuments = initializationOptions["workspace/peekDocuments"] { @@ -997,6 +999,15 @@ extension SourceKitLSPServer { } } + if let textDocumentContent = initializationOptions["workspace/textDocumentContent"] { + if case .dictionary(var experimentalCapabilities) = clientCapabilities.experimental { + experimentalCapabilities["workspace/textDocumentContent"] = textDocumentContent + clientCapabilities.experimental = .dictionary(experimentalCapabilities) + } else { + clientCapabilities.experimental = .dictionary(["workspace/textDocumentContent": textDocumentContent]) + } + } + // The client announces what CodeLenses it supports, and the LSP will only return // ones found in the supportedCommands dictionary. if let codeLens = initializationOptions["textDocument/codeLens"],