Skip to content

Commit 832f1eb

Browse files
committed
Add the abbreviated declaration fragments to LinkDestinationSummary
LinkDestinationSummary contains a summary of an element that you can link to from outside the documentation bundle. [1] This information is meant to be used by a server to provide information to an out-of-process resolver to resolve links to external entities, so that the partner implementation of `LinkDestinationSummary` is `OutOfProcessReferenceResolver.ResolvedInformation` [2]. As part of `LinkDestinationSummary`, we store the full declaration fragments of the symbol [3][4]. However, currently `OutOfProcessReferenceResolver` is using these full declaration fragments to populate `TopicRenderReference.fragmentsVariants` [5], which expects the abbreviated declaration fragments [6]. These abbreviated declaration fragments are meant to be used in the Topics section and the navigation index in order to reduce verbosity and improve readability by removing any declaration fragments non-essential to the core of the symbol declaration. These abbreviated declaration fragments are not captured as part of `LinkDestinationSummary` or `OutOfProcessReferenceResolver.ResolvedInformation`. To start capturing this information, this commit modifies `LinkDestinationSummary` to add a new optional field `subHeadingDeclarationFragments` which stores the abbreviated declaration fragments from `renderNode.metadata.fragmentsVariants` (abbreviated declaration fragments are derived from the `subHeading` of the symbol [7], further processed during the render node transformation phase, and finally stored as `renderNode.metadata.fragmentsVariants` [8]). This will enable us to use them in `OutOfProcessReferenceResolver.ResolvedInformation` to initialise `TopicRenderReference.fragmentsVariants` with the expected value. The final goal is for a symbol's representation in the Topics section and the navigation index to look the same regardless of whether the symbol is a local or external one. Fixes rdar://156488052. Alternatives considered: ------------------------ Considered modifying `LinkDestinationSummary.declarationFragments` instead of adding a new optional field, however the full declaration fragments are used to determine the full name [9] of the symbol for diagnostic reporting [10]. By introducing a new field we also ensure this is a non-breaking change. [1]: https://github.com/swiftlang/swift-docc/blob/65aaf926ec079ddbd40f29540d4180a70af99e5e/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift#L66 [2]: https://github.com/swiftlang/swift-docc/blob/65aaf926ec079ddbd40f29540d4180a70af99e5e/Sources/SwiftDocC/Infrastructure/External%20Data/OutOfProcessReferenceResolver.swift#L558-L562 [3]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift#L140-L141 [4]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift#L445 [5]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/Infrastructure/External%20Data/OutOfProcessReferenceResolver.swift#L169 [6]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/Model/Rendering/References/TopicRenderReference.swift#L50-L53 [7]: https://github.com/swiftlang/swift-docc-symbolkit/blob/ebe89c7da4cf03ded04cd708f3399087c6f2dad7/Sources/SymbolKit/SymbolGraph/Symbol/Names.swift#L28-L31 [8]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift#L1304 [9]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/Infrastructure/Link%20Resolution/ExternalPathHierarchyResolver.swift#L61-L63 [10]: https://github.com/swiftlang/swift-docc/blob/1b4a1850dd2785a8ebabded139ae0af3551bb029/Sources/SwiftDocC/Infrastructure/Link%20Resolution/PathHierarchy%2BError.swift#L115-L117
1 parent 1b4a185 commit 832f1eb

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,12 @@ public struct LinkDestinationSummary: Codable, Equatable {
139139
public typealias DeclarationFragments = [DeclarationRenderSection.Token]
140140
/// The fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
141141
public let declarationFragments: DeclarationFragments?
142-
142+
143+
/// The abbreviated fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
144+
///
145+
/// They are used for displaying in contexts where the full declaration fragments would be too verbose, like in the Topics section or the navigation index.
146+
public let subHeadingDeclarationFragments: DeclarationFragments?
147+
143148
/// Any previous URLs for this element.
144149
///
145150
/// A web server can use this list of URLs to redirect to the current URL.
@@ -197,7 +202,13 @@ public struct LinkDestinationSummary: Codable, Equatable {
197202
///
198203
/// If the summarized element has a declaration but the variant doesn't, this property will be `Optional.some(nil)`.
199204
public let declarationFragments: VariantValue<DeclarationFragments?>
200-
205+
206+
/// The abbreviated declaration of the variant or `nil` if the declaration is the same as the summarized element.
207+
///
208+
/// They are used for displaying in contexts where the full declaration fragments would be too verbose, like in the Topics section or the navigation index.
209+
/// If the summarized element has an abbreviated declaration but the variant doesn't, this property will be `Optional.some(nil)`.
210+
public let subHeadingDeclarationFragments: VariantValue<DeclarationFragments?>
211+
201212
/// Images that are used to represent the summarized element or `nil` if the images are the same as the summarized element.
202213
///
203214
/// If the summarized element has an image but the variant doesn't, this property will be `Optional.some(nil)`.
@@ -215,6 +226,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
215226
/// - taskGroups: The taskGroups of the variant or `nil` if the taskGroups is the same as the summarized element.
216227
/// - usr: The precise symbol identifier of the variant or `nil` if the precise symbol identifier is the same as the summarized element.
217228
/// - declarationFragments: The declaration of the variant or `nil` if the declaration is the same as the summarized element.
229+
/// - subHeadingDeclarationFragments: The abbreviated declaration of the variant or `nil` if the declaration is the same as the summarized element.
218230
/// - topicImages: Images that are used to represent the summarized element or `nil` if the images are the same as the summarized element.
219231
public init(
220232
traits: [RenderNode.Variant.Trait],
@@ -226,6 +238,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
226238
taskGroups: VariantValue<[LinkDestinationSummary.TaskGroup]?> = nil,
227239
usr: VariantValue<String?> = nil,
228240
declarationFragments: VariantValue<LinkDestinationSummary.DeclarationFragments?> = nil,
241+
subHeadingDeclarationFragments: VariantValue<LinkDestinationSummary.DeclarationFragments?> = nil,
229242
topicImages: VariantValue<[TopicImage]?> = nil
230243
) {
231244
self.traits = traits
@@ -237,6 +250,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
237250
self.taskGroups = taskGroups
238251
self.usr = usr
239252
self.declarationFragments = declarationFragments
253+
self.subHeadingDeclarationFragments = subHeadingDeclarationFragments
240254
self.topicImages = topicImages
241255
}
242256
}
@@ -258,6 +272,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
258272
/// - taskGroups: The reference URLs of the summarized element's children, grouped by their task groups.
259273
/// - usr: The unique, precise identifier for this symbol that you use to reference it across different systems, or `nil` if the summarized element isn't a symbol.
260274
/// - declarationFragments: The fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
275+
/// - subHeadingDeclarationFragments: The abbreviated fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
261276
/// - redirects: Any previous URLs for this element, or `nil` if this element has no previous URLs.
262277
/// - topicImages: Images that are used to represent the summarized element, or `nil` if this element has no topic images.
263278
/// - references: References used in the content of the summarized element, or `nil` if this element has no references to other content.
@@ -273,6 +288,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
273288
taskGroups: [LinkDestinationSummary.TaskGroup]? = nil,
274289
usr: String? = nil,
275290
declarationFragments: LinkDestinationSummary.DeclarationFragments? = nil,
291+
subHeadingDeclarationFragments: LinkDestinationSummary.DeclarationFragments? = nil,
276292
redirects: [URL]? = nil,
277293
topicImages: [TopicImage]? = nil,
278294
references: [any RenderReference]? = nil,
@@ -289,6 +305,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
289305
self.taskGroups = taskGroups
290306
self.usr = usr
291307
self.declarationFragments = declarationFragments
308+
self.subHeadingDeclarationFragments = subHeadingDeclarationFragments
292309
self.redirects = redirects
293310
self.topicImages = topicImages
294311
self.references = references
@@ -444,7 +461,11 @@ extension LinkDestinationSummary {
444461
let usr = symbol.externalIDVariants[summaryTrait] ?? symbol.externalID
445462
let declaration = (symbol.declarationVariants[summaryTrait] ?? symbol.declaration).renderDeclarationTokens()
446463
let language = documentationNode.sourceLanguage
447-
464+
// Use the render metadata declaration fragments as the subheading declaration fragments.
465+
// These have been derived from the symbol's original subheading declaration fragments as part of the rendering step.
466+
// They are an abbreviated version of the declaration for display in Topic sections, the navigator, etc..
467+
let subHeadingDeclaration = renderNode.metadata.fragmentsVariants.value(for: language)
468+
448469
let variants: [Variant] = documentationNode.availableVariantTraits.compactMap { trait in
449470
// Skip the variant for the summarized elements source language.
450471
guard let interfaceLanguage = trait.interfaceLanguage, interfaceLanguage != documentationNode.sourceLanguage.id else {
@@ -460,6 +481,11 @@ extension LinkDestinationSummary {
460481
}
461482

462483
let variantTraits = [RenderNode.Variant.Trait.interfaceLanguage(interfaceLanguage)]
484+
485+
// Use the render metadata declaration fragments as the subheading declaration fragments.
486+
// These have been derived from the symbol's original subheading declaration fragments as part of the rendering step.
487+
// They are an abbreviated version of the declaration for display in Topic sections, the navigator, etc..
488+
let subHeadingDeclarationVariant = renderNode.metadata.fragmentsVariants.value(for: variantTraits)
463489
return Variant(
464490
traits: variantTraits,
465491
kind: nilIfEqual(main: kind, variant: symbol.kindVariants[trait].map { DocumentationNode.kind(forKind: $0.identifier) }),
@@ -470,6 +496,7 @@ extension LinkDestinationSummary {
470496
taskGroups: nilIfEqual(main: taskGroups, variant: taskGroupVariants[variantTraits]),
471497
usr: nil, // The symbol variant uses the same USR
472498
declarationFragments: nilIfEqual(main: declaration, variant: declarationVariant),
499+
subHeadingDeclarationFragments: nilIfEqual(main: subHeadingDeclaration, variant: subHeadingDeclarationVariant),
473500
topicImages: nil // The symbol variant doesn't currently have their own images
474501
)
475502
}
@@ -490,6 +517,7 @@ extension LinkDestinationSummary {
490517
taskGroups: taskGroups,
491518
usr: usr,
492519
declarationFragments: declaration,
520+
subHeadingDeclarationFragments: subHeadingDeclaration,
493521
redirects: redirects,
494522
topicImages: topicImages.nilIfEmpty,
495523
references: references.nilIfEmpty,
@@ -574,6 +602,7 @@ extension LinkDestinationSummary {
574602
case kind, referenceURL, title, abstract, language, taskGroups, usr, availableLanguages, platforms, redirects, topicImages, references, variants
575603
case relativePresentationURL = "path"
576604
case declarationFragments = "fragments"
605+
case subHeadingDeclarationFragments = "subheadingFragments"
577606
}
578607

579608
public func encode(to encoder: any Encoder) throws {
@@ -589,6 +618,7 @@ extension LinkDestinationSummary {
589618
try container.encodeIfPresent(taskGroups, forKey: .taskGroups)
590619
try container.encodeIfPresent(usr, forKey: .usr)
591620
try container.encodeIfPresent(declarationFragments, forKey: .declarationFragments)
621+
try container.encodeIfPresent(subHeadingDeclarationFragments, forKey: .subHeadingDeclarationFragments)
592622
try container.encodeIfPresent(redirects, forKey: .redirects)
593623
try container.encodeIfPresent(topicImages, forKey: .topicImages)
594624
try container.encodeIfPresent(references?.map { CodableRenderReference($0) }, forKey: .references)
@@ -626,6 +656,7 @@ extension LinkDestinationSummary {
626656
taskGroups = try container.decodeIfPresent([TaskGroup].self, forKey: .taskGroups)
627657
usr = try container.decodeIfPresent(String.self, forKey: .usr)
628658
declarationFragments = try container.decodeIfPresent(DeclarationFragments.self, forKey: .declarationFragments)
659+
subHeadingDeclarationFragments = try container.decodeIfPresent(DeclarationFragments.self, forKey: .subHeadingDeclarationFragments)
629660
redirects = try container.decodeIfPresent([URL].self, forKey: .redirects)
630661
topicImages = try container.decodeIfPresent([TopicImage].self, forKey: .topicImages)
631662
references = try container.decodeIfPresent([CodableRenderReference].self, forKey: .references).map { decodedReferences in
@@ -641,6 +672,7 @@ extension LinkDestinationSummary.Variant {
641672
case traits, kind, title, abstract, language, usr, taskGroups, topicImages
642673
case relativePresentationURL = "path"
643674
case declarationFragments = "fragments"
675+
case subHeadingDeclarationFragments = "subheadingFragments"
644676
}
645677

646678
public func encode(to encoder: any Encoder) throws {
@@ -653,6 +685,7 @@ extension LinkDestinationSummary.Variant {
653685
try container.encodeIfPresent(language?.id, forKey: .language)
654686
try container.encodeIfPresent(usr, forKey: .usr)
655687
try container.encodeIfPresent(declarationFragments, forKey: .declarationFragments)
688+
try container.encodeIfPresent(subHeadingDeclarationFragments, forKey: .subHeadingDeclarationFragments)
656689
try container.encodeIfPresent(taskGroups, forKey: .taskGroups)
657690
try container.encodeIfPresent(topicImages, forKey: .topicImages)
658691
}
@@ -692,6 +725,8 @@ extension LinkDestinationSummary.Variant {
692725
abstract = try container.decodeIfPresent(LinkDestinationSummary.Abstract?.self, forKey: .abstract)
693726
usr = try container.decodeIfPresent(String?.self, forKey: .usr)
694727
declarationFragments = try container.decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .declarationFragments)
728+
subHeadingDeclarationFragments = try container
729+
.decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .subHeadingDeclarationFragments)
695730
taskGroups = try container.decodeIfPresent([LinkDestinationSummary.TaskGroup]?.self, forKey: .taskGroups)
696731
topicImages = try container.decodeIfPresent([TopicImage]?.self, forKey: .topicImages)
697732
}

Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ class ExternalLinkableTests: XCTestCase {
189189
.init(text: " ", kind: .text, identifier: nil),
190190
.init(text: "MyClass", kind: .identifier, identifier: nil),
191191
])
192+
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
193+
.init(text: "class", kind: .keyword, identifier: nil),
194+
.init(text: " ", kind: .text, identifier: nil),
195+
.init(text: "MyClass", kind: .identifier, identifier: nil),
196+
])
192197
XCTAssertNil(summary.topicImages)
193198
XCTAssertNil(summary.references)
194199

@@ -230,6 +235,13 @@ class ExternalLinkableTests: XCTestCase {
230235
.init(text: " : ", kind: .text, identifier: nil),
231236
.init(text: "Hashable", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "p:hPP"),
232237
])
238+
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
239+
.init(text: "protocol", kind: .keyword, identifier: nil),
240+
.init(text: " ", kind: .text, identifier: nil),
241+
.init(text: "MyProtocol", kind: .identifier, identifier: nil),
242+
.init(text: " : ", kind: .text, identifier: nil),
243+
.init(text: "Hashable", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "p:hPP"),
244+
])
233245
XCTAssertNil(summary.topicImages)
234246
XCTAssertNil(summary.references)
235247

@@ -265,6 +277,7 @@ class ExternalLinkableTests: XCTestCase {
265277
.init(text: "...", kind: .text, identifier: nil),
266278
.init(text: ")", kind: .text, identifier: nil)
267279
])
280+
XCTAssertNil(summary.subHeadingDeclarationFragments)
268281
XCTAssertNil(summary.topicImages)
269282
XCTAssertNil(summary.references)
270283

@@ -303,6 +316,18 @@ class ExternalLinkableTests: XCTestCase {
303316
.init(text: "Int", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:Si"),
304317
.init(text: ")", kind: .text, identifier: nil)
305318
])
319+
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
320+
.init(text: "func", kind: .keyword, identifier: nil),
321+
.init(text: " ", kind: .text, identifier: nil),
322+
.init(text: "globalFunction", kind: .identifier, identifier: nil),
323+
.init(text: "(", kind: .text, identifier: nil),
324+
.init(text: "Data", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:10Foundation4DataV"),
325+
.init(text: ", ", kind: .text, identifier: nil),
326+
.init(text: "considering", kind: .identifier, identifier: nil),
327+
.init(text: ": ", kind: .text, identifier: nil),
328+
.init(text: "Int", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:Si"),
329+
.init(text: ")", kind: .text, identifier: nil)
330+
])
306331
XCTAssertNil(summary.topicImages)
307332
XCTAssertNil(summary.references)
308333

0 commit comments

Comments
 (0)