Skip to content

Commit 9006ab6

Browse files
committed
Use the InitializeRequest from BSP to communicate some static options between the build system and BuildSystemManager
1 parent 022a6de commit 9006ab6

File tree

6 files changed

+223
-25
lines changed

6 files changed

+223
-25
lines changed

Sources/BuildServerProtocol/InitializeBuild.swift

Lines changed: 160 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
1213
import LanguageServerProtocol
1314

15+
public struct InitializeBuildRequestDataKind: RawRepresentable, Hashable, Codable, Sendable {
16+
public let rawValue: String
17+
18+
public init(rawValue: String) {
19+
self.rawValue = rawValue
20+
}
21+
}
22+
1423
/// Like the language server protocol, the initialize request is sent
1524
/// as the first request from the client to the server. If the server
1625
/// receives a request or notification before the initialize request
@@ -25,9 +34,9 @@ import LanguageServerProtocol
2534
/// Until the server has responded to the initialize request with an
2635
/// InitializeBuildResult, the client must not send any additional
2736
/// requests or notifications to the server.
28-
public struct InitializeBuild: RequestType, Hashable {
37+
public struct InitializeBuildRequest: RequestType, Hashable {
2938
public static let method: String = "build/initialize"
30-
public typealias Response = InitializeBuildResult
39+
public typealias Response = InitializeBuildResponse
3140

3241
/// Name of the client
3342
public var displayName: String
@@ -44,18 +53,28 @@ public struct InitializeBuild: RequestType, Hashable {
4453
/// The capabilities of the client
4554
public var capabilities: BuildClientCapabilities
4655

56+
/// Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified. */
57+
public var dataKind: InitializeBuildRequestDataKind?
58+
59+
/// Additional metadata about the client
60+
public var data: LSPAny?
61+
4762
public init(
4863
displayName: String,
4964
version: String,
5065
bspVersion: String,
5166
rootUri: URI,
52-
capabilities: BuildClientCapabilities
67+
capabilities: BuildClientCapabilities,
68+
dataKind: InitializeBuildRequestDataKind? = nil,
69+
data: LSPAny? = nil
5370
) {
5471
self.displayName = displayName
5572
self.version = version
5673
self.bspVersion = bspVersion
5774
self.rootUri = rootUri
5875
self.capabilities = capabilities
76+
self.dataKind = dataKind
77+
self.data = data
5978
}
6079
}
6180

@@ -66,12 +85,67 @@ public struct BuildClientCapabilities: Codable, Hashable, Sendable {
6685
/// languages than those that appear in this list.
6786
public var languageIds: [Language]
6887

69-
public init(languageIds: [Language]) {
88+
/// Mirror capability to BuildServerCapabilities.jvmCompileClasspathProvider
89+
/// The client will request classpath via `buildTarget/jvmCompileClasspath` so
90+
/// it's safe to return classpath in ScalacOptionsItem empty. */
91+
public var jvmCompileClasspathReceiver: Bool?
92+
93+
public init(languageIds: [Language], jvmCompileClasspathReceiver: Bool? = nil) {
7094
self.languageIds = languageIds
95+
self.jvmCompileClasspathReceiver = jvmCompileClasspathReceiver
7196
}
7297
}
7398

74-
public struct InitializeBuildResult: ResponseType, Hashable {
99+
public struct InitializeBuildResponseDataKind: RawRepresentable, Hashable, Codable, Sendable {
100+
public let rawValue: String
101+
102+
public init(rawValue: String) {
103+
self.rawValue = rawValue
104+
}
105+
106+
/// `data` field must contain a `SourceKitInitializeBuildResponseData` object.
107+
public static let sourceKit = InitializeBuildResponseDataKind(rawValue: "sourcekit")
108+
}
109+
110+
public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Sendable {
111+
public var indexDatabasePath: String?
112+
public var indexStorePath: String?
113+
public var supportsPreparation: Bool?
114+
115+
public init(indexDatabasePath: String?, indexStorePath: String?, supportsPreparation: Bool?) {
116+
self.indexDatabasePath = indexDatabasePath
117+
self.indexStorePath = indexStorePath
118+
self.supportsPreparation = supportsPreparation
119+
}
120+
121+
public init?(fromLSPDictionary dictionary: [String: LanguageServerProtocol.LSPAny]) {
122+
if case .string(let indexDatabasePath) = dictionary[CodingKeys.indexDatabasePath.stringValue] {
123+
self.indexDatabasePath = indexDatabasePath
124+
}
125+
if case .string(let indexStorePath) = dictionary[CodingKeys.indexStorePath.stringValue] {
126+
self.indexStorePath = indexStorePath
127+
}
128+
if case .bool(let supportsPreparation) = dictionary[CodingKeys.supportsPreparation.stringValue] {
129+
self.supportsPreparation = supportsPreparation
130+
}
131+
}
132+
133+
public func encodeToLSPAny() -> LanguageServerProtocol.LSPAny {
134+
var result: [String: LSPAny] = [:]
135+
if let indexDatabasePath {
136+
result[CodingKeys.indexDatabasePath.stringValue] = .string(indexDatabasePath)
137+
}
138+
if let indexStorePath {
139+
result[CodingKeys.indexStorePath.stringValue] = .string(indexStorePath)
140+
}
141+
if let supportsPreparation {
142+
result[CodingKeys.supportsPreparation.stringValue] = .bool(supportsPreparation)
143+
}
144+
return .dictionary(result)
145+
}
146+
}
147+
148+
public struct InitializeBuildResponse: ResponseType, Hashable {
75149
/// Name of the server
76150
public var displayName: String
77151

@@ -84,6 +158,9 @@ public struct InitializeBuildResult: ResponseType, Hashable {
84158
/// The capabilities of the build server
85159
public var capabilities: BuildServerCapabilities
86160

161+
/// Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified.
162+
public var dataKind: InitializeBuildResponseDataKind?
163+
87164
/// Optional metadata about the server
88165
public var data: LSPAny?
89166

@@ -92,41 +169,103 @@ public struct InitializeBuildResult: ResponseType, Hashable {
92169
version: String,
93170
bspVersion: String,
94171
capabilities: BuildServerCapabilities,
172+
dataKind: InitializeBuildResponseDataKind? = nil,
95173
data: LSPAny? = nil
96174
) {
97175
self.displayName = displayName
98176
self.version = version
99177
self.bspVersion = bspVersion
100178
self.capabilities = capabilities
179+
self.dataKind = dataKind
101180
self.data = data
102181
}
103182
}
104183

105184
public struct BuildServerCapabilities: Codable, Hashable, Sendable {
106185
/// The languages the server supports compilation via method buildTarget/compile.
107-
public var compileProvider: CompileProvider? = nil
186+
public var compileProvider: CompileProvider?
108187

109188
/// The languages the server supports test execution via method buildTarget/test
110-
public var testProvider: TestProvider? = nil
189+
public var testProvider: TestProvider?
111190

112191
/// The languages the server supports run via method buildTarget/run
113-
public var runProvider: RunProvider? = nil
192+
public var runProvider: RunProvider?
193+
194+
/// The languages the server supports debugging via method debugSession/start.
195+
public var debugProvider: DebugProvider?
114196

115197
/// The server can provide a list of targets that contain a
116198
/// single text document via the method buildTarget/inverseSources
117-
public var inverseSourcesProvider: Bool? = nil
199+
public var inverseSourcesProvider: Bool?
118200

119201
/// The server provides sources for library dependencies
120202
/// via method buildTarget/dependencySources
121-
public var dependencySourcesProvider: Bool? = nil
203+
public var dependencySourcesProvider: Bool?
122204

123205
/// The server provides all the resource dependencies
124206
/// via method buildTarget/resources
125-
public var resourcesProvider: Bool? = nil
207+
public var resourcesProvider: Bool?
208+
209+
/// The server provides all output paths
210+
/// via method buildTarget/outputPaths
211+
public var outputPathsProvider: Bool?
126212

127213
/// The server sends notifications to the client on build
128-
/// target change events via buildTarget/didChange
129-
public var buildTargetChangedProvider: Bool? = nil
214+
/// target change events via `buildTarget/didChange`
215+
public var buildTargetChangedProvider: Bool?
216+
217+
/// The server can respond to `buildTarget/jvmRunEnvironment` requests with the
218+
/// necessary information required to launch a Java process to run a main class.
219+
public var jvmRunEnvironmentProvider: Bool?
220+
221+
/// The server can respond to `buildTarget/jvmTestEnvironment` requests with the
222+
/// necessary information required to launch a Java process for testing or
223+
/// debugging.
224+
public var jvmTestEnvironmentProvider: Bool?
225+
226+
/// The server can respond to `workspace/cargoFeaturesState` and
227+
/// `setCargoFeatures` requests. In other words, supports Cargo Features extension.
228+
public var cargoFeaturesProvider: Bool?
229+
230+
/// Reloading the build state through workspace/reload is supported
231+
public var canReload: Bool?
232+
233+
/// The server can respond to `buildTarget/jvmCompileClasspath` requests with the
234+
/// necessary information about the target's classpath.
235+
public var jvmCompileClasspathProvider: Bool?
236+
237+
public init(
238+
compileProvider: CompileProvider? = nil,
239+
testProvider: TestProvider? = nil,
240+
runProvider: RunProvider? = nil,
241+
debugProvider: DebugProvider? = nil,
242+
inverseSourcesProvider: Bool? = nil,
243+
dependencySourcesProvider: Bool? = nil,
244+
resourcesProvider: Bool? = nil,
245+
outputPathsProvider: Bool? = nil,
246+
buildTargetChangedProvider: Bool? = nil,
247+
jvmRunEnvironmentProvider: Bool? = nil,
248+
jvmTestEnvironmentProvider: Bool? = nil,
249+
cargoFeaturesProvider: Bool? = nil,
250+
canReload: Bool? = nil,
251+
jvmCompileClasspathProvider: Bool? = nil
252+
) {
253+
self.compileProvider = compileProvider
254+
self.testProvider = testProvider
255+
self.runProvider = runProvider
256+
self.debugProvider = debugProvider
257+
self.inverseSourcesProvider = inverseSourcesProvider
258+
self.dependencySourcesProvider = dependencySourcesProvider
259+
self.resourcesProvider = resourcesProvider
260+
self.outputPathsProvider = outputPathsProvider
261+
self.buildTargetChangedProvider = buildTargetChangedProvider
262+
self.jvmRunEnvironmentProvider = jvmRunEnvironmentProvider
263+
self.jvmTestEnvironmentProvider = jvmTestEnvironmentProvider
264+
self.cargoFeaturesProvider = cargoFeaturesProvider
265+
self.canReload = canReload
266+
self.jvmCompileClasspathProvider = jvmCompileClasspathProvider
267+
}
268+
130269
}
131270

132271
public struct CompileProvider: Codable, Hashable, Sendable {
@@ -153,6 +292,14 @@ public struct TestProvider: Codable, Hashable, Sendable {
153292
}
154293
}
155294

295+
public struct DebugProvider: Codable, Hashable, Sendable {
296+
public var languageIds: [Language]
297+
298+
public init(languageIds: [Language]) {
299+
self.languageIds = languageIds
300+
}
301+
}
302+
156303
public struct InitializedBuildNotification: NotificationType {
157304
public static let method: String = "build/initialized"
158305

Sources/BuildServerProtocol/Messages.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fileprivate let requestTypes: [_RequestType.Type] = [
1515
BuildTargetOutputPaths.self,
1616
BuildTargetsRequest.self,
1717
BuildTargetSourcesRequest.self,
18-
InitializeBuild.self,
18+
InitializeBuildRequest.self,
1919
InverseSourcesRequest.self,
2020
RegisterForChanges.self,
2121
ShutdownBuild.self,

Sources/BuildSystemIntegration/BuildServerBuildSystem.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ package actor BuildServerBuildSystem: MessageHandler {
158158
Language.swift,
159159
]
160160

161-
let initializeRequest = InitializeBuild(
161+
let initializeRequest = InitializeBuildRequest(
162162
displayName: "SourceKit-LSP",
163163
version: "1.0",
164164
bspVersion: "2.0",

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
7373
/// The files for which the delegate has requested change notifications, ie.
7474
/// the files for which the delegate wants to get `filesDependenciesUpdated`
7575
/// callbacks if the file's build settings.
76-
var watchedFiles: [DocumentURI: (mainFile: DocumentURI, language: Language)] = [:]
76+
private var watchedFiles: [DocumentURI: (mainFile: DocumentURI, language: Language)] = [:]
7777

7878
/// The underlying primary build system.
7979
///
@@ -82,10 +82,10 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
8282

8383
/// The fallback build system. If present, used when the `buildSystem` is not
8484
/// set or cannot provide settings.
85-
let fallbackBuildSystem: FallbackBuildSystem
85+
private let fallbackBuildSystem: FallbackBuildSystem
8686

8787
/// Provider of file to main file mappings.
88-
var mainFilesProvider: MainFilesProvider?
88+
private var mainFilesProvider: MainFilesProvider?
8989

9090
/// Build system delegate that will receive notifications about setting changes, etc.
9191
private weak var delegate: BuildSystemManagerDelegate?
@@ -97,6 +97,9 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
9797

9898
private let options: SourceKitLSPOptions
9999

100+
/// A task that stores the result of the `build/initialize` request once it is received.
101+
private var initializeResult: Task<InitializeBuildResponse?, Never>!
102+
100103
/// Debounces calls to `delegate.filesDependenciesUpdated`.
101104
///
102105
/// This is to ensure we don't call `filesDependenciesUpdated` for the same file multiple time if the client does not
@@ -120,9 +123,19 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
120123
/// was found.
121124
package let projectRoot: AbsolutePath?
122125

123-
package var supportsPreparation: Bool {
126+
/// The `SourceKitInitializeBuildResponseData` received from the `build/initialize` request, if any.
127+
package var initializationData: SourceKitInitializeBuildResponseData? {
124128
get async {
125-
return await buildSystem?.underlyingBuildSystem.supportsPreparation ?? false
129+
guard let initializeResult = await initializeResult.value else {
130+
return nil
131+
}
132+
guard initializeResult.dataKind == nil || initializeResult.dataKind == .sourceKit else {
133+
return nil
134+
}
135+
guard case .dictionary(let data) = initializeResult.data else {
136+
return nil
137+
}
138+
return SourceKitInitializeBuildResponseData(fromLSPDictionary: data)
126139
}
127140
}
128141

@@ -161,6 +174,26 @@ package actor BuildSystemManager: BuiltInBuildSystemAdapterDelegate {
161174
}
162175
await delegate.filesDependenciesUpdated(changedWatchedFiles)
163176
}
177+
initializeResult = Task { () -> InitializeBuildResponse? in
178+
guard let buildSystem else {
179+
return nil
180+
}
181+
guard let buildSystemKind else {
182+
logger.fault("Created build system without a build system kind?")
183+
return nil
184+
}
185+
return await orLog("Initializing build system") {
186+
try await buildSystem.send(
187+
InitializeBuildRequest(
188+
displayName: "SourceKit-LSP",
189+
version: "unknown",
190+
bspVersion: "2.2.0",
191+
rootUri: URI(buildSystemKind.projectRoot.asURL),
192+
capabilities: BuildClientCapabilities(languageIds: [.c, .cpp, .objective_c, .objective_cpp, .swift])
193+
)
194+
)
195+
}
196+
}
164197
}
165198

166199
package func filesDidChange(_ events: [FileEvent]) async {

Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
121121
self.underlyingBuildSystem = buildSystem
122122
}
123123

124+
private func initialize(request: InitializeBuildRequest) async -> InitializeBuildResponse {
125+
return InitializeBuildResponse(
126+
displayName: "\(type(of: underlyingBuildSystem))",
127+
version: "1.0.0",
128+
bspVersion: "2.2.0",
129+
capabilities: BuildServerCapabilities(),
130+
dataKind: .sourceKit,
131+
data: SourceKitInitializeBuildResponseData(
132+
indexDatabasePath: await underlyingBuildSystem.indexDatabasePath?.pathString,
133+
indexStorePath: await underlyingBuildSystem.indexStorePath?.pathString,
134+
supportsPreparation: underlyingBuildSystem.supportsPreparation
135+
).encodeToLSPAny()
136+
)
137+
}
138+
124139
package func send<R: RequestType>(_ request: R) async throws -> R.Response {
125140
logger.info(
126141
"""
@@ -142,6 +157,8 @@ package actor BuiltInBuildSystemAdapter: BuiltInBuildSystemMessageHandler {
142157
return try await handle(request, underlyingBuildSystem.buildTargets)
143158
case let request as BuildTargetSourcesRequest:
144159
return try await handle(request, underlyingBuildSystem.buildTargetSources)
160+
case let request as InitializeBuildRequest:
161+
return try await handle(request, self.initialize)
145162
case let request as InverseSourcesRequest:
146163
return try await handle(request, underlyingBuildSystem.inverseSources)
147164
case let request as PrepareTargetsRequest:

0 commit comments

Comments
 (0)