@@ -64,6 +64,38 @@ public enum IndexTaskStatus: Comparable {
6464 case executing
6565}
6666
67+ /// The current index status that should be displayed to the editor.
68+ ///
69+ /// In reality, these status are not exclusive. Eg. the index might be preparing one target for editor functionality,
70+ /// re-generating the build graph and indexing files at the same time. To avoid showing too many concurrent status
71+ /// messages to the user, we only show the highest priority task.
72+ public enum IndexProgressStatus {
73+ case preparingFileForEditorFunctionality
74+ case generatingBuildGraph
75+ case indexing( preparationTasks: [ ConfiguredTarget : IndexTaskStatus ] , indexTasks: [ DocumentURI : IndexTaskStatus ] )
76+ case upToDate
77+
78+ public func merging( with other: IndexProgressStatus ) -> IndexProgressStatus {
79+ switch ( self , other) {
80+ case ( _, . preparingFileForEditorFunctionality) , ( . preparingFileForEditorFunctionality, _) :
81+ return . preparingFileForEditorFunctionality
82+ case ( _, . generatingBuildGraph) , ( . generatingBuildGraph, _) :
83+ return . generatingBuildGraph
84+ case (
85+ . indexing( let selfPreparationTasks, let selfIndexTasks) ,
86+ . indexing( let otherPreparationTasks, let otherIndexTasks)
87+ ) :
88+ return . indexing(
89+ preparationTasks: selfPreparationTasks. merging ( otherPreparationTasks) { max ( $0, $1) } ,
90+ indexTasks: selfIndexTasks. merging ( otherIndexTasks) { max ( $0, $1) }
91+ )
92+ case ( . indexing, . upToDate) : return self
93+ case ( . upToDate, . indexing) : return other
94+ case ( . upToDate, . upToDate) : return . upToDate
95+ }
96+ }
97+ }
98+
6799/// Schedules index tasks and keeps track of the index status of files.
68100public final actor SemanticIndexManager {
69101 /// 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
@@ -122,24 +154,22 @@ public final actor SemanticIndexManager {
122154 /// The parameter is the number of files that were scheduled to be indexed.
123155 private let indexTasksWereScheduled : @Sendable ( _ numberOfFileScheduled: Int ) -> Void
124156
125- /// Callback that is called when the progress status of an update indexstore or preparation task finishes.
126- ///
127- /// An object observing this property probably wants to check `inProgressIndexTasks` when the callback is called to
128- /// get the current list of in-progress index tasks.
129- ///
130- /// The number of `indexStatusDidChange` calls does not have to relate to the number of `indexTasksWereScheduled` calls.
131- private let indexStatusDidChange : @Sendable ( ) -> Void
157+ /// Callback that is called when `progressStatus` might have changed.
158+ private let indexProgressStatusDidChange : @Sendable ( ) -> Void
132159
133160 // MARK: - Public API
134161
135162 /// A summary of the tasks that this `SemanticIndexManager` has currently scheduled or is currently indexing.
136- public var inProgressTasks :
137- (
138- isGeneratingBuildGraph: Bool ,
139- indexTasks: [ DocumentURI : IndexTaskStatus ] ,
140- preparationTasks: [ ConfiguredTarget : IndexTaskStatus ]
141- )
142- {
163+ public var progressStatus : IndexProgressStatus {
164+ if inProgressPrepareForEditorTask != nil {
165+ return . preparingFileForEditorFunctionality
166+ }
167+ if generateBuildGraphTask != nil {
168+ return . generatingBuildGraph
169+ }
170+ let preparationTasks = inProgressPreparationTasks. mapValues { queuedTask in
171+ return queuedTask. isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
172+ }
143173 let indexTasks = inProgressIndexTasks. mapValues { status in
144174 switch status {
145175 case . waitingForPreparation:
@@ -148,10 +178,10 @@ public final actor SemanticIndexManager {
148178 return updateIndexStoreTask. isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
149179 }
150180 }
151- let preparationTasks = inProgressPreparationTasks . mapValues { queuedTask in
152- return queuedTask . isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
181+ if preparationTasks. isEmpty && indexTasks . isEmpty {
182+ return . upToDate
153183 }
154- return ( generateBuildGraphTask != nil , indexTasks, preparationTasks )
184+ return . indexing ( preparationTasks : preparationTasks , indexTasks: indexTasks )
155185 }
156186
157187 public init (
@@ -161,15 +191,15 @@ public final actor SemanticIndexManager {
161191 indexTaskScheduler: TaskScheduler < AnyIndexTaskDescription > ,
162192 indexProcessDidProduceResult: @escaping @Sendable ( IndexProcessResult ) -> Void ,
163193 indexTasksWereScheduled: @escaping @Sendable ( Int ) -> Void ,
164- indexStatusDidChange : @escaping @Sendable ( ) -> Void
194+ indexProgressStatusDidChange : @escaping @Sendable ( ) -> Void
165195 ) {
166196 self . index = index
167197 self . buildSystemManager = buildSystemManager
168198 self . testHooks = testHooks
169199 self . indexTaskScheduler = indexTaskScheduler
170200 self . indexProcessDidProduceResult = indexProcessDidProduceResult
171201 self . indexTasksWereScheduled = indexTasksWereScheduled
172- self . indexStatusDidChange = indexStatusDidChange
202+ self . indexProgressStatusDidChange = indexProgressStatusDidChange
173203 }
174204
175205 /// Schedules a task to index `files`. Files that are known to be up-to-date based on `indexStatus` will
@@ -222,7 +252,7 @@ public final actor SemanticIndexManager {
222252 generateBuildGraphTask = nil
223253 }
224254 }
225- indexStatusDidChange ( )
255+ indexProgressStatusDidChange ( )
226256 }
227257
228258 /// Wait for all in-progress index tasks to finish.
@@ -350,11 +380,13 @@ public final actor SemanticIndexManager {
350380 await self . prepare ( targets: [ target] , priority: priority)
351381 if inProgressPrepareForEditorTask? . id == id {
352382 inProgressPrepareForEditorTask = nil
383+ self . indexProgressStatusDidChange ( )
353384 }
354385 }
355386 }
356387 inProgressPrepareForEditorTask? . task. cancel ( )
357388 inProgressPrepareForEditorTask = ( id, uri, task)
389+ self . indexProgressStatusDidChange ( )
358390 }
359391
360392 // MARK: - Helper functions
@@ -388,15 +420,15 @@ public final actor SemanticIndexManager {
388420 }
389421 let preparationTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
390422 guard case . finished = newState else {
391- self . indexStatusDidChange ( )
423+ self . indexProgressStatusDidChange ( )
392424 return
393425 }
394426 for target in targetsToPrepare {
395427 if self . inProgressPreparationTasks [ target] == OpaqueQueuedIndexTask ( task) {
396428 self . inProgressPreparationTasks [ target] = nil
397429 }
398430 }
399- self . indexStatusDidChange ( )
431+ self . indexProgressStatusDidChange ( )
400432 }
401433 for target in targetsToPrepare {
402434 inProgressPreparationTasks [ target] = OpaqueQueuedIndexTask ( preparationTask)
@@ -432,7 +464,7 @@ public final actor SemanticIndexManager {
432464 )
433465 let updateIndexTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
434466 guard case . finished = newState else {
435- self . indexStatusDidChange ( )
467+ self . indexProgressStatusDidChange ( )
436468 return
437469 }
438470 for fileAndTarget in filesAndTargets {
@@ -442,7 +474,7 @@ public final actor SemanticIndexManager {
442474 self . inProgressIndexTasks [ fileAndTarget. file. sourceFile] = nil
443475 }
444476 }
445- self . indexStatusDidChange ( )
477+ self . indexProgressStatusDidChange ( )
446478 }
447479 for fileAndTarget in filesAndTargets {
448480 if case . waitingForPreparation( preparationTaskID, let indexTask) = inProgressIndexTasks [
0 commit comments