@@ -255,20 +255,6 @@ package final actor SemanticIndexManager {
255
255
self . indexProgressStatusDidChange = indexProgressStatusDidChange
256
256
}
257
257
258
- /// Schedules a task to index `files`. Files that are known to be up-to-date based on `indexStatus` will
259
- /// not be re-indexed. The method will re-index files even if they have a unit with a timestamp that matches the
260
- /// source file's mtime. This allows re-indexing eg. after compiler arguments or dependencies have changed.
261
- ///
262
- /// Returns immediately after scheduling that task.
263
- ///
264
- /// Indexing is being performed with a low priority.
265
- private func scheduleBackgroundIndex(
266
- files: some Collection < DocumentURI > & Sendable ,
267
- indexFilesWithUpToDateUnit: Bool
268
- ) async {
269
- _ = await self . scheduleIndexing ( of: files, indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit, priority: . low)
270
- }
271
-
272
258
/// Regenerate the build graph (also resolving package dependencies) and then index all the source files known to the
273
259
/// build system that don't currently have a unit with a timestamp that matches the mtime of the file.
274
260
///
@@ -295,7 +281,7 @@ package final actor SemanticIndexManager {
295
281
await hooks. buildGraphGenerationDidFinish ? ( )
296
282
// TODO: Ideally this would be a type like any Collection<DocumentURI> & Sendable but that doesn't work due to
297
283
// https://github.com/swiftlang/swift/issues/75602
298
- var filesToIndex : [ DocumentURI ] =
284
+ let filesToIndex : [ DocumentURI ] =
299
285
if let filesToIndex {
300
286
filesToIndex
301
287
} else {
@@ -304,21 +290,11 @@ package final actor SemanticIndexManager {
304
290
. sorted { $0. stringValue < $1. stringValue }
305
291
} ?? [ ]
306
292
}
307
- if !indexFilesWithUpToDateUnit {
308
- let index = index. checked ( for: . modifiedFiles)
309
- filesToIndex = filesToIndex. filter {
310
- if index. hasUpToDateUnit ( for: $0) {
311
- return false
312
- }
313
- if case . waitingForPreparation = inProgressIndexTasks [ $0] {
314
- // We haven't started preparing the file yet. Scheduling a new index operation for it won't produce any
315
- // more recent results.
316
- return false
317
- }
318
- return true
319
- }
320
- }
321
- await scheduleBackgroundIndex ( files: filesToIndex, indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit)
293
+ _ = await self . scheduleIndexing (
294
+ of: filesToIndex,
295
+ indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit,
296
+ priority: . low
297
+ )
322
298
scheduleIndexingTasks [ taskId] = nil
323
299
}
324
300
}
@@ -421,33 +397,60 @@ package final actor SemanticIndexManager {
421
397
///
422
398
/// If `files` contains a header file, this will return a `FileToIndex` that re-indexes a main file which includes the
423
399
/// header file to update the header file's index.
400
+ ///
401
+ /// The returned results will not include files that are known to be up-to-date based on `indexStoreUpToDateTracker`
402
+ /// or the unit timestamp will not be re-indexed unless `indexFilesWithUpToDateUnit` is `true`.
424
403
private func filesToIndex(
425
- toCover files: some Collection < DocumentURI > & Sendable
404
+ toCover files: some Collection < DocumentURI > & Sendable ,
405
+ indexFilesWithUpToDateUnits: Bool
426
406
) async -> [ FileToIndex ] {
427
407
let sourceFiles = await orLog ( " Getting source files in project " ) {
428
408
Set ( try await buildSystemManager. sourceFiles ( includeNonBuildableFiles: false ) . keys)
429
409
}
430
410
guard let sourceFiles else {
431
411
return [ ]
432
412
}
433
- let filesToReIndex = await files. asyncCompactMap { ( uri) -> FileToIndex ? in
434
- if sourceFiles. contains ( uri) {
435
- // If this is a source file, just index it.
436
- return . indexableFile( uri)
437
- }
438
- // Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
439
- // index.
440
- // Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
441
- // if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
442
- // realize that its timestamp is later than the modification date of the header and we don't need to re-index.
443
- let mainFile = index. checked ( for: . deletedFiles)
444
- . mainFilesContainingFile ( uri: uri, crossLanguage: false )
445
- . sorted ( by: { $0. stringValue < $1. stringValue } ) . first
446
- guard let mainFile else {
447
- return nil
413
+ let deletedFilesIndex = index. checked ( for: . deletedFiles)
414
+ let modifiedFilesIndex = index. checked ( for: . modifiedFiles)
415
+ let filesToReIndex =
416
+ await files
417
+ . asyncFilter {
418
+ // First, check if we know that the file is up-to-date, in which case we don't need to hit the index or file
419
+ // system at all
420
+ if !indexFilesWithUpToDateUnits, await indexStoreUpToDateTracker. isUpToDate ( $0) {
421
+ return false
422
+ }
423
+ if case . waitingForPreparation = inProgressIndexTasks [ $0] {
424
+ // We haven't started preparing the file yet. Scheduling a new index operation for it won't produce any
425
+ // more recent results.
426
+ return false
427
+ }
428
+ return true
429
+ } . compactMap { ( uri) -> FileToIndex ? in
430
+ if sourceFiles. contains ( uri) {
431
+ if !indexFilesWithUpToDateUnits, modifiedFilesIndex. hasUpToDateUnit ( for: uri) {
432
+ return nil
433
+ }
434
+ // If this is a source file, just index it.
435
+ return . indexableFile( uri)
436
+ }
437
+ // Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
438
+ // index.
439
+ // Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
440
+ // if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
441
+ // realize that its timestamp is later than the modification date of the header and we don't need to re-index.
442
+ let mainFile =
443
+ deletedFilesIndex
444
+ . mainFilesContainingFile ( uri: uri, crossLanguage: false )
445
+ . sorted ( by: { $0. stringValue < $1. stringValue } ) . first
446
+ guard let mainFile else {
447
+ return nil
448
+ }
449
+ if !indexFilesWithUpToDateUnits, modifiedFilesIndex. hasUpToDateUnit ( for: uri, mainFile: mainFile) {
450
+ return nil
451
+ }
452
+ return . headerFile( header: uri, mainFile: mainFile)
448
453
}
449
- return . headerFile( header: uri, mainFile: mainFile)
450
- }
451
454
return filesToReIndex
452
455
}
453
456
@@ -652,7 +655,9 @@ package final actor SemanticIndexManager {
652
655
return await updateIndexTask. waitToFinishPropagatingCancellation ( )
653
656
}
654
657
655
- /// Index the given set of files at the given priority, preparing their targets beforehand, if needed.
658
+ /// Index the given set of files at the given priority, preparing their targets beforehand, if needed. Files that are
659
+ /// known to be up-to-date based on `indexStoreUpToDateTracker` or the unit timestamp will not be re-indexed unless
660
+ /// `indexFilesWithUpToDateUnit` is `true`.
656
661
///
657
662
/// The returned task finishes when all files are indexed.
658
663
private func scheduleIndexing(
@@ -665,14 +670,15 @@ package final actor SemanticIndexManager {
665
670
// We will check the up-to-date status again in `IndexTaskDescription.execute`. This ensures that if we schedule
666
671
// schedule two indexing jobs for the same file in quick succession, only the first one actually updates the index
667
672
// store and the second one will be a no-op once it runs.
668
- let outOfDateFiles = await filesToIndex ( toCover: files) . asyncFilter {
669
- if await indexStoreUpToDateTracker. isUpToDate ( $0. sourceFile) {
670
- return false
671
- }
672
- return true
673
+ let outOfDateFiles = await filesToIndex ( toCover: files, indexFilesWithUpToDateUnits: indexFilesWithUpToDateUnit)
674
+ // sort files to get deterministic indexing order
675
+ . sorted ( by: { $0. sourceFile. stringValue < $1. sourceFile. stringValue } )
676
+
677
+ if outOfDateFiles. isEmpty {
678
+ // Early exit if there are no files to index.
679
+ return Task { }
673
680
}
674
- // sort files to get deterministic indexing order
675
- . sorted ( by: { $0. sourceFile. stringValue < $1. sourceFile. stringValue } )
681
+
676
682
logger. debug ( " Scheduling indexing of \( outOfDateFiles. map ( \. sourceFile. stringValue) . joined ( separator: " , " ) ) " )
677
683
678
684
// Sort the targets in topological order so that low-level targets get built before high-level targets, allowing us
0 commit comments