@@ -147,6 +147,11 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
147147 return node. reference
148148 }
149149 }
150+
151+ /// The topic reference of the root module, if it's the only registered module.
152+ var soleRootModuleReference : ResolvedTopicReference ? {
153+ rootModules. count == 1 ? rootModules. first : nil
154+ }
150155
151156 /// Map of document URLs to topic references.
152157 var documentLocationMap = BidirectionalMap < URL , ResolvedTopicReference > ( )
@@ -1624,9 +1629,23 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
16241629 /// - Parameters:
16251630 /// - articles: Articles to register with the documentation cache.
16261631 /// - bundle: The bundle containing the articles.
1627- private func registerArticles( _ articles: DocumentationContext . Articles , in bundle: DocumentationBundle ) {
1628- for article in articles {
1629- guard let ( documentation, title) = DocumentationContext . documentationNodeAndTitle ( for: article, kind: . article, in: bundle) else { continue }
1632+ /// - Returns: The articles that were registered, with their topic graph node updated to what's been added to the topic graph.
1633+ private func registerArticles(
1634+ _ articles: DocumentationContext . Articles ,
1635+ in bundle: DocumentationBundle
1636+ ) -> DocumentationContext . Articles {
1637+ articles. map { article in
1638+ guard let ( documentation, title) = DocumentationContext . documentationNodeAndTitle (
1639+ for: article,
1640+
1641+ // Articles are available in the same languages the only root module is available in. If there is more
1642+ // than one module, we cannot determine what languages it's available in and default to Swift.
1643+ availableSourceLanguages: soleRootModuleReference? . sourceLanguages,
1644+ kind: . article,
1645+ in: bundle
1646+ ) else {
1647+ return article
1648+ }
16301649 let reference = documentation. reference
16311650
16321651 documentationCache [ reference] = documentation
@@ -1638,6 +1657,11 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
16381657 for anchor in documentation. anchorSections {
16391658 nodeAnchorSections [ anchor. reference] = anchor
16401659 }
1660+
1661+ var article = article
1662+ // Update the article's topic graph node with the one we just added to the topic graph.
1663+ article. topicGraphNode = graphNode
1664+ return article
16411665 }
16421666 }
16431667
@@ -1648,16 +1672,45 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
16481672 /// - kind: The kind that should be used to create the returned documentation node.
16491673 /// - bundle: The documentation bundle this article belongs to.
16501674 /// - Returns: A documentation node and title for the given article semantic result.
1651- static func documentationNodeAndTitle( for article: DocumentationContext . SemanticResult < Article > , kind: DocumentationNode . Kind , in bundle: DocumentationBundle ) -> ( node: DocumentationNode , title: String ) ? {
1675+ static func documentationNodeAndTitle(
1676+ for article: DocumentationContext . SemanticResult < Article > ,
1677+ availableSourceLanguages: Set < SourceLanguage > ? = nil ,
1678+ kind: DocumentationNode . Kind ,
1679+ in bundle: DocumentationBundle
1680+ ) -> ( node: DocumentationNode , title: String ) ? {
16521681 guard let articleMarkup = article. value. markup else {
16531682 return nil
16541683 }
16551684
16561685 let path = NodeURLGenerator . pathForSemantic ( article. value, source: article. source, bundle: bundle)
1657- let reference = ResolvedTopicReference ( bundleIdentifier: bundle. identifier, path: path, sourceLanguage: . swift)
1686+
1687+ // If available source languages are provided and it contains Swift, use Swift as the default language of
1688+ // the article.
1689+ let sourceLanguage = availableSourceLanguages. map { availableSourceLanguages in
1690+ if availableSourceLanguages. contains ( . swift) {
1691+ return . swift
1692+ } else {
1693+ return availableSourceLanguages. first ?? . swift
1694+ }
1695+ } ?? SourceLanguage . swift
1696+
1697+ let reference = ResolvedTopicReference (
1698+ bundleIdentifier: bundle. identifier,
1699+ path: path,
1700+ sourceLanguages: availableSourceLanguages ?? [ . swift]
1701+ )
1702+
16581703 let title = article. topicGraphNode. title
16591704
1660- let documentationNode = DocumentationNode ( reference: reference, kind: kind, sourceLanguage: . swift, name: . conceptual( title: title) , markup: articleMarkup, semantic: article. value)
1705+ let documentationNode = DocumentationNode (
1706+ reference: reference,
1707+ kind: kind,
1708+ sourceLanguage: sourceLanguage,
1709+ availableSourceLanguages: availableSourceLanguages,
1710+ name: . conceptual( title: title) ,
1711+ markup: articleMarkup,
1712+ semantic: article. value
1713+ )
16611714
16621715 return ( documentationNode, title)
16631716 }
@@ -1693,11 +1746,29 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
16931746 }
16941747
16951748 let articleReferences = autoCuratedArticles. map ( \. topicGraphNode. reference)
1696- let autoArticlesSection = AutomaticTaskGroupSection ( title: " Articles " , references: articleReferences, renderPositionPreference: . top)
1749+
1750+ func createAutomaticTaskGroupSection( references: [ ResolvedTopicReference ] ) -> AutomaticTaskGroupSection {
1751+ AutomaticTaskGroupSection (
1752+ title: " Articles " ,
1753+ references: references,
1754+ renderPositionPreference: . top
1755+ )
1756+ }
16971757
16981758 let node = try entity ( with: rootNode. reference)
1699- if var taskGroupProviding = node. semantic as? AutomaticTaskGroupsProviding {
1700- taskGroupProviding. automaticTaskGroups = [ autoArticlesSection]
1759+
1760+ // If the node we're automatically curating the article under is a symbol, automatically curate the article
1761+ // for each language it's available in.
1762+ if let symbol = node. semantic as? Symbol {
1763+ for sourceLanguage in node. availableSourceLanguages {
1764+ symbol. automaticTaskGroupsVariants [
1765+ . init( interfaceLanguage: sourceLanguage. id)
1766+ ] = [ createAutomaticTaskGroupSection ( references: articleReferences) ]
1767+ }
1768+ } else if var taskGroupProviding = node. semantic as? AutomaticTaskGroupsProviding {
1769+ taskGroupProviding. automaticTaskGroups = [
1770+ createAutomaticTaskGroupSection ( references: articleReferences)
1771+ ]
17011772 }
17021773
17031774 return articleReferences
@@ -1779,7 +1850,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
17791850
17801851 // All discovery went well, process the inputs.
17811852 let ( technologies, tutorials, tutorialArticles, allArticles) = result
1782- let ( otherArticles, rootPageArticles) = splitArticles ( allArticles)
1853+ var ( otherArticles, rootPageArticles) = splitArticles ( allArticles)
17831854
17841855 let rootPages = registerRootPages ( from: rootPageArticles, in: bundle)
17851856 let ( moduleReferences, symbolsURLHierarchy) = try registerSymbols ( from: bundle, symbolGraphLoader: symbolGraphLoader)
@@ -1789,9 +1860,9 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
17891860 try shouldContinueRegistration ( )
17901861
17911862 // Articles that will be automatically curated can be resolved but they need to be pre registered before resolving links.
1792- let rootNodeForAutomaticCuration = rootModules . count == 1 ? rootModules . first . flatMap ( topicGraph. nodeWithReference) : nil
1863+ let rootNodeForAutomaticCuration = soleRootModuleReference . flatMap ( topicGraph. nodeWithReference ( _ : ) )
17931864 if rootNodeForAutomaticCuration != nil {
1794- registerArticles ( otherArticles, in: bundle)
1865+ otherArticles = registerArticles ( otherArticles, in: bundle)
17951866 try shouldContinueRegistration ( )
17961867 }
17971868
@@ -2550,32 +2621,6 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
25502621 return results
25512622 }
25522623
2553- /// Finds the presentation language the symbol is curated under by walking
2554- /// the documentation graph upwards and finding a parent that has an interface language.
2555- /// Will return `nil` if no parent with assigned interface language is found
2556- /// (e.g. in a tutorials only bundle).
2557- func interfaceLanguageFor( _ reference: ResolvedTopicReference ) throws -> SourceLanguage ? {
2558- let node = try entity ( with: reference)
2559- guard !( node. semantic is Symbol ) else {
2560- // For symbols just return the source language
2561- return node. sourceLanguage
2562- }
2563-
2564- // `pathsTo()` returns the canonical path first
2565- guard let canonical = pathsTo ( reference) . first else {
2566- // Uncurated symbol, if not expected a warning will be emitted elsewhere
2567- return nil
2568- }
2569-
2570- // Return the language of the first symbol entity
2571- return canonical. mapFirst { reference -> SourceLanguage ? in
2572- guard let node = try ? entity ( with: reference) , node. semantic is Symbol else {
2573- return nil
2574- }
2575- return node. sourceLanguage
2576- }
2577- }
2578-
25792624 func dumpGraph( ) -> String {
25802625 return topicGraph. nodes. values
25812626 . filter { parents ( of: $0. reference) . isEmpty }
0 commit comments