From c692a83f439ff9918794665c00291f9c1943304f Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 17 Jul 2025 08:48:12 +0200 Subject: [PATCH] Construct `LanguageServiceRegistry` outside of `SourceKitLSP` module --- Package.swift | 1 + Sources/InProcessClient/CMakeLists.txt | 3 +- .../InProcessSourceKitLSPClient.swift | 2 +- ...viceRegistry+staticallyKnownServices.swift | 13 +++ .../TestSourceKitLSPClient.swift | 2 +- .../Clang/ClangLanguageService.swift | 84 ++++++++++--------- .../LanguageServiceRegistry.swift | 8 +- Sources/SourceKitLSP/TestDiscovery.swift | 6 -- Sources/sourcekit-lsp/CMakeLists.txt | 1 + Sources/sourcekit-lsp/SourceKitLSP.swift | 3 +- 10 files changed, 68 insertions(+), 55 deletions(-) create mode 100644 Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift diff --git a/Package.swift b/Package.swift index 34a8548f..6cd711d3 100644 --- a/Package.swift +++ b/Package.swift @@ -39,6 +39,7 @@ var targets: [Target] = [ dependencies: [ "BuildServerIntegration", "Diagnose", + "InProcessClient", "LanguageServerProtocol", "LanguageServerProtocolExtensions", "LanguageServerProtocolJSONRPC", diff --git a/Sources/InProcessClient/CMakeLists.txt b/Sources/InProcessClient/CMakeLists.txt index 1b5db8d5..58ed80dc 100644 --- a/Sources/InProcessClient/CMakeLists.txt +++ b/Sources/InProcessClient/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(InProcessClient STATIC - InProcessSourceKitLSPClient.swift) + InProcessSourceKitLSPClient.swift + LanguageServiceRegistry+staticallyKnownServices.swift) set_target_properties(InProcessClient PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) diff --git a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift index 6e7dea57..928461ef 100644 --- a/Sources/InProcessClient/InProcessSourceKitLSPClient.swift +++ b/Sources/InProcessClient/InProcessSourceKitLSPClient.swift @@ -60,7 +60,7 @@ public final class InProcessSourceKitLSPClient: Sendable { self.server = SourceKitLSPServer( client: serverToClientConnection, toolchainRegistry: toolchainRegistry, - languageServerRegistry: LanguageServiceRegistry(), + languageServerRegistry: .staticallyKnownServices, options: options, hooks: hooks, onExit: { diff --git a/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift b/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift new file mode 100644 index 00000000..443cbbac --- /dev/null +++ b/Sources/InProcessClient/LanguageServiceRegistry+staticallyKnownServices.swift @@ -0,0 +1,13 @@ +import LanguageServerProtocol +package import SourceKitLSP + +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]) + registry.register(DocumentationLanguageService.self, for: [.markdown, .tutorial]) + return registry + }() +} diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index db1631f2..24bf4410 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -164,7 +164,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable { server = SourceKitLSPServer( client: serverToClientConnection, toolchainRegistry: toolchainRegistry, - languageServerRegistry: LanguageServiceRegistry(), + languageServerRegistry: .staticallyKnownServices, options: options, hooks: hooks, onExit: { diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift index 7425917b..8b322744 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift @@ -12,15 +12,15 @@ import BuildServerIntegration import Foundation -import LanguageServerProtocol +package import LanguageServerProtocol import LanguageServerProtocolExtensions import LanguageServerProtocolJSONRPC import SKLogging -import SKOptions -import SwiftExtensions -import SwiftSyntax +package import SKOptions +package import SwiftExtensions +package import SwiftSyntax import TSCExtensions -import ToolchainRegistry +package import ToolchainRegistry #if canImport(DocCDocumentation) import DocCDocumentation @@ -38,7 +38,7 @@ import WinSDK /// ``ClangLanguageServerShim`` conforms to ``MessageHandler`` to receive /// requests and notifications **from** clangd, not from the editor, and it will /// forward these requests and notifications to the editor. -actor ClangLanguageService: LanguageService, MessageHandler { +package actor ClangLanguageService: LanguageService, MessageHandler { /// The queue on which all messages that originate from clangd are handled. /// /// These are requests and notifications sent *from* clangd, not replies from @@ -144,12 +144,12 @@ actor ClangLanguageService: LanguageService, MessageHandler { return ClangBuildSettings(settings, clangPath: clangPath) } - nonisolated func canHandle(workspace: Workspace, toolchain: Toolchain) -> Bool { + package nonisolated func canHandle(workspace: Workspace, toolchain: Toolchain) -> Bool { // We launch different clangd instance for each workspace because clangd doesn't have multi-root workspace support. return workspace === self.workspace.value && self.clangdPath == toolchain.clangd } - func addStateChangeHandler(handler: @escaping (LanguageServerState, LanguageServerState) -> Void) { + package func addStateChangeHandler(handler: @escaping (LanguageServerState, LanguageServerState) -> Void) { self.stateChangeHandlers.append(handler) } @@ -244,7 +244,7 @@ actor ClangLanguageService: LanguageService, MessageHandler { /// sending a notification that's intended for the editor. /// /// We should either handle it ourselves or forward it to the editor. - nonisolated func handle(_ params: some NotificationType) { + package nonisolated func handle(_ params: some NotificationType) { logger.info( """ Received notification from clangd: @@ -267,7 +267,7 @@ actor ClangLanguageService: LanguageService, MessageHandler { /// sending a notification that's intended for the editor. /// /// We should either handle it ourselves or forward it to the client. - nonisolated func handle( + package nonisolated func handle( _ params: R, id: RequestID, reply: @Sendable @escaping (LSPResult) -> Void @@ -316,7 +316,7 @@ actor ClangLanguageService: LanguageService, MessageHandler { return nil } - func crash() { + package func crash() { clangdProcess?.terminateImmediately() } } @@ -366,7 +366,7 @@ extension ClangLanguageService { extension ClangLanguageService { - func initialize(_ initialize: InitializeRequest) async throws -> InitializeResult { + package func initialize(_ initialize: InitializeRequest) async throws -> InitializeResult { // Store the initialize request so we can replay it in case clangd crashes self.initializeRequest = initialize @@ -417,7 +417,7 @@ extension ClangLanguageService { clangd.send(notification) } - func reopenDocument(_ notification: ReopenTextDocumentNotification) {} + package func reopenDocument(_ notification: ReopenTextDocumentNotification) {} package func changeDocument( _ notification: DidChangeTextDocumentNotification, @@ -481,20 +481,20 @@ extension ClangLanguageService { return try await forwardRequestToClangd(req) } - func completion(_ req: CompletionRequest) async throws -> CompletionList { + package func completion(_ req: CompletionRequest) async throws -> CompletionList { return try await forwardRequestToClangd(req) } - func completionItemResolve(_ req: CompletionItemResolveRequest) async throws -> CompletionItem { + package func completionItemResolve(_ req: CompletionItemResolveRequest) async throws -> CompletionItem { return try await forwardRequestToClangd(req) } - func hover(_ req: HoverRequest) async throws -> HoverResponse? { + package func hover(_ req: HoverRequest) async throws -> HoverResponse? { return try await forwardRequestToClangd(req) } #if canImport(DocCDocumentation) - func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse { + package func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse { guard let sourceKitLSPServer else { throw ResponseError.unknown("Connection to the editor closed") } @@ -504,26 +504,28 @@ extension ClangLanguageService { } #endif - func symbolInfo(_ req: SymbolInfoRequest) async throws -> [SymbolDetails] { + package func symbolInfo(_ req: SymbolInfoRequest) async throws -> [SymbolDetails] { return try await forwardRequestToClangd(req) } - func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? { + package func documentSymbolHighlight(_ req: DocumentHighlightRequest) async throws -> [DocumentHighlight]? { return try await forwardRequestToClangd(req) } - func documentSymbol(_ req: DocumentSymbolRequest) async throws -> DocumentSymbolResponse? { + package func documentSymbol(_ req: DocumentSymbolRequest) async throws -> DocumentSymbolResponse? { return try await forwardRequestToClangd(req) } - func documentColor(_ req: DocumentColorRequest) async throws -> [ColorInformation] { + package func documentColor(_ req: DocumentColorRequest) async throws -> [ColorInformation] { guard self.capabilities?.colorProvider?.isSupported ?? false else { return [] } return try await forwardRequestToClangd(req) } - func documentSemanticTokens(_ req: DocumentSemanticTokensRequest) async throws -> DocumentSemanticTokensResponse? { + package func documentSemanticTokens( + _ req: DocumentSemanticTokensRequest + ) async throws -> DocumentSemanticTokensResponse? { guard var response = try await forwardRequestToClangd(req) else { return nil } @@ -533,7 +535,7 @@ extension ClangLanguageService { return response } - func documentSemanticTokensDelta( + package func documentSemanticTokensDelta( _ req: DocumentSemanticTokensDeltaRequest ) async throws -> DocumentSemanticTokensDeltaResponse? { guard var response = try await forwardRequestToClangd(req) else { @@ -558,7 +560,7 @@ extension ClangLanguageService { return response } - func documentSemanticTokensRange( + package func documentSemanticTokensRange( _ req: DocumentSemanticTokensRangeRequest ) async throws -> DocumentSemanticTokensResponse? { guard var response = try await forwardRequestToClangd(req) else { @@ -570,49 +572,49 @@ extension ClangLanguageService { return response } - func colorPresentation(_ req: ColorPresentationRequest) async throws -> [ColorPresentation] { + package func colorPresentation(_ req: ColorPresentationRequest) async throws -> [ColorPresentation] { guard self.capabilities?.colorProvider?.isSupported ?? false else { return [] } return try await forwardRequestToClangd(req) } - func documentFormatting(_ req: DocumentFormattingRequest) async throws -> [TextEdit]? { + package func documentFormatting(_ req: DocumentFormattingRequest) async throws -> [TextEdit]? { return try await forwardRequestToClangd(req) } - func documentRangeFormatting(_ req: DocumentRangeFormattingRequest) async throws -> [TextEdit]? { + package func documentRangeFormatting(_ req: DocumentRangeFormattingRequest) async throws -> [TextEdit]? { return try await forwardRequestToClangd(req) } - func documentOnTypeFormatting(_ req: DocumentOnTypeFormattingRequest) async throws -> [TextEdit]? { + package func documentOnTypeFormatting(_ req: DocumentOnTypeFormattingRequest) async throws -> [TextEdit]? { return try await forwardRequestToClangd(req) } - func codeAction(_ req: CodeActionRequest) async throws -> CodeActionRequestResponse? { + package func codeAction(_ req: CodeActionRequest) async throws -> CodeActionRequestResponse? { return try await forwardRequestToClangd(req) } - func inlayHint(_ req: InlayHintRequest) async throws -> [InlayHint] { + package func inlayHint(_ req: InlayHintRequest) async throws -> [InlayHint] { return try await forwardRequestToClangd(req) } - func documentDiagnostic(_ req: DocumentDiagnosticsRequest) async throws -> DocumentDiagnosticReport { + package func documentDiagnostic(_ req: DocumentDiagnosticsRequest) async throws -> DocumentDiagnosticReport { return try await forwardRequestToClangd(req) } - func codeLens(_ req: CodeLensRequest) async throws -> [CodeLens] { + package func codeLens(_ req: CodeLensRequest) async throws -> [CodeLens] { return try await forwardRequestToClangd(req) ?? [] } - func foldingRange(_ req: FoldingRangeRequest) async throws -> [FoldingRange]? { + package func foldingRange(_ req: FoldingRangeRequest) async throws -> [FoldingRange]? { guard self.capabilities?.foldingRangeProvider?.isSupported ?? false else { return nil } return try await forwardRequestToClangd(req) } - func openGeneratedInterface( + package func openGeneratedInterface( document: DocumentURI, moduleName: String, groupName: String?, @@ -621,21 +623,21 @@ extension ClangLanguageService { throw ResponseError.unknown("unsupported method") } - func indexedRename(_ request: IndexedRenameRequest) async throws -> WorkspaceEdit? { + package func indexedRename(_ request: IndexedRenameRequest) async throws -> WorkspaceEdit? { return try await forwardRequestToClangd(request) } // MARK: - Other - func executeCommand(_ req: ExecuteCommandRequest) async throws -> LSPAny? { + package func executeCommand(_ req: ExecuteCommandRequest) async throws -> LSPAny? { return try await forwardRequestToClangd(req) } - func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse { + package func getReferenceDocument(_ req: GetReferenceDocumentRequest) async throws -> GetReferenceDocumentResponse { throw ResponseError.unknown("unsupported method") } - func rename(_ renameRequest: RenameRequest) async throws -> (edits: WorkspaceEdit, usr: String?) { + package func rename(_ renameRequest: RenameRequest) async throws -> (edits: WorkspaceEdit, usr: String?) { async let edits = forwardRequestToClangd(renameRequest) let symbolInfoRequest = SymbolInfoRequest( textDocument: renameRequest.textDocument, @@ -645,7 +647,11 @@ extension ClangLanguageService { return (try await edits ?? WorkspaceEdit(), symbolDetail?.usr) } - func editsToRename( + package func syntacticDocumentTests(for uri: DocumentURI, in workspace: Workspace) async -> [AnnotatedTestItem]? { + return nil + } + + package func editsToRename( locations renameLocations: [RenameLocation], in snapshot: DocumentSnapshot, oldName oldCrossLanguageName: CrossLanguageName, diff --git a/Sources/SourceKitLSP/LanguageServiceRegistry.swift b/Sources/SourceKitLSP/LanguageServiceRegistry.swift index b5fec523..c931f707 100644 --- a/Sources/SourceKitLSP/LanguageServiceRegistry.swift +++ b/Sources/SourceKitLSP/LanguageServiceRegistry.swift @@ -19,13 +19,9 @@ import SKLogging package struct LanguageServiceRegistry { private var byLanguage: [Language: LanguageService.Type] = [:] - package init() { - self.register(ClangLanguageService.self, for: [.c, .cpp, .objective_c, .objective_cpp]) - self.register(SwiftLanguageService.self, for: [.swift]) - self.register(DocumentationLanguageService.self, for: [.markdown, .tutorial]) - } + package init() {} - private mutating func register(_ languageService: LanguageService.Type, for languages: [Language]) { + package mutating func register(_ languageService: LanguageService.Type, for languages: [Language]) { for language in languages { if let existingLanguageService = byLanguage[language] { logger.fault( diff --git a/Sources/SourceKitLSP/TestDiscovery.swift b/Sources/SourceKitLSP/TestDiscovery.swift index 07a7731d..943eb29f 100644 --- a/Sources/SourceKitLSP/TestDiscovery.swift +++ b/Sources/SourceKitLSP/TestDiscovery.swift @@ -623,9 +623,3 @@ extension SwiftLanguageService { return (xctestSymbols + swiftTestingSymbols).sorted { $0.testItem.location < $1.testItem.location } } } - -extension ClangLanguageService { - package func syntacticDocumentTests(for uri: DocumentURI, in workspace: Workspace) async -> [AnnotatedTestItem]? { - return nil - } -} diff --git a/Sources/sourcekit-lsp/CMakeLists.txt b/Sources/sourcekit-lsp/CMakeLists.txt index 03a058f0..b5d4baf5 100644 --- a/Sources/sourcekit-lsp/CMakeLists.txt +++ b/Sources/sourcekit-lsp/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(sourcekit-lsp target_link_libraries(sourcekit-lsp PRIVATE BuildServerIntegration Diagnose + InProcessClient LanguageServerProtocol LanguageServerProtocolExtensions LanguageServerProtocolJSONRPC diff --git a/Sources/sourcekit-lsp/SourceKitLSP.swift b/Sources/sourcekit-lsp/SourceKitLSP.swift index db5ea2d5..71e74ed0 100644 --- a/Sources/sourcekit-lsp/SourceKitLSP.swift +++ b/Sources/sourcekit-lsp/SourceKitLSP.swift @@ -16,6 +16,7 @@ import Csourcekitd // Not needed here, but fixes debugging... import Diagnose import Dispatch import Foundation +import InProcessClient import LanguageServerProtocol import LanguageServerProtocolExtensions import LanguageServerProtocolJSONRPC @@ -294,7 +295,7 @@ struct SourceKitLSP: AsyncParsableCommand { let server = SourceKitLSPServer( client: clientConnection, toolchainRegistry: ToolchainRegistry(installPath: Bundle.main.bundleURL), - languageServerRegistry: LanguageServiceRegistry(), + languageServerRegistry: .staticallyKnownServices, options: globalConfigurationOptions, hooks: Hooks(), onExit: {