@@ -64,6 +64,38 @@ public enum IndexTaskStatus: Comparable {
64
64
case executing
65
65
}
66
66
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
+
67
99
/// Schedules index tasks and keeps track of the index status of files.
68
100
public final actor SemanticIndexManager {
69
101
/// 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 {
122
154
/// The parameter is the number of files that were scheduled to be indexed.
123
155
private let indexTasksWereScheduled : @Sendable ( _ numberOfFileScheduled: Int ) -> Void
124
156
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
132
159
133
160
// MARK: - Public API
134
161
135
162
/// 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
+ }
143
173
let indexTasks = inProgressIndexTasks. mapValues { status in
144
174
switch status {
145
175
case . waitingForPreparation:
@@ -148,10 +178,10 @@ public final actor SemanticIndexManager {
148
178
return updateIndexStoreTask. isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
149
179
}
150
180
}
151
- let preparationTasks = inProgressPreparationTasks . mapValues { queuedTask in
152
- return queuedTask . isExecuting ? IndexTaskStatus . executing : IndexTaskStatus . scheduled
181
+ if preparationTasks. isEmpty && indexTasks . isEmpty {
182
+ return . upToDate
153
183
}
154
- return ( generateBuildGraphTask != nil , indexTasks, preparationTasks )
184
+ return . indexing ( preparationTasks : preparationTasks , indexTasks: indexTasks )
155
185
}
156
186
157
187
public init (
@@ -161,15 +191,15 @@ public final actor SemanticIndexManager {
161
191
indexTaskScheduler: TaskScheduler < AnyIndexTaskDescription > ,
162
192
indexProcessDidProduceResult: @escaping @Sendable ( IndexProcessResult ) -> Void ,
163
193
indexTasksWereScheduled: @escaping @Sendable ( Int ) -> Void ,
164
- indexStatusDidChange : @escaping @Sendable ( ) -> Void
194
+ indexProgressStatusDidChange : @escaping @Sendable ( ) -> Void
165
195
) {
166
196
self . index = index
167
197
self . buildSystemManager = buildSystemManager
168
198
self . testHooks = testHooks
169
199
self . indexTaskScheduler = indexTaskScheduler
170
200
self . indexProcessDidProduceResult = indexProcessDidProduceResult
171
201
self . indexTasksWereScheduled = indexTasksWereScheduled
172
- self . indexStatusDidChange = indexStatusDidChange
202
+ self . indexProgressStatusDidChange = indexProgressStatusDidChange
173
203
}
174
204
175
205
/// 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 {
222
252
generateBuildGraphTask = nil
223
253
}
224
254
}
225
- indexStatusDidChange ( )
255
+ indexProgressStatusDidChange ( )
226
256
}
227
257
228
258
/// Wait for all in-progress index tasks to finish.
@@ -350,11 +380,13 @@ public final actor SemanticIndexManager {
350
380
await self . prepare ( targets: [ target] , priority: priority)
351
381
if inProgressPrepareForEditorTask? . id == id {
352
382
inProgressPrepareForEditorTask = nil
383
+ self . indexProgressStatusDidChange ( )
353
384
}
354
385
}
355
386
}
356
387
inProgressPrepareForEditorTask? . task. cancel ( )
357
388
inProgressPrepareForEditorTask = ( id, uri, task)
389
+ self . indexProgressStatusDidChange ( )
358
390
}
359
391
360
392
// MARK: - Helper functions
@@ -388,15 +420,15 @@ public final actor SemanticIndexManager {
388
420
}
389
421
let preparationTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
390
422
guard case . finished = newState else {
391
- self . indexStatusDidChange ( )
423
+ self . indexProgressStatusDidChange ( )
392
424
return
393
425
}
394
426
for target in targetsToPrepare {
395
427
if self . inProgressPreparationTasks [ target] == OpaqueQueuedIndexTask ( task) {
396
428
self . inProgressPreparationTasks [ target] = nil
397
429
}
398
430
}
399
- self . indexStatusDidChange ( )
431
+ self . indexProgressStatusDidChange ( )
400
432
}
401
433
for target in targetsToPrepare {
402
434
inProgressPreparationTasks [ target] = OpaqueQueuedIndexTask ( preparationTask)
@@ -432,7 +464,7 @@ public final actor SemanticIndexManager {
432
464
)
433
465
let updateIndexTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
434
466
guard case . finished = newState else {
435
- self . indexStatusDidChange ( )
467
+ self . indexProgressStatusDidChange ( )
436
468
return
437
469
}
438
470
for fileAndTarget in filesAndTargets {
@@ -442,7 +474,7 @@ public final actor SemanticIndexManager {
442
474
self . inProgressIndexTasks [ fileAndTarget. file. sourceFile] = nil
443
475
}
444
476
}
445
- self . indexStatusDidChange ( )
477
+ self . indexProgressStatusDidChange ( )
446
478
}
447
479
for fileAndTarget in filesAndTargets {
448
480
if case . waitingForPreparation( preparationTaskID, let indexTask) = inProgressIndexTasks [
0 commit comments