Skip to content

Commit 97153b8

Browse files
committed
add unknown.extension and inContextOf relationship + adapt transformation to build context hierarchy
1 parent e9fd5f4 commit 97153b8

9 files changed

+118
-11
lines changed

Sources/SwiftDocC/Infrastructure/CoverageDataEntry.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ extension CoverageDataEntry {
247247
.extendedClass,
248248
.extendedStructure,
249249
.extendedEnumeration,
250-
.extendedProtocol:
250+
.extendedProtocol,
251+
.unknownExtendedType:
251252
self = .types
252253
case .localVariable,
253254
.instanceProperty,

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
10251025
private func parentChildRelationship(from edge: SymbolGraph.Relationship) -> (ResolvedTopicReference, ResolvedTopicReference)? {
10261026
// Filter only parent <-> child edges
10271027
switch edge.kind {
1028-
case .memberOf, .requirementOf, .declaredIn:
1028+
case .memberOf, .requirementOf, .declaredIn, .inContextOf:
10291029
guard let parentRef = symbolIndex[edge.target]?.reference, let childRef = symbolIndex[edge.source]?.reference else {
10301030
return nil
10311031
}

Sources/SwiftDocC/Infrastructure/External Data/ExternalSymbolResolver+SymbolKind.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ extension ExternalSymbolResolver {
7676
symbolKind = .extendedEnumeration
7777
case .extendedProtocol:
7878
symbolKind = .extendedProtocol
79+
case .unknownExtendedType:
80+
symbolKind = .unknownExtendedType
7981

8082
// There shouldn't be any reason for a symbol graph file to reference one of these kinds outside of the symbol graph itself.
8183
// Return `.class` as the symbol kind (acting as "any symbol") so that the render reference gets a "symbol" role.

Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypesFormatExtension.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import SymbolKit
1414

1515
extension SymbolGraph.Relationship.Kind {
1616
static let declaredIn = Self(rawValue: "declaredIn")
17+
18+
static let inContextOf = Self(rawValue: "inContextOf")
1719
}
1820

1921
// MARK: Custom Symbol Kind Identifiers
@@ -27,6 +29,8 @@ extension SymbolGraph.Symbol.KindIdentifier {
2729

2830
static let extendedEnumeration = Self(rawValue: "enum.extension")
2931

32+
static let unknownExtendedType = Self(rawValue: "unknown.extension")
33+
3034
static let extendedModule = Self(rawValue: "module.extension")
3135

3236
init?(extending other: Self) {
@@ -72,9 +76,11 @@ extension SymbolGraph.Symbol.Kind {
7276
case .some(.extendedEnumeration):
7377
return Self(parsedIdentifier: .extendedEnumeration, displayName: "Extended Enumeration")
7478
default:
75-
return Self(rawIdentifier: "unknown.extension", displayName: "Extended Type")
79+
return unknownExtendedType
7680
}
7781
}
82+
83+
static let unknownExtendedType = Self(parsedIdentifier: .unknownExtendedType, displayName: "Extended Type")
7884
}
7985

8086

Sources/SwiftDocC/Infrastructure/Symbol Graph/ExtendedTypesFormatTransformation.swift

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ extension ExtendedTypesFormatTransformation {
194194

195195
var (extendedTypeSymbols,
196196
extensionBlockToExtendedTypeMapping,
197-
extendedTypeToExtensionBlockMapping) = synthesizeExtendedTypeSymbols(using: extensionBlockSymbols, extensionToRelationships)
197+
extendedTypeToExtensionBlockMapping) = synthesizePrimaryExtendedTypeSymbols(using: extensionBlockSymbols, extensionToRelationships)
198+
199+
let contextOfRelationships = synthesizeSecondaryExtendedTypeSymbols(&extendedTypeSymbols)
198200

199201
redirect(\.target, of: &memberOfRelationships, using: extensionBlockToExtendedTypeMapping)
200202

@@ -217,12 +219,79 @@ extension ExtendedTypesFormatTransformation {
217219

218220
symbolGraph.relationships.append(contentsOf: memberOfRelationships)
219221
symbolGraph.relationships.append(contentsOf: conformsToRelationships)
222+
symbolGraph.relationships.append(contentsOf: contextOfRelationships)
220223
extendedTypeSymbols.values.forEach { symbol in symbolGraph.symbols[symbol.identifier.precise] = symbol }
221224

222-
try synthesizeExtendedModuleSymbolsAndDeclaredInRelationships(on: &symbolGraph, using: extendedTypeSymbols.values.map(\.identifier.precise))
225+
try synthesizeExtendedModuleSymbolsAndDeclaredInRelationships(on: &symbolGraph, using: extendedTypeSymbols.values.filter { symbol in symbol.pathComponents.count == 2 }.map(\.identifier.precise))
223226

224227
return true
225228
}
229+
230+
private static func synthesizeSecondaryExtendedTypeSymbols(_ extendedTypeSymbols: inout [String: SymbolGraph.Symbol]) -> [SymbolGraph.Relationship] {
231+
let sortedKeys: [(pathComponents: [String], preciseId: String)] = extendedTypeSymbols.map { key, value in
232+
(value.pathComponents, key)
233+
}.sorted(by: { a, b in a.pathComponents.count <= b.pathComponents.count && a.preciseId < b.preciseId })
234+
235+
var pathComponentsToSymbolIds: [ArraySlice<String>: String] = [:]
236+
pathComponentsToSymbolIds.reserveCapacity(extendedTypeSymbols.count)
237+
for (key, symbol) in extendedTypeSymbols {
238+
pathComponentsToSymbolIds[symbol.pathComponents[1...]] = key
239+
}
240+
241+
func lookupSymbol(_ pathComponents: ArraySlice<String>) -> SymbolGraph.Symbol? {
242+
guard let id = pathComponentsToSymbolIds[pathComponents] else {
243+
return nil
244+
}
245+
246+
return extendedTypeSymbols[id]
247+
}
248+
249+
var relationships = [SymbolGraph.Relationship]()
250+
var symbolIsConnectedToParent = [String: Bool]()
251+
symbolIsConnectedToParent.reserveCapacity(extendedTypeSymbols.count)
252+
253+
for (pathComponents, preciseId) in sortedKeys {
254+
guard var symbol = extendedTypeSymbols[preciseId] else {
255+
continue
256+
}
257+
258+
let modulePrefix = pathComponents[0]
259+
var pathComponents = pathComponents[1..<pathComponents.count-1]
260+
261+
while !pathComponents.isEmpty {
262+
let parent = lookupSymbol(pathComponents)?.replacing(\.accessLevel) { oldSymbol in
263+
max(oldSymbol.accessLevel, symbol.accessLevel)
264+
} ?? SymbolGraph.Symbol(identifier: .init(precise: "s:e:" + symbol.identifier.precise,
265+
interfaceLanguage: symbol.identifier.interfaceLanguage),
266+
names: .init(title: pathComponents.joined(separator: "."),
267+
navigator: pathComponents.last?.asDeclarationFragment(.identifier),
268+
subHeading: nil,
269+
prose: nil),
270+
pathComponents: [modulePrefix] + pathComponents,
271+
docComment: nil,
272+
accessLevel: symbol.accessLevel,
273+
kind: .unknownExtendedType,
274+
mixins: symbol.mixins.keeping(SymbolGraph.Symbol.Swift.Extension.mixinKey))
275+
276+
277+
pathComponentsToSymbolIds[pathComponents] = parent.identifier.precise
278+
extendedTypeSymbols[parent.identifier.precise] = parent
279+
280+
if !symbolIsConnectedToParent[symbol.identifier.precise, default: false] {
281+
relationships.append(.init(source: symbol.identifier.precise,
282+
target: parent.identifier.precise,
283+
kind: .inContextOf,
284+
targetFallback: parent.title))
285+
symbolIsConnectedToParent[symbol.identifier.precise] = true
286+
}
287+
288+
symbol = parent
289+
pathComponents.removeLast()
290+
}
291+
}
292+
293+
return relationships
294+
}
226295

227296
/// Tries to obtain `docComment`s for all `targets` and copies the documentaiton from sources to the target.
228297
///
@@ -344,7 +413,7 @@ extension ExtendedTypesFormatTransformation {
344413
///
345414
/// - Returns: - the created extended type symbols keyed by their precise identifier, along with a bidirectional
346415
/// mapping between the extended type symbols and the `.extension` symbols
347-
private static func synthesizeExtendedTypeSymbols<RS: Sequence>(using extensionBlockSymbols: [String: SymbolGraph.Symbol],
416+
private static func synthesizePrimaryExtendedTypeSymbols<RS: Sequence>(using extensionBlockSymbols: [String: SymbolGraph.Symbol],
348417
_ extensionToRelationships: RS)
349418
-> (extendedTypeSymbols: [String: SymbolGraph.Symbol],
350419
extensionBlockToExtendedTypeMapping: [String: String],
@@ -354,10 +423,11 @@ extension ExtendedTypesFormatTransformation {
354423
var extendedTypeSymbols: [String: SymbolGraph.Symbol] = [:]
355424
var extensionBlockToExtendedTypeMapping: [String: String] = [:]
356425
var extendedTypeToExtensionBlockMapping: [String: [String]] = [:]
426+
var pathComponentToExtendedTypeMapping: [ArraySlice<String>: String] = [:]
357427

358428
extensionBlockToExtendedTypeMapping.reserveCapacity(extensionBlockSymbols.count)
359429

360-
let createExtendedTypeSymbol = { (extensionBlockSymbol: SymbolGraph.Symbol, id: String) -> SymbolGraph.Symbol in
430+
let createExtendedTypeSymbolAndAnchestors = { (extensionBlockSymbol: SymbolGraph.Symbol, id: String) -> SymbolGraph.Symbol in
361431
var newMixins = [String: Mixin]()
362432

363433
if var swiftExtension = extensionBlockSymbol[mixin: SymbolGraph.Symbol.Swift.Extension.self] {
@@ -408,13 +478,14 @@ extension ExtendedTypesFormatTransformation {
408478

409479
let symbol: SymbolGraph.Symbol = extendedTypeSymbols[extendedSymbolId]?.replacing(\.accessLevel) { oldSymbol in
410480
max(oldSymbol.accessLevel, extensionBlockSymbol.accessLevel)
411-
} ?? createExtendedTypeSymbol(extensionBlockSymbol, extendedSymbolId)
481+
} ?? createExtendedTypeSymbolAndAnchestors(extensionBlockSymbol, extendedSymbolId)
482+
483+
pathComponentToExtendedTypeMapping[symbol.pathComponents[...]] = symbol.identifier.precise
412484

413485
extendedTypeSymbols[symbol.identifier.precise] = symbol
414486

415487
extensionBlockToExtendedTypeMapping[extensionTo.source] = symbol.identifier.precise
416-
extendedTypeToExtensionBlockMapping[symbol.identifier.precise]
417-
= (extendedTypeToExtensionBlockMapping[symbol.identifier.precise] ?? []) + [extensionBlockSymbol.identifier.precise]
488+
extendedTypeToExtensionBlockMapping[symbol.identifier.precise, default: []] += [extensionBlockSymbol.identifier.precise]
418489
}
419490

420491
return (extendedTypeSymbols, extensionBlockToExtendedTypeMapping, extendedTypeToExtensionBlockMapping)
@@ -522,3 +593,21 @@ private extension SymbolGraph.Relationship {
522593
return new
523594
}
524595
}
596+
597+
private extension String {
598+
func asDeclarationFragment(_ kind: SymbolGraph.Symbol.DeclarationFragments.Fragment.Kind) -> [SymbolGraph.Symbol.DeclarationFragments.Fragment] {
599+
[.init(kind: kind, spelling: self, preciseIdentifier: nil)]
600+
}
601+
}
602+
603+
private extension Dictionary {
604+
func keeping(_ keys: Key...) -> Self {
605+
var new = Self()
606+
607+
for key in keys {
608+
new[key] = self[key]
609+
}
610+
611+
return new
612+
}
613+
}

Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ struct SymbolGraphLoader {
135135

136136
// feed the loaded graphs into the `graphLoader`
137137
for (url, (_, graph)) in loadedGraphs {
138+
let encoder = JSONEncoder()
139+
encoder.outputFormatting = .prettyPrinted
140+
print(url.absoluteString)
141+
print(String(data: try! encoder.encode(graph), encoding: .utf8)!)
138142
graphLoader.mergeSymbolGraph(graph, at: url)
139143
}
140144

Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ extension AutomaticCuration {
209209
case .extendedStructure: return "Extended Structures"
210210
case .extendedEnumeration: return "Extended Enumerations"
211211
case .extendedProtocol: return "Extended Protocols"
212+
case .unknownExtendedType: return "Extended Types"
212213
default: return "Symbols"
213214
}
214215
}
@@ -244,5 +245,6 @@ extension AutomaticCuration {
244245
.extendedProtocol,
245246
.extendedStructure,
246247
.extendedEnumeration,
248+
.unknownExtendedType,
247249
]
248250
}

Sources/SwiftDocC/Model/DocumentationNode.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ public struct DocumentationNode {
473473
case .extendedClass: return .extendedClass
474474
case .extendedEnumeration: return .extendedEnumeration
475475
case .extendedProtocol: return .extendedProtocol
476+
case .unknownExtendedType: return .unknownExtendedType
476477
default: return .unknown
477478
}
478479
}

Sources/SwiftDocC/Model/Kind.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ extension DocumentationNode.Kind {
165165
public static let extendedEnumeration = DocumentationNode.Kind(name: "Extended Enumeration", id: "org.swift.docc.kind.extendedEnumeration", isSymbol: true)
166166

167167
public static let extendedProtocol = DocumentationNode.Kind(name: "Extended Protocol", id: "org.swift.docc.kind.extendedProtocol", isSymbol: true)
168+
169+
public static let unknownExtendedType = DocumentationNode.Kind(name: "Extended Type", id: "org.swift.docc.kind.unknownExtendedType", isSymbol: true)
168170

169171
/// The list of all known kinds of documentation nodes.
170172
/// - Note: The `unknown` value is not included.
@@ -182,7 +184,7 @@ extension DocumentationNode.Kind {
182184
// Data
183185
.buildSetting, .propertyListKey,
184186
// Extended Symbols
185-
.extendedModule, .extendedStructure, .extendedClass, .extendedEnumeration, .extendedProtocol,
187+
.extendedModule, .extendedStructure, .extendedClass, .extendedEnumeration, .extendedProtocol, .unknownExtendedType,
186188
// Other
187189
.keyword, .restAPI, .tag, .propertyList, .object
188190
]

0 commit comments

Comments
 (0)