@@ -98,10 +98,6 @@ public enum IndexProgressStatus {
98
98
99
99
/// See `SemanticIndexManager.inProgressPrepareForEditorTask`.
100
100
fileprivate struct InProgressPrepareForEditorTask {
101
- fileprivate enum State {
102
- case determiningCanonicalConfiguredTarget
103
- case preparingTarget
104
- }
105
101
/// A unique ID that identifies the preparation task and is used to set
106
102
/// `SemanticIndexManager.inProgressPrepareForEditorTask` to `nil` when the current in progress task finishes.
107
103
let id : UUID
@@ -111,9 +107,21 @@ fileprivate struct InProgressPrepareForEditorTask {
111
107
112
108
/// The task that prepares the document. Should never be awaited and only be used to cancel the task.
113
109
let task : Task < Void , Never >
110
+ }
111
+
112
+ /// The reason why a target is being prepared. This is used to determine the `IndexProgressStatus`.
113
+ fileprivate enum TargetPreparationPurpose : Comparable {
114
+ /// We are preparing the target so we can index files in it.
115
+ case forIndexing
116
+
117
+ /// We are preparing the target to provide semantic functionality in one of its files.
118
+ case forEditorFunctionality
119
+ }
114
120
115
- /// Whether the task is currently determining the file's target or actually preparing the document.
116
- var state : State
121
+ /// An entry in `SemanticIndexManager.inProgressPreparationTasks`.
122
+ fileprivate struct InProgressPreparationTask {
123
+ let task : OpaqueQueuedIndexTask
124
+ let purpose : TargetPreparationPurpose
117
125
}
118
126
119
127
/// Schedules index tasks and keeps track of the index status of files.
@@ -139,7 +147,7 @@ public final actor SemanticIndexManager {
139
147
/// executing.
140
148
///
141
149
/// After a preparation task finishes, it is removed from this dictionary.
142
- private var inProgressPreparationTasks : [ ConfiguredTarget : OpaqueQueuedIndexTask ] = [ : ]
150
+ private var inProgressPreparationTasks : [ ConfiguredTarget : InProgressPreparationTask ] = [ : ]
143
151
144
152
/// The files that are currently being index, either waiting for their target to be prepared, waiting for the index
145
153
/// store update task to be scheduled in the task scheduler or which currently have an index store update running.
@@ -175,14 +183,14 @@ public final actor SemanticIndexManager {
175
183
176
184
/// A summary of the tasks that this `SemanticIndexManager` has currently scheduled or is currently indexing.
177
185
public var progressStatus : IndexProgressStatus {
178
- if let inProgressPrepareForEditorTask , inProgressPrepareForEditorTask . state == . preparingTarget {
186
+ if inProgressPreparationTasks . values . contains ( where : { $0 . purpose == . forEditorFunctionality } ) {
179
187
return . preparingFileForEditorFunctionality
180
188
}
181
189
if generateBuildGraphTask != nil {
182
190
return . generatingBuildGraph
183
191
}
184
- let preparationTasks = inProgressPreparationTasks. mapValues { queuedTask in
185
- return queuedTask . isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
192
+ let preparationTasks = inProgressPreparationTasks. mapValues { inProgressTask in
193
+ return inProgressTask . task . isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
186
194
}
187
195
let indexTasks = inProgressIndexTasks. mapValues { status in
188
196
switch status {
@@ -385,38 +393,18 @@ public final actor SemanticIndexManager {
385
393
let id = UUID ( )
386
394
let task = Task ( priority: priority) {
387
395
await withLoggingScope ( " preparation " ) {
388
- defer {
389
- if inProgressPrepareForEditorTask? . id == id {
390
- inProgressPrepareForEditorTask = nil
391
- self . indexProgressStatusDidChange ( )
392
- }
393
- }
394
- // Should be kept in sync with `prepareFileForEditorFunctionality`
395
- guard let target = await buildSystemManager. canonicalConfiguredTarget ( for: uri) else {
396
- return
397
- }
398
- if Task . isCancelled {
399
- return
400
- }
401
- if await preparationUpToDateTracker. isUpToDate ( target) {
402
- // If the target is up-to-date, there is nothing to prepare.
403
- return
404
- }
396
+ await prepareFileForEditorFunctionality ( uri)
405
397
if inProgressPrepareForEditorTask? . id == id {
406
- if inProgressPrepareForEditorTask? . state != . determiningCanonicalConfiguredTarget {
407
- logger. fault ( " inProgressPrepareForEditorTask is in unexpected state " )
408
- }
409
- inProgressPrepareForEditorTask? . state = . preparingTarget
398
+ inProgressPrepareForEditorTask = nil
399
+ self . indexProgressStatusDidChange ( )
410
400
}
411
- await self . prepare ( targets: [ target] , priority: nil )
412
401
}
413
402
}
414
403
inProgressPrepareForEditorTask? . task. cancel ( )
415
404
inProgressPrepareForEditorTask = InProgressPrepareForEditorTask (
416
405
id: id,
417
406
document: uri,
418
- task: task,
419
- state: . determiningCanonicalConfiguredTarget
407
+ task: task
420
408
)
421
409
self . indexProgressStatusDidChange ( )
422
410
}
@@ -426,20 +414,22 @@ public final actor SemanticIndexManager {
426
414
///
427
415
/// If file's target is known to be up-to-date, this returns almost immediately.
428
416
public func prepareFileForEditorFunctionality( _ uri: DocumentURI ) async {
429
- // Should be kept in sync with `schedulePreparationForEditorFunctionality`.
430
417
guard let target = await buildSystemManager. canonicalConfiguredTarget ( for: uri) else {
431
418
return
432
419
}
433
420
if Task . isCancelled {
434
421
return
435
422
}
436
- await self . prepare ( targets: [ target] , priority: nil )
423
+ if await preparationUpToDateTracker. isUpToDate ( target) {
424
+ // If the target is up-to-date, there is nothing to prepare.
425
+ return
426
+ }
427
+ await self . prepare ( targets: [ target] , purpose: . forEditorFunctionality, priority: nil )
437
428
}
438
429
439
430
// MARK: - Helper functions
440
431
441
- /// Prepare the given targets for indexing.
442
- private func prepare( targets: [ ConfiguredTarget ] , priority: TaskPriority ? ) async {
432
+ private func prepare( targets: [ ConfiguredTarget ] , purpose: TargetPreparationPurpose , priority: TaskPriority ? ) async {
443
433
// Perform a quick initial check whether the target is up-to-date, in which case we don't need to schedule a
444
434
// preparation operation at all.
445
435
// We will check the up-to-date status again in `PreparationTaskDescription.execute`. This ensures that if we
@@ -471,14 +461,25 @@ public final actor SemanticIndexManager {
471
461
return
472
462
}
473
463
for target in targetsToPrepare {
474
- if self . inProgressPreparationTasks [ target] == OpaqueQueuedIndexTask ( task) {
464
+ if self . inProgressPreparationTasks [ target] ? . task == OpaqueQueuedIndexTask ( task) {
475
465
self . inProgressPreparationTasks [ target] = nil
476
466
}
477
467
}
478
468
self . indexProgressStatusDidChange ( )
479
469
}
480
470
for target in targetsToPrepare {
481
- inProgressPreparationTasks [ target] = OpaqueQueuedIndexTask ( preparationTask)
471
+ // If we are preparing the same target for indexing and editor functionality, pick editor functionality as the
472
+ // purpose because it is more significant.
473
+ let mergedPurpose =
474
+ if let existingPurpose = inProgressPreparationTasks [ target] ? . purpose {
475
+ max ( existingPurpose, purpose)
476
+ } else {
477
+ purpose
478
+ }
479
+ inProgressPreparationTasks [ target] = InProgressPreparationTask (
480
+ task: OpaqueQueuedIndexTask ( preparationTask) ,
481
+ purpose: mergedPurpose
482
+ )
482
483
}
483
484
await withTaskCancellationHandler {
484
485
return await preparationTask. waitToFinish ( )
@@ -603,7 +604,7 @@ public final actor SemanticIndexManager {
603
604
let preparationTaskID = UUID ( )
604
605
let indexTask = Task ( priority: priority) {
605
606
// First prepare the targets.
606
- await prepare ( targets: targetsBatch, priority: priority)
607
+ await prepare ( targets: targetsBatch, purpose : . forIndexing , priority: priority)
607
608
608
609
// And after preparation is done, index the files in the targets.
609
610
await withTaskGroup ( of: Void . self) { taskGroup in
0 commit comments