-
Notifications
You must be signed in to change notification settings - Fork 162
Emit language variants for articles #81
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,14 +138,13 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| } | ||
|
|
||
| /// The root module nodes of the Topic Graph. | ||
| public var rootModules: [ResolvedTopicReference] { | ||
| return topicGraph.nodes.values.compactMap { node in | ||
| guard node.kind == .module, | ||
| !onlyHasSnippetRelatedChildren(for: node.reference) else { | ||
| return nil | ||
| } | ||
| return node.reference | ||
| } | ||
| /// | ||
| /// This property is initialized during the registration of a documentation bundle. | ||
| public private(set) var rootModules: [ResolvedTopicReference]! | ||
|
|
||
| /// The topic reference of the root module, if it's the only registered module. | ||
| var soleRootModuleReference: ResolvedTopicReference? { | ||
| rootModules.count == 1 ? rootModules.first : nil | ||
| } | ||
|
|
||
| /// Map of document URLs to topic references. | ||
|
|
@@ -1624,9 +1623,23 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| /// - Parameters: | ||
| /// - articles: Articles to register with the documentation cache. | ||
| /// - bundle: The bundle containing the articles. | ||
| private func registerArticles(_ articles: DocumentationContext.Articles, in bundle: DocumentationBundle) { | ||
| for article in articles { | ||
| guard let (documentation, title) = DocumentationContext.documentationNodeAndTitle(for: article, kind: .article, in: bundle) else { continue } | ||
| /// - Returns: The articles that were registered, with their topic graph node updated to what's been added to the topic graph. | ||
| private func registerArticles( | ||
| _ articles: DocumentationContext.Articles, | ||
| in bundle: DocumentationBundle | ||
| ) -> DocumentationContext.Articles { | ||
| articles.map { article in | ||
| guard let (documentation, title) = DocumentationContext.documentationNodeAndTitle( | ||
| for: article, | ||
|
|
||
| // Articles are available in the same languages the only root module is available in. If there is more | ||
| // than one module, we cannot determine what languages it's available in and default to Swift. | ||
| availableSourceLanguages: soleRootModuleReference?.sourceLanguages, | ||
| kind: .article, | ||
| in: bundle | ||
| ) else { | ||
| return article | ||
| } | ||
| let reference = documentation.reference | ||
|
|
||
| documentationCache[reference] = documentation | ||
|
|
@@ -1638,6 +1651,11 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| for anchor in documentation.anchorSections { | ||
| nodeAnchorSections[anchor.reference] = anchor | ||
| } | ||
|
|
||
| var article = article | ||
| // Update the article's topic graph node with the one we just added to the topic graph. | ||
| article.topicGraphNode = graphNode | ||
| return article | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1648,16 +1666,45 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| /// - kind: The kind that should be used to create the returned documentation node. | ||
| /// - bundle: The documentation bundle this article belongs to. | ||
| /// - Returns: A documentation node and title for the given article semantic result. | ||
| static func documentationNodeAndTitle(for article: DocumentationContext.SemanticResult<Article>, kind: DocumentationNode.Kind, in bundle: DocumentationBundle) -> (node: DocumentationNode, title: String)? { | ||
| static func documentationNodeAndTitle( | ||
| for article: DocumentationContext.SemanticResult<Article>, | ||
| availableSourceLanguages: Set<SourceLanguage>? = nil, | ||
| kind: DocumentationNode.Kind, | ||
| in bundle: DocumentationBundle | ||
| ) -> (node: DocumentationNode, title: String)? { | ||
| guard let articleMarkup = article.value.markup else { | ||
| return nil | ||
| } | ||
|
|
||
| let path = NodeURLGenerator.pathForSemantic(article.value, source: article.source, bundle: bundle) | ||
| let reference = ResolvedTopicReference(bundleIdentifier: bundle.identifier, path: path, sourceLanguage: .swift) | ||
|
|
||
| // If available source languages are provided and it contains Swift, use Swift as the default language of | ||
| // the article. | ||
| let defaultSourceLanguage = availableSourceLanguages.map { availableSourceLanguages in | ||
| if availableSourceLanguages.contains(.swift) { | ||
| return .swift | ||
| } else { | ||
| return availableSourceLanguages.first ?? .swift | ||
| } | ||
| } ?? SourceLanguage.swift | ||
|
|
||
| let reference = ResolvedTopicReference( | ||
| bundleIdentifier: bundle.identifier, | ||
| path: path, | ||
| sourceLanguages: availableSourceLanguages ?? [.swift] | ||
| ) | ||
|
|
||
| let title = article.topicGraphNode.title | ||
|
|
||
| let documentationNode = DocumentationNode(reference: reference, kind: kind, sourceLanguage: .swift, name: .conceptual(title: title), markup: articleMarkup, semantic: article.value) | ||
| let documentationNode = DocumentationNode( | ||
| reference: reference, | ||
| kind: kind, | ||
| sourceLanguage: defaultSourceLanguage, | ||
| availableSourceLanguages: availableSourceLanguages, | ||
| name: .conceptual(title: title), | ||
| markup: articleMarkup, | ||
| semantic: article.value | ||
| ) | ||
|
|
||
| return (documentationNode, title) | ||
| } | ||
|
|
@@ -1693,11 +1740,29 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| } | ||
|
|
||
| let articleReferences = autoCuratedArticles.map(\.topicGraphNode.reference) | ||
| let autoArticlesSection = AutomaticTaskGroupSection(title: "Articles", references: articleReferences, renderPositionPreference: .top) | ||
|
|
||
| func createAutomaticTaskGroupSection(references: [ResolvedTopicReference]) -> AutomaticTaskGroupSection { | ||
| AutomaticTaskGroupSection( | ||
| title: "Articles", | ||
| references: references, | ||
| renderPositionPreference: .top | ||
| ) | ||
| } | ||
|
|
||
| let node = try entity(with: rootNode.reference) | ||
| if var taskGroupProviding = node.semantic as? AutomaticTaskGroupsProviding { | ||
| taskGroupProviding.automaticTaskGroups = [autoArticlesSection] | ||
|
|
||
| // If the node we're automatically curating the article under is a symbol, automatically curate the article | ||
| // for each language it's available in. | ||
| if let symbol = node.semantic as? Symbol { | ||
| for sourceLanguage in node.availableSourceLanguages { | ||
| symbol.automaticTaskGroupsVariants[ | ||
| .init(interfaceLanguage: sourceLanguage.id) | ||
| ] = [createAutomaticTaskGroupSection(references: articleReferences)] | ||
| } | ||
| } else if var taskGroupProviding = node.semantic as? AutomaticTaskGroupsProviding { | ||
| taskGroupProviding.automaticTaskGroups = [ | ||
| createAutomaticTaskGroupSection(references: articleReferences) | ||
| ] | ||
| } | ||
|
|
||
| return articleReferences | ||
|
|
@@ -1779,7 +1844,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
|
|
||
| // All discovery went well, process the inputs. | ||
| let (technologies, tutorials, tutorialArticles, allArticles) = result | ||
| let (otherArticles, rootPageArticles) = splitArticles(allArticles) | ||
| var (otherArticles, rootPageArticles) = splitArticles(allArticles) | ||
|
|
||
| let rootPages = registerRootPages(from: rootPageArticles, in: bundle) | ||
| let (moduleReferences, symbolsURLHierarchy) = try registerSymbols(from: bundle, symbolGraphLoader: symbolGraphLoader) | ||
|
|
@@ -1788,10 +1853,20 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
|
|
||
| try shouldContinueRegistration() | ||
|
|
||
| // Keep track of the root modules registered from symbol graph files, we'll need them to automatically | ||
| // curate articles. | ||
| rootModules = topicGraph.nodes.values.compactMap { node in | ||
| guard node.kind == .module, | ||
| !onlyHasSnippetRelatedChildren(for: node.reference) else { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This snippet check is a little confusing to me. Maybe we should consider a It almost seems like we should have a separate kind
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good to me. This logic was just moved in this PR, but we should consider revisiting this in the future. |
||
| return nil | ||
| } | ||
| return node.reference | ||
| } | ||
|
|
||
| // Articles that will be automatically curated can be resolved but they need to be pre registered before resolving links. | ||
| let rootNodeForAutomaticCuration = rootModules.count == 1 ? rootModules.first.flatMap(topicGraph.nodeWithReference) : nil | ||
| let rootNodeForAutomaticCuration = soleRootModuleReference.flatMap(topicGraph.nodeWithReference(_:)) | ||
| if rootNodeForAutomaticCuration != nil { | ||
| registerArticles(otherArticles, in: bundle) | ||
| otherArticles = registerArticles(otherArticles, in: bundle) | ||
| try shouldContinueRegistration() | ||
| } | ||
|
|
||
|
|
@@ -2550,32 +2625,6 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate { | |
| return results | ||
| } | ||
|
|
||
| /// Finds the presentation language the symbol is curated under by walking | ||
| /// the documentation graph upwards and finding a parent that has an interface language. | ||
| /// Will return `nil` if no parent with assigned interface language is found | ||
| /// (e.g. in a tutorials only bundle). | ||
| func interfaceLanguageFor(_ reference: ResolvedTopicReference) throws -> SourceLanguage? { | ||
| let node = try entity(with: reference) | ||
| guard !(node.semantic is Symbol) else { | ||
| // For symbols just return the source language | ||
| return node.sourceLanguage | ||
| } | ||
|
|
||
| // `pathsTo()` returns the canonical path first | ||
| guard let canonical = pathsTo(reference).first else { | ||
| // Uncurated symbol, if not expected a warning will be emitted elsewhere | ||
| return nil | ||
| } | ||
|
|
||
| // Return the language of the first symbol entity | ||
| return canonical.mapFirst { reference -> SourceLanguage? in | ||
| guard let node = try? entity(with: reference), node.semantic is Symbol else { | ||
| return nil | ||
| } | ||
| return node.sourceLanguage | ||
| } | ||
| } | ||
|
|
||
| func dumpGraph() -> String { | ||
| return topicGraph.nodes.values | ||
| .filter { parents(of: $0.reference).isEmpty } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.