Skip to content

Commit 4991e15

Browse files
committed
Re-index header files when they are modified
1 parent d10c868 commit 4991e15

File tree

5 files changed

+213
-107
lines changed

5 files changed

+213
-107
lines changed

Sources/BuildServerProtocol/SupportTypes/BuildTarget.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public struct BuildTarget: Codable, Hashable, Sendable {
7171
displayName: String? = nil,
7272
baseDirectory: URI? = nil,
7373
tags: [BuildTargetTag] = [],
74-
capabilities: BuildTargetCapabilities,
74+
capabilities: BuildTargetCapabilities = BuildTargetCapabilities(),
7575
languageIds: [Language],
7676
dependencies: [BuildTargetIdentifier],
7777
dataKind: BuildTargetDataKind? = nil,

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -811,24 +811,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
811811
return Array(targets)
812812
}
813813

814-
/// The `OutputPath` with which `uri` should be indexed in `target`. This may return:
815-
/// - `.path` if the build server supports output paths and produced a result
816-
/// - `.notSupported` if the build server does not support output paths. In this case we will assume that the index
817-
/// for `uri` is up-to-date if we have any up-to-date unit for it.
818-
/// - `nil` if the build server supports output paths but did not return an output path for `uri` in `target`.
819-
package func outputPath(for document: DocumentURI, in target: BuildTargetIdentifier) async throws -> OutputPath? {
820-
guard await initializationData?.outputPathsProvider ?? false else {
821-
// Early exit if the build server doesn't support output paths.
822-
return .notSupported
823-
}
824-
guard let sourceFileInfo = await sourceFileInfo(for: document),
825-
let outputPath = sourceFileInfo.targetsToOutputPaths[target]
826-
else {
827-
return nil
828-
}
829-
return outputPath
830-
}
831-
832814
/// Returns the `BuildTargetIdentifier` that should be used for semantic functionality of the given document.
833815
package func canonicalTarget(for document: DocumentURI) async -> BuildTargetIdentifier? {
834816
// Sort the targets to deterministically pick the same `BuildTargetIdentifier` every time.

Sources/SKTestSupport/CustomBuildServerTestProject.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,18 +173,31 @@ package extension CustomBuildServer {
173173
)
174174
}
175175

176-
func initializationResponseSupportingBackgroundIndexing(projectRoot: URL) throws -> InitializeBuildResponse {
176+
func initializationResponseSupportingBackgroundIndexing(
177+
projectRoot: URL,
178+
outputPathsProvider: Bool
179+
) throws -> InitializeBuildResponse {
177180
return initializationResponse(
178181
initializeData: SourceKitInitializeBuildResponseData(
179182
indexDatabasePath: try projectRoot.appendingPathComponent("index-db").filePath,
180183
indexStorePath: try projectRoot.appendingPathComponent("index-store").filePath,
181-
outputPathsProvider: true,
184+
outputPathsProvider: outputPathsProvider,
182185
prepareProvider: true,
183186
sourceKitOptionsProvider: true
184187
)
185188
)
186189
}
187190

191+
func sourceItem(for url: URL, outputPath: String) -> SourceItem {
192+
SourceItem(
193+
uri: URI(url),
194+
kind: .file,
195+
generated: false,
196+
dataKind: .sourceKit,
197+
data: SourceKitSourceItemData(outputPath: outputPath).encodeToLSPAny()
198+
)
199+
}
200+
188201
func dummyTargetSourcesResponse(_ files: some Sequence<DocumentURI>) -> BuildTargetSourcesResponse {
189202
return BuildTargetSourcesResponse(items: [
190203
SourcesItem(target: .dummy, sources: files.map { SourceItem(uri: $0, kind: .file, generated: false) })

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -468,68 +468,78 @@ package final actor SemanticIndexManager {
468468
}
469469
let modifiedFilesIndex = index.checked(for: .modifiedFiles)
470470

471-
let filesWithTargetAndOutput: [(file: DocumentURI, target: BuildTargetIdentifier, outputPath: OutputPath?)] =
472-
await files.asyncFlatMap { file in
473-
await buildSystemManager.sourceFileInfo(for: file)?.targetsToOutputPaths.map { (file, $0, $1) } ?? []
474-
}
475-
476-
let filesToReIndex =
477-
await filesWithTargetAndOutput
478-
.asyncCompactMap { (uri, target, outputPath) -> (FileIndexInfo, Date?)? in
471+
var filesToReIndex: [(FileIndexInfo, Date?)] = []
472+
for uri in files {
473+
var didFindTargetToIndex = false
474+
for (target, outputPath) in await buildSystemManager.sourceFileInfo(for: uri)?.targetsToOutputPaths ?? [:] {
479475
// 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
480476
// system at all
481477
if !indexFilesWithUpToDateUnits, await indexStoreUpToDateTracker.isUpToDate(uri, target) {
482-
return nil
478+
continue
483479
}
484480
if sourceFiles.contains(uri) {
485481
guard let outputPath else {
486482
logger.info("Not indexing \(uri.forLogging) because its output file could not be determined")
487-
return nil
483+
continue
488484
}
489485
if !indexFilesWithUpToDateUnits, modifiedFilesIndex.hasUpToDateUnit(for: uri, outputPath: outputPath) {
490-
return nil
486+
continue
491487
}
492488
// If this is a source file, just index it.
493-
return (
494-
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
495-
modifiedFilesIndex.modificationDate(of: uri)
489+
didFindTargetToIndex = true
490+
filesToReIndex.append(
491+
(
492+
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
493+
modifiedFilesIndex.modificationDate(of: uri)
494+
)
496495
)
497496
}
498-
// Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
499-
// index.
500-
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
501-
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
502-
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
503-
let mainFile = await buildSystemManager.mainFiles(containing: uri)
504-
.filter { sourceFiles.contains($0) }
505-
.sorted(by: { $0.stringValue < $1.stringValue }).first
506-
guard let mainFile else {
507-
logger.info("Not indexing \(uri) because its main file could not be inferred")
508-
return nil
509-
}
510-
let mainFileOutputPath = await orLog("Getting output path") {
511-
try await buildSystemManager.outputPath(for: mainFile, in: target)
512-
}
513-
guard let mainFileOutputPath else {
514-
logger.info(
515-
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
516-
)
517-
return nil
518-
}
519-
if !indexFilesWithUpToDateUnits,
520-
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: mainFileOutputPath)
521-
{
522-
return nil
523-
}
524-
return (
497+
}
498+
if didFindTargetToIndex {
499+
continue
500+
}
501+
// If we haven't found any ways to index the file, see if it is a header file. If so, index a main file that
502+
// that imports it to update header file's index.
503+
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
504+
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
505+
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
506+
let mainFile = await buildSystemManager.mainFiles(containing: uri)
507+
.filter { sourceFiles.contains($0) }
508+
.sorted(by: { $0.stringValue < $1.stringValue }).first
509+
guard let mainFile else {
510+
logger.info("Not indexing \(uri.forLogging) because its main file could not be inferred")
511+
continue
512+
}
513+
let targetAndOutputPath = (await buildSystemManager.sourceFileInfo(for: mainFile)?.targetsToOutputPaths ?? [:])
514+
.sorted(by: { $0.key.uri.stringValue < $1.key.uri.stringValue }).first
515+
guard let targetAndOutputPath else {
516+
logger.info(
517+
"Not indexing \(uri.forLogging) because the target file of its main file \(mainFile.forLogging) could not be determined"
518+
)
519+
continue
520+
}
521+
guard let outputPath = targetAndOutputPath.value else {
522+
logger.info(
523+
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
524+
)
525+
continue
526+
}
527+
if !indexFilesWithUpToDateUnits,
528+
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: outputPath)
529+
{
530+
continue
531+
}
532+
filesToReIndex.append(
533+
(
525534
FileIndexInfo(
526535
file: .headerFile(header: uri, mainFile: mainFile),
527-
target: target,
528-
outputPath: mainFileOutputPath
536+
target: targetAndOutputPath.key,
537+
outputPath: outputPath
529538
),
530539
modifiedFilesIndex.modificationDate(of: uri)
531540
)
532-
}
541+
)
542+
}
533543
return filesToReIndex
534544
}
535545

0 commit comments

Comments
 (0)