@@ -38,6 +38,19 @@ private enum IndexStatus<T> {
3838 }
3939}
4040
41+ /// See `SemanticIndexManager.preparationStatus`
42+ private struct PreparationTaskStatusData {
43+ /// A UUID to track the task. This is used to ensure that status updates from this task don't update
44+ /// `preparationStatus` for targets that are tracked by a different task.
45+ let taskID : UUID
46+
47+ /// The list of targets that are being prepared in a joint preparation operation.
48+ let targets : [ ConfiguredTarget ]
49+
50+ /// The task that prepares the target
51+ let task : Task < Void , Never >
52+ }
53+
4154/// Schedules index tasks and keeps track of the index status of files.
4255public final actor SemanticIndexManager {
4356 /// The underlying index. This is used to check if the index of a file is already up-to-date, in which case it doesn't
@@ -47,20 +60,16 @@ public final actor SemanticIndexManager {
4760 /// The build system manager that is used to get compiler arguments for a file.
4861 private let buildSystemManager : BuildSystemManager
4962
63+ private let testHooks : IndexTestHooks
64+
5065 /// The task to generate the build graph (resolving package dependencies, generating the build description,
5166 /// ...). `nil` if no build graph is currently being generated.
5267 private var generateBuildGraphTask : Task < Void , Never > ?
5368
5469 /// The preparation status of the targets that the `SemanticIndexManager` has started preparation for.
5570 ///
5671 /// Targets will be removed from this dictionary when they are no longer known to be up-to-date.
57- ///
58- /// The associated values of the `IndexStatus` are:
59- /// - A UUID to track the task. This is used to ensure that status updates from this task don't update
60- /// `preparationStatus` for targets that are tracked by a different task.
61- /// - The list of targets that are being prepared in a joint preparation operation
62- /// - The task that prepares the target
63- private var preparationStatus : [ ConfiguredTarget : IndexStatus < ( UUID , [ ConfiguredTarget ] , Task < Void , Never > ) > ] = [ : ]
72+ private var preparationStatus : [ ConfiguredTarget : IndexStatus < PreparationTaskStatusData > ] = [ : ]
6473
6574 /// The index status of the source files that the `SemanticIndexManager` knows about.
6675 ///
@@ -114,12 +123,14 @@ public final actor SemanticIndexManager {
114123 public init (
115124 index: UncheckedIndex ,
116125 buildSystemManager: BuildSystemManager ,
126+ testHooks: IndexTestHooks ,
117127 indexTaskScheduler: TaskScheduler < AnyIndexTaskDescription > ,
118128 indexTasksWereScheduled: @escaping @Sendable ( Int ) -> Void ,
119129 indexTaskDidFinish: @escaping @Sendable ( ) -> Void
120130 ) {
121131 self . index = index
122132 self . buildSystemManager = buildSystemManager
133+ self . testHooks = testHooks
123134 self . indexTaskScheduler = indexTaskScheduler
124135 self . indexTasksWereScheduled = indexTasksWereScheduled
125136 self . indexTaskDidFinish = indexTaskDidFinish
@@ -278,16 +289,16 @@ public final actor SemanticIndexManager {
278289 switch preparationStatus [ target] {
279290 case . upToDate:
280291 break
281- case . scheduled( ( _ , let existingTaskTargets , let task ) ) , . executing( ( _ , let existingTaskTargets , let task ) ) :
292+ case . scheduled( let existingTaskData ) , . executing( let existingTaskData ) :
282293 // If we already have a task scheduled that prepares fewer targets, await that instead of overriding the
283294 // target's preparation status with a longer-running task. The key benefit here is that when we get many
284295 // preparation requests for the same target (eg. one for every text document request sent to a file), we don't
285296 // re-create new `PreparationTaskDescription`s for every preparation request. Instead, all the preparation
286297 // requests await the same task. At the same time, if we have a multi-file preparation request and then get a
287298 // single-file preparation request, we will override the preparation of that target with the single-file
288299 // preparation task, ensuring that the task gets prepared as quickly as possible.
289- if existingTaskTargets . count <= targets. count {
290- preparationTasksToAwait. append ( task)
300+ if existingTaskData . targets . count <= targets. count {
301+ preparationTasksToAwait. append ( existingTaskData . task)
291302 } else {
292303 targetsToPrepare. append ( target)
293304 }
@@ -299,7 +310,8 @@ public final actor SemanticIndexManager {
299310 let taskDescription = AnyIndexTaskDescription (
300311 PreparationTaskDescription (
301312 targetsToPrepare: targetsToPrepare,
302- buildSystemManager: self . buildSystemManager
313+ buildSystemManager: self . buildSystemManager,
314+ testHooks: testHooks
303315 )
304316 )
305317 if !targetsToPrepare. isEmpty {
@@ -311,20 +323,22 @@ public final actor SemanticIndexManager {
311323 switch newState {
312324 case . executing:
313325 for target in targetsToPrepare {
314- if case . scheduled( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
315- self . preparationStatus [ target] = . executing( ( taskID, targets, task) )
326+ if case . scheduled( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
327+ {
328+ self . preparationStatus [ target] = . executing( existingTaskData)
316329 }
317330 }
318331 case . cancelledToBeRescheduled:
319332 for target in targetsToPrepare {
320- if case . executing( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
321- self . preparationStatus [ target] = . scheduled( ( taskID, targets, task) )
333+ if case . executing( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
334+ {
335+ self . preparationStatus [ target] = . scheduled( existingTaskData)
322336 }
323337 }
324338 case . finished:
325339 for target in targetsToPrepare {
326340 switch self . preparationStatus [ target] {
327- case . executing( ( taskID, _ , _ ) ) :
341+ case . executing( let existingTaskData ) where existingTaskData . taskID == taskID :
328342 self . preparationStatus [ target] = . upToDate
329343 default :
330344 break
@@ -334,14 +348,16 @@ public final actor SemanticIndexManager {
334348 }
335349 }
336350 for target in targetsToPrepare {
337- preparationStatus [ target] = . scheduled( ( taskID, targetsToPrepare, preparationTask) )
351+ preparationStatus [ target] = . scheduled(
352+ PreparationTaskStatusData ( taskID: taskID, targets: targetsToPrepare, task: preparationTask)
353+ )
338354 }
339355 preparationTasksToAwait. append ( preparationTask)
340356 }
341357 await withTaskGroup ( of: Void . self) { taskGroup in
342358 for task in preparationTasksToAwait {
343359 taskGroup. addTask {
344- await task. value
360+ await task. valuePropagatingCancellation
345361 }
346362 }
347363 await taskGroup. waitForAll ( )
@@ -354,7 +370,8 @@ public final actor SemanticIndexManager {
354370 UpdateIndexStoreTaskDescription (
355371 filesToIndex: filesAndTargets,
356372 buildSystemManager: self . buildSystemManager,
357- index: index
373+ index: index,
374+ testHooks: testHooks
358375 )
359376 )
360377 let updateIndexStoreTask = await self . indexTaskScheduler. schedule ( priority: priority, taskDescription) { newState in
0 commit comments