From dddc983dd22bcc4cd7a822e59ce2157849a7a5ba Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 3 Sep 2025 08:39:59 +0200 Subject: [PATCH 1/4] Make BuildServerManager.toolchain only take a target, no document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The document wasn’t used in here. --- .../BuildServerIntegration/BuildServerManager.swift | 8 ++------ .../UpdateIndexStoreTaskDescription.swift | 2 +- Sources/SourceKitLSP/SourceKitLSPServer.swift | 11 ++++++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Sources/BuildServerIntegration/BuildServerManager.swift b/Sources/BuildServerIntegration/BuildServerManager.swift index 44a03768..6a8e007b 100644 --- a/Sources/BuildServerIntegration/BuildServerManager.swift +++ b/Sources/BuildServerIntegration/BuildServerManager.swift @@ -757,16 +757,12 @@ package actor BuildServerManager: QueueBasedMessageHandler { // MARK: Build server queries - /// Returns the toolchain that should be used to process the given document. + /// Returns the toolchain that should be used to process the given target. package func toolchain( - for uri: DocumentURI, - in target: BuildTargetIdentifier?, + for target: BuildTargetIdentifier, language: Language ) async -> Toolchain? { let toolchainPath = await orLog("Getting toolchain from build targets") { () -> URL? in - guard let target else { - return nil - } let targets = try await self.buildTargets() guard let target = targets[target]?.target else { logger.error("Failed to find target \(target.forLogging) to determine toolchain") diff --git a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift index c43bdafe..971909a5 100644 --- a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift +++ b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift @@ -280,7 +280,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { logger.error("Not indexing \(file.forLogging) because it has fallback compiler arguments") return } - guard let toolchain = await buildServerManager.toolchain(for: file.mainFile, in: target, language: language) else { + guard let toolchain = await buildServerManager.toolchain(for: target, language: language) else { logger.error( "Not updating index store for \(file.forLogging) because no toolchain could be determined for the document" ) diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index b06eed91..298b80e7 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -567,11 +567,12 @@ package actor SourceKitLSPServer { return existingLanguageServices } - let toolchain = await workspace.buildServerManager.toolchain( - for: uri, - in: workspace.buildServerManager.canonicalTarget(for: uri), - language: language - ) + let toolchain: Toolchain? = + if let target = await workspace.buildServerManager.canonicalTarget(for: uri) { + await workspace.buildServerManager.toolchain(for: target, language: language) + } else { + nil + } guard let toolchain else { logger.error("Failed to determine toolchain for \(uri)") return [] From 690fd9288f9e20a11a061d12c975e21083431b92 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 3 Sep 2025 08:45:04 +0200 Subject: [PATCH 2/4] Extract target out of the `FileIndexInfo` used by `UpdateIndexStoreTaskDescription` This way we can guarantee that all files passed to `UpdateIndexStoreTaskDescription` belong to the same target, which will simplify multi-file indexing. --- .../SemanticIndex/SemanticIndexManager.swift | 41 ++++++++++++++++--- .../UpdateIndexStoreTaskDescription.swift | 29 +++++++------ .../ExpectedIndexTaskTracker.swift | 2 +- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/Sources/SemanticIndex/SemanticIndexManager.swift b/Sources/SemanticIndex/SemanticIndexManager.swift index c1138660..4bb116d6 100644 --- a/Sources/SemanticIndex/SemanticIndexManager.swift +++ b/Sources/SemanticIndex/SemanticIndexManager.swift @@ -91,6 +91,13 @@ private struct InProgressIndexStore { var fileModificationDate: Date? } +/// The information that's needed to index a file within a given target. +package struct FileIndexInfo: Sendable, Hashable { + package let file: FileToIndex + package let target: BuildTargetIdentifier + package let outputPath: OutputPath +} + /// Status of document indexing / target preparation in `inProgressIndexAndPreparationTasks`. package enum IndexTaskStatus: Comparable { case scheduled @@ -722,16 +729,18 @@ package final actor SemanticIndexManager { } } - /// Update the index store for the given files, assuming that their targets have already been prepared. + /// Update the index store for the given files, assuming that their targets has already been prepared. private func updateIndexStore( - for filesAndTargets: [FileIndexInfo], + for fileAndOutputPaths: [FileAndOutputPath], + target: BuildTargetIdentifier, indexFilesWithUpToDateUnit: Bool, preparationTaskID: UUID, priority: TaskPriority? ) async { let taskDescription = AnyIndexTaskDescription( UpdateIndexStoreTaskDescription( - filesToIndex: filesAndTargets, + filesToIndex: fileAndOutputPaths, + target: target, buildServerManager: self.buildServerManager, index: index, indexStoreUpToDateTracker: indexStoreUpToDateTracker, @@ -747,7 +756,12 @@ package final actor SemanticIndexManager { self.indexProgressStatusDidChange() return } - for fileAndTarget in filesAndTargets { + for fileAndOutputPath in fileAndOutputPaths { + let fileAndTarget = FileIndexInfo( + file: fileAndOutputPath.file, + target: target, + outputPath: fileAndOutputPath.outputPath + ) switch self.inProgressIndexTasks[fileAndTarget]?.state { case .updatingIndexStore(let registeredTask, _): if registeredTask == OpaqueQueuedIndexTask(task) { @@ -763,7 +777,12 @@ package final actor SemanticIndexManager { } self.indexProgressStatusDidChange() } - for fileAndTarget in filesAndTargets { + for fileAndOutputPath in fileAndOutputPaths { + let fileAndTarget = FileIndexInfo( + file: fileAndOutputPath.file, + target: target, + outputPath: fileAndOutputPath.outputPath + ) switch inProgressIndexTasks[fileAndTarget]?.state { case .waitingForPreparation(preparationTaskID, let indexTask), .preparing(preparationTaskID, let indexTask): inProgressIndexTasks[fileAndTarget]?.state = .updatingIndexStore( @@ -938,8 +957,18 @@ package final actor SemanticIndexManager { // (https://github.com/swiftlang/sourcekit-lsp/issues/1268) for fileBatch in filesByTarget[target]!.partition(intoBatchesOfSize: 1) { taskGroup.addTask { + let fileAndOutputPaths: [FileAndOutputPath] = fileBatch.compactMap { + guard $0.target == target else { + logger.fault( + "FileIndexInfo refers to different target than should be indexed \($0.target.forLogging) vs \(target.forLogging)" + ) + return nil + } + return FileAndOutputPath(file: $0.file, outputPath: $0.outputPath) + } await self.updateIndexStore( - for: fileBatch, + for: fileAndOutputPaths, + target: target, indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit, preparationTaskID: preparationTaskID, priority: priority diff --git a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift index 971909a5..62c6eda1 100644 --- a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift +++ b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift @@ -79,11 +79,13 @@ package enum FileToIndex: CustomLogStringConvertible, Hashable { } } -/// The information that's needed to index a file within a given target. -package struct FileIndexInfo: Sendable, Hashable { +/// A source file to index and the output path that should be used for indexing. +package struct FileAndOutputPath: Sendable, Hashable { package let file: FileToIndex - package let target: BuildTargetIdentifier package let outputPath: OutputPath + + fileprivate var mainFile: DocumentURI { file.mainFile } + fileprivate var sourceFile: DocumentURI { file.sourceFile } } /// Describes a task to index a set of source files. @@ -94,7 +96,10 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { package let id = updateIndexStoreIDForLogging.fetchAndIncrement() /// The files that should be indexed. - package let filesToIndex: [FileIndexInfo] + package let filesToIndex: [FileAndOutputPath] + + /// The target in whose context the files should be indexed. + package let target: BuildTargetIdentifier /// The build server manager that is used to get the toolchain and build settings for the files to index. private let buildServerManager: BuildServerManager @@ -143,7 +148,8 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { } init( - filesToIndex: [FileIndexInfo], + filesToIndex: [FileAndOutputPath], + target: BuildTargetIdentifier, buildServerManager: BuildServerManager, index: UncheckedIndex, indexStoreUpToDateTracker: UpToDateTracker, @@ -156,6 +162,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { hooks: IndexHooks ) { self.filesToIndex = filesToIndex + self.target = target self.buildServerManager = buildServerManager self.index = index self.indexStoreUpToDateTracker = indexStoreUpToDateTracker @@ -185,11 +192,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { // TODO: Once swiftc supports it, we should group files by target and index files within the same target together // in one swiftc invocation. (https://github.com/swiftlang/sourcekit-lsp/issues/1268) for fileIndexInfo in filesToIndex { - await updateIndexStore( - forSingleFile: fileIndexInfo.file, - in: fileIndexInfo.target, - outputPath: fileIndexInfo.outputPath - ) + await updateIndexStore(forSingleFile: fileIndexInfo.file, outputPath: fileIndexInfo.outputPath) } // If we know the output paths, make sure that we load their units into indexstore-db. We would eventually also // pick the units up through file watching but that would leave a short time period in which we think that @@ -230,11 +233,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { } } - private func updateIndexStore( - forSingleFile file: FileToIndex, - in target: BuildTargetIdentifier, - outputPath: OutputPath - ) async { + private func updateIndexStore(forSingleFile file: FileToIndex, outputPath: OutputPath) async { guard await !indexStoreUpToDateTracker.isUpToDate(file.sourceFile, target) else { // If we know that the file is up-to-date without having ot hit the index, do that because it's fastest. return diff --git a/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift b/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift index bfc77c24..2e283534 100644 --- a/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift +++ b/Tests/SourceKitLSPTests/ExpectedIndexTaskTracker.swift @@ -264,7 +264,7 @@ actor ExpectedIndexTaskTracker { } } -fileprivate extension FileIndexInfo { +fileprivate extension FileAndOutputPath { var sourceFileName: String? { return self.file.sourceFile.fileURL?.lastPathComponent } From 5c4f1ca93c4969c96bc0c39846eddf847d00675c Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 3 Sep 2025 08:58:01 +0200 Subject: [PATCH 3/4] Make `UpdateIndexStoreTaskDescription.updateIndexStore` handle mulitple files This pushes multi-file indexing one level closer to the actual compiler invocation. --- .../UpdateIndexStoreTaskDescription.swift | 170 +++++++++--------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift index 62c6eda1..09ccf9f5 100644 --- a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift +++ b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift @@ -189,11 +189,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { "Starting updating index store with priority \(Task.currentPriority.rawValue, privacy: .public): \(filesToIndexDescription)" ) let filesToIndex = filesToIndex.sorted(by: { $0.file.sourceFile.stringValue < $1.file.sourceFile.stringValue }) - // TODO: Once swiftc supports it, we should group files by target and index files within the same target together - // in one swiftc invocation. (https://github.com/swiftlang/sourcekit-lsp/issues/1268) - for fileIndexInfo in filesToIndex { - await updateIndexStore(forSingleFile: fileIndexInfo.file, outputPath: fileIndexInfo.outputPath) - } + await updateIndexStore(forFiles: filesToIndex) // If we know the output paths, make sure that we load their units into indexstore-db. We would eventually also // pick the units up through file watching but that would leave a short time period in which we think that // indexing has finished (because the index process has terminated) but when the new symbols aren't present in @@ -233,88 +229,100 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { } } - private func updateIndexStore(forSingleFile file: FileToIndex, outputPath: OutputPath) async { - guard await !indexStoreUpToDateTracker.isUpToDate(file.sourceFile, target) else { + private func updateIndexStore(forFiles fileInfos: [FileAndOutputPath]) async { + let fileInfos = await fileInfos.asyncFilter { fileInfo in // If we know that the file is up-to-date without having ot hit the index, do that because it's fastest. - return - } - guard - indexFilesWithUpToDateUnit - || !index.checked(for: .modifiedFiles).hasUpToDateUnit( - for: file.sourceFile, - mainFile: file.mainFile, - outputPath: outputPath - ) - else { - logger.debug("Not indexing \(file.forLogging) because index has an up-to-date unit") - // We consider a file's index up-to-date if we have any up-to-date unit. Changing build settings does not - // invalidate the up-to-date status of the index. - return - } - if file.mainFile != file.sourceFile { - logger.log("Updating index store of \(file.forLogging) using main file \(file.mainFile.forLogging)") - } - guard let language = await buildServerManager.defaultLanguage(for: file.mainFile, in: target) else { - logger.error("Not indexing \(file.forLogging) because its language could not be determined") - return - } - let buildSettings = await buildServerManager.buildSettings( - for: file.mainFile, - in: target, - language: language, - fallbackAfterTimeout: false - ) - guard let buildSettings else { - logger.error("Not indexing \(file.forLogging) because it has no compiler arguments") - return - } - if buildSettings.isFallback { - // Fallback build settings don’t even have an indexstore path set, so they can't generate index data that we would - // pick up. Also, indexing with fallback args has some other problems: - // - If it did generate a unit file, we would consider the file’s index up-to-date even if the compiler arguments - // change, which means that we wouldn't get any up-to-date-index even when we have build settings for the file. - // - It's unlikely that the index from a single file with fallback arguments will be very useful as it can't tie - // into the rest of the project. - // So, don't index the file. - logger.error("Not indexing \(file.forLogging) because it has fallback compiler arguments") - return - } - guard let toolchain = await buildServerManager.toolchain(for: target, language: language) else { - logger.error( - "Not updating index store for \(file.forLogging) because no toolchain could be determined for the document" + if await indexStoreUpToDateTracker.isUpToDate(fileInfo.file.sourceFile, target) { + return false + } + if indexFilesWithUpToDateUnit { + return true + } + let hasUpToDateUnit = index.checked(for: .modifiedFiles).hasUpToDateUnit( + for: fileInfo.sourceFile, + mainFile: fileInfo.mainFile, + outputPath: fileInfo.outputPath ) + if !hasUpToDateUnit { + logger.debug("Not indexing \(fileInfo.file.forLogging) because index has an up-to-date unit") + // We consider a file's index up-to-date if we have any up-to-date unit. Changing build settings does not + // invalidate the up-to-date status of the index. + } + return !hasUpToDateUnit + } + if fileInfos.isEmpty { return } - let startDate = Date() - switch language.semanticKind { - case .swift: - do { - try await updateIndexStore( - forSwiftFile: file.mainFile, - buildSettings: buildSettings, - toolchain: toolchain - ) - } catch { - logger.error("Updating index store for \(file.forLogging) failed: \(error.forLogging)") - BuildSettingsLogger.log(settings: buildSettings, for: file.mainFile) - } - case .clang: - do { - try await updateIndexStore( - forClangFile: file.mainFile, - buildSettings: buildSettings, - toolchain: toolchain - ) - } catch { - logger.error("Updating index store for \(file) failed: \(error.forLogging)") - BuildSettingsLogger.log(settings: buildSettings, for: file.mainFile) - } - case nil: - logger.error( - "Not updating index store for \(file) because it is a language that is not supported by background indexing" + for fileInfo in fileInfos where fileInfo.mainFile != fileInfo.sourceFile { + logger.log( + "Updating index store of \(fileInfo.file.forLogging) using main file \(fileInfo.mainFile.forLogging)" ) } - await indexStoreUpToDateTracker.markUpToDate([(file.sourceFile, target)], updateOperationStartDate: startDate) + + for fileInfo in fileInfos { + guard let language = await buildServerManager.defaultLanguage(for: fileInfo.mainFile, in: target) else { + logger.error("Not indexing \(fileInfo.file.forLogging) because its language could not be determined") + continue + } + let buildSettings = await buildServerManager.buildSettings( + for: fileInfo.mainFile, + in: target, + language: language, + fallbackAfterTimeout: false + ) + guard let buildSettings else { + logger.error("Not indexing \(fileInfo.file.forLogging) because it has no compiler arguments") + continue + } + if buildSettings.isFallback { + // Fallback build settings don’t even have an indexstore path set, so they can't generate index data that we would + // pick up. Also, indexing with fallback args has some other problems:∂ + // - If it did generate a unit file, we would consider the file’s index up-to-date even if the compiler arguments + // change, which means that we wouldn't get any up-to-date-index even when we have build settings for the file. + // - It's unlikely that the index from a single file with fallback arguments will be very useful as it can't tie + // into the rest of the project. + // So, don't index the file. + logger.error("Not indexing \(fileInfo.file.forLogging) because it has fallback compiler arguments") + continue + } + + guard let toolchain = await buildServerManager.toolchain(for: target, language: buildSettings.language) else { + logger.fault( + "Unable to determine toolchain to index \(buildSettings.language.description, privacy: .public) files in \(target.forLogging)" + ) + continue + } + let startDate = Date() + switch buildSettings.language.semanticKind { + case .swift: + do { + try await updateIndexStore( + forSwiftFile: fileInfo.mainFile, + buildSettings: buildSettings, + toolchain: toolchain + ) + } catch { + logger.error("Updating index store for \(fileInfo.mainFile) failed: \(error.forLogging)") + BuildSettingsLogger.log(settings: buildSettings, for: fileInfo.mainFile) + } + case .clang: + do { + try await updateIndexStore( + forClangFile: fileInfo.mainFile, + buildSettings: buildSettings, + toolchain: toolchain + ) + } catch { + logger.error("Updating index store for \(fileInfo.mainFile.forLogging) failed: \(error.forLogging)") + BuildSettingsLogger.log(settings: buildSettings, for: fileInfo.mainFile) + } + case nil: + logger.error( + "Not updating index store for \(fileInfo.mainFile.forLogging) because it is a language that is not supported by background indexing" + ) + } + await indexStoreUpToDateTracker.markUpToDate([(fileInfo.sourceFile, target)], updateOperationStartDate: startDate) + } } /// If `args` does not contain an `-index-store-path` argument, add it, pointing to the build server's index store From 78608933ba42a3afabec23de4656d585f4f545b3 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 3 Sep 2025 09:09:37 +0200 Subject: [PATCH 4/4] Guarantee that all files in a `UpdateIndexStoreTaskDescription` have the same language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t make sense to try and index files with different langauges in a single compiler invocation. --- .../BuildServerManager.swift | 8 ++- .../SemanticIndex/SemanticIndexManager.swift | 67 +++++++++++-------- .../UpdateIndexStoreTaskDescription.swift | 9 +-- Sources/SourceKitLSP/SourceKitLSPServer.swift | 10 ++- 4 files changed, 56 insertions(+), 38 deletions(-) diff --git a/Sources/BuildServerIntegration/BuildServerManager.swift b/Sources/BuildServerIntegration/BuildServerManager.swift index 6a8e007b..4180f746 100644 --- a/Sources/BuildServerIntegration/BuildServerManager.swift +++ b/Sources/BuildServerIntegration/BuildServerManager.swift @@ -758,11 +758,17 @@ package actor BuildServerManager: QueueBasedMessageHandler { // MARK: Build server queries /// Returns the toolchain that should be used to process the given target. + /// + /// If `target` is `nil` or the build server does not explicitly specify a toolchain for this target, the preferred + /// toolchain for the given language is returned. package func toolchain( - for target: BuildTargetIdentifier, + for target: BuildTargetIdentifier?, language: Language ) async -> Toolchain? { let toolchainPath = await orLog("Getting toolchain from build targets") { () -> URL? in + guard let target else { + return nil + } let targets = try await self.buildTargets() guard let target = targets[target]?.target else { logger.error("Failed to find target \(target.forLogging) to determine toolchain") diff --git a/Sources/SemanticIndex/SemanticIndexManager.swift b/Sources/SemanticIndex/SemanticIndexManager.swift index 4bb116d6..be73209e 100644 --- a/Sources/SemanticIndex/SemanticIndexManager.swift +++ b/Sources/SemanticIndex/SemanticIndexManager.swift @@ -94,6 +94,7 @@ private struct InProgressIndexStore { /// The information that's needed to index a file within a given target. package struct FileIndexInfo: Sendable, Hashable { package let file: FileToIndex + package let language: Language package let target: BuildTargetIdentifier package let outputPath: OutputPath } @@ -507,10 +508,13 @@ package final actor SemanticIndexManager { continue } // If this is a source file, just index it. + guard let language = await buildServerManager.defaultLanguage(for: uri, in: target) else { + continue + } didFindTargetToIndex = true filesToReIndex.append( ( - FileIndexInfo(file: .indexableFile(uri), target: target, outputPath: outputPath), + FileIndexInfo(file: .indexableFile(uri), language: language, target: target, outputPath: outputPath), modifiedFilesIndex.modificationDate(of: uri) ) ) @@ -550,10 +554,14 @@ package final actor SemanticIndexManager { { continue } + guard let language = await buildServerManager.defaultLanguage(for: uri, in: targetAndOutputPath.key) else { + continue + } filesToReIndex.append( ( FileIndexInfo( file: .headerFile(header: uri, mainFile: mainFile), + language: language, target: targetAndOutputPath.key, outputPath: outputPath ), @@ -733,6 +741,7 @@ package final actor SemanticIndexManager { private func updateIndexStore( for fileAndOutputPaths: [FileAndOutputPath], target: BuildTargetIdentifier, + language: Language, indexFilesWithUpToDateUnit: Bool, preparationTaskID: UUID, priority: TaskPriority? @@ -741,6 +750,7 @@ package final actor SemanticIndexManager { UpdateIndexStoreTaskDescription( filesToIndex: fileAndOutputPaths, target: target, + language: language, buildServerManager: self.buildServerManager, index: index, indexStoreUpToDateTracker: indexStoreUpToDateTracker, @@ -759,6 +769,7 @@ package final actor SemanticIndexManager { for fileAndOutputPath in fileAndOutputPaths { let fileAndTarget = FileIndexInfo( file: fileAndOutputPath.file, + language: language, target: target, outputPath: fileAndOutputPath.outputPath ) @@ -780,6 +791,7 @@ package final actor SemanticIndexManager { for fileAndOutputPath in fileAndOutputPaths { let fileAndTarget = FileIndexInfo( file: fileAndOutputPath.file, + language: language, target: target, outputPath: fileAndOutputPath.outputPath ) @@ -873,13 +885,7 @@ package final actor SemanticIndexManager { var newIndexTasks = 0 for (fileIndexInfo, fileModificationDate) in filesToIndex { - guard - let language = await buildServerManager.defaultLanguage( - for: fileIndexInfo.file.mainFile, - in: fileIndexInfo.target - ), - UpdateIndexStoreTaskDescription.canIndex(language: language) - else { + guard UpdateIndexStoreTaskDescription.canIndex(language: fileIndexInfo.language) else { continue } @@ -952,27 +958,34 @@ package final actor SemanticIndexManager { // And after preparation is done, index the files in the targets. await withTaskGroup(of: Void.self) { taskGroup in for target in targetsBatch { - // TODO: Once swiftc supports indexing of multiple files in a single invocation, increase the batch size to - // allow it to share AST builds between multiple files within a target. - // (https://github.com/swiftlang/sourcekit-lsp/issues/1268) - for fileBatch in filesByTarget[target]!.partition(intoBatchesOfSize: 1) { - taskGroup.addTask { - let fileAndOutputPaths: [FileAndOutputPath] = fileBatch.compactMap { - guard $0.target == target else { - logger.fault( - "FileIndexInfo refers to different target than should be indexed \($0.target.forLogging) vs \(target.forLogging)" - ) - return nil + var filesByLanguage: [Language: [FileIndexInfo]] = [:] + for fileInfo in filesByTarget[target]! { + filesByLanguage[fileInfo.language, default: []].append(fileInfo) + } + for (language, fileInfos) in filesByLanguage { + // TODO: Once swiftc supports indexing of multiple files in a single invocation, increase the batch size to + // allow it to share AST builds between multiple files within a target. + // (https://github.com/swiftlang/sourcekit-lsp/issues/1268) + for fileBatch in fileInfos.partition(intoBatchesOfSize: 1) { + taskGroup.addTask { + let fileAndOutputPaths: [FileAndOutputPath] = fileBatch.compactMap { + guard $0.target == target else { + logger.fault( + "FileIndexInfo refers to different target than should be indexed \($0.target.forLogging) vs \(target.forLogging)" + ) + return nil + } + return FileAndOutputPath(file: $0.file, outputPath: $0.outputPath) } - return FileAndOutputPath(file: $0.file, outputPath: $0.outputPath) + await self.updateIndexStore( + for: fileAndOutputPaths, + target: target, + language: language, + indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit, + preparationTaskID: preparationTaskID, + priority: priority + ) } - await self.updateIndexStore( - for: fileAndOutputPaths, - target: target, - indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit, - preparationTaskID: preparationTaskID, - priority: priority - ) } } } diff --git a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift index 09ccf9f5..ba289aa1 100644 --- a/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift +++ b/Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift @@ -101,6 +101,9 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { /// The target in whose context the files should be indexed. package let target: BuildTargetIdentifier + /// The common language of all main files in `filesToIndex`. + package let language: Language + /// The build server manager that is used to get the toolchain and build settings for the files to index. private let buildServerManager: BuildServerManager @@ -150,6 +153,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { init( filesToIndex: [FileAndOutputPath], target: BuildTargetIdentifier, + language: Language, buildServerManager: BuildServerManager, index: UncheckedIndex, indexStoreUpToDateTracker: UpToDateTracker, @@ -163,6 +167,7 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { ) { self.filesToIndex = filesToIndex self.target = target + self.language = language self.buildServerManager = buildServerManager self.index = index self.indexStoreUpToDateTracker = indexStoreUpToDateTracker @@ -260,10 +265,6 @@ package struct UpdateIndexStoreTaskDescription: IndexTaskDescription { } for fileInfo in fileInfos { - guard let language = await buildServerManager.defaultLanguage(for: fileInfo.mainFile, in: target) else { - logger.error("Not indexing \(fileInfo.file.forLogging) because its language could not be determined") - continue - } let buildSettings = await buildServerManager.buildSettings( for: fileInfo.mainFile, in: target, diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 298b80e7..b3d331c3 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -567,12 +567,10 @@ package actor SourceKitLSPServer { return existingLanguageServices } - let toolchain: Toolchain? = - if let target = await workspace.buildServerManager.canonicalTarget(for: uri) { - await workspace.buildServerManager.toolchain(for: target, language: language) - } else { - nil - } + let toolchain = await workspace.buildServerManager.toolchain( + for: await workspace.buildServerManager.canonicalTarget(for: uri), + language: language + ) guard let toolchain else { logger.error("Failed to determine toolchain for \(uri)") return []