Skip to content

Commit

Permalink
Merge automatically curated task groups (#931)
Browse files Browse the repository at this point in the history
* Merges automatically curated groups with manually curated groups

Changes the automatic curation logic for symbols to only create a new section if an existing manually curated section does not already exist.

If the section already exists, it adds the automatically curated symbols onto the existing section.

* Adds test for merging automatically curated symbol task groups

Adds a unit test to verify that the automatic curation logic for symbols is to only create a new section if an existing manually curated section does not already exist (rdar://61899214).

* Adds comment about combined references in merged topics

Adds a comment explaining that merging references between manually & automatic curated topics without checking for duplicates should be safe, and explains the logic behind this thinking.

Resolves rdar://61899214.
  • Loading branch information
anferbui authored May 31, 2024
1 parent fd9a406 commit d304317
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 7 deletions.
21 changes: 14 additions & 7 deletions Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1546,16 +1546,23 @@ public struct RenderNodeTranslator: SemanticVisitor {

// Children of the current symbol that have not been curated manually in a task group will all
// be automatically curated in task groups after their symbol kind: "Properties", "Enumerations", etc.
let alreadyCurated = Set(sections.flatMap { $0.identifiers })
let groups = try! AutomaticCuration.topics(for: documentationNode, withTraits: allowedTraits, context: context)

sections.append(contentsOf: groups.compactMap { group in
let newReferences = group.references.filter { !alreadyCurated.contains($0.absoluteString) }
guard !newReferences.isEmpty else { return nil }

for group in groups {
let newReferences = group.references
contentCompiler.collectedTopicReferences.append(contentsOf: newReferences)
return TaskGroupRenderSection(taskGroup: (title: group.title, references: newReferences))
})

// If the section has been manually curated, merge the references of both the automatic curation and the manual curation into one section (rdar://61899214).
if let duplicateSectionIndex = sections.firstIndex(where: { $0.title == group.title }) {
let originalSection = sections[duplicateSectionIndex]
// Combining all references here without checking for duplicates should be safe,
// because the automatic curation of topics only returns symbols that haven't already been manually curated.
let combinedReferences = originalSection.identifiers + newReferences.map { $0.absoluteString }
sections[duplicateSectionIndex] = TaskGroupRenderSection(title: originalSection.title, abstract: originalSection.abstract, discussion: originalSection.discussion, identifiers: combinedReferences)
} else {
sections.append(TaskGroupRenderSection(taskGroup: (title: group.title, references: newReferences)))
}
}

// Place "bottom" rendering preference automatic task groups
// after any user-defined task groups but before automatic curation.
Expand Down
56 changes: 56 additions & 0 deletions Tests/SwiftDocCTests/Infrastructure/AutomaticCurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,62 @@ class AutomaticCurationTests: XCTestCase {
"doc://com.shapes.ShapeKit/documentation/ShapeKit/OverloadedProtocol/fourthTestMemberName(test:)-961zx",
])
}

func testAutomaticallyCuratedSymbolTopicsAreMergedWithManuallyCuratedTopics() throws {
for kind in availableNonExtensionSymbolKinds {
let containerID = "some-container-id"
let memberID = "some-member-id"
let topicSectionTitle = AutomaticCuration.groupTitle(for: kind)

let exampleDocumentation = Folder(name: "CatalogName.docc", content: [
JSONFile(name: "ModuleName.symbols.json",
content: makeSymbolGraph(moduleName: "ModuleName", symbols: [
makeSymbol(identifier: containerID, kind: .class, pathComponents: ["SomeClass"]),
makeSymbol(identifier: memberID, kind: kind, pathComponents: ["SomeClass", "someMember"]),
], relationships: [
.init(source: memberID, target: containerID, kind: .memberOf, targetFallback: nil),
])),
TextFile(name: "SomeArticle.md", utf8Content: """
# Some article
An article with some content.
"""),
TextFile(name: "SomeExtension.md", utf8Content: """
# ``ModuleName/SomeClass``
Curate an article under a manually curated section and leave the symbol documentation to automatic curation.
## Topics
### \(topicSectionTitle)
- <doc:SomeArticle>
"""),
])
let catalogURL = try exampleDocumentation.write(inside: createTemporaryDirectory())
let (_, bundle, context) = try loadBundle(from: catalogURL)

let node = try context.entity(with: ResolvedTopicReference(bundleIdentifier: bundle.identifier, path: "/documentation/ModuleName/SomeClass", sourceLanguage: .swift))

// Compile docs and verify the generated Topics section
var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: node.reference, source: nil)
let renderNode = try XCTUnwrap(translator.visit(node.semantic) as? RenderNode)

// Verify that there are no duplicate sections in `SomeClass`'s "Topics" section
XCTAssertEqual(renderNode.topicSections.map { $0.title }, [topicSectionTitle])

// Verify that uncurated element `ModuleName/SomeClass/someMember` is
// automatically curated in `SomeClass`'s "Topics" under the existing manually curated topics section
// along with manually curated article "SomeArticle"
XCTAssertEqual([
"doc://CatalogName/documentation/CatalogName/SomeArticle",
"doc://CatalogName/documentation/ModuleName/SomeClass/someMember",
], renderNode.topicSections.first?.identifiers)

// Verify that the merged section under `SideClass`'s "Topics" is correctly marked as containing manual content
XCTAssertFalse(renderNode.topicSections.first?.generated ?? false)
}
}
}

private func makeSymbol(
Expand Down

0 comments on commit d304317

Please sign in to comment.