Skip to content

Commit a8eae3c

Browse files
remove protocols from DocCDocumentation module
1 parent c1a6e65 commit a8eae3c

16 files changed

+213
-342
lines changed

Sources/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ add_subdirectory(CCompletionScoring)
88
add_subdirectory(CompletionScoring)
99
add_subdirectory(Csourcekitd)
1010
add_subdirectory(Diagnose)
11-
add_subdirectory(DocCDocumentation)
1211
add_subdirectory(InProcessClient)
1312
add_subdirectory(LanguageServerProtocol)
1413
add_subdirectory(LanguageServerProtocolExtensions)

Sources/DocCDocumentation/CMakeLists.txt

Lines changed: 0 additions & 20 deletions
This file was deleted.

Sources/DocCDocumentation/DocCCatalogIndexManager.swift

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,11 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
package import Foundation
14-
15-
package protocol DocCCatalogIndex: Sendable {
16-
func documentationExtension(for symbolLink: any DocCSymbolLink) -> URL?
17-
}
18-
19-
#if canImport(SwiftDocC)
2014
@preconcurrency import SwiftDocC
2115

2216
final actor DocCCatalogIndexManager {
2317
private let server: DocCServer
24-
private var catalogToIndexMap = [URL: Result<DocCCatalogIndexImpl, DocCIndexError>]()
18+
private var catalogToIndexMap = [URL: Result<DocCCatalogIndex, DocCIndexError>]()
2519

2620
init(server: DocCServer) {
2721
self.server = server
@@ -36,11 +30,11 @@ final actor DocCCatalogIndexManager {
3630
}
3731
}
3832

39-
func index(for catalogURL: URL) async throws(DocCIndexError) -> DocCCatalogIndexImpl {
33+
func index(for catalogURL: URL) async throws(DocCIndexError) -> DocCCatalogIndex {
4034
if let existingCatalog = catalogToIndexMap[catalogURL] {
4135
return try existingCatalog.get()
4236
}
43-
let catalogIndexResult: Result<DocCCatalogIndexImpl, DocCIndexError>
37+
let catalogIndexResult: Result<DocCCatalogIndex, DocCIndexError>
4438
do {
4539
let convertResponse = try await server.convert(
4640
externalIDsToConvert: [],
@@ -66,7 +60,7 @@ final actor DocCCatalogIndexManager {
6660
Result { try JSONDecoder().decode(RenderReferenceStore.self, from: renderReferenceStoreData) }
6761
.flatMapError { .failure(.decodingFailure($0)) }
6862
}
69-
.map { DocCCatalogIndexImpl(from: $0) }
63+
.map { DocCCatalogIndex(from: $0) }
7064
} catch {
7165
catalogIndexResult = .failure(.internalError(error))
7266
}
@@ -93,10 +87,10 @@ package enum DocCIndexError: LocalizedError {
9387
}
9488
}
9589

96-
package struct DocCCatalogIndexImpl: DocCCatalogIndex {
90+
package struct DocCCatalogIndex: Sendable {
9791
private let assetReferenceToDataAsset: [String: DataAsset]
9892
private let fuzzyAssetReferenceToDataAsset: [String: DataAsset]
99-
private let documentationExtensionToSourceURL: [DocCSymbolLinkImpl: URL]
93+
private let documentationExtensionToSourceURL: [DocCSymbolLink: URL]
10094
let articlePathToSourceURLAndReference: [String: (URL, TopicRenderReference)]
10195
let tutorialPathToSourceURLAndReference: [String: (URL, TopicRenderReference)]
10296
let tutorialOverviewPathToSourceURLAndReference: [String: (URL, TopicRenderReference)]
@@ -105,10 +99,7 @@ package struct DocCCatalogIndexImpl: DocCCatalogIndex {
10599
assetReferenceToDataAsset[assetReference.assetName] ?? fuzzyAssetReferenceToDataAsset[assetReference.assetName]
106100
}
107101

108-
package func documentationExtension(for symbolLink: any DocCSymbolLink) -> URL? {
109-
guard let symbolLink = symbolLink as? DocCSymbolLinkImpl else {
110-
return nil
111-
}
102+
package func documentationExtension(for symbolLink: DocCSymbolLink) -> URL? {
112103
return documentationExtensionToSourceURL[symbolLink]
113104
}
114105

@@ -128,7 +119,7 @@ package struct DocCCatalogIndexImpl: DocCCatalogIndex {
128119
self.assetReferenceToDataAsset = assetReferenceToDataAsset
129120
self.fuzzyAssetReferenceToDataAsset = fuzzyAssetReferenceToDataAsset
130121
// Markdown and Tutorial content
131-
var documentationExtensionToSourceURL = [DocCSymbolLinkImpl: URL]()
122+
var documentationExtensionToSourceURL = [DocCSymbolLink: URL]()
132123
var articlePathToSourceURLAndReference = [String: (URL, TopicRenderReference)]()
133124
var tutorialPathToSourceURLAndReference = [String: (URL, TopicRenderReference)]()
134125
var tutorialOverviewPathToSourceURLAndReference = [String: (URL, TopicRenderReference)]()
@@ -145,7 +136,7 @@ package struct DocCCatalogIndexImpl: DocCCatalogIndex {
145136
else {
146137
continue
147138
}
148-
let doccSymbolLink = DocCSymbolLinkImpl(absoluteSymbolLink: absoluteSymbolLink)
139+
let doccSymbolLink = DocCSymbolLink(absoluteSymbolLink: absoluteSymbolLink)
149140
documentationExtensionToSourceURL[doccSymbolLink] = topicContentValue.source
150141
} else if topicRenderReference.kind == .article {
151142
articlePathToSourceURLAndReference[renderReferenceKey.url.lastPathComponent] = (
@@ -175,4 +166,3 @@ fileprivate extension URL {
175166
return components?.url ?? self
176167
}
177168
}
178-
#endif

Sources/DocCDocumentation/DocCDocumentationManager.swift

Lines changed: 158 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,117 @@
1-
//===----------------------------------------------------------------------===//
2-
//
3-
// This source file is part of the Swift.org open source project
4-
//
5-
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6-
// Licensed under Apache License v2.0 with Runtime Library Exception
7-
//
8-
// See https://swift.org/LICENSE.txt for license information
9-
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10-
//
11-
//===----------------------------------------------------------------------===//
12-
1+
import BuildServerProtocol
2+
import BuildSystemIntegration
133
package import Foundation
14-
@preconcurrency package import IndexStoreDB
4+
package import IndexStoreDB
155
package import LanguageServerProtocol
166
package import SemanticIndex
7+
import SwiftDocC
178

18-
/// An actor that can be used to interface with SwiftDocC
19-
package protocol DocCDocumentationManager: Actor {
20-
func getRenderingSupport() -> DocCDocumentationManagerWithRendering?
21-
}
9+
package struct DocCDocumentationManager: Sendable {
10+
private let doccServer: DocCServer
11+
private let referenceResolutionService: DocCReferenceResolutionService
12+
private let catalogIndexManager: DocCCatalogIndexManager
2213

23-
/// An actor that can be used to render SwiftDocC documentation
24-
package protocol DocCDocumentationManagerWithRendering: DocCDocumentationManager {
25-
func filesDidChange(_: [FileEvent]) async
14+
package init() {
15+
let symbolResolutionServer = DocumentationServer(qualityOfService: .unspecified)
16+
doccServer = DocCServer(
17+
peer: symbolResolutionServer,
18+
qualityOfService: .default
19+
)
20+
catalogIndexManager = DocCCatalogIndexManager(server: doccServer)
21+
referenceResolutionService = DocCReferenceResolutionService()
22+
symbolResolutionServer.register(service: referenceResolutionService)
23+
}
2624

27-
func catalogIndex(for: URL) async throws -> DocCCatalogIndex
25+
package func filesDidChange(_ events: [FileEvent]) async {
26+
let affectedCatalogURLs = events.reduce(into: Set<URL>()) { affectedCatalogURLs, event in
27+
guard let catalogURL = event.uri.fileURL?.doccCatalogURL else {
28+
return
29+
}
30+
affectedCatalogURLs.insert(catalogURL)
31+
}
32+
await catalogIndexManager.invalidate(catalogURLs: affectedCatalogURLs)
33+
}
2834

29-
func symbolLink(string: String) -> DocCSymbolLink?
35+
package func catalogIndex(for catalogURL: URL) async throws(DocCIndexError) -> DocCCatalogIndex {
36+
try await catalogIndexManager.index(for: catalogURL)
37+
}
3038

31-
func symbolLink(forUSR: String, in: CheckedIndex) -> DocCSymbolLink?
39+
package func symbolLink(string: String) -> DocCSymbolLink? {
40+
DocCSymbolLink(string: string)
41+
}
3242

33-
func primaryDefinitionOrDeclarationOccurrence(
34-
ofDocCSymbolLink: DocCSymbolLink,
35-
in: CheckedIndex
36-
) -> SymbolOccurrence?
43+
package func symbolLink(forUSR usr: String, in index: CheckedIndex) -> DocCSymbolLink? {
44+
guard let topLevelSymbolOccurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
45+
return nil
46+
}
47+
let module = topLevelSymbolOccurrence.location.moduleName
48+
var components = [topLevelSymbolOccurrence.symbol.name]
49+
// Find any child symbols
50+
var symbolOccurrence: SymbolOccurrence? = topLevelSymbolOccurrence
51+
while let currentSymbolOccurrence = symbolOccurrence, components.count > 0 {
52+
let parentRelation = currentSymbolOccurrence.relations.first { $0.roles.contains(.childOf) }
53+
guard let parentRelation else {
54+
break
55+
}
56+
if parentRelation.symbol.kind == .extension {
57+
symbolOccurrence = index.occurrences(relatedToUSR: parentRelation.symbol.usr, roles: .extendedBy).first
58+
} else {
59+
symbolOccurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: parentRelation.symbol.usr)
60+
}
61+
if let symbolOccurrence {
62+
components.insert(symbolOccurrence.symbol.name, at: 0)
63+
}
64+
}
65+
return DocCSymbolLink(string: module)?.appending(components: components)
66+
}
3767

38-
func renderDocCDocumentation(
39-
symbolUSR: String?,
40-
symbolGraph: String?,
41-
overrideDocComments: [String]?,
42-
markupFile: String?,
43-
tutorialFile: String?,
44-
moduleName: String?,
45-
catalogURL: URL?
46-
) async throws -> DoccDocumentationResponse
47-
}
68+
/// Find a `SymbolOccurrence` that is considered the primary definition of the symbol with the given `DocCSymbolLink`.
69+
///
70+
/// If the `DocCSymbolLink` has an ambiguous definition, the most important role of this function is to deterministically return
71+
/// the same result every time.
72+
package func primaryDefinitionOrDeclarationOccurrence(
73+
ofDocCSymbolLink symbolLink: DocCSymbolLink,
74+
in index: CheckedIndex
75+
) -> SymbolOccurrence? {
76+
var components = symbolLink.components
77+
guard components.count > 0 else {
78+
return nil
79+
}
80+
// Do a lookup to find the top level symbol
81+
let topLevelSymbolName = components.removeLast().name
82+
var topLevelSymbolOccurrences = [SymbolOccurrence]()
83+
index.forEachCanonicalSymbolOccurrence(byName: topLevelSymbolName) { symbolOccurrence in
84+
guard symbolOccurrence.location.moduleName == symbolLink.moduleName else {
85+
return true
86+
}
87+
topLevelSymbolOccurrences.append(symbolOccurrence)
88+
return true
89+
}
90+
guard let topLevelSymbolOccurrence = topLevelSymbolOccurrences.first else {
91+
return nil
92+
}
93+
// Find any child symbols
94+
var symbolOccurrence: SymbolOccurrence? = topLevelSymbolOccurrence
95+
while let currentSymbolOccurrence = symbolOccurrence, components.count > 0 {
96+
let nextComponent = components.removeLast()
97+
let parentRelation = currentSymbolOccurrence.relations.first {
98+
$0.roles.contains(.childOf) && $0.symbol.name == nextComponent.name
99+
}
100+
guard let parentRelation else {
101+
break
102+
}
103+
if parentRelation.symbol.kind == .extension {
104+
symbolOccurrence = index.occurrences(relatedToUSR: parentRelation.symbol.usr, roles: .extendedBy).first
105+
} else {
106+
symbolOccurrence = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: parentRelation.symbol.usr)
107+
}
108+
}
109+
guard symbolOccurrence != nil else {
110+
return nil
111+
}
112+
return topLevelSymbolOccurrence
113+
}
48114

49-
extension DocCDocumentationManagerWithRendering {
50115
package func renderDocCDocumentation(
51116
symbolUSR: String? = nil,
52117
symbolGraph: String? = nil,
@@ -56,21 +121,61 @@ extension DocCDocumentationManagerWithRendering {
56121
moduleName: String?,
57122
catalogURL: URL?
58123
) async throws -> DoccDocumentationResponse {
59-
try await renderDocCDocumentation(
60-
symbolUSR: symbolUSR,
61-
symbolGraph: symbolGraph,
62-
overrideDocComments: overrideDocComments,
63-
markupFile: markupFile,
64-
tutorialFile: tutorialFile,
65-
moduleName: moduleName,
66-
catalogURL: catalogURL
124+
// Make inputs consumable by DocC
125+
var externalIDsToConvert: [String]? = nil
126+
var overridingDocumentationComments = [String: [String]]()
127+
if let symbolUSR {
128+
externalIDsToConvert = [symbolUSR]
129+
if let overrideDocComments {
130+
overridingDocumentationComments[symbolUSR] = overrideDocComments
131+
}
132+
}
133+
var symbolGraphs = [Data]()
134+
if let symbolGraphData = symbolGraph?.data(using: .utf8) {
135+
symbolGraphs.append(symbolGraphData)
136+
}
137+
var markupFiles = [Data]()
138+
if let markupFile = markupFile?.data(using: .utf8) {
139+
markupFiles.append(markupFile)
140+
}
141+
var tutorialFiles = [Data]()
142+
if let tutorialFile = tutorialFile?.data(using: .utf8) {
143+
tutorialFiles.append(tutorialFile)
144+
}
145+
// Store the convert request identifier in order to fulfill index requests from SwiftDocC
146+
let convertRequestIdentifier = UUID().uuidString
147+
var catalogIndex: DocCCatalogIndex? = nil
148+
if let catalogURL {
149+
catalogIndex = try await catalogIndexManager.index(for: catalogURL)
150+
}
151+
referenceResolutionService.addContext(
152+
DocCReferenceResolutionContext(
153+
catalogURL: catalogURL,
154+
catalogIndex: catalogIndex
155+
),
156+
withKey: convertRequestIdentifier
67157
)
158+
// Send the convert request to SwiftDocC and wait for the response
159+
let convertResponse = try await doccServer.convert(
160+
externalIDsToConvert: externalIDsToConvert,
161+
documentPathsToConvert: nil,
162+
includeRenderReferenceStore: false,
163+
documentationBundleLocation: nil,
164+
documentationBundleDisplayName: moduleName ?? "Unknown",
165+
documentationBundleIdentifier: "unknown",
166+
symbolGraphs: symbolGraphs,
167+
overridingDocumentationComments: overridingDocumentationComments,
168+
emitSymbolSourceFileURIs: false,
169+
markupFiles: markupFiles,
170+
tutorialFiles: tutorialFiles,
171+
convertRequestIdentifier: convertRequestIdentifier
172+
)
173+
guard let renderNodeData = convertResponse.renderNodes.first else {
174+
throw ResponseError.internalError("SwiftDocC did not return any render nodes")
175+
}
176+
guard let renderNode = String(data: renderNodeData, encoding: .utf8) else {
177+
throw ResponseError.internalError("Failed to encode render node from SwiftDocC")
178+
}
179+
return DoccDocumentationResponse(renderNode: renderNode)
68180
}
69181
}
70-
71-
/// Creates a new ``DocCDocumentationManager`` that can be used to interface with SwiftDocC.
72-
///
73-
/// - Returns: An instance of ``DocCDocumentationManager``
74-
package func createDocCDocumentationManager() -> any DocCDocumentationManager {
75-
DocCDocumentationManagerImpl()
76-
}

0 commit comments

Comments
 (0)