Skip to content

Commit 135a55e

Browse files
authored
Don't enforce unique filenames for documentation extensions (#863)
* Allow documentation extensions to have the same name Since documentation extensions' filenames have no impact on the URL of any pages, there's no need to enforce unique filenames for them. DocC currently uses only the filename / last path component of articles to determine if there is a duplicate article. This change excludes doc extensions from that check to allow documentation extensions to have the same filename. rdar://117174884 * Add test to ensure doc extensions can have the same filename Checks that doc extensions with the same filename do not produce a warning and DocC no longer drops the content from one of the files.
1 parent 3e0c8e2 commit 135a55e

File tree

2 files changed

+75
-7
lines changed

2 files changed

+75
-7
lines changed

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,11 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
854854
let path = NodeURLGenerator.pathForSemantic(analyzed, source: url, bundle: bundle)
855855
let reference = ResolvedTopicReference(bundleIdentifier: bundle.identifier, path: path, sourceLanguage: .swift)
856856

857-
if let firstFoundAtURL = references[reference] {
857+
// Since documentation extensions' filenames have no impact on the URL of pages, there is no need to enforce unique filenames for them.
858+
// At this point we consider all articles with an H1 containing link a "documentation extension."
859+
let isDocumentationExtension = (analyzed as? Article)?.title?.child(at: 0) is AnyLink
860+
861+
if let firstFoundAtURL = references[reference], !isDocumentationExtension {
858862
let problem = Problem(
859863
diagnostic: Diagnostic(
860864
source: url,
@@ -874,7 +878,9 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
874878
continue
875879
}
876880

877-
references[reference] = url
881+
if !isDocumentationExtension {
882+
references[reference] = url
883+
}
878884

879885
/*
880886
Add all topic graph nodes up front before resolution starts, because
@@ -907,9 +913,8 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
907913
let result = SemanticResult(value: article, source: url, topicGraphNode: topicGraphNode)
908914

909915
// Separate articles that look like documentation extension files from other articles, so that the documentation extension files can be matched up with a symbol.
910-
// At this point we consider all articles with an H1 containing link "documentation extension" - some links might not resolve in the final documentation hierarchy
911-
// and we will emit warnings for those later on when we finalize the bundle discovery phase.
912-
if result.value.title?.child(at: 0) is AnyLink {
916+
// Some links might not resolve in the final documentation hierarchy and we will emit warnings for those later on when we finalize the bundle discovery phase.
917+
if isDocumentationExtension {
913918
documentationExtensions.append(result)
914919

915920
// Warn for an incorrect root page metadata directive.

Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,68 @@ class DocumentationContextTests: XCTestCase {
667667
let localizedSummarySecond = try XCTUnwrap(problemWithDuplicateReference[1].diagnostic.summary)
668668
XCTAssertEqual(localizedSummarySecond, "Redeclaration of \'overview.md\'; this file will be skipped")
669669
}
670+
671+
func testUsesMultipleDocExtensionFilesWithSameName() throws {
672+
673+
// Generate 2 different symbols with the same name.
674+
let someSymbol = makeSymbol(name: "MyEnum", identifier: "someEnumSymbol-id", kind: .init(rawValue: "enum"), pathComponents: ["SomeDirectory", "MyEnum"])
675+
let anotherSymbol = makeSymbol(name: "MyEnum", identifier: "anotherEnumSymbol-id", kind: .init(rawValue: "enum"), pathComponents: ["AnotherDirectory", "MyEnum"])
676+
let symbols: [SymbolGraph.Symbol] = [someSymbol, anotherSymbol]
677+
678+
// Create a catalog with doc extension files with the same filename for each symbol.
679+
let tempURL = try createTempFolder(content: [
680+
Folder(name: "unit-test.docc", content: [
681+
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(
682+
moduleName: "ModuleName",
683+
symbols: symbols
684+
)),
685+
686+
Folder(name: "SomeDirectory", content: [
687+
TextFile(name: "MyEnum.md", utf8Content:
688+
"""
689+
# ``SomeDirectory/MyEnum``
690+
691+
A documentation extension for my enum.
692+
"""
693+
)
694+
]),
695+
696+
Folder(name: "AnotherDirectory", content: [
697+
TextFile(name: "MyEnum.md", utf8Content:
698+
"""
699+
# ``AnotherDirectory/MyEnum``
700+
701+
A documentation extension for an unrelated enum.
702+
"""
703+
)
704+
]),
705+
706+
// An unrelated article that happens to have the same filename
707+
TextFile(name: "MyEnum.md", utf8Content:
708+
"""
709+
# MyEnum
710+
711+
Here is a regular article about MyEnum.
712+
"""
713+
)
714+
])
715+
])
716+
717+
let (_, _, context) = try loadBundle(from: tempURL)
718+
719+
// Since documentation extensions' filenames have no impact on the URL of pages, we should not see warnings enforcing unique filenames for them.
720+
let problemWithDuplicateReference = context.problems.filter { $0.diagnostic.identifier == "org.swift.docc.DuplicateReference" }
721+
XCTAssertEqual(problemWithDuplicateReference.count, 0)
722+
723+
// Ensure the content from both documentation extensions was used.
724+
let someEnumNode = try XCTUnwrap(context.documentationCache["someEnumSymbol-id"])
725+
let someEnumSymbol = try XCTUnwrap(someEnumNode.semantic as? Symbol)
726+
XCTAssertEqual(someEnumSymbol.abstract?.plainText, "A documentation extension for my enum.", "The abstract should be from the symbol's documentation extension.")
727+
728+
let anotherEnumNode = try XCTUnwrap(context.documentationCache["anotherEnumSymbol-id"])
729+
let anotherEnumSymbol = try XCTUnwrap(anotherEnumNode.semantic as? Symbol)
730+
XCTAssertEqual(anotherEnumSymbol.abstract?.plainText, "A documentation extension for an unrelated enum.", "The abstract should be from the symbol's documentation extension.")
731+
}
670732

671733
func testGraphChecks() throws {
672734
let workspace = DocumentationWorkspace()
@@ -4457,12 +4519,13 @@ let expected = """
44574519
private func makeSymbol(
44584520
name: String = "SymbolName",
44594521
identifier: String,
4460-
kind: SymbolGraph.Symbol.KindIdentifier
4522+
kind: SymbolGraph.Symbol.KindIdentifier,
4523+
pathComponents: [String]? = nil
44614524
) -> SymbolGraph.Symbol {
44624525
return SymbolGraph.Symbol(
44634526
identifier: .init(precise: identifier, interfaceLanguage: SourceLanguage.swift.id),
44644527
names: .init(title: name, navigator: nil, subHeading: nil, prose: nil),
4465-
pathComponents: [name],
4528+
pathComponents: pathComponents ?? [name],
44664529
docComment: nil,
44674530
accessLevel: .public,
44684531
kind: .init(parsedIdentifier: kind, displayName: "Kind Display Name"),

0 commit comments

Comments
 (0)