Skip to content

Commit 7cd47f8

Browse files
committed
Re-index header files when they are modified
1 parent 422bb34 commit 7cd47f8

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
@@ -75,7 +75,7 @@ public struct BuildTarget: Codable, Hashable, Sendable {
7575
displayName: String? = nil,
7676
baseDirectory: URI? = nil,
7777
tags: [BuildTargetTag] = [],
78-
capabilities: BuildTargetCapabilities,
78+
capabilities: BuildTargetCapabilities = BuildTargetCapabilities(),
7979
languageIds: [Language],
8080
dependencies: [BuildTargetIdentifier],
8181
dataKind: BuildTargetDataKind? = nil,

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -827,24 +827,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
827827
return Array(targets)
828828
}
829829

830-
/// The `OutputPath` with which `uri` should be indexed in `target`. This may return:
831-
/// - `.path` if the build server supports output paths and produced a result
832-
/// - `.notSupported` if the build server does not support output paths. In this case we will assume that the index
833-
/// for `uri` is up-to-date if we have any up-to-date unit for it.
834-
/// - `nil` if the build server supports output paths but did not return an output path for `uri` in `target`.
835-
package func outputPath(for document: DocumentURI, in target: BuildTargetIdentifier) async throws -> OutputPath? {
836-
guard await initializationData?.outputPathsProvider ?? false else {
837-
// Early exit if the build server doesn't support output paths.
838-
return .notSupported
839-
}
840-
guard let sourceFileInfo = await sourceFileInfo(for: document),
841-
let outputPath = sourceFileInfo.targetsToOutputPaths[target]
842-
else {
843-
return nil
844-
}
845-
return outputPath
846-
}
847-
848830
/// Returns the `BuildTargetIdentifier` that should be used for semantic functionality of the given document.
849831
package func canonicalTarget(for document: DocumentURI) async -> BuildTargetIdentifier? {
850832
// 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
@@ -128,18 +128,31 @@ package extension CustomBuildServer {
128128
)
129129
}
130130

131-
func initializationResponseSupportingBackgroundIndexing(projectRoot: URL) throws -> InitializeBuildResponse {
131+
func initializationResponseSupportingBackgroundIndexing(
132+
projectRoot: URL,
133+
outputPathsProvider: Bool
134+
) throws -> InitializeBuildResponse {
132135
return initializationResponse(
133136
initializeData: SourceKitInitializeBuildResponseData(
134137
indexDatabasePath: try projectRoot.appendingPathComponent("index-db").filePath,
135138
indexStorePath: try projectRoot.appendingPathComponent("index-store").filePath,
136-
outputPathsProvider: true,
139+
outputPathsProvider: outputPathsProvider,
137140
prepareProvider: true,
138141
sourceKitOptionsProvider: true
139142
)
140143
)
141144
}
142145

146+
func sourceItem(for url: URL, outputPath: String) -> SourceItem {
147+
SourceItem(
148+
uri: URI(url),
149+
kind: .file,
150+
generated: false,
151+
dataKind: .sourceKit,
152+
data: SourceKitSourceItemData(outputPath: outputPath).encodeToLSPAny()
153+
)
154+
}
155+
143156
func dummyTargetSourcesResponse(_ files: some Sequence<DocumentURI>) -> BuildTargetSourcesResponse {
144157
return BuildTargetSourcesResponse(items: [
145158
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
@@ -475,67 +475,78 @@ package final actor SemanticIndexManager {
475475
}
476476
let modifiedFilesIndex = index.checked(for: .modifiedFiles)
477477

478-
let filesWithTargetAndOutput: [(file: DocumentURI, target: BuildTargetIdentifier, outputPath: OutputPath?)] =
479-
await files.asyncFlatMap { file in
480-
await buildSystemManager.sourceFileInfo(for: file)?.targetsToOutputPaths.map { (file, $0, $1) } ?? []
481-
}
482-
483-
let filesToReIndex =
484-
await filesWithTargetAndOutput
485-
.asyncCompactMap { (uri, target, outputPath) -> (FileIndexInfo, Date?)? in
478+
var filesToReIndex: [(FileIndexInfo, Date?)] = []
479+
for uri in files {
480+
var didFindTargetToIndex = false
481+
for (target, outputPath) in await buildSystemManager.sourceFileInfo(for: uri)?.targetsToOutputPaths ?? [:] {
486482
// 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
487483
// system at all
488484
if !indexFilesWithUpToDateUnits, await indexStoreUpToDateTracker.isUpToDate(uri, target) {
489-
return nil
485+
continue
490486
}
491487
if sourceFiles.contains(uri) {
492488
guard let outputPath else {
493489
logger.info("Not indexing \(uri.forLogging) because its output file could not be determined")
494-
return nil
490+
continue
495491
}
496492
if !indexFilesWithUpToDateUnits, modifiedFilesIndex.hasUpToDateUnit(for: uri, outputPath: outputPath) {
497-
return nil
493+
continue
498494
}
499495
// If this is a source file, just index it.
500-
return (
501-
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
502-
modifiedFilesIndex.modificationDate(of: uri)
503-
)
504-
}
505-
// Otherwise, see if it is a header file. If so, index a main file that that imports it to update header file's
506-
// index.
507-
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
508-
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
509-
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
510-
let mainFile = await buildSystemManager.mainFiles(containing: uri)
511-
.sorted(by: { $0.stringValue < $1.stringValue }).first
512-
guard let mainFile else {
513-
logger.info("Not indexing \(uri) because its main file could not be inferred")
514-
return nil
515-
}
516-
let mainFileOutputPath = await orLog("Getting output path") {
517-
try await buildSystemManager.outputPath(for: mainFile, in: target)
518-
}
519-
guard let mainFileOutputPath else {
520-
logger.info(
521-
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
496+
didFindTargetToIndex = true
497+
filesToReIndex.append(
498+
(
499+
FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath),
500+
modifiedFilesIndex.modificationDate(of: uri)
501+
)
522502
)
523-
return nil
524503
}
525-
if !indexFilesWithUpToDateUnits,
526-
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: mainFileOutputPath)
527-
{
528-
return nil
529-
}
530-
return (
504+
}
505+
506+
if didFindTargetToIndex {
507+
continue
508+
}
509+
// 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
510+
// that imports it to update header file's index.
511+
// Deterministically pick a main file. This ensures that we always pick the same main file for a header. This way,
512+
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
513+
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
514+
let mainFile = await buildSystemManager.mainFiles(containing: uri)
515+
.sorted(by: { $0.stringValue < $1.stringValue }).first
516+
guard let mainFile else {
517+
logger.info("Not indexing \(uri.forLogging) because its main file could not be inferred")
518+
continue
519+
}
520+
let targetAndOutputPath = (await buildSystemManager.sourceFileInfo(for: mainFile)?.targetsToOutputPaths ?? [:])
521+
.sorted(by: { $0.key.uri.stringValue < $1.key.uri.stringValue }).first
522+
guard let targetAndOutputPath else {
523+
logger.info(
524+
"Not indexing \(uri.forLogging) because the target file of its main file \(mainFile.forLogging) could not be determined"
525+
)
526+
continue
527+
}
528+
guard let outputPath = targetAndOutputPath.value else {
529+
logger.info(
530+
"Not indexing \(uri.forLogging) because the output file of its main file \(mainFile.forLogging) could not be determined"
531+
)
532+
continue
533+
}
534+
if !indexFilesWithUpToDateUnits,
535+
modifiedFilesIndex.hasUpToDateUnit(for: uri, mainFile: mainFile, outputPath: outputPath)
536+
{
537+
continue
538+
}
539+
filesToReIndex.append(
540+
(
531541
FileIndexInfo(
532542
file: .headerFile(header: uri, mainFile: mainFile),
533-
target: target,
534-
outputPath: mainFileOutputPath
543+
target: targetAndOutputPath.key,
544+
outputPath: outputPath
535545
),
536546
modifiedFilesIndex.modificationDate(of: uri)
537547
)
538-
}
548+
)
549+
}
539550
return filesToReIndex
540551
}
541552

0 commit comments

Comments
 (0)