Skip to content

Commit a7af0ff

Browse files
committed
Fix @SupportedLanguage directive for articles
The `@SupportedLanguage` directive allows specifying a language that an article is available in. It can be used within the `@Metadata` directive like in the below example, where the article is made available in both Swift and Objective-C: ``` @metadata { @SupportedLanguage(swift) @SupportedLanguage(objc) } ``` This directive is processed when creating the topic graph node for the article. In cases where the article is the root page or a standalone article (when no technology root is defined), the directive is correctly processed as a part of registering the article in the documentation cache. However, in the case of a non-root article in a catalog containing a root article, this step was being skipped. This patch fixes this to ensure that the directive is processed for all articles in a catalog. rdar://160284853
1 parent 82cc6c8 commit a7af0ff

File tree

6 files changed

+83
-14
lines changed

6 files changed

+83
-14
lines changed

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,13 @@ public class DocumentationContext {
829829

830830
insertLandmarks(tutorialArticle.landmarks, from: topicGraphNode, source: url)
831831
} else if let article = analyzed as? Article {
832-
832+
// If the article contains any `@SupportedLanguage` directives in the metadata,
833+
// include those languages in the set of source languages for the reference.
834+
let reference = if let supportedLanguages = article.supportedLanguages {
835+
reference.withSourceLanguages(supportedLanguages)
836+
} else {
837+
reference
838+
}
833839
// Here we create a topic graph node with the prepared data but we don't add it to the topic graph just yet
834840
// because we don't know where in the hierarchy the article belongs, we will add it later when crawling the manual curation via Topics task groups.
835841
let topicGraphNode = TopicGraph.Node(reference: reference, kind: .article, source: .file(url: url), title: article.title!.plainText)
@@ -1883,17 +1889,7 @@ public class DocumentationContext {
18831889
let path = NodeURLGenerator.pathForSemantic(article.value, source: article.source, bundle: bundle)
18841890

18851891
// Use the languages specified by the `@SupportedLanguage` directives if present.
1886-
let availableSourceLanguages = article.value
1887-
.metadata
1888-
.flatMap { metadata in
1889-
let languages = Set(
1890-
metadata.supportedLanguages
1891-
.map(\.language)
1892-
)
1893-
1894-
return languages.isEmpty ? nil : languages
1895-
}
1896-
?? availableSourceLanguages
1892+
let availableSourceLanguages = article.value.supportedLanguages ?? availableSourceLanguages
18971893

18981894
// If available source languages are provided and it contains Swift, use Swift as the default language of
18991895
// the article.

Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ public struct RenderNodeTranslator: SemanticVisitor {
630630
node.hierarchyVariants = hierarchyVariants
631631

632632
// Emit variants only if we're not compiling an article-only catalog to prevent renderers from
633-
// advertising the page as "Swift", which is the language DocC assigns to pages in article only pages.
633+
// advertising the page as "Swift", which is the language DocC assigns to pages in article only catalogs.
634634
// (github.com/swiftlang/swift-docc/issues/240).
635635
if let topLevelModule = context.soleRootModuleReference,
636636
try! context.entity(with: topLevelModule).kind.isSymbol

Sources/SwiftDocC/Semantics/Article/Article.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected,
6666
return abstractSection?.paragraph
6767
}
6868

69+
/// The list of supported languages for the article, if present.
70+
///
71+
/// This information is available via `@SupportedLanguage` in the `@Metadata` directive.
72+
public var supportedLanguages: Set<SourceLanguage>? {
73+
guard let metadata = self.metadata else {
74+
return nil
75+
}
76+
77+
let langs = metadata.supportedLanguages.map(\.language)
78+
return langs.isEmpty ? nil : Set(langs)
79+
}
80+
6981
/// An optional custom deprecation summary for a deprecated symbol.
7082
private(set) public var deprecationSummary: MarkupContainer?
7183

Sources/SwiftDocC/Semantics/Metadata/Metadata.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public final class Metadata: Semantic, AutomaticDirectiveConvertible {
105105

106106
func validate(source: URL?, problems: inout [Problem]) -> Bool {
107107
// Check that something is configured in the metadata block
108-
if documentationOptions == nil && technologyRoot == nil && displayName == nil && pageImages.isEmpty && customMetadata.isEmpty && callToAction == nil && availability.isEmpty && pageKind == nil && pageColor == nil && titleHeading == nil && redirects == nil && alternateRepresentations.isEmpty {
108+
if documentationOptions == nil && technologyRoot == nil && displayName == nil && pageImages.isEmpty && customMetadata.isEmpty && callToAction == nil && availability.isEmpty && pageKind == nil && pageColor == nil && supportedLanguages.isEmpty && titleHeading == nil && redirects == nil && alternateRepresentations.isEmpty {
109109
let diagnostic = Diagnostic(
110110
source: source,
111111
severity: .information,

Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5773,6 +5773,45 @@ let expected = """
57735773
XCTAssertEqual(solution.replacements.first?.replacement, "")
57745774
}
57755775

5776+
func testSupportedLanguageDirectiveInNonRootArticleWithSymbols() async throws {
5777+
let catalog = Folder(name: "unit-test.docc", content: [
5778+
TextFile(name: "Root.md", utf8Content: """
5779+
# Root
5780+
5781+
@Metadata {
5782+
@TechnologyRoot
5783+
@SupportedLanguage(objc)
5784+
@SupportedLanguage(data)
5785+
}
5786+
5787+
## Topics
5788+
5789+
- <doc:Article>
5790+
"""),
5791+
5792+
TextFile(name: "Article.md", utf8Content: """
5793+
# Article
5794+
5795+
@Metadata {
5796+
@SupportedLanguage(objc)
5797+
@SupportedLanguage(data)
5798+
}
5799+
"""),
5800+
])
5801+
5802+
let (bundle, context) = try await loadBundle(catalog: catalog)
5803+
5804+
XCTAssert(context.problems.isEmpty, "Unexpected problems:\n\(context.problems.map(\.diagnostic.summary).joined(separator: "\n"))")
5805+
5806+
do {
5807+
let reference = ResolvedTopicReference(bundleID: bundle.id, path: "/documentation/unit-test/Article", sourceLanguage: .swift)
5808+
let node = try context.entity(with: reference)
5809+
5810+
let supportedLanguages = try XCTUnwrap((node.semantic as? Article)?.metadata?.supportedLanguages)
5811+
XCTAssertEqual(supportedLanguages.map(\.language), [.objectiveC, .data])
5812+
}
5813+
}
5814+
57765815
}
57775816

57785817
func assertEqualDumps(_ lhs: String, _ rhs: String, file: StaticString = #filePath, line: UInt = #line) {

Tests/SwiftDocCTests/Semantics/ArticleTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,26 @@ class ArticleTests: XCTestCase {
249249
XCTAssertNil(semantic.metadata?.pageKind)
250250
XCTAssertNil(semantic.metadata?.titleHeading)
251251
}
252+
253+
func testSupportedLanguageDirective() async throws {
254+
let source = """
255+
# Root
256+
257+
@Metadata {
258+
@SupportedLanguage(swift)
259+
@SupportedLanguage(objc)
260+
@SupportedLanguage(data)
261+
}
262+
"""
263+
let document = Document(parsing: source, options: [.parseBlockDirectives])
264+
let (bundle, _) = try await testBundleAndContext()
265+
var problems = [Problem]()
266+
let article = Article(from: document, source: nil, for: bundle, problems: &problems)
267+
268+
XCTAssert(problems.isEmpty, "Unexpectedly found problems: \(DiagnosticConsoleWriter.formattedDescription(for: problems))")
269+
270+
XCTAssertNotNil(article)
271+
XCTAssertNotNil(article?.metadata, "Article should have a metadata container since the markup has a @Metadata directive")
272+
XCTAssertEqual(article?.metadata?.supportedLanguages.map(\.language), [.swift, .objectiveC, .data])
273+
}
252274
}

0 commit comments

Comments
 (0)