Files
sourcekit-lsp/Sources/SourceKitLSP/OnDiskDocumentManager.swift
2025-10-31 14:11:11 -07:00

96 lines
3.7 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
package import BuildServerIntegration
import Foundation
@_spi(SourceKitLSP) package import LanguageServerProtocol
@_spi(SourceKitLSP) import SKLogging
import SKUtilities
import SwiftExtensions
package actor OnDiskDocumentManager {
private let sourceKitLSPServer: SourceKitLSPServer
private var openSnapshots:
[DocumentURI: (snapshot: DocumentSnapshot, buildSettings: FileBuildSettings, workspace: Workspace)]
fileprivate init(sourceKitLSPServer: SourceKitLSPServer) {
self.sourceKitLSPServer = sourceKitLSPServer
openSnapshots = [:]
}
/// Opens a dummy ``DocumentSnapshot`` with contents from disk for a given ``DocumentURI`` and ``Language``.
///
/// The snapshot will remain cached until ``closeAllDocuments()`` is called.
package func open(
uri: DocumentURI,
language: Language,
in workspace: Workspace
) async throws -> (snapshot: DocumentSnapshot, buildSettings: FileBuildSettings) {
guard let fileURL = uri.fileURL else {
throw ResponseError.unknown("Cannot create snapshot with on-disk contents for non-file URI \(uri.forLogging)")
}
if let cachedSnapshot = openSnapshots[uri] {
return (cachedSnapshot.snapshot, cachedSnapshot.buildSettings)
}
let snapshot = DocumentSnapshot(
uri: try DocumentURI(filePath: "\(UUID().uuidString)/\(fileURL.filePath)", isDirectory: false),
language: language,
version: 0,
lineTable: LineTable(try String(contentsOf: fileURL, encoding: .utf8))
)
let languageService = try await sourceKitLSPServer.primaryLanguageService(for: uri, language, in: workspace)
let originalBuildSettings = await workspace.buildServerManager.buildSettingsInferredFromMainFile(
for: uri,
language: language,
fallbackAfterTimeout: false
)
guard let originalBuildSettings else {
throw ResponseError.unknown("Failed to infer build settings for \(uri)")
}
let patchedBuildSettings = originalBuildSettings.patching(newFile: snapshot.uri, originalFile: uri)
try await languageService.openOnDiskDocument(snapshot: snapshot, buildSettings: patchedBuildSettings)
openSnapshots[uri] = (snapshot, patchedBuildSettings, workspace)
return (snapshot, patchedBuildSettings)
}
/// Close all of the ``DocumentSnapshot``s that were opened by this ``OnDiskDocumentManager``.
fileprivate func closeAllDocuments() async {
for (snapshot, _, workspace) in openSnapshots.values {
await orLog("Closing snapshot from on-disk contents: \(snapshot.uri.forLogging)") {
let languageService =
try await sourceKitLSPServer.primaryLanguageService(for: snapshot.uri, snapshot.language, in: workspace)
try await languageService.closeOnDiskDocument(uri: snapshot.uri)
}
}
openSnapshots = [:]
}
}
package extension SourceKitLSPServer {
nonisolated func withOnDiskDocumentManager<T>(
_ body: (OnDiskDocumentManager) async throws -> T
) async rethrows -> T {
let manager = OnDiskDocumentManager(sourceKitLSPServer: self)
do {
let result = try await body(manager)
await manager.closeAllDocuments()
return result
} catch {
await manager.closeAllDocuments()
throw error
}
}
}