Skip to content

Commit c632e47

Browse files
committed
Re-index header files when they are modified
1 parent 35afd9f commit c632e47

File tree

5 files changed

+211
-106
lines changed

5 files changed

+211
-106
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
@@ -120,18 +120,31 @@ package extension CustomBuildServer {
120120
)
121121
}
122122

123-
func initializationResponseSupportingBackgroundIndexing(projectRoot: URL) throws -> InitializeBuildResponse {
123+
func initializationResponseSupportingBackgroundIndexing(
124+
projectRoot: URL,
125+
outputPathsProvider: Bool
126+
) throws -> InitializeBuildResponse {
124127
return initializationResponse(
125128
initializeData: SourceKitInitializeBuildResponseData(
126129
indexDatabasePath: try projectRoot.appendingPathComponent("index-db").filePath,
127130
indexStorePath: try projectRoot.appendingPathComponent("index-store").filePath,
128-
outputPathsProvider: true,
131+
outputPathsProvider: outputPathsProvider,
129132
prepareProvider: true,
130133
sourceKitOptionsProvider: true
131134
)
132135
)
133136
}
134137

138+
func sourceItem(for url: URL, outputPath: String) -> SourceItem {
139+
SourceItem(
140+
uri: URI(url),
141+
kind: .file,
142+
generated: false,
143+
dataKind: .sourceKit,
144+
data: SourceKitSourceItemData(outputPath: outputPath).encodeToLSPAny()
145+
)
146+
}
147+
135148
func dummyTargetSourcesResponse(_ files: some Sequence<DocumentURI>) -> BuildTargetSourcesResponse {
136149
return BuildTargetSourcesResponse(items: [
137150
SourcesItem(target: .dummy, sources: files.map { SourceItem(uri: $0, kind: .file, generated: false) })

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -465,67 +465,78 @@ package final actor SemanticIndexManager {
465465
}
466466
let modifiedFilesIndex = index.checked(for: .modifiedFiles)
467467

468-
let filesWithTargetAndOutput: [(file: DocumentURI, target: BuildTargetIdentifier, outputPath: OutputPath?)] =
469-
await files.asyncFlatMap { file in
470-
await buildSystemManager.sourceFileInfo(for: file)?.targetsToOutputPaths.map { (file, $0, $1) } ?? []
471-
}
472-
473-
let filesToReIndex =
474-
await filesWithTargetAndOutput
475-
.asyncCompactMap { (uri, target, outputPath) -> (FileIndexInfo, Date?)? in
468+
var filesToReIndex: [(FileIndexInfo, Date?)] = []
469+
for uri in files {
470+
var didFindTargetToIndex = false
471+
for (target, outputPath) in await buildSystemManager.sourceFileInfo(for: uri)?.targetsToOutputPaths ?? [:] {
476472
// 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
477473
// system at all
478474
if !indexFilesWithUpToDateUnits, await indexStoreUpToDateTracker.isUpToDate(uri, target) {
479-
return nil
475+
continue
480476
}
481477
if sourceFiles.contains(uri) {
482478
guard let outputPath else {
483479
logger.info("Not indexing \(uri.forLogging) because its output file could not be determined")
484-
return nil
480+
continue
485481
}
486482
if !indexFilesWithUpToDateUnits, modifiedFilesIndex.hasUpToDateUnit(for: uri, outputPath: outputPath) {
487-
return nil
483+
continue
488484
}
489485
// If this is a source file, just index it.
490-
return (
491-
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
492-
modifiedFilesIndex.modificationDate(of: uri)
493-
)
494-
}
495-
// Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
496-
// index.
497-
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
498-
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
499-
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
500-
let mainFile = await buildSystemManager.mainFiles(containing: uri)
501-
.sorted(by: { $0.stringValue < $1.stringValue }).first
502-
guard let mainFile else {
503-
logger.info("Not indexing \(uri) because its main file could not be inferred")
504-
return nil
505-
}
506-
let mainFileOutputPath = await orLog("Getting output path") {
507-
try await buildSystemManager.outputPath(for: mainFile, in: target)
508-
}
509-
guard let mainFileOutputPath else {
510-
logger.info(
511-
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
486+
didFindTargetToIndex = true
487+
filesToReIndex.append(
488+
(
489+
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
490+
modifiedFilesIndex.modificationDate(of: uri)
491+
)
512492
)
513-
return nil
514493
}
515-
if !indexFilesWithUpToDateUnits,
516-
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: mainFileOutputPath)
517-
{
518-
return nil
519-
}
520-
return (
494+
}
495+
496+
if didFindTargetToIndex {
497+
continue
498+
}
499+
// 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
500+
// that imports it to update header file's index.
501+
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
502+
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
503+
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
504+
let mainFile = await buildSystemManager.mainFiles(containing: uri)
505+
.sorted(by: { $0.stringValue < $1.stringValue }).first
506+
guard let mainFile else {
507+
logger.info("Not indexing \(uri.forLogging) because its main file could not be inferred")
508+
continue
509+
}
510+
let targetAndOutputPath = (await buildSystemManager.sourceFileInfo(for: mainFile)?.targetsToOutputPaths ?? [:])
511+
.sorted(by: { $0.key.uri.stringValue < $1.key.uri.stringValue }).first
512+
guard let targetAndOutputPath else {
513+
logger.info(
514+
"Not indexing \(uri.forLogging) because the target file of its main file \(mainFile.forLogging) could not be determined"
515+
)
516+
continue
517+
}
518+
guard let outputPath = targetAndOutputPath.value else {
519+
logger.info(
520+
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
521+
)
522+
continue
523+
}
524+
if !indexFilesWithUpToDateUnits,
525+
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: outputPath)
526+
{
527+
continue
528+
}
529+
filesToReIndex.append(
530+
(
521531
FileIndexInfo(
522532
file: .headerFile(header: uri, mainFile: mainFile),
523-
target: target,
524-
outputPath: mainFileOutputPath
533+
target: targetAndOutputPath.key,
534+
outputPath: outputPath
525535
),
526536
modifiedFilesIndex.modificationDate(of: uri)
527537
)
528-
}
538+
)
539+
}
529540
return filesToReIndex
530541
}
531542

0 commit comments

Comments
 (0)