Skip to content

Allow injection of build systems into SourceKitLSP #1922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 12 additions & 25 deletions Sources/BuildSystemIntegration/BuildSystemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private extension BuildSystemSpec {
private static func createBuiltInBuildSystemAdapter(
projectRoot: URL,
messagesToSourceKitLSPHandler: any MessageHandler,
buildSystemTestHooks: BuildSystemTestHooks,
buildSystemHooks: BuildSystemHooks,
_ createBuildSystem: @Sendable (_ connectionToSourceKitLSP: any Connection) async throws -> BuiltInBuildSystem?
) async -> BuildSystemAdapter? {
let connectionToSourceKitLSP = LocalConnection(
Expand All @@ -164,7 +164,7 @@ private extension BuildSystemSpec {
let buildSystemAdapter = BuiltInBuildSystemAdapter(
underlyingBuildSystem: buildSystem,
connectionToSourceKitLSP: connectionToSourceKitLSP,
buildSystemTestHooks: buildSystemTestHooks
buildSystemHooks: buildSystemHooks
)
let connectionToBuildSystem = LocalConnection(
receiverName: "\(type(of: buildSystem)) for \(projectRoot.lastPathComponent)"
Expand All @@ -178,7 +178,7 @@ private extension BuildSystemSpec {
func createBuildSystemAdapter(
toolchainRegistry: ToolchainRegistry,
options: SourceKitLSPOptions,
buildSystemTestHooks testHooks: BuildSystemTestHooks,
buildSystemHooks: BuildSystemHooks,
messagesToSourceKitLSPHandler: any MessageHandler
) async -> BuildSystemAdapter? {
switch self.kind {
Expand All @@ -199,7 +199,7 @@ private extension BuildSystemSpec {
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
CompilationDatabaseBuildSystem(
projectRoot: projectRoot,
Expand All @@ -214,26 +214,26 @@ private extension BuildSystemSpec {
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
try await SwiftPMBuildSystem(
projectRoot: projectRoot,
toolchainRegistry: toolchainRegistry,
options: options,
connectionToSourceKitLSP: connectionToSourceKitLSP,
testHooks: testHooks.swiftPMTestHooks
testHooks: buildSystemHooks.swiftPMTestHooks
)
}
#else
return nil
#endif
case .testBuildSystem:
case .injected(let injector):
return await Self.createBuiltInBuildSystemAdapter(
projectRoot: projectRoot,
messagesToSourceKitLSPHandler: messagesToSourceKitLSPHandler,
buildSystemTestHooks: testHooks
buildSystemHooks: buildSystemHooks
) { connectionToSourceKitLSP in
TestBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
await injector.createBuildSystem(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
}
}
}
Expand Down Expand Up @@ -275,19 +275,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
}
}

/// If the underlying build system is a `TestBuildSystem`, return it. Otherwise, `nil`
///
/// - Important: For testing purposes only.
package var testBuildSystem: TestBuildSystem? {
get async {
switch buildSystemAdapter {
case .builtIn(let builtInBuildSystemAdapter, _): return await builtInBuildSystemAdapter.testBuildSystem
case .external: return nil
case nil: return nil
}
}
}

/// Provider of file to main file mappings.
private var mainFilesProvider: MainFilesProvider?

Expand Down Expand Up @@ -367,7 +354,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
toolchainRegistry: ToolchainRegistry,
options: SourceKitLSPOptions,
connectionToClient: BuildSystemManagerConnectionToClient,
buildSystemTestHooks: BuildSystemTestHooks
buildSystemHooks: BuildSystemHooks
) async {
self.toolchainRegistry = toolchainRegistry
self.options = options
Expand All @@ -376,7 +363,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
self.buildSystemAdapter = await buildSystemSpec?.createBuildSystemAdapter(
toolchainRegistry: toolchainRegistry,
options: options,
buildSystemTestHooks: buildSystemTestHooks,
buildSystemHooks: buildSystemHooks,
messagesToSourceKitLSPHandler: WeakMessageHandler(self)
)

Expand Down Expand Up @@ -432,7 +419,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
let adapter = BuiltInBuildSystemAdapter(
underlyingBuildSystem: legacyBuildServer,
connectionToSourceKitLSP: legacyBuildServer.connectionToSourceKitLSP,
buildSystemTestHooks: buildSystemTestHooks
buildSystemHooks: buildSystemHooks
)
let connectionToBuildSystem = LocalConnection(receiverName: "Legacy BSP server")
connectionToBuildSystem.start(handler: adapter)
Expand Down
20 changes: 16 additions & 4 deletions Sources/BuildSystemIntegration/BuildSystemTestHooks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

#if compiler(>=6)
package import LanguageServerProtocol
package import Foundation
#else
import LanguageServerProtocol
import Foundation
#endif

package struct SwiftPMTestHooks: Sendable {
Expand All @@ -29,19 +31,29 @@ package struct SwiftPMTestHooks: Sendable {
}
}

package struct BuildSystemTestHooks: Sendable {
/// When running SourceKit-LSP in-process, allows the creator of `SourceKitLSPServer` to create the build system instead
/// of SourceKit-LSP creating build systems as needed.
package protocol BuildSystemInjector: Sendable {
func createBuildSystem(projectRoot: URL, connectionToSourceKitLSP: any Connection) async -> BuiltInBuildSystem
}

package struct BuildSystemHooks: Sendable {
package var swiftPMTestHooks: SwiftPMTestHooks

/// A hook that will be executed before a request is handled by a `BuiltInBuildSystem`.
///
/// This allows requests to be artificially delayed.
package var handleRequest: (@Sendable (any RequestType) async -> Void)?
package var preHandleRequest: (@Sendable (any RequestType) async -> Void)?

package var buildSystemInjector: BuildSystemInjector?

package init(
swiftPMTestHooks: SwiftPMTestHooks = SwiftPMTestHooks(),
handleRequest: (@Sendable (any RequestType) async -> Void)? = nil
preHandleRequest: (@Sendable (any RequestType) async -> Void)? = nil,
buildSystemInjector: BuildSystemInjector? = nil
) {
self.swiftPMTestHooks = swiftPMTestHooks
self.handleRequest = handleRequest
self.preHandleRequest = preHandleRequest
self.buildSystemInjector = buildSystemInjector
}
}
17 changes: 5 additions & 12 deletions Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ package struct BuildSystemSpec {
case buildServer
case compilationDatabase
case swiftPM
case testBuildSystem
case injected(BuildSystemInjector)
}

package var kind: Kind
Expand Down Expand Up @@ -59,25 +59,18 @@ actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
/// The connection with which messages are sent to `BuildSystemManager`.
private let connectionToSourceKitLSP: LocalConnection

private let buildSystemTestHooks: BuildSystemTestHooks

/// If the underlying build system is a `TestBuildSystem`, return it. Otherwise, `nil`
///
/// - Important: For testing purposes only.
var testBuildSystem: TestBuildSystem? {
return underlyingBuildSystem as? TestBuildSystem
}
private let buildSystemHooks: BuildSystemHooks

/// Create a `BuiltInBuildSystemAdapter` form an existing `BuiltInBuildSystem` and connection to communicate messages
/// from the build system to SourceKit-LSP.
init(
underlyingBuildSystem: BuiltInBuildSystem,
connectionToSourceKitLSP: LocalConnection,
buildSystemTestHooks: BuildSystemTestHooks
buildSystemHooks: BuildSystemHooks
) {
self.underlyingBuildSystem = underlyingBuildSystem
self.connectionToSourceKitLSP = connectionToSourceKitLSP
self.buildSystemTestHooks = buildSystemTestHooks
self.buildSystemHooks = buildSystemHooks
}

deinit {
Expand Down Expand Up @@ -124,7 +117,7 @@ actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
) async {
let request = RequestAndReply(request, reply: reply)
await buildSystemTestHooks.handleRequest?(request.params)
await buildSystemHooks.preHandleRequest?(request.params)
switch request {
case let request as RequestAndReply<BuildShutdownRequest>:
await request.reply { VoidResponse() }
Expand Down
7 changes: 6 additions & 1 deletion Sources/BuildSystemIntegration/DetermineBuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ import struct TSCBasic.AbsolutePath
/// Returns `nil` if no build system can handle this workspace folder.
package func determineBuildSystem(
forWorkspaceFolder workspaceFolder: DocumentURI,
options: SourceKitLSPOptions
options: SourceKitLSPOptions,
hooks: BuildSystemHooks
) -> BuildSystemSpec? {
if let workspaceURL = workspaceFolder.fileURL, let buildSystemInjector = hooks.buildSystemInjector {
return BuildSystemSpec(kind: .injected(buildSystemInjector), projectRoot: workspaceURL)
}

var buildSystemPreference: [WorkspaceType] = [
.buildServer, .swiftPM, .compilationDatabase,
]
Expand Down
21 changes: 7 additions & 14 deletions Sources/InProcessClient/InProcessSourceKitLSPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,22 @@
//
//===----------------------------------------------------------------------===//

#if compiler(>=6)
import BuildSystemIntegration
public import Foundation
public import LanguageServerProtocol
import LanguageServerProtocolExtensions
package import SKOptions
import SourceKitLSP
import SwiftExtensions
import ToolchainRegistry
import TSCExtensions
import ToolchainRegistry

import struct TSCBasic.AbsolutePath

#if compiler(>=6)
package import SKOptions
package import SourceKitLSP
#else
import BuildSystemIntegration
import Foundation
import LanguageServerProtocol
import LanguageServerProtocolExtensions
import SKOptions
import SourceKitLSP
import SwiftExtensions
import ToolchainRegistry
import TSCExtensions

import struct TSCBasic.AbsolutePath
#endif

/// Launches a `SourceKitLSPServer` in-process and allows sending messages to it.
Expand Down Expand Up @@ -63,6 +55,7 @@ public final class InProcessSourceKitLSPClient: Sendable {
package init(
toolchainPath: URL?,
options: SourceKitLSPOptions = SourceKitLSPOptions(),
hooks: Hooks = Hooks(),
capabilities: ClientCapabilities = ClientCapabilities(),
workspaceFolders: [WorkspaceFolder],
messageHandler: any MessageHandler
Expand All @@ -72,7 +65,7 @@ public final class InProcessSourceKitLSPClient: Sendable {
client: serverToClientConnection,
toolchainRegistry: ToolchainRegistry(installPath: toolchainPath),
options: options,
testHooks: TestHooks(),
hooks: hooks,
onExit: {
serverToClientConnection.close()
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/SKTestSupport/MultiFileTestProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ package class MultiFileTestProject {
initializationOptions: LSPAny? = nil,
capabilities: ClientCapabilities = ClientCapabilities(),
options: SourceKitLSPOptions = .testDefault(),
testHooks: TestHooks = TestHooks(),
hooks: Hooks = Hooks(),
enableBackgroundIndexing: Bool = false,
usePullDiagnostics: Bool = true,
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
Expand All @@ -145,7 +145,7 @@ package class MultiFileTestProject {

self.testClient = try await TestSourceKitLSPClient(
options: options,
testHooks: testHooks,
hooks: hooks,
initializationOptions: initializationOptions,
capabilities: capabilities,
usePullDiagnostics: usePullDiagnostics,
Expand Down
4 changes: 2 additions & 2 deletions Sources/SKTestSupport/SwiftPMTestProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
initializationOptions: LSPAny? = nil,
capabilities: ClientCapabilities = ClientCapabilities(),
options: SourceKitLSPOptions = .testDefault(),
testHooks: TestHooks = TestHooks(),
hooks: Hooks = Hooks(),
enableBackgroundIndexing: Bool = false,
usePullDiagnostics: Bool = true,
pollIndex: Bool = true,
Expand Down Expand Up @@ -238,7 +238,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
initializationOptions: initializationOptions,
capabilities: capabilities,
options: options,
testHooks: testHooks,
hooks: hooks,
enableBackgroundIndexing: enableBackgroundIndexing,
usePullDiagnostics: usePullDiagnostics,
preInitialization: preInitialization,
Expand Down
4 changes: 2 additions & 2 deletions Sources/SKTestSupport/TestSourceKitLSPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
/// needed.
package init(
options: SourceKitLSPOptions = .testDefault(),
testHooks: TestHooks = TestHooks(),
hooks: Hooks = Hooks(),
initialize: Bool = true,
initializationOptions: LSPAny? = nil,
capabilities: ClientCapabilities = ClientCapabilities(),
Expand Down Expand Up @@ -177,7 +177,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
client: serverToClientConnection,
toolchainRegistry: ToolchainRegistry.forTesting,
options: options,
testHooks: testHooks,
hooks: hooks,
onExit: {
serverToClientConnection.close()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/SourceKitLSP/Clang/ClangLanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ actor ClangLanguageService: LanguageService, MessageHandler {
sourceKitLSPServer: SourceKitLSPServer,
toolchain: Toolchain,
options: SourceKitLSPOptions,
testHooks: TestHooks,
hooks: Hooks,
workspace: Workspace
) async throws {
guard let clangdPath = toolchain.clangd else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package actor DocumentationLanguageService: LanguageService, Sendable {
sourceKitLSPServer: SourceKitLSPServer,
toolchain: Toolchain,
options: SourceKitLSPOptions,
testHooks: TestHooks,
hooks: Hooks,
workspace: Workspace
) async throws {}

Expand Down
2 changes: 1 addition & 1 deletion Sources/SourceKitLSP/LanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ package protocol LanguageService: AnyObject, Sendable {
sourceKitLSPServer: SourceKitLSPServer,
toolchain: Toolchain,
options: SourceKitLSPOptions,
testHooks: TestHooks,
hooks: Hooks,
workspace: Workspace
) async throws

Expand Down
Loading