@@ -255,20 +255,6 @@ package final actor SemanticIndexManager {
255255 self . indexProgressStatusDidChange = indexProgressStatusDidChange
256256 }
257257
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-
272258 /// Regenerate the build graph (also resolving package dependencies) and then index all the source files known to the
273259 /// build system that don't currently have a unit with a timestamp that matches the mtime of the file.
274260 ///
@@ -295,7 +281,7 @@ package final actor SemanticIndexManager {
295281 await hooks. buildGraphGenerationDidFinish ? ( )
296282 // TODO: Ideally this would be a type like any Collection<DocumentURI> & Sendable but that doesn't work due to
297283 // https://github.com/swiftlang/swift/issues/75602
298- var filesToIndex : [ DocumentURI ] =
284+ let filesToIndex : [ DocumentURI ] =
299285 if let filesToIndex {
300286 filesToIndex
301287 } else {
@@ -304,21 +290,11 @@ package final actor SemanticIndexManager {
304290 . sorted { $0. stringValue < $1. stringValue }
305291 } ?? [ ]
306292 }
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+ )
322298 scheduleIndexingTasks [ taskId] = nil
323299 }
324300 }
@@ -421,33 +397,60 @@ package final actor SemanticIndexManager {
421397 ///
422398 /// If `files` contains a header file, this will return a `FileToIndex` that re-indexes a main file which includes the
423399 /// 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`.
424403 private func filesToIndex(
425- toCover files: some Collection < DocumentURI > & Sendable
404+ toCover files: some Collection < DocumentURI > & Sendable ,
405+ indexFilesWithUpToDateUnits: Bool
426406 ) async -> [ FileToIndex ] {
427407 let sourceFiles = await orLog ( " Getting source files in project " ) {
428408 Set ( try await buildSystemManager. sourceFiles ( includeNonBuildableFiles: false ) . keys)
429409 }
430410 guard let sourceFiles else {
431411 return [ ]
432412 }
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)
448453 }
449- return . headerFile( header: uri, mainFile: mainFile)
450- }
451454 return filesToReIndex
452455 }
453456
@@ -652,7 +655,9 @@ package final actor SemanticIndexManager {
652655 return await updateIndexTask. waitToFinishPropagatingCancellation ( )
653656 }
654657
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`.
656661 ///
657662 /// The returned task finishes when all files are indexed.
658663 private func scheduleIndexing(
@@ -665,14 +670,15 @@ package final actor SemanticIndexManager {
665670 // We will check the up-to-date status again in `IndexTaskDescription.execute`. This ensures that if we schedule
666671 // schedule two indexing jobs for the same file in quick succession, only the first one actually updates the index
667672 // 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 { }
673680 }
674- // sort files to get deterministic indexing order
675- . sorted ( by: { $0. sourceFile. stringValue < $1. sourceFile. stringValue } )
681+
676682 logger. debug ( " Scheduling indexing of \( outOfDateFiles. map ( \. sourceFile. stringValue) . joined ( separator: " , " ) ) " )
677683
678684 // Sort the targets in topological order so that low-level targets get built before high-level targets, allowing us
0 commit comments