@@ -98,10 +98,6 @@ public enum IndexProgressStatus {
9898
9999/// See `SemanticIndexManager.inProgressPrepareForEditorTask`.
100100fileprivate struct InProgressPrepareForEditorTask {
101- fileprivate enum State {
102- case determiningCanonicalConfiguredTarget
103- case preparingTarget
104- }
105101 /// A unique ID that identifies the preparation task and is used to set
106102 /// `SemanticIndexManager.inProgressPrepareForEditorTask` to `nil` when the current in progress task finishes.
107103 let id : UUID
@@ -111,9 +107,21 @@ fileprivate struct InProgressPrepareForEditorTask {
111107
112108 /// The task that prepares the document. Should never be awaited and only be used to cancel the task.
113109 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+ }
114120
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
117125}
118126
119127/// Schedules index tasks and keeps track of the index status of files.
@@ -139,7 +147,7 @@ public final actor SemanticIndexManager {
139147 /// executing.
140148 ///
141149 /// After a preparation task finishes, it is removed from this dictionary.
142- private var inProgressPreparationTasks : [ ConfiguredTarget : OpaqueQueuedIndexTask ] = [ : ]
150+ private var inProgressPreparationTasks : [ ConfiguredTarget : InProgressPreparationTask ] = [ : ]
143151
144152 /// The files that are currently being index, either waiting for their target to be prepared, waiting for the index
145153 /// 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 {
175183
176184 /// A summary of the tasks that this `SemanticIndexManager` has currently scheduled or is currently indexing.
177185 public var progressStatus : IndexProgressStatus {
178- if let inProgressPrepareForEditorTask , inProgressPrepareForEditorTask . state == . preparingTarget {
186+ if inProgressPreparationTasks . values . contains ( where : { $0 . purpose == . forEditorFunctionality } ) {
179187 return . preparingFileForEditorFunctionality
180188 }
181189 if generateBuildGraphTask != nil {
182190 return . generatingBuildGraph
183191 }
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
186194 }
187195 let indexTasks = inProgressIndexTasks. mapValues { status in
188196 switch status {
@@ -385,38 +393,18 @@ public final actor SemanticIndexManager {
385393 let id = UUID ( )
386394 let task = Task ( priority: priority) {
387395 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)
405397 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 ( )
410400 }
411- await self . prepare ( targets: [ target] , priority: nil )
412401 }
413402 }
414403 inProgressPrepareForEditorTask? . task. cancel ( )
415404 inProgressPrepareForEditorTask = InProgressPrepareForEditorTask (
416405 id: id,
417406 document: uri,
418- task: task,
419- state: . determiningCanonicalConfiguredTarget
407+ task: task
420408 )
421409 self . indexProgressStatusDidChange ( )
422410 }
@@ -426,20 +414,22 @@ public final actor SemanticIndexManager {
426414 ///
427415 /// If file's target is known to be up-to-date, this returns almost immediately.
428416 public func prepareFileForEditorFunctionality( _ uri: DocumentURI ) async {
429- // Should be kept in sync with `schedulePreparationForEditorFunctionality`.
430417 guard let target = await buildSystemManager. canonicalConfiguredTarget ( for: uri) else {
431418 return
432419 }
433420 if Task . isCancelled {
434421 return
435422 }
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 )
437428 }
438429
439430 // MARK: - Helper functions
440431
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 {
443433 // Perform a quick initial check whether the target is up-to-date, in which case we don't need to schedule a
444434 // preparation operation at all.
445435 // 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 {
471461 return
472462 }
473463 for target in targetsToPrepare {
474- if self . inProgressPreparationTasks [ target] == OpaqueQueuedIndexTask ( task) {
464+ if self . inProgressPreparationTasks [ target] ? . task == OpaqueQueuedIndexTask ( task) {
475465 self . inProgressPreparationTasks [ target] = nil
476466 }
477467 }
478468 self . indexProgressStatusDidChange ( )
479469 }
480470 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+ )
482483 }
483484 await withTaskCancellationHandler {
484485 return await preparationTask. waitToFinish ( )
@@ -603,7 +604,7 @@ public final actor SemanticIndexManager {
603604 let preparationTaskID = UUID ( )
604605 let indexTask = Task ( priority: priority) {
605606 // First prepare the targets.
606- await prepare ( targets: targetsBatch, priority: priority)
607+ await prepare ( targets: targetsBatch, purpose : . forIndexing , priority: priority)
607608
608609 // And after preparation is done, index the files in the targets.
609610 await withTaskGroup ( of: Void . self) { taskGroup in
0 commit comments