mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
Merge pull request #2226 from matthewbastien/docc-cache-snapshots
[DocC Live Preview] Cache on-disk snapshots opened in sourcekitd
This commit is contained in:
@@ -795,6 +795,28 @@ package actor BuildServerManager: QueueBasedMessageHandler {
|
||||
return languageFromBuildServer ?? Language(inferredFromFileExtension: document)
|
||||
}
|
||||
|
||||
/// Returns the language that a document should be interpreted in for background tasks where the editor doesn't
|
||||
/// specify the document's language.
|
||||
///
|
||||
/// If the language could not be determined, this method throws an error.
|
||||
package func defaultLanguageInCanonicalTarget(for document: DocumentURI) async throws -> Language {
|
||||
struct UnableToInferLanguage: Error, CustomStringConvertible {
|
||||
let document: DocumentURI
|
||||
var description: String { "Unable to infer language for \(document)" }
|
||||
}
|
||||
|
||||
guard let canonicalTarget = await self.canonicalTarget(for: document) else {
|
||||
guard let language = Language(inferredFromFileExtension: document) else {
|
||||
throw UnableToInferLanguage(document: document)
|
||||
}
|
||||
return language
|
||||
}
|
||||
guard let language = await defaultLanguage(for: document, in: canonicalTarget) else {
|
||||
throw UnableToInferLanguage(document: document)
|
||||
}
|
||||
return language
|
||||
}
|
||||
|
||||
/// Retrieve information about the given source file within the build server.
|
||||
package func sourceFileInfo(for document: DocumentURI) async -> SourceFileInfo? {
|
||||
return await orLog("Getting targets for source file") {
|
||||
|
||||
@@ -496,14 +496,10 @@ extension ClangLanguageService {
|
||||
}
|
||||
|
||||
package func doccDocumentation(_ req: DoccDocumentationRequest) async throws -> DoccDocumentationResponse {
|
||||
guard let sourceKitLSPServer else {
|
||||
throw ResponseError.unknown("Connection to the editor closed")
|
||||
guard let language = openDocuments[req.textDocument.uri] else {
|
||||
throw ResponseError.requestFailed("Documentation preview is not available for clang files")
|
||||
}
|
||||
|
||||
let snapshot = try sourceKitLSPServer.documentManager.latestSnapshot(req.textDocument.uri)
|
||||
throw ResponseError.requestFailed(
|
||||
"Documentation preview is not available for \(snapshot.language.description) files"
|
||||
)
|
||||
throw ResponseError.requestFailed("Documentation preview is not available for \(language.description) files")
|
||||
}
|
||||
|
||||
package func symbolInfo(_ req: SymbolInfoRequest) async throws -> [SymbolDetails] {
|
||||
|
||||
@@ -23,7 +23,7 @@ enum DocCDocumentationError: LocalizedError {
|
||||
case .unsupportedLanguage(let language):
|
||||
return "Documentation preview is not available for \(language.description) files"
|
||||
case .indexNotAvailable:
|
||||
return "The index is not availble to complete the request"
|
||||
return "The index is not available to complete the request"
|
||||
case .symbolNotFound(let symbolName):
|
||||
return "Could not find symbol \(symbolName) in the project"
|
||||
}
|
||||
|
||||
@@ -104,47 +104,35 @@ extension DocumentationLanguageService {
|
||||
guard let index = workspace.index(checkedFor: .deletedFiles) else {
|
||||
throw ResponseError.requestFailed(doccDocumentationError: .indexNotAvailable)
|
||||
}
|
||||
guard let symbolLink = DocCSymbolLink(linkString: symbolName),
|
||||
let symbolOccurrence = try await index.primaryDefinitionOrDeclarationOccurrence(
|
||||
ofDocCSymbolLink: symbolLink,
|
||||
fetchSymbolGraph: { location in
|
||||
guard let symbolWorkspace = await sourceKitLSPServer.workspaceForDocument(uri: location.documentUri) else {
|
||||
throw ResponseError.internalError("Unable to find language service for \(location.documentUri)")
|
||||
return try await sourceKitLSPServer.withOnDiskDocumentManager { onDiskDocumentManager in
|
||||
guard let symbolLink = DocCSymbolLink(linkString: symbolName),
|
||||
let symbolOccurrence = try await index.primaryDefinitionOrDeclarationOccurrence(
|
||||
ofDocCSymbolLink: symbolLink,
|
||||
fetchSymbolGraph: { location in
|
||||
return try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: location.documentUri,
|
||||
workspace.buildServerManager.defaultLanguageInCanonicalTarget(for: location.documentUri),
|
||||
in: workspace
|
||||
)
|
||||
.symbolGraph(forOnDiskContentsAt: location, in: workspace, manager: onDiskDocumentManager)
|
||||
}
|
||||
let languageService = try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: location.documentUri,
|
||||
.swift,
|
||||
in: symbolWorkspace
|
||||
)
|
||||
return try await languageService.symbolGraph(
|
||||
forOnDiskContentsOf: location.documentUri,
|
||||
at: location
|
||||
)
|
||||
}
|
||||
)
|
||||
else {
|
||||
throw ResponseError.requestFailed(doccDocumentationError: .symbolNotFound(symbolName))
|
||||
}
|
||||
let symbolGraph = try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: symbolOccurrence.location.documentUri,
|
||||
workspace.buildServerManager.defaultLanguageInCanonicalTarget(for: symbolOccurrence.location.documentUri),
|
||||
in: workspace
|
||||
).symbolGraph(forOnDiskContentsAt: symbolOccurrence.location, in: workspace, manager: onDiskDocumentManager)
|
||||
return try await documentationManager.renderDocCDocumentation(
|
||||
symbolUSR: symbolOccurrence.symbol.usr,
|
||||
symbolGraph: symbolGraph,
|
||||
markupFile: snapshot.text,
|
||||
moduleName: moduleName,
|
||||
catalogURL: catalogURL
|
||||
)
|
||||
else {
|
||||
throw ResponseError.requestFailed(doccDocumentationError: .symbolNotFound(symbolName))
|
||||
}
|
||||
let symbolDocumentUri = symbolOccurrence.location.documentUri
|
||||
guard let symbolWorkspace = await sourceKitLSPServer.workspaceForDocument(uri: symbolDocumentUri) else {
|
||||
throw ResponseError.internalError("Unable to find language service for \(symbolDocumentUri)")
|
||||
}
|
||||
let languageService = try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: symbolDocumentUri,
|
||||
.swift,
|
||||
in: symbolWorkspace
|
||||
)
|
||||
let symbolGraph = try await languageService.symbolGraph(
|
||||
forOnDiskContentsOf: symbolDocumentUri,
|
||||
at: symbolOccurrence.location
|
||||
)
|
||||
return try await documentationManager.renderDocCDocumentation(
|
||||
symbolUSR: symbolOccurrence.symbol.usr,
|
||||
symbolGraph: symbolGraph,
|
||||
markupFile: snapshot.text,
|
||||
moduleName: moduleName,
|
||||
catalogURL: catalogURL
|
||||
)
|
||||
}
|
||||
// This is a page representing the module itself.
|
||||
// Create a dummy symbol graph and tell SwiftDocC to convert the module name.
|
||||
@@ -175,24 +163,26 @@ extension DocumentationLanguageService {
|
||||
in: workspace
|
||||
).symbolGraph(for: snapshot, at: position)
|
||||
// Locate the documentation extension and include it in the request if one exists
|
||||
let markupExtensionFile = await orLog("Finding markup extension file for symbol \(symbolUSR)") {
|
||||
try await findMarkupExtensionFile(
|
||||
workspace: workspace,
|
||||
documentationManager: documentationManager,
|
||||
catalogURL: catalogURL,
|
||||
for: symbolUSR,
|
||||
fetchSymbolGraph: { location in
|
||||
guard let symbolWorkspace = await sourceKitLSPServer.workspaceForDocument(uri: location.documentUri) else {
|
||||
throw ResponseError.internalError("Unable to find language service for \(location.documentUri)")
|
||||
let markupExtensionFile = await sourceKitLSPServer.withOnDiskDocumentManager {
|
||||
[documentationManager, documentManager = try documentManager] onDiskDocumentManager in
|
||||
await orLog("Finding markup extension file for symbol \(symbolUSR)") {
|
||||
try await Self.findMarkupExtensionFile(
|
||||
workspace: workspace,
|
||||
documentationManager: documentationManager,
|
||||
documentManager: documentManager,
|
||||
catalogURL: catalogURL,
|
||||
for: symbolUSR,
|
||||
fetchSymbolGraph: { location in
|
||||
try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: location.documentUri,
|
||||
snapshot.language,
|
||||
in: workspace
|
||||
)
|
||||
.symbolGraph(forOnDiskContentsAt: location, in: workspace, manager: onDiskDocumentManager)
|
||||
|
||||
}
|
||||
let languageService = try await sourceKitLSPServer.primaryLanguageService(
|
||||
for: location.documentUri,
|
||||
.swift,
|
||||
in: symbolWorkspace
|
||||
)
|
||||
return try await languageService.symbolGraph(forOnDiskContentsOf: location.documentUri, at: location)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
return try await documentationManager.renderDocCDocumentation(
|
||||
symbolUSR: symbolUSR,
|
||||
@@ -204,9 +194,10 @@ extension DocumentationLanguageService {
|
||||
)
|
||||
}
|
||||
|
||||
private func findMarkupExtensionFile(
|
||||
private static func findMarkupExtensionFile(
|
||||
workspace: Workspace,
|
||||
documentationManager: DocCDocumentationManager,
|
||||
documentManager: DocumentManager,
|
||||
catalogURL: URL?,
|
||||
for symbolUSR: String,
|
||||
fetchSymbolGraph: @Sendable (SymbolLocation) async throws -> String?
|
||||
@@ -215,16 +206,17 @@ extension DocumentationLanguageService {
|
||||
return nil
|
||||
}
|
||||
let catalogIndex = try await documentationManager.catalogIndex(for: catalogURL)
|
||||
guard let index = workspace.index(checkedFor: .deletedFiles),
|
||||
let symbolInformation = try await index.doccSymbolInformation(
|
||||
ofUSR: symbolUSR,
|
||||
fetchSymbolGraph: fetchSymbolGraph
|
||||
),
|
||||
let markupExtensionFileURL = catalogIndex.documentationExtension(for: symbolInformation)
|
||||
else {
|
||||
guard let index = workspace.index(checkedFor: .deletedFiles) else {
|
||||
return nil
|
||||
}
|
||||
return try? documentManager.latestSnapshotOrDisk(
|
||||
let symbolInformation = try await index.doccSymbolInformation(
|
||||
ofUSR: symbolUSR,
|
||||
fetchSymbolGraph: fetchSymbolGraph
|
||||
)
|
||||
guard let markupExtensionFileURL = catalogIndex.documentationExtension(for: symbolInformation) else {
|
||||
return nil
|
||||
}
|
||||
return documentManager.latestSnapshotOrDisk(
|
||||
DocumentURI(markupExtensionFileURL),
|
||||
language: .markdown
|
||||
)?.text
|
||||
|
||||
@@ -40,7 +40,7 @@ extension CheckedIndex {
|
||||
var result: [SymbolOccurrence] = []
|
||||
for occurrence in topLevelSymbolOccurrences {
|
||||
let info = try await doccSymbolInformation(ofUSR: occurrence.symbol.usr, fetchSymbolGraph: fetchSymbolGraph)
|
||||
if let info, info.matches(symbolLink) {
|
||||
if info.matches(symbolLink) {
|
||||
result.append(occurrence)
|
||||
}
|
||||
}
|
||||
@@ -60,9 +60,9 @@ extension CheckedIndex {
|
||||
func doccSymbolInformation(
|
||||
ofUSR usr: String,
|
||||
fetchSymbolGraph: (SymbolLocation) async throws -> String?
|
||||
) async throws -> DocCSymbolInformation? {
|
||||
) async throws -> DocCSymbolInformation {
|
||||
guard let topLevelSymbolOccurrence = primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
|
||||
return nil
|
||||
throw DocCCheckedIndexError.emptyDocCSymbolLink
|
||||
}
|
||||
let moduleName = topLevelSymbolOccurrence.location.moduleName
|
||||
var symbols = [topLevelSymbolOccurrence]
|
||||
|
||||
@@ -13,6 +13,7 @@ add_library(SourceKitLSP STATIC
|
||||
LogMessageNotification+representingStructureUsingEmojiPrefixIfNecessary.swift
|
||||
MacroExpansionReferenceDocumentURLData.swift
|
||||
MessageHandlingDependencyTracker.swift
|
||||
OnDiskDocumentManager.swift
|
||||
ReferenceDocumentURL.swift
|
||||
Rename.swift
|
||||
SemanticTokensLegend+SourceKitLSPLegend.swift
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package import BuildServerIntegration
|
||||
import Foundation
|
||||
package import IndexStoreDB
|
||||
package import LanguageServerProtocol
|
||||
@@ -148,6 +149,18 @@ package protocol LanguageService: AnyObject, Sendable {
|
||||
/// Sent to close a document on the Language Server.
|
||||
func closeDocument(_ notification: DidCloseTextDocumentNotification) async
|
||||
|
||||
/// Sent to open up a document on the Language Server whose contents are on-disk.
|
||||
///
|
||||
/// The snapshot will have a synthesized name and the caller is responsible for synthesizing build settings for it.
|
||||
///
|
||||
/// - Important: This should only be called by `OnDiskDocumentManager`.
|
||||
func openOnDiskDocument(snapshot: DocumentSnapshot, buildSettings: FileBuildSettings) async throws
|
||||
|
||||
/// Sent to close a document that was opened by `openOnDiskDocument`.
|
||||
///
|
||||
/// - Important: This should only be called by `OnDiskDocumentManager`.
|
||||
func closeOnDiskDocument(uri: DocumentURI) async throws
|
||||
|
||||
/// Re-open the given document, discarding any in-memory state and forcing an AST to be re-built after build settings
|
||||
/// have been changed. This needs to be handled via a notification to ensure that no other request for this document
|
||||
/// is executing at the same time.
|
||||
@@ -197,8 +210,9 @@ package protocol LanguageService: AnyObject, Sendable {
|
||||
/// Return the symbol graph at the given location for the contents of the document as they are on-disk (opposed to the
|
||||
/// in-memory modified version of the document).
|
||||
func symbolGraph(
|
||||
forOnDiskContentsOf symbolDocumentUri: DocumentURI,
|
||||
at location: SymbolLocation
|
||||
forOnDiskContentsAt location: SymbolLocation,
|
||||
in workspace: Workspace,
|
||||
manager: OnDiskDocumentManager
|
||||
) async throws -> String
|
||||
|
||||
/// Request a generated interface of a module to display in the IDE.
|
||||
@@ -330,6 +344,14 @@ package extension LanguageService {
|
||||
|
||||
func clientInitialized(_ initialized: LanguageServerProtocol.InitializedNotification) async {}
|
||||
|
||||
func openOnDiskDocument(snapshot: DocumentSnapshot, buildSettings: FileBuildSettings) async throws {
|
||||
throw ResponseError.unknown("\(#function) not implemented in \(Self.self)")
|
||||
}
|
||||
|
||||
func closeOnDiskDocument(uri: DocumentURI) async throws {
|
||||
throw ResponseError.unknown("\(#function) not implemented in \(Self.self)")
|
||||
}
|
||||
|
||||
func willSaveDocument(_ notification: WillSaveTextDocumentNotification) async {}
|
||||
|
||||
func didSaveDocument(_ notification: DidSaveTextDocumentNotification) async {}
|
||||
@@ -368,8 +390,9 @@ package extension LanguageService {
|
||||
}
|
||||
|
||||
func symbolGraph(
|
||||
forOnDiskContentsOf symbolDocumentUri: DocumentURI,
|
||||
at location: SymbolLocation
|
||||
forOnDiskContentsAt location: SymbolLocation,
|
||||
in workspace: Workspace,
|
||||
manager: OnDiskDocumentManager
|
||||
) async throws -> String {
|
||||
throw ResponseError.internalError("\(#function) not implemented in \(Self.self)")
|
||||
}
|
||||
|
||||
95
Sources/SourceKitLSP/OnDiskDocumentManager.swift
Normal file
95
Sources/SourceKitLSP/OnDiskDocumentManager.swift
Normal file
@@ -0,0 +1,95 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
package import LanguageServerProtocol
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ add_library(SwiftLanguageService STATIC
|
||||
SemanticRefactorCommand.swift
|
||||
DocumentFormatting.swift
|
||||
DiagnosticReportManager.swift
|
||||
WithSnapshotFromDiskOpenedInSourcekitd.swift
|
||||
SemanticTokens.swift
|
||||
ExpandMacroCommand.swift
|
||||
Diagnostic.swift
|
||||
|
||||
@@ -294,29 +294,24 @@ package actor SwiftLanguageService: LanguageService, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
func buildSettings(for document: DocumentURI, fallbackAfterTimeout: Bool) async -> FileBuildSettings? {
|
||||
let buildSettingsFile = document.buildSettingsFile
|
||||
|
||||
func compileCommand(for document: DocumentURI, fallbackAfterTimeout: Bool) async -> SwiftCompileCommand? {
|
||||
guard let sourceKitLSPServer else {
|
||||
logger.fault("Cannot retrieve build settings because SourceKitLSPServer is no longer alive")
|
||||
return nil
|
||||
}
|
||||
guard let workspace = await sourceKitLSPServer.workspaceForDocument(uri: buildSettingsFile) else {
|
||||
guard let workspace = await sourceKitLSPServer.workspaceForDocument(uri: document.buildSettingsFile) else {
|
||||
return nil
|
||||
}
|
||||
return await workspace.buildServerManager.buildSettingsInferredFromMainFile(
|
||||
for: buildSettingsFile,
|
||||
let settings = await workspace.buildServerManager.buildSettingsInferredFromMainFile(
|
||||
for: document.buildSettingsFile,
|
||||
language: .swift,
|
||||
fallbackAfterTimeout: fallbackAfterTimeout
|
||||
)
|
||||
}
|
||||
|
||||
func compileCommand(for document: DocumentURI, fallbackAfterTimeout: Bool) async -> SwiftCompileCommand? {
|
||||
if let settings = await self.buildSettings(for: document, fallbackAfterTimeout: fallbackAfterTimeout) {
|
||||
return SwiftCompileCommand(settings)
|
||||
} else {
|
||||
guard let settings else {
|
||||
return nil
|
||||
}
|
||||
return SwiftCompileCommand(settings)
|
||||
}
|
||||
|
||||
func send(
|
||||
@@ -586,6 +581,22 @@ extension SwiftLanguageService {
|
||||
}
|
||||
}
|
||||
|
||||
package func openOnDiskDocument(snapshot: DocumentSnapshot, buildSettings: FileBuildSettings) async throws {
|
||||
_ = try await send(
|
||||
sourcekitdRequest: \.editorOpen,
|
||||
self.openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: SwiftCompileCommand(buildSettings)),
|
||||
snapshot: snapshot
|
||||
)
|
||||
}
|
||||
|
||||
package func closeOnDiskDocument(uri: DocumentURI) async throws {
|
||||
_ = try await send(
|
||||
sourcekitdRequest: \.editorClose,
|
||||
self.closeDocumentSourcekitdRequest(uri: uri),
|
||||
snapshot: nil
|
||||
)
|
||||
}
|
||||
|
||||
/// Cancels any in-flight tasks to send a `PublishedDiagnosticsNotification` after edits.
|
||||
private func cancelInFlightPublishDiagnosticsTask(for document: DocumentURI) {
|
||||
if let inFlightTask = inFlightPublishDiagnosticsTasks[document] {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
|
||||
// Copyright (c) 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
|
||||
@@ -10,6 +10,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import BuildServerIntegration
|
||||
import Foundation
|
||||
package import IndexStoreDB
|
||||
package import LanguageServerProtocol
|
||||
@@ -19,24 +20,22 @@ import SwiftSyntax
|
||||
|
||||
extension SwiftLanguageService {
|
||||
package func symbolGraph(
|
||||
forOnDiskContentsOf symbolDocumentUri: DocumentURI,
|
||||
at location: SymbolLocation
|
||||
forOnDiskContentsAt location: SymbolLocation,
|
||||
in workspace: Workspace,
|
||||
manager: OnDiskDocumentManager
|
||||
) async throws -> String {
|
||||
return try await withSnapshotFromDiskOpenedInSourcekitd(
|
||||
uri: symbolDocumentUri,
|
||||
fallbackSettingsAfterTimeout: false
|
||||
) { snapshot, compileCommand in
|
||||
let symbolGraph = try await cursorInfo(
|
||||
snapshot,
|
||||
compileCommand: compileCommand,
|
||||
Range(snapshot.position(of: location)),
|
||||
includeSymbolGraph: true
|
||||
).symbolGraph
|
||||
guard let symbolGraph else {
|
||||
throw ResponseError.internalError("Unable to retrieve symbol graph")
|
||||
}
|
||||
return symbolGraph
|
||||
let (snapshot, buildSettings) = try await manager.open(uri: location.documentUri, language: .swift, in: workspace)
|
||||
|
||||
let symbolGraph = try await cursorInfo(
|
||||
snapshot,
|
||||
compileCommand: SwiftCompileCommand(buildSettings),
|
||||
Range(snapshot.position(of: location)),
|
||||
includeSymbolGraph: true
|
||||
).symbolGraph
|
||||
guard let symbolGraph else {
|
||||
throw ResponseError.internalError("Unable to retrieve symbol graph")
|
||||
}
|
||||
return symbolGraph
|
||||
}
|
||||
|
||||
package func symbolGraph(
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import BuildServerIntegration
|
||||
import Foundation
|
||||
import LanguageServerProtocol
|
||||
import SKLogging
|
||||
import SKUtilities
|
||||
import SourceKitLSP
|
||||
import SwiftExtensions
|
||||
|
||||
extension SwiftLanguageService {
|
||||
/// Open a unique dummy document in sourcekitd that has the contents of the file on disk for `uri` but an arbitrary
|
||||
/// URI which doesn't exist on disk. Invoke `body` with a snapshot that contains the on-disk document contents and has
|
||||
/// that dummy URI as well as build settings that were inferred from `uri` but have that URI replaced with the dummy
|
||||
/// URI. Close the document in sourcekit after `body` has finished.
|
||||
func withSnapshotFromDiskOpenedInSourcekitd<Result: Sendable>(
|
||||
uri: DocumentURI,
|
||||
fallbackSettingsAfterTimeout: Bool,
|
||||
body: (_ snapshot: DocumentSnapshot, _ patchedCompileCommand: SwiftCompileCommand?) async throws -> Result
|
||||
) async throws -> Result {
|
||||
guard let fileURL = uri.fileURL else {
|
||||
throw ResponseError.unknown("Cannot create snapshot with on-disk contents for non-file URI \(uri.forLogging)")
|
||||
}
|
||||
let snapshot = DocumentSnapshot(
|
||||
uri: try DocumentURI(filePath: "\(UUID().uuidString)/\(fileURL.filePath)", isDirectory: false),
|
||||
language: .swift,
|
||||
version: 0,
|
||||
lineTable: LineTable(try String(contentsOf: fileURL, encoding: .utf8))
|
||||
)
|
||||
let patchedCompileCommand: SwiftCompileCommand? =
|
||||
if let buildSettings = await self.buildSettings(
|
||||
for: uri,
|
||||
fallbackAfterTimeout: fallbackSettingsAfterTimeout
|
||||
) {
|
||||
SwiftCompileCommand(buildSettings.patching(newFile: snapshot.uri, originalFile: uri))
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
|
||||
_ = try await send(
|
||||
sourcekitdRequest: \.editorOpen,
|
||||
self.openDocumentSourcekitdRequest(snapshot: snapshot, compileCommand: patchedCompileCommand),
|
||||
snapshot: snapshot
|
||||
)
|
||||
let result: Swift.Result<Result, Error>
|
||||
do {
|
||||
result = .success(try await body(snapshot, patchedCompileCommand))
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
await orLog("Close helper document '\(snapshot.uri)' for cursorInfoFromDisk") {
|
||||
_ = try await send(
|
||||
sourcekitdRequest: \.editorClose,
|
||||
self.closeDocumentSourcekitdRequest(uri: snapshot.uri),
|
||||
snapshot: snapshot
|
||||
)
|
||||
}
|
||||
return try result.get()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user