@@ -115,7 +115,7 @@ public final actor SemanticIndexManager {
115
115
/// Returns immediately after scheduling that task.
116
116
///
117
117
/// Indexing is being performed with a low priority.
118
- public func scheduleBackgroundIndex( files: some Collection < DocumentURI > ) async {
118
+ private func scheduleBackgroundIndex( files: some Collection < DocumentURI > ) async {
119
119
await self . index ( files: files, priority: . low)
120
120
}
121
121
@@ -127,7 +127,7 @@ public final actor SemanticIndexManager {
127
127
generateBuildGraphTask = Task ( priority: . low) {
128
128
await orLog ( " Generating build graph " ) { try await self . buildSystemManager. generateBuildGraph ( ) }
129
129
let index = index. checked ( for: . modifiedFiles)
130
- let filesToIndex = await self . buildSystemManager. sourceFiles ( ) . map ( \. uri)
130
+ let filesToIndex = await self . buildSystemManager. sourceFiles ( ) . lazy . map ( \. uri)
131
131
. filter { uri in
132
132
guard let url = uri. fileURL else {
133
133
// The URI is not a file, so there's nothing we can index.
@@ -185,31 +185,41 @@ public final actor SemanticIndexManager {
185
185
}
186
186
187
187
public func filesDidChange( _ events: [ FileEvent ] ) async {
188
- let filesToReIndex = await filesToReIndex ( changedFiles: events. map ( \. uri) )
189
-
188
+ // We only re-index the files that were changed and don't re-index any of their dependencies. See the
189
+ // `Documentation/Files_To_Reindex.md` file.
190
+ let changedFiles = events. map ( \. uri)
190
191
// Reset the index status for these files so they get re-indexed by `index(files:priority:)`
191
- for uri in filesToReIndex {
192
+ for uri in changedFiles {
192
193
indexStatus [ uri] = nil
193
194
}
194
- await scheduleBackgroundIndex ( files: filesToReIndex )
195
+ await scheduleBackgroundIndex ( files: changedFiles )
195
196
}
196
197
197
- /// Returns the files that should be re- indexed if the given files have been modified .
198
+ /// Returns the files that should be indexed to get up-to-date index information for the given files .
198
199
///
199
- /// - SeeAlso: The `Documentation/Files_To_Reindex.md` file.
200
- private func filesToReIndex( changedFiles: [ DocumentURI ] ) async -> Set < DocumentURI > {
200
+ /// If `files` contains a header file, this will return a `FileToIndex` that re-indexes a main file which includes the
201
+ /// header file to update the header file's index.
202
+ private func filesToIndex( toCover files: some Collection < DocumentURI > ) async -> [ FileToIndex ] {
201
203
let sourceFiles = Set ( await buildSystemManager. sourceFiles ( ) . map ( \. uri) )
202
- let filesToReIndex = await changedFiles . asyncMap { ( uri) -> [ DocumentURI ] in
204
+ let filesToReIndex = await files . asyncCompactMap { ( uri) -> FileToIndex ? in
203
205
if sourceFiles. contains ( uri) {
204
- // If this is a source file, re-index it
205
- return [ uri]
206
+ // If this is a source file, just index it.
207
+ return FileToIndex ( uri: uri, mainFile: nil )
208
+ }
209
+ // Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
210
+ // index.
211
+ // Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
212
+ // if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
213
+ // realize that its timestamp is later than the modification date of the header and we don't need to re-index.
214
+ let mainFile = index. checked ( for: . deletedFiles)
215
+ . mainFilesContainingFile ( uri: uri, crossLanguage: false )
216
+ . sorted ( by: { $0. stringValue < $1. stringValue } ) . first
217
+ guard let mainFile else {
218
+ return nil
206
219
}
207
- // Otherwise, see if it is a header file. If so, re-index all the files that import it.
208
- // We don't want to re-index `.swift` files that depend on a header file, similar to how we don't re-index a Swift
209
- // module if one of its dependent modules has changed.
210
- return index. checked ( for: . deletedFiles) . mainFilesContainingFile ( uri: uri, crossLanguage: false )
211
- } . flatMap { $0 }
212
- return Set ( filesToReIndex)
220
+ return FileToIndex ( uri: uri, mainFile: mainFile)
221
+ }
222
+ return filesToReIndex
213
223
}
214
224
215
225
// MARK: - Helper functions
@@ -227,46 +237,47 @@ public final actor SemanticIndexManager {
227
237
}
228
238
229
239
/// Update the index store for the given files, assuming that their targets have already been prepared.
230
- private func updateIndexStore( for files: [ DocumentURI ] , priority: TaskPriority ? ) async {
240
+ private func updateIndexStore( for files: [ FileToIndex ] , priority: TaskPriority ? ) async {
231
241
let taskDescription = AnyIndexTaskDescription (
232
242
UpdateIndexStoreTaskDescription (
233
243
filesToIndex: Set ( files) ,
234
- buildSystemManager: self . buildSystemManager
244
+ buildSystemManager: self . buildSystemManager,
245
+ index: index
235
246
)
236
247
)
237
248
let updateIndexStoreTask = await self . indexTaskScheduler. schedule ( priority: priority, taskDescription) { newState in
238
249
switch newState {
239
250
case . executing:
240
251
for file in files {
241
- if case . scheduled( let task) = self . indexStatus [ file] {
242
- self . indexStatus [ file] = . executing( task)
252
+ if case . scheduled( let task) = self . indexStatus [ file. uri ] {
253
+ self . indexStatus [ file. uri ] = . executing( task)
243
254
} else {
244
255
logger. fault (
245
256
"""
246
- Index status of \( file) is in an unexpected state \
247
- ' \( self . indexStatus [ file] ? . description ?? " <nil> " , privacy: . public) ' when update index store task \
257
+ Index status of \( file. uri ) is in an unexpected state \
258
+ ' \( self . indexStatus [ file. uri ] ? . description ?? " <nil> " , privacy: . public) ' when update index store task \
248
259
started executing
249
260
"""
250
261
)
251
262
}
252
263
}
253
264
case . cancelledToBeRescheduled:
254
265
for file in files {
255
- if case . executing( let task) = self . indexStatus [ file] {
256
- self . indexStatus [ file] = . scheduled( task)
266
+ if case . executing( let task) = self . indexStatus [ file. uri ] {
267
+ self . indexStatus [ file. uri ] = . scheduled( task)
257
268
} else {
258
269
logger. fault (
259
270
"""
260
- Index status of \( file) is in an unexpected state \
261
- ' \( self . indexStatus [ file] ? . description ?? " <nil> " , privacy: . public) ' when update index store task \
271
+ Index status of \( file. uri ) is in an unexpected state \
272
+ ' \( self . indexStatus [ file. uri ] ? . description ?? " <nil> " , privacy: . public) ' when update index store task \
262
273
is cancelled to be rescheduled.
263
274
"""
264
275
)
265
276
}
266
277
}
267
278
case . finished:
268
279
for file in files {
269
- self . indexStatus [ file] = . upToDate
280
+ self . indexStatus [ file. uri ] = . upToDate
270
281
}
271
282
self . indexTaskDidFinish ( )
272
283
}
@@ -279,20 +290,20 @@ public final actor SemanticIndexManager {
279
290
/// The returned task finishes when all files are indexed.
280
291
@discardableResult
281
292
private func index( files: some Collection < DocumentURI > , priority: TaskPriority ? ) async -> Task < Void , Never > {
282
- let outOfDateFiles = files. filter {
283
- if case . upToDate = indexStatus [ $0] {
293
+ let outOfDateFiles = await filesToIndex ( toCover : files) . filter {
294
+ if case . upToDate = indexStatus [ $0. uri ] {
284
295
return false
285
296
}
286
297
return true
287
298
}
288
- . sorted ( by: { $0. stringValue < $1. stringValue } ) // sort files to get deterministic indexing order
299
+ . sorted ( by: { $0. uri . stringValue < $1. uri . stringValue } ) // sort files to get deterministic indexing order
289
300
290
301
// Sort the targets in topological order so that low-level targets get built before high-level targets, allowing us
291
302
// to index the low-level targets ASAP.
292
- var filesByTarget : [ ConfiguredTarget : [ DocumentURI ] ] = [ : ]
303
+ var filesByTarget : [ ConfiguredTarget : [ FileToIndex ] ] = [ : ]
293
304
for file in outOfDateFiles {
294
- guard let target = await buildSystemManager. canonicalConfiguredTarget ( for: file) else {
295
- logger. error ( " Not indexing \( file. forLogging) because the target could not be determined " )
305
+ guard let target = await buildSystemManager. canonicalConfiguredTarget ( for: file. uri ) else {
306
+ logger. error ( " Not indexing \( file. uri . forLogging) because the target could not be determined " )
296
307
continue
297
308
}
298
309
filesByTarget [ target, default: [ ] ] . append ( file)
@@ -349,7 +360,7 @@ public final actor SemanticIndexManager {
349
360
// setting it to `.scheduled` because we don't have an `await` call between the creation of `indexTask` and
350
361
// this loop, so we still have exclusive access to the `SemanticIndexManager` actor and hence `updateIndexStore`
351
362
// can't execute until we have set all index statuses to `.scheduled`.
352
- indexStatus [ file] = . scheduled( indexTask)
363
+ indexStatus [ file. uri ] = . scheduled( indexTask)
353
364
}
354
365
indexTasksWereScheduled ( filesToIndex. count)
355
366
}
0 commit comments