Skip to content

Commit aa7f7dd

Browse files
committed
Populate declaration fragments in navigator metadata for external links
Now that the declaration fragments will be the abbreviated declaration fragments from `LinkDestinationSummary`, we can propagate those to the navigator metadata for them to be used to inform the title of the navigator item [1]. Fixes rdar://156488052. [1]: https://github.com/swiftlang/swift-docc/blob/65aaf926ec079ddbd40f29540d4180a70af99e5e/Sources/SwiftDocC/Indexing/Navigator/RenderNode%2BNavigatorIndex.swift#L140
1 parent 18ba146 commit aa7f7dd

File tree

3 files changed

+56
-17
lines changed

3 files changed

+56
-17
lines changed

Sources/SwiftDocC/Infrastructure/Link Resolution/LinkResolver+NavigatorIndex.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ package struct ExternalRenderNode {
6969
topicRenderReference.navigatorTitleVariants
7070
}
7171

72+
/// The variants of the abbreviated declaration of the symbol to display in links and fall-back to in navigation.
73+
///
74+
/// This value is `nil` if the referenced page is not a symbol.
75+
var fragmentsVariants: VariantCollection<[DeclarationRenderSection.Token]?> {
76+
topicRenderReference.fragmentsVariants
77+
}
78+
7279
/// Author provided images that represent this page.
7380
var images: [TopicImage] {
7481
entity.topicImages ?? []
@@ -129,7 +136,8 @@ struct NavigatorExternalRenderNode: NavigatorIndexableRenderNodeRepresentation {
129136
role: renderNode.role,
130137
symbolKind: renderNode.symbolKind?.renderingIdentifier,
131138
images: renderNode.images,
132-
isBeta: renderNode.isBeta
139+
isBeta: renderNode.isBeta,
140+
fragments: renderNode.fragmentsVariants.value(for: traits)
133141
)
134142
}
135143
}
@@ -143,19 +151,16 @@ struct ExternalRenderNodeMetadataRepresentation: NavigatorIndexableRenderMetadat
143151
var symbolKind: String?
144152
var images: [TopicImage]
145153
var isBeta: Bool
154+
var fragments: [DeclarationRenderSection.Token]?
146155

147156
// Values that we have insufficient information to derive.
148157
// These are needed to conform to the navigator indexable metadata protocol.
149158
//
150-
// The fragments that we get as part of the external link are the full declaration fragments.
151-
// These are too verbose for the navigator, so instead of using them, we rely on the title, navigator title and symbol kind instead.
152-
//
153159
// The role heading is used to identify Property Lists.
154160
// The value being missing is used for computing the final navigator title.
155161
//
156162
// The platforms are used for generating the availability index,
157163
// but doesn't affect how the node is rendered in the sidebar.
158-
var fragments: [DeclarationRenderSection.Token]? = nil
159164
var roleHeading: String? = nil
160165
var platforms: [AvailabilityRenderItem]? = nil
161166
}

Tests/SwiftDocCTests/Indexing/ExternalRenderNodeTests.swift

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ class ExternalRenderNodeTests: XCTestCase {
4141
title: "SwiftSymbol",
4242
kind: .class,
4343
language: .swift,
44+
declarationFragments: .init(declarationFragments: [
45+
.init(kind: .keyword, spelling: "class", preciseIdentifier: nil),
46+
.init(kind: .text, spelling: " ", preciseIdentifier: nil),
47+
.init(kind: .identifier, spelling: "SwiftSymbol", preciseIdentifier: nil)
48+
]),
4449
platforms: [.init(name: "iOS", introduced: nil, isBeta: true)]
4550
)
4651
)
@@ -50,6 +55,13 @@ class ExternalRenderNodeTests: XCTestCase {
5055
title: "ObjCSymbol",
5156
kind: .function,
5257
language: .objectiveC,
58+
declarationFragments: .init(declarationFragments: [
59+
.init(kind: .text, spelling: "- ", preciseIdentifier: nil),
60+
.init(kind: .text, spelling: "(", preciseIdentifier: nil),
61+
.init(kind: .typeIdentifier, spelling: "void", preciseIdentifier: nil),
62+
.init(kind: .text, spelling: ") ", preciseIdentifier: nil),
63+
.init(kind: .identifier, spelling: "ObjCSymbol", preciseIdentifier: nil)
64+
]),
5365
platforms: [.init(name: "macOS", introduced: nil, isBeta: false)]
5466
)
5567
)
@@ -152,12 +164,14 @@ class ExternalRenderNodeTests: XCTestCase {
152164
)
153165
XCTAssertEqual(swiftNavigatorExternalRenderNode.metadata.title, swiftTitle)
154166
XCTAssertFalse(swiftNavigatorExternalRenderNode.metadata.isBeta)
155-
167+
XCTAssertEqual(swiftNavigatorExternalRenderNode.metadata.fragments, swiftFragments)
168+
156169
let objcNavigatorExternalRenderNode = try XCTUnwrap(
157170
NavigatorExternalRenderNode(renderNode: externalRenderNode, trait: .interfaceLanguage(SourceLanguage.objectiveC.id))
158171
)
159172
XCTAssertEqual(objcNavigatorExternalRenderNode.metadata.title, objcTitle)
160173
XCTAssertFalse(objcNavigatorExternalRenderNode.metadata.isBeta)
174+
XCTAssertEqual(objcNavigatorExternalRenderNode.metadata.fragments, objcFragments)
161175
}
162176

163177
func testNavigatorWithExternalNodes() async throws {
@@ -218,19 +232,38 @@ class ExternalRenderNodeTests: XCTestCase {
218232
XCTAssertEqual(renderIndex.interfaceLanguages[SourceLanguage.swift.id]?.count(where: \.isExternal), 0)
219233
XCTAssertEqual(renderIndex.interfaceLanguages[SourceLanguage.objectiveC.id]?.count(where: \.isExternal), 0)
220234

235+
236+
func externalNodes(by language: SourceLanguage) -> [RenderIndex.Node]? {
237+
renderIndex.interfaceLanguages[language.id]?.first?.children?.filter(\.isExternal)
238+
}
239+
221240
// Verify that the curated external links are part of the index.
222-
let swiftExternalNodes = (renderIndex.interfaceLanguages[SourceLanguage.swift.id]?.first?.children?.filter(\.isExternal) ?? []).sorted(by: \.title)
223-
let objcExternalNodes = (renderIndex.interfaceLanguages[SourceLanguage.objectiveC.id]?.first?.children?.filter(\.isExternal) ?? []).sorted(by: \.title)
241+
let swiftExternalNodes = try XCTUnwrap(externalNodes(by: .swift))
224242
XCTAssertEqual(swiftExternalNodes.count, 2)
243+
244+
let objcExternalNodes = try XCTUnwrap(externalNodes(by: .objectiveC))
225245
XCTAssertEqual(objcExternalNodes.count, 2)
226-
XCTAssertEqual(swiftExternalNodes.map(\.title), ["SwiftArticle", "SwiftSymbol"])
227-
XCTAssertEqual(objcExternalNodes.map(\.title), ["ObjCArticle", "ObjCSymbol"])
228-
XCTAssert(swiftExternalNodes.first?.isBeta == false)
229-
XCTAssert(swiftExternalNodes.last?.isBeta == true)
230-
XCTAssert(objcExternalNodes.first?.isBeta == true)
231-
XCTAssert(objcExternalNodes.last?.isBeta == false)
232-
XCTAssertEqual(swiftExternalNodes.map(\.type), ["article", "class"])
233-
XCTAssertEqual(objcExternalNodes.map(\.type), ["article", "func"])
246+
247+
let swiftArticleExternalNode = try XCTUnwrap(swiftExternalNodes.first(where: { $0.path == "/path/to/external/swiftarticle" }))
248+
let swiftSymbolExternalNode = try XCTUnwrap(swiftExternalNodes.first(where: { $0.path == "/path/to/external/swiftsymbol" }))
249+
let objcArticleExternalNode = try XCTUnwrap(objcExternalNodes.first(where: { $0.path == "/path/to/external/objcarticle" }))
250+
let objcSymbolExternalNode = try XCTUnwrap(objcExternalNodes.first(where: { $0.path == "/path/to/external/objcsymbol" }))
251+
252+
XCTAssertEqual(swiftArticleExternalNode.title, "SwiftArticle")
253+
XCTAssertEqual(swiftArticleExternalNode.isBeta, false)
254+
XCTAssertEqual(swiftArticleExternalNode.type, "article")
255+
256+
XCTAssertEqual(swiftSymbolExternalNode.title, "SwiftSymbol") // Classes don't use declaration fragments in their navigator title
257+
XCTAssertEqual(swiftSymbolExternalNode.isBeta, true)
258+
XCTAssertEqual(swiftSymbolExternalNode.type, "class")
259+
260+
XCTAssertEqual(objcArticleExternalNode.title, "ObjCArticle")
261+
XCTAssertEqual(objcArticleExternalNode.isBeta, true)
262+
XCTAssertEqual(objcArticleExternalNode.type, "article")
263+
264+
XCTAssertEqual(objcSymbolExternalNode.title, "- (void) ObjCSymbol")
265+
XCTAssertEqual(objcSymbolExternalNode.isBeta, false)
266+
XCTAssertEqual(objcSymbolExternalNode.type, "func")
234267
}
235268

236269
func testNavigatorWithExternalNodesOnlyAddsCuratedNodesToNavigator() async throws {
@@ -299,7 +332,7 @@ class ExternalRenderNodeTests: XCTestCase {
299332
XCTAssertEqual(swiftExternalNodes.count, 1)
300333
XCTAssertEqual(objcExternalNodes.count, 1)
301334
XCTAssertEqual(swiftExternalNodes.map(\.title), ["SwiftArticle"])
302-
XCTAssertEqual(objcExternalNodes.map(\.title), ["ObjCSymbol"])
335+
XCTAssertEqual(objcExternalNodes.map(\.title), ["- (void) ObjCSymbol"])
303336
XCTAssertEqual(swiftExternalNodes.map(\.type), ["article"])
304337
XCTAssertEqual(objcExternalNodes.map(\.type), ["func"])
305338
}

Tests/SwiftDocCTests/Infrastructure/TestExternalReferenceResolvers.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class TestMultiResultExternalReferenceResolver: ExternalDocumentationSource {
9292
title: entityInfo.title,
9393
availableLanguages: [entityInfo.language],
9494
platforms: entityInfo.platforms,
95+
declarationFragments: entityInfo.declarationFragments?.declarationFragments.map { .init(fragment: $0, identifier: nil) },
9596
topicImages: entityInfo.topicImages?.map(\.0),
9697
references: entityInfo.topicImages?.map { topicImage, altText in
9798
ImageReference(identifier: topicImage.identifier, altText: altText, imageAsset: assetsToReturn[topicImage.identifier.identifier] ?? .init())

0 commit comments

Comments
 (0)