Skip to content

Populate abbreviated declaration fragments in external render nodes #1255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ public struct LinkDestinationSummary: Codable, Equatable {
public typealias DeclarationFragments = [DeclarationRenderSection.Token]
/// The fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
public let declarationFragments: DeclarationFragments?


/// The abbreviated fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
///
/// 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.
public let subHeadingDeclarationFragments: DeclarationFragments?

/// Any previous URLs for this element.
///
/// A web server can use this list of URLs to redirect to the current URL.
Expand Down Expand Up @@ -197,7 +202,13 @@ public struct LinkDestinationSummary: Codable, Equatable {
///
/// If the summarized element has a declaration but the variant doesn't, this property will be `Optional.some(nil)`.
public let declarationFragments: VariantValue<DeclarationFragments?>


/// The abbreviated declaration of the variant or `nil` if the declaration is the same as the summarized element.
///
/// 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.
/// If the summarized element has an abbreviated declaration but the variant doesn't, this property will be `Optional.some(nil)`.
public let subHeadingDeclarationFragments: VariantValue<DeclarationFragments?>

/// Images that are used to represent the summarized element or `nil` if the images are the same as the summarized element.
///
/// If the summarized element has an image but the variant doesn't, this property will be `Optional.some(nil)`.
Expand All @@ -215,6 +226,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
/// - taskGroups: The taskGroups of the variant or `nil` if the taskGroups is the same as the summarized element.
/// - usr: The precise symbol identifier of the variant or `nil` if the precise symbol identifier is the same as the summarized element.
/// - declarationFragments: The declaration of the variant or `nil` if the declaration is the same as the summarized element.
/// - subHeadingDeclarationFragments: The abbreviated declaration of the variant or `nil` if the declaration is the same as the summarized element.
/// - topicImages: Images that are used to represent the summarized element or `nil` if the images are the same as the summarized element.
public init(
traits: [RenderNode.Variant.Trait],
Expand All @@ -226,6 +238,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
taskGroups: VariantValue<[LinkDestinationSummary.TaskGroup]?> = nil,
usr: VariantValue<String?> = nil,
declarationFragments: VariantValue<LinkDestinationSummary.DeclarationFragments?> = nil,
subHeadingDeclarationFragments: VariantValue<LinkDestinationSummary.DeclarationFragments?> = nil,
topicImages: VariantValue<[TopicImage]?> = nil
) {
self.traits = traits
Expand All @@ -237,6 +250,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
self.taskGroups = taskGroups
self.usr = usr
self.declarationFragments = declarationFragments
self.subHeadingDeclarationFragments = subHeadingDeclarationFragments
self.topicImages = topicImages
}
}
Expand All @@ -258,6 +272,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
/// - taskGroups: The reference URLs of the summarized element's children, grouped by their task groups.
/// - 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.
/// - declarationFragments: The fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
/// - subHeadingDeclarationFragments: The abbreviated fragments for this symbol's declaration, or `nil` if the summarized element isn't a symbol.
/// - redirects: Any previous URLs for this element, or `nil` if this element has no previous URLs.
/// - topicImages: Images that are used to represent the summarized element, or `nil` if this element has no topic images.
/// - references: References used in the content of the summarized element, or `nil` if this element has no references to other content.
Expand All @@ -273,6 +288,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
taskGroups: [LinkDestinationSummary.TaskGroup]? = nil,
usr: String? = nil,
declarationFragments: LinkDestinationSummary.DeclarationFragments? = nil,
subHeadingDeclarationFragments: LinkDestinationSummary.DeclarationFragments? = nil,
redirects: [URL]? = nil,
topicImages: [TopicImage]? = nil,
references: [any RenderReference]? = nil,
Expand All @@ -289,6 +305,7 @@ public struct LinkDestinationSummary: Codable, Equatable {
self.taskGroups = taskGroups
self.usr = usr
self.declarationFragments = declarationFragments
self.subHeadingDeclarationFragments = subHeadingDeclarationFragments
self.redirects = redirects
self.topicImages = topicImages
self.references = references
Expand Down Expand Up @@ -444,7 +461,11 @@ extension LinkDestinationSummary {
let usr = symbol.externalIDVariants[summaryTrait] ?? symbol.externalID
let declaration = (symbol.declarationVariants[summaryTrait] ?? symbol.declaration).renderDeclarationTokens()
let language = documentationNode.sourceLanguage

// Use the render metadata declaration fragments as the subheading declaration fragments.
// These have been derived from the symbol's original subheading declaration fragments as part of the rendering step.
// They are an abbreviated version of the declaration for display in Topic sections, the navigator, etc..
let subHeadingDeclaration = renderNode.metadata.fragmentsVariants.value(for: language)

let variants: [Variant] = documentationNode.availableVariantTraits.compactMap { trait in
// Skip the variant for the summarized elements source language.
guard let interfaceLanguage = trait.interfaceLanguage, interfaceLanguage != documentationNode.sourceLanguage.id else {
Expand All @@ -460,6 +481,11 @@ extension LinkDestinationSummary {
}

let variantTraits = [RenderNode.Variant.Trait.interfaceLanguage(interfaceLanguage)]

// Use the render metadata declaration fragments as the subheading declaration fragments.
// These have been derived from the symbol's original subheading declaration fragments as part of the rendering step.
// They are an abbreviated version of the declaration for display in Topic sections, the navigator, etc..
let subHeadingDeclarationVariant = renderNode.metadata.fragmentsVariants.value(for: variantTraits)
return Variant(
traits: variantTraits,
kind: nilIfEqual(main: kind, variant: symbol.kindVariants[trait].map { DocumentationNode.kind(forKind: $0.identifier) }),
Expand All @@ -470,6 +496,7 @@ extension LinkDestinationSummary {
taskGroups: nilIfEqual(main: taskGroups, variant: taskGroupVariants[variantTraits]),
usr: nil, // The symbol variant uses the same USR
declarationFragments: nilIfEqual(main: declaration, variant: declarationVariant),
subHeadingDeclarationFragments: nilIfEqual(main: subHeadingDeclaration, variant: subHeadingDeclarationVariant),
topicImages: nil // The symbol variant doesn't currently have their own images
)
}
Expand All @@ -490,6 +517,7 @@ extension LinkDestinationSummary {
taskGroups: taskGroups,
usr: usr,
declarationFragments: declaration,
subHeadingDeclarationFragments: subHeadingDeclaration,
redirects: redirects,
topicImages: topicImages.nilIfEmpty,
references: references.nilIfEmpty,
Expand Down Expand Up @@ -574,6 +602,7 @@ extension LinkDestinationSummary {
case kind, referenceURL, title, abstract, language, taskGroups, usr, availableLanguages, platforms, redirects, topicImages, references, variants
case relativePresentationURL = "path"
case declarationFragments = "fragments"
case subHeadingDeclarationFragments = "subheadingFragments"
}

public func encode(to encoder: any Encoder) throws {
Expand All @@ -589,6 +618,7 @@ extension LinkDestinationSummary {
try container.encodeIfPresent(taskGroups, forKey: .taskGroups)
try container.encodeIfPresent(usr, forKey: .usr)
try container.encodeIfPresent(declarationFragments, forKey: .declarationFragments)
try container.encodeIfPresent(subHeadingDeclarationFragments, forKey: .subHeadingDeclarationFragments)
try container.encodeIfPresent(redirects, forKey: .redirects)
try container.encodeIfPresent(topicImages, forKey: .topicImages)
try container.encodeIfPresent(references?.map { CodableRenderReference($0) }, forKey: .references)
Expand Down Expand Up @@ -626,6 +656,7 @@ extension LinkDestinationSummary {
taskGroups = try container.decodeIfPresent([TaskGroup].self, forKey: .taskGroups)
usr = try container.decodeIfPresent(String.self, forKey: .usr)
declarationFragments = try container.decodeIfPresent(DeclarationFragments.self, forKey: .declarationFragments)
subHeadingDeclarationFragments = try container.decodeIfPresent(DeclarationFragments.self, forKey: .subHeadingDeclarationFragments)
redirects = try container.decodeIfPresent([URL].self, forKey: .redirects)
topicImages = try container.decodeIfPresent([TopicImage].self, forKey: .topicImages)
references = try container.decodeIfPresent([CodableRenderReference].self, forKey: .references).map { decodedReferences in
Expand All @@ -641,6 +672,7 @@ extension LinkDestinationSummary.Variant {
case traits, kind, title, abstract, language, usr, taskGroups, topicImages
case relativePresentationURL = "path"
case declarationFragments = "fragments"
case subHeadingDeclarationFragments = "subheadingFragments"
}

public func encode(to encoder: any Encoder) throws {
Expand All @@ -653,6 +685,7 @@ extension LinkDestinationSummary.Variant {
try container.encodeIfPresent(language?.id, forKey: .language)
try container.encodeIfPresent(usr, forKey: .usr)
try container.encodeIfPresent(declarationFragments, forKey: .declarationFragments)
try container.encodeIfPresent(subHeadingDeclarationFragments, forKey: .subHeadingDeclarationFragments)
try container.encodeIfPresent(taskGroups, forKey: .taskGroups)
try container.encodeIfPresent(topicImages, forKey: .topicImages)
}
Expand Down Expand Up @@ -692,6 +725,8 @@ extension LinkDestinationSummary.Variant {
abstract = try container.decodeIfPresent(LinkDestinationSummary.Abstract?.self, forKey: .abstract)
usr = try container.decodeIfPresent(String?.self, forKey: .usr)
declarationFragments = try container.decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .declarationFragments)
subHeadingDeclarationFragments = try container
.decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .subHeadingDeclarationFragments)
taskGroups = try container.decodeIfPresent([LinkDestinationSummary.TaskGroup]?.self, forKey: .taskGroups)
topicImages = try container.decodeIfPresent([TopicImage]?.self, forKey: .topicImages)
}
Expand Down
25 changes: 25 additions & 0 deletions Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ class ExternalLinkableTests: XCTestCase {
.init(text: " ", kind: .text, identifier: nil),
.init(text: "MyClass", kind: .identifier, identifier: nil),
])
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
.init(text: "class", kind: .keyword, identifier: nil),
.init(text: " ", kind: .text, identifier: nil),
.init(text: "MyClass", kind: .identifier, identifier: nil),
])
XCTAssertNil(summary.topicImages)
XCTAssertNil(summary.references)

Expand Down Expand Up @@ -230,6 +235,13 @@ class ExternalLinkableTests: XCTestCase {
.init(text: " : ", kind: .text, identifier: nil),
.init(text: "Hashable", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "p:hPP"),
])
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
.init(text: "protocol", kind: .keyword, identifier: nil),
.init(text: " ", kind: .text, identifier: nil),
.init(text: "MyProtocol", kind: .identifier, identifier: nil),
.init(text: " : ", kind: .text, identifier: nil),
.init(text: "Hashable", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "p:hPP"),
])
XCTAssertNil(summary.topicImages)
XCTAssertNil(summary.references)

Expand Down Expand Up @@ -265,6 +277,7 @@ class ExternalLinkableTests: XCTestCase {
.init(text: "...", kind: .text, identifier: nil),
.init(text: ")", kind: .text, identifier: nil)
])
XCTAssertNil(summary.subHeadingDeclarationFragments)
XCTAssertNil(summary.topicImages)
XCTAssertNil(summary.references)

Expand Down Expand Up @@ -303,6 +316,18 @@ class ExternalLinkableTests: XCTestCase {
.init(text: "Int", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:Si"),
.init(text: ")", kind: .text, identifier: nil)
])
XCTAssertEqual(summary.subHeadingDeclarationFragments, [
.init(text: "func", kind: .keyword, identifier: nil),
.init(text: " ", kind: .text, identifier: nil),
.init(text: "globalFunction", kind: .identifier, identifier: nil),
.init(text: "(", kind: .text, identifier: nil),
.init(text: "Data", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:10Foundation4DataV"),
.init(text: ", ", kind: .text, identifier: nil),
.init(text: "considering", kind: .identifier, identifier: nil),
.init(text: ": ", kind: .text, identifier: nil),
.init(text: "Int", kind: .typeIdentifier, identifier: nil, preciseIdentifier: "s:Si"),
.init(text: ")", kind: .text, identifier: nil)
])
Comment on lines +319 to +330
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an example where the subheading declaration fragments and the full declaration fragments are not the same?

XCTAssertNil(summary.topicImages)
XCTAssertNil(summary.references)

Expand Down