From 637249ac892ef6c8daefb7497ebceb8098865358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20R=C3=B6nnqvist?= Date: Tue, 30 Sep 2025 15:10:21 +0200 Subject: [PATCH 1/2] Deprecate hierarchy information from the link summaries. These link summaries are not meant to represent the documentation hierarchy and this task group information is too limited to work well. Clients who need this information should get it from the "link-hierarchy.json" file instead. --- .../ConvertActionConverter.swift | 4 +- .../LinkTargets/LinkDestinationSummary.swift | 123 ++++++++----- .../DocumentationContextTests.swift | 2 +- .../ExternalPathHierarchyResolverTests.swift | 4 +- .../LinkDestinationSummaryTests.swift | 174 ------------------ .../ConvertActionTests.swift | 30 +-- 6 files changed, 81 insertions(+), 256 deletions(-) diff --git a/Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift b/Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift index 17a5db0a7..c0c22f2c9 100644 --- a/Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift +++ b/Sources/SwiftDocC/Infrastructure/ConvertActionConverter.swift @@ -148,7 +148,7 @@ package enum ConvertActionConverter { } if emitDigest { - let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: true) + let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode) let nodeIndexingRecords = try renderNode.indexingRecords(onPage: identifier) resultsGroup.async(queue: resultsSyncQueue) { @@ -157,7 +157,7 @@ package enum ConvertActionConverter { indexingRecords.append(contentsOf: nodeIndexingRecords) } } else if FeatureFlags.current.isExperimentalLinkHierarchySerializationEnabled { - let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false) + let nodeLinkSummaries = entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode) resultsGroup.async(queue: resultsSyncQueue) { linkSummaries.append(contentsOf: nodeLinkSummaries) diff --git a/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift b/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift index a092359f2..d63e38ca5 100644 --- a/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift +++ b/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift @@ -109,6 +109,7 @@ public struct LinkDestinationSummary: Codable, Equatable { // wouldn't necessarily meet these new requirements. /// A collection of identifiers that all relate to some common task, as described by the title. + @available(*, deprecated, message: "Link summaries aren't meant as a source of _hierarchy_ information. This deprecated API will be removed after 6.4 is released.") public struct TaskGroup: Codable, Equatable { /// The title of this task group public let title: String? @@ -130,7 +131,8 @@ public struct LinkDestinationSummary: Codable, Equatable { /// /// - Note: It's possible for more than one task group to have the same title. /// - Note: This property represents conceptual children. Since See Also sections conceptually represent siblings they should not be included. - public let taskGroups: [TaskGroup]? + @available(*, deprecated, message: "Link summaries aren't meant as a source of _hierarchy_ information. This deprecated API will be removed after 6.4 is released.") + public let taskGroups: [TaskGroup]? = nil /// 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. public let usr: String? @@ -207,7 +209,8 @@ public struct LinkDestinationSummary: Codable, Equatable { /// The taskGroups of the variant or `nil` if the taskGroups is the same as the summarized element. /// /// If the summarized element has task groups but the variant doesn't, this property will be `Optional.some(nil)`. - public let taskGroups: VariantValue<[TaskGroup]?> + @available(*, deprecated, message: "Link summaries aren't meant as a source of _hierarchy_ information. This deprecated API will be removed after 6.4 is released.") + public let taskGroups: VariantValue<[TaskGroup]?> = nil /// The precise symbol identifier of the variant or `nil` if the precise symbol identifier is the same as the summarized element. /// @@ -253,7 +256,6 @@ public struct LinkDestinationSummary: Codable, Equatable { /// - relativePresentationURL: The relative presentation URL of the variant or `nil` if the relative is the same as the summarized element. /// - title: The title of the variant or `nil` if the title is the same as the summarized element. /// - abstract: The abstract of the variant or `nil` if the abstract is the same as the summarized element. - /// - 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. /// - plainTextDeclaration: The plain text declaration of this symbol, derived from its full declaration fragments, or `nil` if the precise symbol identifier is the same as the summarized element. /// - subheadingDeclarationFragments: The simplified "subheading" declaration fragments for this symbol, to display in topic groups, or `nil` if the declaration is the same as the summarized element. @@ -265,7 +267,6 @@ public struct LinkDestinationSummary: Codable, Equatable { relativePresentationURL: VariantValue = nil, title: VariantValue = nil, abstract: VariantValue = nil, - taskGroups: VariantValue<[LinkDestinationSummary.TaskGroup]?> = nil, usr: VariantValue = nil, plainTextDeclaration: VariantValue = nil, subheadingDeclarationFragments: VariantValue = nil, @@ -277,13 +278,40 @@ public struct LinkDestinationSummary: Codable, Equatable { self.relativePresentationURL = relativePresentationURL self.title = title self.abstract = abstract - self.taskGroups = taskGroups self.usr = usr self.plainTextDeclaration = plainTextDeclaration self.subheadingDeclarationFragments = subheadingDeclarationFragments self.navigatorDeclarationFragments = navigatorDeclarationFragments } + @available(*, deprecated, renamed: "init(traits:kind:language:relativePresentationURL:title:abstract:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:)", message: "Link summaries aren't meant as a source of _hierarchy_ information. This deprecated API will be removed after 6.4 is released.") + public init( + traits: [RenderNode.Variant.Trait], + kind: VariantValue = nil, + language: VariantValue = nil, + relativePresentationURL: VariantValue = nil, + title: VariantValue = nil, + abstract: VariantValue = nil, + taskGroups _: VariantValue<[LinkDestinationSummary.TaskGroup]?> = nil, + usr: VariantValue = nil, + plainTextDeclaration: VariantValue = nil, + subheadingDeclarationFragments: VariantValue = nil, + navigatorDeclarationFragments: VariantValue = nil + ) { + self.init( + traits: traits, + kind: kind, + language: language, + relativePresentationURL: relativePresentationURL, + title: title, + abstract: abstract, + usr: usr, + plainTextDeclaration: plainTextDeclaration, + subheadingDeclarationFragments: subheadingDeclarationFragments, + navigatorDeclarationFragments: navigatorDeclarationFragments + ) + } + @available(*, deprecated, renamed: "init(traits:kind:language:relativePresentationURL:title:abstract:taskGroups:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:)", message: "Use `init(traits:kind:language:relativePresentationURL:title:abstract:taskGroups:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:)` instead. `TopicRenderReference` doesn't support variant specific topic images. This property will be removed after 6.3 is released") public init( traits: [RenderNode.Variant.Trait], @@ -329,7 +357,6 @@ public struct LinkDestinationSummary: Codable, Equatable { /// - abstract: The abstract of the summarized element. /// - availableLanguages: All the languages in which the summarized element is available. /// - platforms: Information about the platforms for which the summarized element is available. - /// - 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. /// - plainTextDeclaration: The plain text declaration of this symbol, derived from its full declaration fragments, or `nil` if the summarized element isn't a symbol. /// - subheadingDeclarationFragments: The simplified "subheading" fragments for this symbol, to display in topic groups, or `nil` if the summarized element isn't a symbol. @@ -346,7 +373,6 @@ public struct LinkDestinationSummary: Codable, Equatable { abstract: LinkDestinationSummary.Abstract? = nil, availableLanguages: Set, platforms: [LinkDestinationSummary.PlatformAvailability]? = nil, - taskGroups: [LinkDestinationSummary.TaskGroup]? = nil, usr: String? = nil, plainTextDeclaration: String? = nil, subheadingDeclarationFragments: LinkDestinationSummary.DeclarationFragments? = nil, @@ -364,7 +390,6 @@ public struct LinkDestinationSummary: Codable, Equatable { self.abstract = abstract self.availableLanguages = availableLanguages self.platforms = platforms - self.taskGroups = taskGroups self.usr = usr self.plainTextDeclaration = plainTextDeclaration self.subheadingDeclarationFragments = subheadingDeclarationFragments @@ -375,6 +400,45 @@ public struct LinkDestinationSummary: Codable, Equatable { self.variants = variants } + @available(*, deprecated, renamed: "iinit(kind:language:relativePresentationURL:referenceURL:title:abstract:availableLanguages:platforms:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:redirects:topicImages:references:variants:)", message: "Link summaries aren't meant as a source of _hierarchy_ information. This deprecated API will be removed after 6.4 is released.") + public init( + kind: DocumentationNode.Kind, + language: SourceLanguage, + relativePresentationURL: URL, + referenceURL: URL, title: String, + abstract: LinkDestinationSummary.Abstract? = nil, + availableLanguages: Set, + platforms: [LinkDestinationSummary.PlatformAvailability]? = nil, + taskGroups _: [LinkDestinationSummary.TaskGroup]? = nil, + usr: String? = nil, + plainTextDeclaration: String? = nil, + subheadingDeclarationFragments: LinkDestinationSummary.DeclarationFragments? = nil, + navigatorDeclarationFragments: LinkDestinationSummary.DeclarationFragments? = nil, + redirects: [URL]? = nil, + topicImages: [TopicImage]? = nil, + references: [any RenderReference]? = nil, + variants: [LinkDestinationSummary.Variant] + ) { + self.init( + kind: kind, + language: language, + relativePresentationURL: relativePresentationURL, + referenceURL: referenceURL, + title: title, + abstract: abstract, + availableLanguages: availableLanguages, + platforms: platforms, + usr: usr, + plainTextDeclaration: plainTextDeclaration, + subheadingDeclarationFragments: subheadingDeclarationFragments, + navigatorDeclarationFragments: navigatorDeclarationFragments, + redirects: redirects, + topicImages: topicImages, + references: references, + variants: variants + ) + } + @available(*, deprecated, renamed: "init(kind:language:relativePresentationURL:referenceURL:title:abstract:availableLanguages:platforms:taskGroups:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:redirects:topicImages:references:variants:)", message: "Use `init(kind:language:relativePresentationURL:referenceURL:title:abstract:availableLanguages:platforms:taskGroups:usr:plainTextDeclaration:subheadingDeclarationFragments:navigatorDeclarationFragments:redirects:topicImages:references:variants:)` instead. This property will be removed after 6.3 is released") public init( kind: DocumentationNode.Kind, @@ -424,12 +488,10 @@ public extension DocumentationNode { /// - Parameters: /// - context: The context in which references that are found the node's content are resolved in. /// - renderNode: The render node representation of this documentation node. - /// - includeTaskGroups: Whether or not the link summaries should include task groups /// - Returns: The list of summary elements, with the node's summary as the first element. func externallyLinkableElementSummaries( context: DocumentationContext, - renderNode: RenderNode, - includeTaskGroups: Bool = true + renderNode: RenderNode ) -> [LinkDestinationSummary] { let bundle = context.bundle guard bundle.id == reference.bundleID else { @@ -447,34 +509,11 @@ public extension DocumentationNode { LinkDestinationSummary(landmark: $0, relativeParentPresentationURL: relativePresentationURL, page: self, platforms: platforms, compiler: &compiler) } - var taskGroupVariants: [[RenderNode.Variant.Trait]: [LinkDestinationSummary.TaskGroup]] = [:] - let taskGroups: [LinkDestinationSummary.TaskGroup]? - if includeTaskGroups { - switch kind { - case .tutorial, .tutorialArticle, .tutorialTableOfContents, .chapter, .volume, .onPageLandmark: - taskGroups = [.init(title: nil, identifiers: context.children(of: reference).map { $0.reference.absoluteString })] - default: - var topicSectionGroups: [LinkDestinationSummary.TaskGroup] = renderNode.topicSections.map { group in .init(title: group.title, identifiers: group.identifiers) } - - if let overloads = context.linkResolver.localResolver.overloads(ofGroup: reference) { - topicSectionGroups.append(.init(title: "Overloads", identifiers: overloads.map(\.absoluteString))) - } - - taskGroups = topicSectionGroups - for variant in renderNode.topicSectionsVariants.variants { - taskGroupVariants[variant.traits] = renderNode.topicSectionsVariants.value(for: variant.traits).map { group in .init(title: group.title, identifiers: group.identifiers) } - } - } - } else { - taskGroups = nil - } return [ LinkDestinationSummary( documentationNode: self, renderNode: renderNode, relativePresentationURL: relativePresentationURL, - taskGroups: taskGroups, - taskGroupVariants: taskGroupVariants, platforms: platforms, compiler: &compiler ) @@ -504,14 +543,11 @@ extension LinkDestinationSummary { /// - Parameters: /// - documentationNode: The render node to summarize. /// - relativePresentationURL: The relative presentation URL for this page. - /// - taskGroups: The task groups that lists the children of this page. /// - compiler: The content compiler that's used to render the node's abstract. init( documentationNode: DocumentationNode, renderNode: RenderNode, relativePresentationURL: URL, - taskGroups: [TaskGroup]?, - taskGroupVariants: [[RenderNode.Variant.Trait]: [TaskGroup]], platforms: [PlatformAvailability]?, compiler: inout RenderContentCompiler ) { @@ -536,7 +572,6 @@ extension LinkDestinationSummary { abstract: (documentationNode.semantic as? (any Abstracted))?.renderedAbstract(using: &compiler), availableLanguages: documentationNode.availableSourceLanguages, platforms: platforms, - taskGroups: taskGroups, usr: nil, subheadingDeclarationFragments: nil, redirects: redirects, @@ -600,7 +635,6 @@ extension LinkDestinationSummary { relativePresentationURL: nil, // The symbol variant uses the same relative path title: nilIfEqual(main: title, variant: symbol.titleVariants[trait]), abstract: nilIfEqual(main: abstract, variant: abstractVariant), - taskGroups: nilIfEqual(main: taskGroups, variant: taskGroupVariants[variantTraits]), usr: nil, // The symbol variant uses the same USR plainTextDeclaration: nilIfEqual(main: plainTextDeclaration, variant: plainTextDeclarationVariant), subheadingDeclarationFragments: nilIfEqual(main: subheadingDeclarationFragments, variant: subheadingDeclarationFragmentsVariant), @@ -621,7 +655,6 @@ extension LinkDestinationSummary { abstract: abstract, availableLanguages: documentationNode.availableSourceLanguages, platforms: platforms, - taskGroups: taskGroups, usr: usr, plainTextDeclaration: plainTextDeclaration, subheadingDeclarationFragments: subheadingDeclarationFragments, @@ -691,7 +724,6 @@ extension LinkDestinationSummary { abstract: abstract, availableLanguages: page.availableSourceLanguages, platforms: platforms, - taskGroups: [], // Landmarks have no children usr: nil, // Only symbols have a USR subheadingDeclarationFragments: nil, // Only symbols have declarations redirects: (landmark as? (any Redirected))?.redirects?.map { $0.oldPath }, @@ -707,7 +739,7 @@ extension LinkDestinationSummary { // Add Codable methods—which include an initializer—in an extension so that it doesn't override the member-wise initializer. extension LinkDestinationSummary { enum CodingKeys: String, CodingKey { - case kind, referenceURL, title, abstract, language, taskGroups, usr, availableLanguages, platforms, redirects, topicImages, references, variants, plainTextDeclaration + case kind, referenceURL, title, abstract, language, usr, availableLanguages, platforms, redirects, topicImages, references, variants, plainTextDeclaration case relativePresentationURL = "path" case subheadingDeclarationFragments = "fragments" case navigatorDeclarationFragments = "navigatorFragments" @@ -738,7 +770,6 @@ extension LinkDestinationSummary { } } try container.encodeIfPresent(platforms, forKey: .platforms) - try container.encodeIfPresent(taskGroups, forKey: .taskGroups) try container.encodeIfPresent(usr, forKey: .usr) try container.encodeIfPresent(plainTextDeclaration, forKey: .plainTextDeclaration) try container.encodeIfPresent(subheadingDeclarationFragments, forKey: .subheadingDeclarationFragments) @@ -796,7 +827,6 @@ extension LinkDestinationSummary { availableLanguages = decodedLanguages platforms = try container.decodeIfPresent([AvailabilityRenderItem].self, forKey: .platforms) - taskGroups = try container.decodeIfPresent([TaskGroup].self, forKey: .taskGroups) usr = try container.decodeIfPresent(String.self, forKey: .usr) plainTextDeclaration = try container.decodeIfPresent(String.self, forKey: .plainTextDeclaration) subheadingDeclarationFragments = try container.decodeIfPresent(DeclarationFragments.self, forKey: .subheadingDeclarationFragments) @@ -813,7 +843,7 @@ extension LinkDestinationSummary { extension LinkDestinationSummary.Variant { enum CodingKeys: String, CodingKey { - case traits, kind, title, abstract, language, usr, taskGroups, plainTextDeclaration + case traits, kind, title, abstract, language, usr, plainTextDeclaration case relativePresentationURL = "path" case declarationFragments = "fragments" case navigatorDeclarationFragments = "navigatorFragments" @@ -843,7 +873,6 @@ extension LinkDestinationSummary.Variant { try container.encodeIfPresent(plainTextDeclaration, forKey: .plainTextDeclaration) try container.encodeIfPresent(subheadingDeclarationFragments, forKey: .declarationFragments) try container.encodeIfPresent(navigatorDeclarationFragments, forKey: .navigatorDeclarationFragments) - try container.encodeIfPresent(taskGroups, forKey: .taskGroups) } public init(from decoder: any Decoder) throws { @@ -887,7 +916,6 @@ extension LinkDestinationSummary.Variant { subheadingDeclarationFragments = try container.decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .declarationFragments) navigatorDeclarationFragments = try container .decodeIfPresent(LinkDestinationSummary.DeclarationFragments?.self, forKey: .navigatorDeclarationFragments) - taskGroups = try container.decodeIfPresent([LinkDestinationSummary.TaskGroup]?.self, forKey: .taskGroups) } } @@ -910,7 +938,6 @@ extension LinkDestinationSummary { guard lhs.abstract == rhs.abstract else { return false } guard lhs.availableLanguages == rhs.availableLanguages else { return false } guard lhs.platforms == rhs.platforms else { return false } - guard lhs.taskGroups == rhs.taskGroups else { return false } guard lhs.subheadingDeclarationFragments == rhs.subheadingDeclarationFragments else { return false } guard lhs.redirects == rhs.redirects else { return false } guard lhs.topicImages == rhs.topicImages else { return false } diff --git a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift index acc5b9d50..8a19c11a1 100644 --- a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift @@ -5497,7 +5497,7 @@ let expected = """ let entity = try context.entity(with: reference) let renderNode = try XCTUnwrap(converter.convert(entity)) - return entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false) + return entity.externallyLinkableElementSummaries(context: context, renderNode: renderNode) } let linkResolutionInformation = try context.linkResolver.localResolver.prepareForSerialization(bundleID: bundle.id) diff --git a/Tests/SwiftDocCTests/Infrastructure/ExternalPathHierarchyResolverTests.swift b/Tests/SwiftDocCTests/Infrastructure/ExternalPathHierarchyResolverTests.swift index 9a62ddf51..46af020cf 100644 --- a/Tests/SwiftDocCTests/Infrastructure/ExternalPathHierarchyResolverTests.swift +++ b/Tests/SwiftDocCTests/Infrastructure/ExternalPathHierarchyResolverTests.swift @@ -713,7 +713,7 @@ class ExternalPathHierarchyResolverTests: XCTestCase { let entity = try dependencyContext.entity(with: reference) let renderNode = try XCTUnwrap(dependencyConverter.renderNode(for: entity)) - return entity.externallyLinkableElementSummaries(context: dependencyContext, renderNode: renderNode, includeTaskGroups: false) + return entity.externallyLinkableElementSummaries(context: dependencyContext, renderNode: renderNode) } let linkResolutionInformation = try dependencyContext.linkResolver.localResolver.prepareForSerialization(bundleID: dependencyBundle.id) @@ -1004,7 +1004,7 @@ class ExternalPathHierarchyResolverTests: XCTestCase { for reference in context.knownPages { let node = try context.entity(with: reference) let renderNode = converter.convert(node) - entitySummaries.append(contentsOf: node.externallyLinkableElementSummaries(context: context, renderNode: renderNode, includeTaskGroups: false)) + entitySummaries.append(contentsOf: node.externallyLinkableElementSummaries(context: context, renderNode: renderNode)) } let externalResolver = ExternalPathHierarchyResolver( diff --git a/Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift b/Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift index 560695810..c7bfe53d7 100644 --- a/Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift +++ b/Tests/SwiftDocCTests/LinkTargets/LinkDestinationSummaryTests.swift @@ -104,11 +104,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(pageSummary.referenceURL.absoluteString, "doc://com.test.example/tutorials/TestBundle/Tutorial") XCTAssertEqual(pageSummary.language, .swift) XCTAssertEqual(pageSummary.kind, .tutorial) - XCTAssertEqual(pageSummary.taskGroups, [ - .init(title: nil, - identifiers: ["doc://com.test.example/tutorials/TestBundle/Tutorial#Create-a-New-AR-Project-%F0%9F%92%BB"] - ), - ]) XCTAssertEqual(pageSummary.availableLanguages, [.swift]) XCTAssertEqual(pageSummary.platforms, renderNode.metadata.platforms) XCTAssertEqual(pageSummary.redirects, nil) @@ -126,7 +121,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(sectionSummary.referenceURL.absoluteString, "doc://com.test.example/tutorials/TestBundle/Tutorial#Create-a-New-AR-Project-%F0%9F%92%BB") XCTAssertEqual(sectionSummary.language, .swift) XCTAssertEqual(sectionSummary.kind, .onPageLandmark) - XCTAssertEqual(sectionSummary.taskGroups, []) XCTAssertEqual(sectionSummary.availableLanguages, [.swift]) XCTAssertEqual(sectionSummary.platforms, nil) XCTAssertEqual(sectionSummary.redirects, [ @@ -165,22 +159,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .class) XCTAssertEqual(summary.abstract, [.text("MyClass abstract.")]) - XCTAssertEqual(summary.taskGroups?.map { $0.title }, [ - "MyClass members (relative)", - "MyClass members (module level)", - "MyClass members (absolute)", - "MyClass members (topic relative)", - "MyClass members (topic module level)", - "MyClass members (topic absolute)", - ]) - for group in summary.taskGroups ?? [] { - // All 6 topic sections curate the same 3 symbols using different syntax and different specificity - XCTAssertEqual(group.identifiers, [ - summary.referenceURL.appendingPathComponent("init()-33vaw").absoluteString, - summary.referenceURL.appendingPathComponent("init()-3743d").absoluteString, - summary.referenceURL.appendingPathComponent("myFunction()").absoluteString, - ]) - } XCTAssertEqual(summary.availableLanguages, [.swift]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "s:5MyKit0A5ClassC") @@ -213,17 +191,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .protocol) XCTAssertEqual(summary.abstract, [.text("An abstract of a protocol using a "), .codeVoice(code: "String"), .text(" id value.")]) - XCTAssertEqual(summary.taskGroups, [ - .init( - title: "Task Group Exercising Symbol Links", - identifiers: [ - // MyClass is curated 3 times using different syntax. - summary.referenceURL.deletingLastPathComponent().appendingPathComponent("MyClass").absoluteString, - summary.referenceURL.deletingLastPathComponent().appendingPathComponent("MyClass").absoluteString, - summary.referenceURL.deletingLastPathComponent().appendingPathComponent("MyClass").absoluteString, - ] - ), - ]) XCTAssertEqual(summary.availableLanguages, [.swift]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "s:5MyKit0A5ProtocolP") @@ -258,7 +225,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .instanceMethod) XCTAssertEqual(summary.abstract, [.text("A cool API to call.")]) - XCTAssertEqual(summary.taskGroups, []) XCTAssertEqual(summary.availableLanguages, [.swift]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "s:5MyKit0A5ClassC10myFunctionyyF") @@ -295,7 +261,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .function) XCTAssertEqual(summary.abstract, nil) - XCTAssertEqual(summary.taskGroups, []) XCTAssertEqual(summary.availableLanguages, [.swift]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "s:5MyKit14globalFunction_11consideringy10Foundation4DataV_SitF") @@ -363,7 +328,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .instanceMethod) XCTAssertEqual(summary.abstract, [.text("A cool API to call.")]) - XCTAssertEqual(summary.taskGroups, []) XCTAssertEqual(summary.availableLanguages, [.swift]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "s:5MyKit0A5ClassC10myFunctionyyF") @@ -470,14 +434,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.language, .swift) XCTAssertEqual(summary.kind, .class) XCTAssertEqual(summary.abstract, [.text("A bar.")]) - XCTAssertEqual(summary.taskGroups, [ - .init( - title: "Type Methods", - identifiers: [ - summary.referenceURL.appendingPathComponent("myStringFunction(_:)").absoluteString, - ] - ), - ]) XCTAssertEqual(summary.availableLanguages.sorted(), [.swift, .objectiveC]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "c:objc(cs)Bar") @@ -515,7 +471,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(variant.abstract, nil) XCTAssertEqual(variant.usr, nil) XCTAssertEqual(variant.kind, nil) - XCTAssertEqual(variant.taskGroups, nil) let encoded = try JSONEncoder().encode(summary) let decoded = try JSONDecoder().decode(LinkDestinationSummary.self, from: encoded) @@ -536,15 +491,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(summary.kind, .typeMethod) XCTAssertEqual(summary.abstract, [.text("Does a string function.")]) - XCTAssertEqual( - summary.taskGroups, - [], - """ - Expected no task groups for the Swift documentation because the symbol \ - it curates (``Foo-c.typealias``) is available in Objective-C only. - """ - ) - XCTAssertEqual(summary.availableLanguages.sorted(), [.swift, .objectiveC]) XCTAssertEqual(summary.platforms, renderNode.metadata.platforms) XCTAssertEqual(summary.usr, "c:objc(cs)Bar(cm)myStringFunction:error:") @@ -585,20 +531,6 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssertEqual(variant.abstract, nil) XCTAssertEqual(variant.usr, nil) XCTAssertEqual(variant.kind, nil) - XCTAssertEqual( - variant.taskGroups, - [ - .init( - title: "Custom", - identifiers: [ - summary.referenceURL - .deletingLastPathComponent() // myStringFunction:error: - .deletingLastPathComponent() // Bar - .appendingPathComponent("Foo-c.typealias").absoluteString, - ] - ) - ] - ) let encoded = try JSONEncoder().encode(summary) let decoded = try JSONDecoder().decode(LinkDestinationSummary.self, from: encoded) @@ -723,110 +655,4 @@ class LinkDestinationSummaryTests: XCTestCase { XCTAssert(decoded.variants.isEmpty) } - - /// Ensure that the task group link summary for overload group pages doesn't overwrite any manual curation. - func testOverloadSymbolsWithManualCuration() async throws { - enableFeatureFlag(\.isExperimentalOverloadedSymbolPresentationEnabled) - - let symbolGraph = SymbolGraph.init( - metadata: .init(formatVersion: .init(string: "1.0.0")!, generator: "unit-test"), - module: .init(name: "MyModule", platform: .init()), - symbols: [ - .init( - identifier: .init(precise: "s:MyClass", interfaceLanguage: "swift"), - names: .init(title: "MyClass", navigator: nil, subHeading: nil, prose: nil), - pathComponents: ["MyClass"], - docComment: nil, - accessLevel: .public, - kind: .init(parsedIdentifier: .class, displayName: "Class"), - mixins: [:] - ), - .init( - identifier: .init(precise: "s:MyClass:myFunc-1", interfaceLanguage: "swift"), - names: .init(title: "myFunc()", navigator: nil, subHeading: nil, prose: nil), - pathComponents: ["MyClass", "myFunc()"], - docComment: .init([ - .init( - text: """ - A wonderful overloaded function. - - ## Topics - - ### Other Cool Symbols - - - ``MyStruct`` - """, - range: nil) - ]), - accessLevel: .public, - kind: .init(parsedIdentifier: .method, displayName: "Instance Method"), - mixins: [:] - ), - .init( - identifier: .init(precise: "s:MyClass:myFunc-2", interfaceLanguage: "swift"), - names: .init(title: "myFunc()", navigator: nil, subHeading: nil, prose: nil), - pathComponents: ["MyClass", "myFunc()"], - docComment: nil, - accessLevel: .public, - kind: .init(parsedIdentifier: .method, displayName: "Instance Method"), - mixins: [:] - ), - .init( - identifier: .init(precise: "s:MyStruct", interfaceLanguage: "swift"), - names: .init(title: "MyStruct", navigator: nil, subHeading: nil, prose: nil), - pathComponents: ["MyStruct"], - docComment: nil, - accessLevel: .public, - kind: .init(parsedIdentifier: .struct, displayName: "Structure"), - mixins: [:] - ), - ], - relationships: [ - .init( - source: "s:MyClass:myFunc-1", - target: "s:MyClass", - kind: .memberOf, - targetFallback: nil - ), - .init( - source: "s:MyClass:myFunc-2", - target: "s:MyClass", - kind: .memberOf, - targetFallback: nil - ), - ] - ) - - let catalogHierarchy = Folder(name: "unit-test.docc", content: [ - JSONFile(name: "MyModule.symbols.json", content: symbolGraph), - InfoPlist(displayName: "MyModule", identifier: "com.example.mymodule") - ]) - let (bundle, context) = try await loadBundle(catalog: catalogHierarchy) - - let converter = DocumentationNodeConverter(bundle: bundle, context: context) - - let node = try context.entity(with: ResolvedTopicReference(bundleID: bundle.id, path: "/documentation/MyModule/MyClass/myFunc()", sourceLanguage: .swift)) - let renderNode = converter.convert(node) - - let summaries = node.externallyLinkableElementSummaries(context: context, renderNode: renderNode) - let pageSummary = summaries[0] - - let taskGroups = try XCTUnwrap(pageSummary.taskGroups) - - guard taskGroups.count == 2 else { - XCTFail("Expected 2 task groups, found \(taskGroups.count): \(taskGroups.map(\.title))") - return - } - - XCTAssertEqual(taskGroups[0].title, "Other Cool Symbols") - XCTAssertEqual(taskGroups[0].identifiers, [ - "doc://com.example.mymodule/documentation/MyModule/MyStruct" - ]) - - XCTAssertEqual(taskGroups[1].title, "Overloads") - XCTAssertEqual(Set(taskGroups[1].identifiers), [ - "doc://com.example.mymodule/documentation/MyModule/MyClass/myFunc()-9a7pr", - "doc://com.example.mymodule/documentation/MyModule/MyClass/myFunc()-9a7po", - ]) - } } diff --git a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift index 72f42ba47..289678b76 100644 --- a/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift +++ b/Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift @@ -1003,11 +1003,6 @@ class ConvertActionTests: XCTestCase { title: "TestBed", language: .swift, abstract: "TestBed abstract.", - taskGroups: [ - .init(title: "Basics", identifiers: ["doc://com.test.example/documentation/TestBundle/Article"]), - .init(title: "Articles", identifiers: ["doc://com.test.example/documentation/TestBundle/SampleArticle"]), - .init(title: "Structures", identifiers: ["doc://com.test.example/documentation/TestBed/A"]), - ], usr: "TestBed", availableLanguages: [.swift], platforms: nil, @@ -1025,7 +1020,6 @@ class ConvertActionTests: XCTestCase { title: "A", language: .swift, abstract: "An abstract.", - taskGroups: [], usr: "s:7TestBed1AV", availableLanguages: [.swift], platforms: nil, @@ -1043,7 +1037,6 @@ class ConvertActionTests: XCTestCase { title: "This is an article", language: .swift, abstract: "Article abstract.", - taskGroups: [], availableLanguages: [.swift], platforms: nil, topicImages: nil, @@ -1060,7 +1053,6 @@ class ConvertActionTests: XCTestCase { title: "Sample Article", language: .swift, abstract: "Sample abstract.", - taskGroups: [], availableLanguages: [.swift], platforms: nil, topicImages: nil, @@ -1161,22 +1153,7 @@ class ConvertActionTests: XCTestCase { } // Rather than comparing all the linkable entities in the file, pull out one overload group - // and check its task groups - let overloadGroupEntity = try XCTUnwrap(resultLikableEntities.first(where: { $0.usr == "s:8ShapeKit18OverloadedProtocolP20fourthTestMemberName4testSdSS_tF::OverloadGroup" })) - - let taskGroups = try XCTUnwrap(overloadGroupEntity.taskGroups) - guard taskGroups.count == 1, let overloadTaskGroup = taskGroups.first else { - XCTFail("Expected one task group, found \(taskGroups.count): \(taskGroups.map(\.title?.singleQuoted))") - return - } - - XCTAssertEqual(overloadTaskGroup.title, "Overloads") - XCTAssertEqual(Set(overloadTaskGroup.identifiers), [ - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-91hxs", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-961zx", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-8iuz7", - "doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-1h173", - ]) + XCTAssertTrue(resultLikableEntities.contains(where: { $0.usr == "s:8ShapeKit18OverloadedProtocolP20fourthTestMemberName4testSdSS_tF::OverloadGroup" })) } func testDownloadMetadataIsWrittenToOutputFolder() async throws { @@ -1407,7 +1384,6 @@ class ConvertActionTests: XCTestCase { title: "Making an Augmented Reality App", language: .swift, abstract: "This is an abstract for the intro.", - taskGroups: [.init(title: nil, identifiers: [reference.withFragment("Section-Name").absoluteString])], availableLanguages: [.swift], platforms: nil, topicImages: nil, @@ -1421,7 +1397,6 @@ class ConvertActionTests: XCTestCase { title: "Section Name", language: .swift, abstract: nil, - taskGroups: [], availableLanguages: [.swift], platforms: nil, topicImages: nil, @@ -1438,7 +1413,6 @@ class ConvertActionTests: XCTestCase { title: "Technology X", language: .swift, abstract: "Learn about some stuff in Technology X.", - taskGroups: [.init(title: nil, identifiers: [reference.appendingPath("Volume-1").absoluteString])], availableLanguages: [.swift], platforms: nil, topicImages: nil, @@ -3199,7 +3173,6 @@ private extension LinkDestinationSummary { title: String, language: SourceLanguage, abstract: String?, - taskGroups: [TaskGroup], usr: String? = nil, availableLanguages: Set, platforms: [PlatformAvailability]?, @@ -3216,7 +3189,6 @@ private extension LinkDestinationSummary { abstract: abstract.map { [.text($0)] }, availableLanguages: availableLanguages, platforms: platforms, - taskGroups: taskGroups, usr: usr, subheadingDeclarationFragments: nil, redirects: redirects, From dc0127af054a57e1be8d31b1ddd6484e250d7650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20R=C3=B6nnqvist?= Date: Tue, 30 Sep 2025 15:16:22 +0200 Subject: [PATCH 2/2] Remove task group information from the specification --- .../Resources/LinkableEntities.json | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/Sources/SwiftDocC/SwiftDocC.docc/Resources/LinkableEntities.json b/Sources/SwiftDocC/SwiftDocC.docc/Resources/LinkableEntities.json index a07d1c18a..4f048b8b2 100644 --- a/Sources/SwiftDocC/SwiftDocC.docc/Resources/LinkableEntities.json +++ b/Sources/SwiftDocC/SwiftDocC.docc/Resources/LinkableEntities.json @@ -2,7 +2,7 @@ "openapi": "3.0.0", "info": { "description": "Specification of the DocC linkable-entities.json digest file.", - "version": "0.3.0", + "version": "0.3.1", "title": "Linkable Entities" }, "paths": { }, @@ -59,12 +59,6 @@ "$ref": "#/components/schemas/PlatformAvailability" } }, - "taskGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskGroup" - } - }, "usr": { "type": "string" }, @@ -184,13 +178,6 @@ "$ref": "#/components/schemas/DeclarationToken" }, "nullable": true - }, - "taskGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskGroup" - }, - "nullable": true } } }, @@ -517,23 +504,6 @@ } } }, - "TaskGroup": { - "type": "object", - "required": [ - "identifiers" - ], - "properties": { - "title": { - "type": "string" - }, - "identifiers": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "DeclarationToken": { "required": [ "text",