Skip to content

Commit 364f3dc

Browse files
committed
Let the server control the batch size instead
1 parent 9fbdeeb commit 364f3dc

File tree

13 files changed

+155
-107
lines changed

13 files changed

+155
-107
lines changed

Contributor Documentation/BSP Extensions.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export interface SourceKitInitializeBuildResponseData {
2222
* for `swiftc` or `clang` invocations **/
2323
indexStorePath?: string;
2424

25+
/** Options to control how many targets should be prepared simultaneously by SourceKit-LSP. */
26+
multiTargetPreparation?: MultiTargetPreparationSupport
27+
2528
/** Whether the server set the `outputPath` property in the `buildTarget/sources` request */
2629
outputPathsProvider?: bool;
2730

@@ -34,7 +37,7 @@ export interface SourceKitInitializeBuildResponseData {
3437
/** The files to watch for changes.
3538
* Changes to these files are sent to the BSP server using `workspace/didChangeWatchedFiles`.
3639
* `FileSystemWatcher` is the same as in LSP. */
37-
watchers: [FileSystemWatcher]?
40+
watchers?: [FileSystemWatcher]
3841
}
3942
```
4043

Sources/BuildServerIntegration/BuiltInBuildServer.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ package protocol BuiltInBuildServer: AnyObject, Sendable {
3535
/// The path to put the index database, if any.
3636
var indexDatabasePath: URL? { get async }
3737

38+
/// Whether the build server can prepare multiple targets in parallel.
39+
var supportsMultiTargetPreparation: Bool { get }
40+
3841
/// Whether the build server is capable of preparing a target for indexing and determining the output paths for the
3942
/// target, ie. whether the `prepare` method has been implemented and this build server populates the `outputPath`
4043
/// property in the `buildTarget/sources` request.

Sources/BuildServerIntegration/BuiltInBuildServerAdapter.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ actor BuiltInBuildServerAdapter: QueueBasedMessageHandler {
9494
indexStorePath: await orLog("getting index store file path") {
9595
try await underlyingBuildServer.indexStorePath?.filePath
9696
},
97+
multiTargetPreparation: MultiTargetPreparationSupport(supported: underlyingBuildServer.supportsMultiTargetPreparation),
9798
outputPathsProvider: underlyingBuildServer.supportsPreparationAndOutputPaths,
9899
prepareProvider: underlyingBuildServer.supportsPreparationAndOutputPaths,
99100
sourceKitOptionsProvider: true,

Sources/BuildServerIntegration/FixedCompilationDatabaseBuildServer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ package actor FixedCompilationDatabaseBuildServer: BuiltInBuildServer {
5959
indexStorePath?.deletingLastPathComponent().appendingPathComponent("IndexDatabase")
6060
}
6161

62+
package nonisolated var supportsMultiTargetPreparation: Bool { true }
63+
6264
package nonisolated var supportsPreparationAndOutputPaths: Bool { false }
6365

6466
private static func parseCompileFlags(at configPath: URL) throws -> [String] {

Sources/BuildServerIntegration/JSONCompilationDatabaseBuildServer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ package actor JSONCompilationDatabaseBuildServer: BuiltInBuildServer {
9696
indexStorePath?.deletingLastPathComponent().appendingPathComponent("IndexDatabase")
9797
}
9898

99+
package nonisolated var supportsMultiTargetPreparation: Bool { true }
100+
99101
package nonisolated var supportsPreparationAndOutputPaths: Bool { false }
100102

101103
package init(

Sources/BuildServerIntegration/LegacyBuildServer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ actor LegacyBuildServer: MessageHandler, BuiltInBuildServer {
138138
connectionToSourceKitLSP.send(OnBuildTargetDidChangeNotification(changes: nil))
139139
}
140140

141+
package nonisolated var supportsMultiTargetPreparation: Bool { true }
142+
141143
package nonisolated var supportsPreparationAndOutputPaths: Bool { false }
142144

143145
package func buildTargets(request: WorkspaceBuildTargetsRequest) async throws -> WorkspaceBuildTargetsResponse {

Sources/BuildServerIntegration/SwiftPMBuildServer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ package actor SwiftPMBuildServer: BuiltInBuildServer {
459459
signposter.endInterval("Reloading package", state)
460460
}
461461

462+
package nonisolated var supportsMultiTargetPreparation: Bool { false }
463+
462464
package nonisolated var supportsPreparationAndOutputPaths: Bool { options.backgroundIndexingOrDefault }
463465

464466
package var buildPath: URL {

Sources/BuildServerProtocol/Messages/InitializeBuildRequest.swift

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
269269
/// The path at which SourceKit-LSP can store its index database, aggregating data from `indexStorePath`
270270
public var indexStorePath: String?
271271

272+
/// Options to control how many targets should be prepared simultaneously by SourceKit-LSP.
273+
public var multiTargetPreparation: MultiTargetPreparationSupport?
274+
272275
/// Whether the server implements the `buildTarget/outputPaths` request.
273276
public var outputPathsProvider: Bool?
274277

@@ -278,9 +281,6 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
278281
/// Whether the server implements the `textDocument/sourceKitOptions` request.
279282
public var sourceKitOptionsProvider: Bool?
280283

281-
/// The number of targets to prepare concurrently, when an index request is scheduled.
282-
public var indexTaskBatchSize: Int?
283-
284284
/// The files to watch for changes.
285285
public var watchers: [FileSystemWatcher]?
286286

@@ -289,14 +289,14 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
289289
public init(
290290
indexDatabasePath: String? = nil,
291291
indexStorePath: String? = nil,
292-
indexTaskBatchSize: Int? = nil,
292+
multiTargetPreparation: MultiTargetPreparationSupport? = nil,
293293
watchers: [FileSystemWatcher]? = nil,
294294
prepareProvider: Bool? = nil,
295295
sourceKitOptionsProvider: Bool? = nil
296296
) {
297297
self.indexDatabasePath = indexDatabasePath
298298
self.indexStorePath = indexStorePath
299-
self.indexTaskBatchSize = indexTaskBatchSize
299+
self.multiTargetPreparation = multiTargetPreparation
300300
self.watchers = watchers
301301
self.prepareProvider = prepareProvider
302302
self.sourceKitOptionsProvider = sourceKitOptionsProvider
@@ -305,15 +305,15 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
305305
public init(
306306
indexDatabasePath: String? = nil,
307307
indexStorePath: String? = nil,
308-
indexTaskBatchSize: Int? = nil,
308+
multiTargetPreparation: MultiTargetPreparationSupport? = nil,
309309
outputPathsProvider: Bool? = nil,
310310
prepareProvider: Bool? = nil,
311311
sourceKitOptionsProvider: Bool? = nil,
312312
watchers: [FileSystemWatcher]? = nil
313313
) {
314314
self.indexDatabasePath = indexDatabasePath
315315
self.indexStorePath = indexStorePath
316-
self.indexTaskBatchSize = indexTaskBatchSize
316+
self.multiTargetPreparation = multiTargetPreparation
317317
self.outputPathsProvider = outputPathsProvider
318318
self.prepareProvider = prepareProvider
319319
self.sourceKitOptionsProvider = sourceKitOptionsProvider
@@ -327,8 +327,8 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
327327
if case .string(let indexStorePath) = dictionary[CodingKeys.indexStorePath.stringValue] {
328328
self.indexStorePath = indexStorePath
329329
}
330-
if case .int(let indexTaskBatchSize) = dictionary[CodingKeys.indexTaskBatchSize.stringValue] {
331-
self.indexTaskBatchSize = indexTaskBatchSize
330+
if case .dictionary(let multiTargetPreparation) = dictionary[CodingKeys.multiTargetPreparation.stringValue] {
331+
self.multiTargetPreparation = MultiTargetPreparationSupport(fromLSPDictionary: multiTargetPreparation)
332332
}
333333
if case .bool(let outputPathsProvider) = dictionary[CodingKeys.outputPathsProvider.stringValue] {
334334
self.outputPathsProvider = outputPathsProvider
@@ -352,8 +352,8 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
352352
if let indexStorePath {
353353
result[CodingKeys.indexStorePath.stringValue] = .string(indexStorePath)
354354
}
355-
if let indexTaskBatchSize {
356-
result[CodingKeys.indexTaskBatchSize.stringValue] = .int(indexTaskBatchSize)
355+
if let multiTargetPreparation {
356+
result[CodingKeys.multiTargetPreparation.stringValue] = multiTargetPreparation.encodeToLSPAny()
357357
}
358358
if let outputPathsProvider {
359359
result[CodingKeys.outputPathsProvider.stringValue] = .bool(outputPathsProvider)
@@ -370,3 +370,26 @@ public struct SourceKitInitializeBuildResponseData: LSPAnyCodable, Codable, Send
370370
return .dictionary(result)
371371
}
372372
}
373+
374+
public struct MultiTargetPreparationSupport: LSPAnyCodable, Codable, Sendable {
375+
/// Whether the build server can prepare multiple targets in parallel. If this value is omitted, it is assumed to be `true`.
376+
public var supported: Bool?
377+
378+
public init(supported: Bool? = nil) {
379+
self.supported = supported
380+
}
381+
382+
public init?(fromLSPDictionary dictionary: [String: LanguageServerProtocol.LSPAny]) {
383+
if case .bool(let supported) = dictionary[CodingKeys.supported.stringValue] {
384+
self.supported = supported
385+
}
386+
}
387+
388+
public func encodeToLSPAny() -> LanguageServerProtocol.LSPAny {
389+
var result: [String: LSPAny] = [:]
390+
if let supported {
391+
result[CodingKeys.supported.stringValue] = .bool(supported)
392+
}
393+
return .dictionary(result)
394+
}
395+
}

Sources/SKTestSupport/CustomBuildServerTestProject.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,15 @@ package extension CustomBuildServer {
175175

176176
func initializationResponseSupportingBackgroundIndexing(
177177
projectRoot: URL,
178-
outputPathsProvider: Bool,
179-
indexTaskBatchSize: Int? = nil
178+
outputPathsProvider: Bool
180179
) throws -> InitializeBuildResponse {
181180
return initializationResponse(
182181
initializeData: SourceKitInitializeBuildResponseData(
183182
indexDatabasePath: try projectRoot.appendingPathComponent("index-db").filePath,
184183
indexStorePath: try projectRoot.appendingPathComponent("index-store").filePath,
185-
indexTaskBatchSize: indexTaskBatchSize,
186184
outputPathsProvider: outputPathsProvider,
187185
prepareProvider: true,
188-
sourceKitOptionsProvider: true
186+
sourceKitOptionsProvider: true,
189187
)
190188
)
191189
}

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ package final actor SemanticIndexManager {
222222
/// The parameter is the number of files that were scheduled to be indexed.
223223
private let indexTasksWereScheduled: @Sendable (_ numberOfFileScheduled: Int) -> Void
224224

225-
/// The number of targets to prepare concurrently, whenever a index request is scheduled.
226-
private let indexTaskBatchSize: Int
225+
/// Determines whether or not the `SemanticIndexManager` should dispatch preparation tasks in batches.
226+
private let shouldIndexInParallel: Bool
227227

228228
/// Callback that is called when `progressStatus` might have changed.
229229
private let indexProgressStatusDidChange: @Sendable () -> Void
@@ -264,7 +264,7 @@ package final actor SemanticIndexManager {
264264
updateIndexStoreTimeout: Duration,
265265
hooks: IndexHooks,
266266
indexTaskScheduler: TaskScheduler<AnyIndexTaskDescription>,
267-
indexTaskBatchSize: Int,
267+
shouldIndexInParallel: Bool,
268268
logMessageToIndexLog:
269269
@escaping @Sendable (
270270
_ message: String, _ type: WindowMessageType, _ structure: StructuredLogKind
@@ -277,7 +277,7 @@ package final actor SemanticIndexManager {
277277
self.updateIndexStoreTimeout = updateIndexStoreTimeout
278278
self.hooks = hooks
279279
self.indexTaskScheduler = indexTaskScheduler
280-
self.indexTaskBatchSize = indexTaskBatchSize
280+
self.shouldIndexInParallel = shouldIndexInParallel
281281
self.logMessageToIndexLog = logMessageToIndexLog
282282
self.indexTasksWereScheduled = indexTasksWereScheduled
283283
self.indexProgressStatusDidChange = indexProgressStatusDidChange
@@ -654,7 +654,6 @@ package final actor SemanticIndexManager {
654654
guard !targetsToPrepare.isEmpty else {
655655
return
656656
}
657-
658657
let taskDescription = AnyIndexTaskDescription(
659658
PreparationTaskDescription(
660659
targetsToPrepare: targetsToPrepare,
@@ -882,7 +881,14 @@ package final actor SemanticIndexManager {
882881

883882
var indexTasks: [Task<Void, Never>] = []
884883

885-
for targetsBatch in sortedTargets.partition(intoBatchesOfSize: indexTaskBatchSize) {
884+
let batchSize: Int
885+
if shouldIndexInParallel {
886+
let processorCount = ProcessInfo.processInfo.activeProcessorCount
887+
batchSize = max(1, processorCount * 5)
888+
} else {
889+
batchSize = 1
890+
}
891+
for targetsBatch in sortedTargets.partition(intoBatchesOfSize: batchSize) {
886892
let preparationTaskID = UUID()
887893
let filesToIndex = targetsBatch.flatMap({ filesByTarget[$0]! })
888894

@@ -906,10 +912,7 @@ package final actor SemanticIndexManager {
906912
// And after preparation is done, index the files in the targets.
907913
await withTaskGroup(of: Void.self) { taskGroup in
908914
for target in targetsBatch {
909-
// TODO: Once swiftc supports indexing of multiple files in a single invocation, increase the batch size to
910-
// allow it to share AST builds between multiple files within a target.
911-
// (https://github.com/swiftlang/sourcekit-lsp/issues/1268)
912-
for fileBatch in filesByTarget[target]!.partition(intoBatchesOfSize: 1) {
915+
for fileBatch in filesByTarget[target]!.partition(intoBatchesOfSize: batchSize) {
913916
taskGroup.addTask {
914917
await self.updateIndexStore(
915918
for: fileBatch,

0 commit comments

Comments
 (0)