diff --git a/Package.swift b/Package.swift index d70271ecb..e78f7dd09 100644 --- a/Package.swift +++ b/Package.swift @@ -237,7 +237,22 @@ var targets: [Target] = [ swiftSettings: globalSwiftSettings ), - // MARK: DocCDocumentation + // MARK: DocumentationLanguageService + + .target( + name: "DocumentationLanguageService", + dependencies: [ + "BuildServerIntegration", + "DocCDocumentation", + "LanguageServerProtocol", + "SKUtilities", + "SourceKitLSP", + "SemanticIndex", + .product(name: "Markdown", package: "swift-markdown"), + ], + exclude: [], + swiftSettings: globalSwiftSettings + ), .target( name: "DocCDocumentation", @@ -263,6 +278,7 @@ var targets: [Target] = [ dependencies: [ "BuildServerIntegration", "ClangLanguageService", + "DocumentationLanguageService", "LanguageServerProtocol", "SKLogging", "SKOptions", diff --git a/Sources/ClangLanguageService/ClangLanguageService.swift b/Sources/ClangLanguageService/ClangLanguageService.swift index 70a26e336..b56258a94 100644 --- a/Sources/ClangLanguageService/ClangLanguageService.swift +++ b/Sources/ClangLanguageService/ClangLanguageService.swift @@ -24,10 +24,6 @@ package import SwiftSyntax import TSCExtensions package import ToolchainRegistry -#if canImport(DocCDocumentation) -import DocCDocumentation -#endif - #if os(Windows) import WinSDK #endif @@ -497,16 +493,16 @@ extension ClangLanguageService { return try await forwardRequestToClangd(req) } - #if canImport(DocCDocumentation) package func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse { guard let sourceKitLSPServer else { throw ResponseError.unknown("Connection to the editor closed") } let snapshot = try sourceKitLSPServer.documentManager.latestSnapshot(req.textDocument.uri) - throw ResponseError.requestFailed(doccDocumentationError: .unsupportedLanguage(snapshot.language)) + throw ResponseError.requestFailed( + "Documentation preview is not available for \(snapshot.language.description) files" + ) } - #endif package func symbolInfo(_ req: SymbolInfoRequest) async throws -> [SymbolDetails] { return try await forwardRequestToClangd(req) diff --git a/Sources/SourceKitLSP/Documentation/DoccDocumentationHandler.swift b/Sources/DocumentationLanguageService/DoccDocumentationHandler.swift similarity index 91% rename from Sources/SourceKitLSP/Documentation/DoccDocumentationHandler.swift rename to Sources/DocumentationLanguageService/DoccDocumentationHandler.swift index a2ccaa16f..d8f6232f2 100644 --- a/Sources/SourceKitLSP/Documentation/DoccDocumentationHandler.swift +++ b/Sources/DocumentationLanguageService/DoccDocumentationHandler.swift @@ -18,6 +18,7 @@ import Foundation package import LanguageServerProtocol import Markdown import SKUtilities +import SourceKitLSP import SemanticIndex extension DocumentationLanguageService { @@ -63,9 +64,13 @@ extension DocumentationLanguageService { ofDocCSymbolLink: symbolLink, fetchSymbolGraph: { location in guard let symbolWorkspace = try await workspaceForDocument(uri: location.documentUri), - let languageService = try await languageService(for: location.documentUri, .swift, in: symbolWorkspace) + let languageService = await sourceKitLSPServer.languageService( + for: location.documentUri, + .swift, + in: symbolWorkspace + ) else { - throw ResponseError.internalError("Unable to find Swift language service for \(location.documentUri)") + throw ResponseError.internalError("Unable to find language service for \(location.documentUri)") } return try await languageService.symbolGraph(forOnDiskContentsOf: location.documentUri, at: location) } @@ -76,9 +81,13 @@ extension DocumentationLanguageService { let symbolDocumentUri = symbolOccurrence.location.documentUri guard let symbolWorkspace = try await workspaceForDocument(uri: symbolDocumentUri), - let languageService = try await languageService(for: symbolDocumentUri, .swift, in: symbolWorkspace) + let languageService = await sourceKitLSPServer.languageService( + for: symbolDocumentUri, + .swift, + in: symbolWorkspace + ) else { - throw ResponseError.internalError("Unable to find Swift language service for \(symbolDocumentUri)") + throw ResponseError.internalError("Unable to find language service for \(symbolDocumentUri)") } let symbolGraph = try await languageService.symbolGraph( forOnDiskContentsOf: symbolDocumentUri, diff --git a/Sources/SourceKitLSP/Documentation/DocumentationLanguageService.swift b/Sources/DocumentationLanguageService/DocumentationLanguageService.swift similarity index 99% rename from Sources/SourceKitLSP/Documentation/DocumentationLanguageService.swift rename to Sources/DocumentationLanguageService/DocumentationLanguageService.swift index 6999a4ab3..f10fd911d 100644 --- a/Sources/SourceKitLSP/Documentation/DocumentationLanguageService.swift +++ b/Sources/DocumentationLanguageService/DocumentationLanguageService.swift @@ -14,6 +14,7 @@ import Foundation package import IndexStoreDB package import LanguageServerProtocol package import SKOptions +package import SourceKitLSP import SwiftExtensions package import SwiftSyntax package import ToolchainRegistry @@ -52,7 +53,7 @@ package actor DocumentationLanguageService: LanguageService, Sendable { func languageService( for uri: DocumentURI, - _ language: Language, + _ language: LanguageServerProtocol.Language, in workspace: Workspace ) async throws -> LanguageService? { guard let sourceKitLSPServer else { diff --git a/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift b/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift index be37bec31..83c5b9241 100644 --- a/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift +++ b/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift @@ -15,13 +15,19 @@ import LanguageServerProtocol package import SourceKitLSP import SwiftLanguageService +#if canImport(DocumentationLanguageService) +import DocumentationLanguageService +#endif + extension LanguageServiceRegistry { /// All types conforming to `LanguageService` that are known at compile time. package static let staticallyKnownServices = { var registry = LanguageServiceRegistry() registry.register(ClangLanguageService.self, for: [.c, .cpp, .objective_c, .objective_cpp]) registry.register(SwiftLanguageService.self, for: [.swift]) + #if canImport(DocumentationLanguageService) registry.register(DocumentationLanguageService.self, for: [.markdown, .tutorial]) + #endif return registry }() } diff --git a/Sources/LanguageServerProtocol/Error.swift b/Sources/LanguageServerProtocol/Error.swift index eccdb1bfd..5a4bbcc55 100644 --- a/Sources/LanguageServerProtocol/Error.swift +++ b/Sources/LanguageServerProtocol/Error.swift @@ -83,6 +83,9 @@ public struct ErrorCode: RawRepresentable, Codable, Hashable, Sendable { // MARK: SourceKit-LSP specific error codes public static let workspaceNotOpen: ErrorCode = ErrorCode(rawValue: -32003) + + /// The method is not implemented in this `LanguageService`. + public static let requestNotImplemented: ErrorCode = ErrorCode(rawValue: -32004) } /// An error response represented by a code and message. @@ -131,6 +134,10 @@ extension ResponseError { public static func internalError(_ message: String) -> ResponseError { return ResponseError(code: .internalError, message: message) } + + public static func requestNotImplemented(_ method: any RequestType.Type) -> ResponseError { + return ResponseError(code: .requestNotImplemented, message: "request not implemented: \(method.method)") + } } /// An error during message decoding. diff --git a/Sources/SourceKitLSP/CMakeLists.txt b/Sources/SourceKitLSP/CMakeLists.txt index 5f9338c78..1536bd6c5 100644 --- a/Sources/SourceKitLSP/CMakeLists.txt +++ b/Sources/SourceKitLSP/CMakeLists.txt @@ -26,10 +26,6 @@ add_library(SourceKitLSP STATIC TextEdit+IsNoop.swift Workspace.swift ) -target_sources(SourceKitLSP PRIVATE - Documentation/DocCDocumentationHandler.swift - Documentation/DocumentationLanguageService.swift -) set_target_properties(SourceKitLSP PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY} ) diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index 924d92468..054e072d8 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -176,9 +176,7 @@ package protocol LanguageService: AnyObject, Sendable { func completion(_ req: CompletionRequest) async throws -> CompletionList func completionItemResolve(_ req: CompletionItemResolveRequest) async throws -> CompletionItem func hover(_ req: HoverRequest) async throws -> HoverResponse? - #if canImport(DocCDocumentation) func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse - #endif func symbolInfo(_ request: SymbolInfoRequest) async throws -> [SymbolDetails] /// Return the symbol graph at the given location for the contents of the document as they are on-disk (opposed to the diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 8bc1c4fb7..272f9e2d8 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -757,10 +757,8 @@ extension SourceKitLSPServer: QueueBasedMessageHandler { await self.handleRequest(for: request, requestHandler: self.declaration) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.definition) - #if canImport(DocCDocumentation) case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.doccDocumentation) - #endif case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.documentColor) case let request as RequestAndReply: @@ -1573,7 +1571,6 @@ extension SourceKitLSPServer { return try await documentService(for: uri).completionItemResolve(request) } - #if canImport(DocCDocumentation) func doccDocumentation( _ req: DoccDocumentationRequest, workspace: Workspace, @@ -1581,7 +1578,6 @@ extension SourceKitLSPServer { ) async throws -> DoccDocumentationResponse { return try await languageService.doccDocumentation(req) } - #endif func hover( _ req: HoverRequest, diff --git a/Sources/SwiftLanguageService/DoccDocumentation.swift b/Sources/SwiftLanguageService/DoccDocumentation.swift index f050017f1..41406856a 100644 --- a/Sources/SwiftLanguageService/DoccDocumentation.swift +++ b/Sources/SwiftLanguageService/DoccDocumentation.swift @@ -10,20 +10,23 @@ // //===----------------------------------------------------------------------===// -#if canImport(DocCDocumentation) import BuildServerIntegration -import DocCDocumentation import Foundation import IndexStoreDB package import LanguageServerProtocol -import SemanticIndex import SKLogging +import SemanticIndex +import SourceKitLSP import SwiftExtensions import SwiftSyntax -import SourceKitLSP + +#if canImport(DocCDocumentation) +import DocCDocumentation +#endif extension SwiftLanguageService { package func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse { + #if canImport(DocCDocumentation) guard let sourceKitLSPServer else { throw ResponseError.internalError("SourceKit-LSP is shutting down") } @@ -100,8 +103,12 @@ extension SwiftLanguageService { moduleName: moduleName, catalogURL: catalogURL ) + #else + throw ResponseError.requestFailed("Documentation preview is not available in this build of SourceKit-LSP") + #endif } + #if canImport(DocCDocumentation) private func findMarkupExtensionFile( workspace: Workspace, documentationManager: DocCDocumentationManager, @@ -127,6 +134,7 @@ extension SwiftLanguageService { language: .markdown )?.text } + #endif } private struct DocumentableSymbol { @@ -216,4 +224,3 @@ private struct DocumentableSymbol { return nil } } -#endif diff --git a/Tests/SourceKitLSPTests/DoccDocumentationTests.swift b/Tests/SourceKitLSPTests/DoccDocumentationTests.swift index 39ec46c92..dcf67a339 100644 --- a/Tests/SourceKitLSPTests/DoccDocumentationTests.swift +++ b/Tests/SourceKitLSPTests/DoccDocumentationTests.swift @@ -21,7 +21,6 @@ import SwiftDocC import XCTest final class DoccDocumentationTests: XCTestCase { - func testUnsupportedLanguage() async throws { try await renderDocumentation( markedText: "1️⃣", diff --git a/Tests/SourceKitLSPTests/DocumentationLanguageServiceTests.swift b/Tests/SourceKitLSPTests/DocumentationLanguageServiceTests.swift index b03170524..f7a802e50 100644 --- a/Tests/SourceKitLSPTests/DocumentationLanguageServiceTests.swift +++ b/Tests/SourceKitLSPTests/DocumentationLanguageServiceTests.swift @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#if canImport(DocCDocumentation) import LanguageServerProtocol import SKTestSupport import SourceKitLSP @@ -39,3 +40,4 @@ private func assertHandles(language: Language) async throws { ) XCTAssertEqual(completions, .init(isIncomplete: false, items: [])) } +#endif