mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
214 lines
8.2 KiB
Swift
214 lines
8.2 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_spi(SourceKitLSP) import BuildServerProtocol
|
|
import Foundation
|
|
@_spi(SourceKitLSP) import LanguageServerProtocol
|
|
@_spi(SourceKitLSP) import LanguageServerProtocolExtensions
|
|
@_spi(SourceKitLSP) import LanguageServerProtocolTransport
|
|
@_spi(SourceKitLSP) import SKLogging
|
|
import SKOptions
|
|
import SwiftExtensions
|
|
import ToolchainRegistry
|
|
import ToolsProtocolsSwiftExtensions
|
|
|
|
#if compiler(>=6.5)
|
|
#warning("We have had a two year transition period to the pull based build server. Consider removing this build server")
|
|
#endif
|
|
|
|
/// Bridges the gap between the legacy push-based BSP settings model and the new pull based BSP settings model.
|
|
///
|
|
/// On the one side, this type is a `BuiltInBuildServer` that offers pull-based build settings. To serve these queries,
|
|
/// it communicates with an external BSP server that uses `build/sourceKitOptionsChanged` notifications to communicate
|
|
/// build settings.
|
|
///
|
|
/// This build server should be phased out in favor of the pull-based settings model described in
|
|
/// https://forums.swift.org/t/extending-functionality-of-build-server-protocol-with-sourcekit-lsp/74400
|
|
actor LegacyBuildServer: MessageHandler, BuiltInBuildServer {
|
|
private var buildServer: JSONRPCConnection?
|
|
|
|
/// The queue on which all messages that originate from the build server are
|
|
/// handled.
|
|
///
|
|
/// These are requests and notifications sent *from* the build server,
|
|
/// not replies from the build server.
|
|
///
|
|
/// This ensures that messages from the build server are handled in the order
|
|
/// they were received. Swift concurrency does not guarentee in-order
|
|
/// execution of tasks.
|
|
private let bspMessageHandlingQueue = AsyncQueue<Serial>()
|
|
|
|
package let projectRoot: URL
|
|
|
|
package let configPath: URL
|
|
|
|
var fileWatchers: [FileSystemWatcher] = []
|
|
|
|
let indexDatabasePath: URL?
|
|
let indexStorePath: URL?
|
|
|
|
package let connectionToSourceKitLSP: LocalConnection
|
|
|
|
/// The build settings that have been received from the build server.
|
|
private var buildSettings: [DocumentURI: TextDocumentSourceKitOptionsResponse] = [:]
|
|
|
|
/// The files for which we have sent a `textDocument/registerForChanges` to the BSP server.
|
|
private var urisRegisteredForChanges: Set<URI> = []
|
|
|
|
init(
|
|
projectRoot: URL,
|
|
configPath: URL,
|
|
initializationData: InitializeBuildResponse,
|
|
_ externalBuildServerAdapter: ExternalBuildServerAdapter
|
|
) async {
|
|
self.projectRoot = projectRoot
|
|
self.configPath = configPath
|
|
self.indexDatabasePath = nil
|
|
self.indexStorePath = nil
|
|
self.connectionToSourceKitLSP = LocalConnection(
|
|
receiverName: "BuildServerManager",
|
|
handler: await externalBuildServerAdapter.messagesToSourceKitLSPHandler
|
|
)
|
|
await externalBuildServerAdapter.changeMessageToSourceKitLSPHandler(to: self)
|
|
self.buildServer = await externalBuildServerAdapter.connectionToBuildServer
|
|
}
|
|
|
|
/// Handler for notifications received **from** the builder server, ie.
|
|
/// the build server has sent us a notification.
|
|
///
|
|
/// We need to notify the delegate about any updated build settings.
|
|
package nonisolated func handle(_ params: some NotificationType) {
|
|
logger.info(
|
|
"""
|
|
Received notification from legacy BSP server:
|
|
\(params.forLogging)
|
|
"""
|
|
)
|
|
bspMessageHandlingQueue.async {
|
|
if let params = params as? OnBuildTargetDidChangeNotification {
|
|
await self.handleBuildTargetsChanged(params)
|
|
} else if let params = params as? FileOptionsChangedNotification {
|
|
await self.handleFileOptionsChanged(params)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Handler for requests received **from** the build server.
|
|
///
|
|
/// We currently can't handle any requests sent from the build server to us.
|
|
package nonisolated func handle<R: RequestType>(
|
|
_ params: R,
|
|
id: RequestID,
|
|
reply: @escaping (LSPResult<R.Response>) -> Void
|
|
) {
|
|
logger.info(
|
|
"""
|
|
Received request from legacy BSP server:
|
|
\(params.forLogging)
|
|
"""
|
|
)
|
|
reply(.failure(ResponseError.methodNotFound(R.method)))
|
|
}
|
|
|
|
func handleBuildTargetsChanged(_ notification: OnBuildTargetDidChangeNotification) {
|
|
connectionToSourceKitLSP.send(notification)
|
|
}
|
|
|
|
func handleFileOptionsChanged(_ notification: FileOptionsChangedNotification) async {
|
|
let result = notification.updatedOptions
|
|
let settings = TextDocumentSourceKitOptionsResponse(
|
|
compilerArguments: result.options,
|
|
workingDirectory: result.workingDirectory
|
|
)
|
|
await self.buildSettingsChanged(for: notification.uri, settings: settings)
|
|
}
|
|
|
|
/// Record the new build settings for the given document and inform the delegate
|
|
/// about the changed build settings.
|
|
private func buildSettingsChanged(for document: DocumentURI, settings: TextDocumentSourceKitOptionsResponse?) async {
|
|
buildSettings[document] = settings
|
|
connectionToSourceKitLSP.send(OnBuildTargetDidChangeNotification(changes: nil))
|
|
}
|
|
|
|
package nonisolated var supportsPreparationAndOutputPaths: Bool { false }
|
|
|
|
package func buildTargets(request: WorkspaceBuildTargetsRequest) async throws -> WorkspaceBuildTargetsResponse {
|
|
return WorkspaceBuildTargetsResponse(targets: [
|
|
BuildTarget(
|
|
id: .dummy,
|
|
displayName: "BuildServer",
|
|
tags: [.test],
|
|
capabilities: BuildTargetCapabilities(),
|
|
// Be conservative with the languages that might be used in the target. SourceKit-LSP doesn't use this property.
|
|
languageIds: [.c, .cpp, .objective_c, .objective_cpp, .swift],
|
|
dependencies: []
|
|
)
|
|
])
|
|
}
|
|
|
|
package func buildTargetSources(request: BuildTargetSourcesRequest) async throws -> BuildTargetSourcesResponse {
|
|
guard request.targets.contains(.dummy) else {
|
|
return BuildTargetSourcesResponse(items: [])
|
|
}
|
|
return BuildTargetSourcesResponse(items: [
|
|
SourcesItem(
|
|
target: .dummy,
|
|
sources: [SourceItem(uri: DocumentURI(self.projectRoot), kind: .directory, generated: false)]
|
|
)
|
|
])
|
|
}
|
|
|
|
package func didChangeWatchedFiles(notification: OnWatchedFilesDidChangeNotification) {}
|
|
|
|
package func prepare(request: BuildTargetPrepareRequest) async throws -> VoidResponse {
|
|
throw ResponseError.methodNotFound(BuildTargetPrepareRequest.method)
|
|
}
|
|
|
|
package func sourceKitOptions(
|
|
request: TextDocumentSourceKitOptionsRequest
|
|
) async throws -> TextDocumentSourceKitOptionsResponse? {
|
|
// Support the pre Swift 6.1 build settings workflow where SourceKit-LSP registers for changes for a file and then
|
|
// expects updates to those build settings to get pushed to SourceKit-LSP with `FileOptionsChangedNotification`.
|
|
// We do so by registering for changes when requesting build settings for a document for the first time. We never
|
|
// unregister for changes. The expectation is that all BSP servers migrate to the `SourceKitOptionsRequest` soon,
|
|
// which renders this code path dead.
|
|
let uri = request.textDocument.uri
|
|
if urisRegisteredForChanges.insert(uri).inserted {
|
|
let request = RegisterForChanges(uri: uri, action: .register)
|
|
_ = self.buildServer?.send(request) { result in
|
|
if let error = result.failure {
|
|
logger.error("Error registering \(request.uri): \(error.forLogging)")
|
|
|
|
Task {
|
|
// BuildServer registration failed, so tell our delegate that no build
|
|
// settings are available.
|
|
await self.buildSettingsChanged(for: request.uri, settings: nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
guard let buildSettings = buildSettings[uri] else {
|
|
return nil
|
|
}
|
|
|
|
return TextDocumentSourceKitOptionsResponse(
|
|
compilerArguments: buildSettings.compilerArguments,
|
|
workingDirectory: buildSettings.workingDirectory
|
|
)
|
|
}
|
|
|
|
package func waitForBuildSystemUpdates(request: WorkspaceWaitForBuildSystemUpdatesRequest) async -> VoidResponse {
|
|
return VoidResponse()
|
|
}
|
|
}
|