Skip to content

Commit e1e3a97

Browse files
franklinschPeter Wilson
andcommitted
Convert symbol graph dictionary structures
Add base support for dictionary and dictionaryKey structures. These model JSON request/responses for example. rdar://101407961 Co-Authored-By: Peter Wilson <[email protected]>
1 parent ccf26c9 commit e1e3a97

37 files changed

+1286
-161
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SwiftDocC/Coverage/DocumentationCoverageOptions.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ extension DocumentationCoverageOptions {
9999
internal static let typeProperty = KindFilterOptions(rawValue: BitFlagRepresentation.typeProperty.bitMask)
100100
internal static let typeSubscript = KindFilterOptions(rawValue: BitFlagRepresentation.typeSubscript.bitMask)
101101
internal static let globalVariable = KindFilterOptions(rawValue: BitFlagRepresentation.globalVariable.bitMask)
102+
internal static let dictionary = KindFilterOptions(rawValue: BitFlagRepresentation.dictionary.bitMask)
102103

103104
/// Mask with all valid/used bit flags set to 1.
104105
internal static let allSingleBitOptions: [KindFilterOptions] = {
@@ -172,6 +173,7 @@ extension DocumentationCoverageOptions.KindFilterOptions {
172173
case typeProperty
173174
case typeSubscript
174175
case globalVariable
176+
case dictionary
175177

176178
/// Parses given `String` to corresponding `BitFlagRepresentation` if possible. Returns `nil` if the given string does not specify a representable value.
177179
public init?(string: String) {
@@ -225,6 +227,8 @@ extension DocumentationCoverageOptions.KindFilterOptions {
225227
self = .typeSubscript
226228
case .globalVariable: // 20
227229
self = .globalVariable
230+
case .dictionary: // 21
231+
self = .dictionary
228232
default:
229233
return nil
230234
}
@@ -275,6 +279,8 @@ extension DocumentationCoverageOptions.KindFilterOptions {
275279
return 1 << 19
276280
case .globalVariable:
277281
return 1 << 20
282+
case .dictionary:
283+
return 1 << 21
278284
}
279285
}
280286

@@ -327,8 +333,10 @@ extension DocumentationCoverageOptions.KindFilterOptions {
327333
return .typeProperty
328334
case .typeSubscript: // 19
329335
return .typeSubscript
330-
case .globalVariable:
336+
case .globalVariable: // 20
331337
return .globalVariable
338+
case .dictionary: // 21
339+
return .dictionary
332340
}
333341
}
334342

@@ -376,6 +384,8 @@ extension DocumentationCoverageOptions.KindFilterOptions {
376384
return "type-subscript"
377385
case .globalVariable: // 20
378386
return "global-variable"
387+
case .dictionary: // 21
388+
return "dictionary"
379389
}
380390
}
381391
/// A dictionary where keys are all valid argument strings and values are corresponding instances of ``BitFlagRepresentation``.
@@ -421,6 +431,8 @@ extension DocumentationCoverageOptions.KindFilterOptions {
421431
"type-subscript": .typeSubscript,
422432
// 20
423433
"global-variable": .globalVariable,
434+
// 21
435+
"dictionary": .dictionary,
424436
]
425437

426438
}

Sources/SwiftDocC/Indexing/RenderSection+TextIndexing.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,10 @@ extension RenderAttribute {
302302
case .default(let value): return value
303303
case .maximum(let value): return value
304304
case .maximumExclusive(let value): return value
305+
case .maximumLength(let value): return value
305306
case .minimum(let value): return value
306307
case .minimumExclusive(let value): return value
308+
case .minimumLength(let value): return value
307309
case .allowedValues(let values): return values.joined(separator: " ")
308310
case .allowedTypes(let values): return values.map { return $0.map { $0.text }.joined() }.joined(separator: " ")
309311
}

Sources/SwiftDocC/Infrastructure/CoverageDataEntry.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ extension CoverageDataEntry {
239239
switch documentationNodeKind {
240240
case .class,
241241
.structure,
242+
.dictionary,
242243
.enumeration,
243244
.protocol,
244245
.typeAlias,
@@ -277,6 +278,7 @@ extension CoverageDataEntry {
277278
enum KindSpecificData: Equatable, Codable {
278279
case `class`(memberStats: [InstanceMemberType: RatioStatistic])
279280
case structure(memberStats: [InstanceMemberType: RatioStatistic])
281+
case dictionary
280282
case enumeration(memberStats: [InstanceMemberType: RatioStatistic])
281283
case `protocol`(memberStats: [InstanceMemberType: RatioStatistic])
282284
case typeAlias
@@ -444,6 +446,7 @@ extension CoverageDataEntry.KindSpecificData {
444446
internal enum Discriminant: String, Codable {
445447
case `class`
446448
case structure
449+
case dictionary
447450
case enumeration
448451
case `protocol`
449452
case `operator`
@@ -477,6 +480,7 @@ extension CoverageDataEntry.KindSpecificData {
477480
return CoverageDataEntry.KindSpecificData.`operator`(parameterStats:)
478481
case .class,
479482
.`structure`,
483+
.dictionary,
480484
.enumeration,
481485
.protocol,
482486
.typeAlias,
@@ -509,6 +513,7 @@ extension CoverageDataEntry.KindSpecificData {
509513
return CoverageDataEntry.KindSpecificData.class(memberStats:)
510514
case .instanceMethod,
511515
.initializer,
516+
.dictionary,
512517
.typeAlias,
513518
.instanceProperty,
514519
.enumerationCase,
@@ -533,6 +538,8 @@ extension CoverageDataEntry.KindSpecificData {
533538
return .class
534539
case .structure:
535540
return .`structure`
541+
case .dictionary:
542+
return .dictionary
536543
case .enumeration:
537544
return .enumeration
538545
case .protocol:
@@ -625,6 +632,8 @@ extension CoverageDataEntry.KindSpecificData {
625632
)
626633
self = try discriminant.associatedMemberStatisticsInitializer()(associatedValue)
627634

635+
case .dictionary:
636+
self = .dictionary
628637
case .typeAlias:
629638
self = .typeAlias
630639
case .instanceProperty:
@@ -657,6 +666,7 @@ extension CoverageDataEntry.KindSpecificData {
657666
try container.encode(stats, forKey: .associatedValue)
658667

659668
case .typeAlias,
669+
.dictionary,
660670
.instanceProperty,
661671
.variable,
662672
.framework,

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,8 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
14461446
for (selector, relationships) in combinedRelationships {
14471447
// Build relationships in the completed graph
14481448
buildRelationships(relationships, selector: selector, bundle: bundle, engine: diagnosticEngine)
1449+
// Merge dictionary keys into target dictionaries
1450+
populateDictionaryKeys(from: relationships, selector: selector)
14491451
}
14501452

14511453
// Index references
@@ -1535,6 +1537,42 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
15351537
}
15361538
}
15371539

1540+
/// Identifies all the dictionary keys and records them in the appropriate target dictionaries.
1541+
private func populateDictionaryKeys(
1542+
from relationships: Set<SymbolGraph.Relationship>,
1543+
selector: UnifiedSymbolGraph.Selector
1544+
) {
1545+
var keysByTarget = [String:[DictionaryKey]]()
1546+
1547+
for edge in relationships {
1548+
if edge.kind == .memberOf || edge.kind == .optionalMemberOf {
1549+
if let source = symbolIndex[edge.source], let target = symbolIndex[edge.target], let keySymbol = source.symbol,
1550+
source.kind == .dictionaryKey && target.kind == .dictionary {
1551+
let dictionaryKey = DictionaryKey(name: keySymbol.title, contents: [], symbol: keySymbol, required: (edge.kind == .memberOf))
1552+
if keysByTarget[edge.target] == nil {
1553+
keysByTarget[edge.target] = [dictionaryKey]
1554+
} else {
1555+
keysByTarget[edge.target]?.append(dictionaryKey)
1556+
}
1557+
}
1558+
}
1559+
}
1560+
1561+
// Merge in all the dictionary keys for each target into their section variants.
1562+
keysByTarget.forEach { targetIdentifier, keys in
1563+
let target = symbolIndex[targetIdentifier]
1564+
if let semantic = target?.semantic as? Symbol {
1565+
let keys = keys.sorted { $0.name < $1.name }
1566+
let trait = DocumentationDataVariantsTrait(for: selector)
1567+
if semantic.dictionaryKeysSectionVariants[trait] == nil {
1568+
semantic.dictionaryKeysSectionVariants[trait] = DictionaryKeysSection(dictionaryKeys: keys)
1569+
} else {
1570+
semantic.dictionaryKeysSectionVariants[trait]?.mergeDictionaryKeys(keys)
1571+
}
1572+
}
1573+
}
1574+
}
1575+
15381576
/// Look up and add symbols that are _referenced_ in the symbol graph but don't exist in the symbol graph, using an `externalSymbolResolver` (if not `nil`).
15391577
func resolveExternalSymbols(
15401578
in symbols: [String: UnifiedSymbolGraph.Symbol],

Sources/SwiftDocC/Infrastructure/Extensions/KindIdentifier+Curation.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import SymbolKit
1313
extension SymbolGraph.Symbol.KindIdentifier {
1414
/// The kinds of symbols that should not generate pages in the documentation hierarchy.
1515
static let noPageKinds: [SymbolGraph.Symbol.KindIdentifier] = [
16+
.dictionaryKey,
1617
.module,
1718
.snippet,
1819
.snippetGroup

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ extension ExternalSymbolResolver {
5252
symbolKind = .protocol
5353
case .structure:
5454
symbolKind = .struct
55+
case .dictionary:
56+
symbolKind = .dictionary
57+
case .dictionaryKey:
58+
symbolKind = .dictionaryKey
5559
case .instanceSubscript:
5660
symbolKind = .subscript
5761
case .typeMethod:

Sources/SwiftDocC/Infrastructure/External Data/OutOfProcessReferenceResolver.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,7 @@ extension OutOfProcessReferenceResolver {
982982
seeAlsoVariants: .empty,
983983
returnsSectionVariants: .empty,
984984
parametersSectionVariants: .empty,
985+
dictionaryKeysSectionVariants: .empty,
985986
redirectsVariants: .empty
986987
)
987988
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ enum GeneratedDocumentationTopics {
188188
semantic: Article(markup: nil, metadata: nil, redirects: nil, options: [:])
189189
)
190190

191-
let collectionTaskGroups = try AutomaticCuration.topics(for: temporaryCollectionNode, withTrait: nil, context: context)
191+
let collectionTaskGroups = try AutomaticCuration.topics(for: temporaryCollectionNode, withTraits: [], context: context)
192192
.map { taskGroup in
193193
AutomaticTaskGroupSection(
194194
// Force-unwrapping the title since automatically-generated task groups always have a title.

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public struct AutomaticCuration {
4747
/// - Returns: An array of title and references list tuples.
4848
static func topics(
4949
for node: DocumentationNode,
50-
withTrait variantsTrait: DocumentationDataVariantsTrait?,
50+
withTraits variantsTraits: Set<DocumentationDataVariantsTrait>,
5151
context: DocumentationContext
5252
) throws -> [TaskGroup] {
5353
// Get any default implementation relationships for this symbol
@@ -87,8 +87,12 @@ public struct AutomaticCuration {
8787
//
8888
// Otherwise, we'll fall back to the first kind variant.
8989
let childSymbolKindIdentifier: SymbolGraph.Symbol.KindIdentifier?
90-
if let variantsTrait = variantsTrait {
91-
childSymbolKindIdentifier = childSymbol.kindVariants[variantsTrait]?.identifier
90+
if variantsTraits.count > 0 {
91+
if let matchingTrait = variantsTraits.first(where: { childSymbol.kindVariants[$0] != nil }) {
92+
childSymbolKindIdentifier = childSymbol.kindVariants[matchingTrait]?.identifier
93+
} else {
94+
childSymbolKindIdentifier = nil
95+
}
9296
} else {
9397
childSymbolKindIdentifier = childSymbol.kindVariants.firstValue?.identifier
9498
}
@@ -117,7 +121,7 @@ public struct AutomaticCuration {
117121
/// `nil` if the method can't find any relevant links to automatically generate a See Also content.
118122
static func seeAlso(
119123
for node: DocumentationNode,
120-
withTrait variantsTrait: DocumentationDataVariantsTrait,
124+
withTraits variantsTraits: Set<DocumentationDataVariantsTrait>,
121125
context: DocumentationContext,
122126
bundle: DocumentationBundle,
123127
renderContext: RenderContext?,
@@ -142,11 +146,11 @@ public struct AutomaticCuration {
142146
// Don't include the current node.
143147
.filter { $0 != node.reference }
144148

145-
// Filter out nodes that aren't available in the given trait.
149+
// Filter out nodes that aren't available in any of the given traits.
146150
.filter { reference in
147-
try context.entity(with: reference)
151+
try !context.entity(with: reference)
148152
.availableVariantTraits
149-
.contains(variantsTrait)
153+
.isDisjoint(with: variantsTraits)
150154
}
151155
}
152156

@@ -192,6 +196,7 @@ extension AutomaticCuration {
192196
case .`deinit`: return "Deinitializers"
193197
case .`enum`: return "Enumerations"
194198
case .`case`: return "Enumeration Cases"
199+
case .dictionary: return "Dictionaries"
195200
case .extension: return "Extensions"
196201
case .`func`: return "Functions"
197202
case .`operator`: return "Operators"
@@ -227,6 +232,7 @@ extension AutomaticCuration {
227232
.`class`,
228233
.`protocol`,
229234
.`struct`,
235+
.`dictionary`,
230236
.`var`,
231237
.`func`,
232238
.`operator`,

0 commit comments

Comments
 (0)