Skip to content

Commit f390891

Browse files
authored
Fix a doc anchor is not encoded correctly (#867)
* Fix a doc anchor is not encoded correctly * Add a test for rendering heading anchors * Pass stored unencoded fragment * Remove the unsuitable test case Also apply suggestions from code review. * Apply suggestions from code review * Treat a link destination as the "authored link" * Revert "Treat a link destination as the "authored link"" This reverts commit 4e3f8da. * Mark the test article as a technology root
1 parent e824c42 commit f390891

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

Sources/SwiftDocC/Infrastructure/NodeURLGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public struct NodeURLGenerator {
171171
)
172172
} else {
173173
let url = baseURL.appendingPathComponent(safePath, isDirectory: false)
174-
return url.withFragment(reference.url.fragment)
174+
return url.withFragment(reference.fragment)
175175
}
176176
}
177177

Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ struct RenderContentCompiler: MarkupVisitor {
5151
}
5252

5353
mutating func visitHeading(_ heading: Heading) -> [RenderContent] {
54-
return [RenderBlockContent.heading(.init(level: heading.level, text: heading.plainText, anchor: urlReadableFragment(heading.plainText)))]
54+
return [RenderBlockContent.heading(.init(level: heading.level, text: heading.plainText, anchor: urlReadableFragment(heading.plainText).addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)))]
5555
}
5656

5757
mutating func visitListItem(_ listItem: ListItem) -> [RenderContent] {

Tests/SwiftDocCTests/Model/RenderContentMetadataTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,24 @@ class RenderContentMetadataTests: XCTestCase {
297297
default: XCTFail("Unexpected element")
298298
}
299299
}
300+
301+
func testHeadingAnchorShouldBeEncoded() throws {
302+
let (bundle, context) = try testBundleAndContext(named: "TestBundle")
303+
var renderContentCompiler = RenderContentCompiler(context: context, bundle: bundle, identifier: ResolvedTopicReference(bundleIdentifier: bundle.identifier, path: "/path", fragment: nil, sourceLanguage: .swift))
304+
305+
let source = """
306+
## テスト
307+
"""
308+
let document = Document(parsing: source)
309+
310+
let result = try XCTUnwrap(renderContentCompiler.visit(document.child(at: 0)!))
311+
let element = try XCTUnwrap(result.first as? RenderBlockContent)
312+
switch element {
313+
case .heading(let heading):
314+
XCTAssertEqual(heading.level, 2)
315+
XCTAssertEqual(heading.text, "テスト")
316+
XCTAssertEqual(heading.anchor, "%E3%83%86%E3%82%B9%E3%83%88", "The UTF-8 representation of テスト is E3 83 86 E3 82 B9 E3 83 88")
317+
default: XCTFail("Unexpected element")
318+
}
319+
}
300320
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Foundation
12+
import XCTest
13+
@testable import SwiftDocC
14+
import SwiftDocCTestUtilities
15+
16+
class HeadingAnchorTests: XCTestCase {
17+
func testEncodeHeadingAnchor() throws {
18+
let catalogURL = try createTempFolder(content: [
19+
Folder(name: "unit-test.docc", content: [
20+
TextFile(name: "Root.md", utf8Content: """
21+
# My root page
22+
23+
This single article defines two headings and links to them
24+
25+
@Metadata {
26+
@TechnologyRoot
27+
}
28+
29+
### テスト
30+
- <doc:#テスト>
31+
32+
### Some heading
33+
- <doc:#Some-heading>
34+
"""),
35+
])
36+
])
37+
let (_, bundle, context) = try loadBundle(from: catalogURL)
38+
39+
let reference = try XCTUnwrap(context.soleRootModuleReference)
40+
let node = try context.entity(with: reference)
41+
let renderContext = RenderContext(documentationContext: context, bundle: bundle)
42+
let converter = DocumentationContextConverter(bundle: bundle, context: context, renderContext: renderContext)
43+
let renderNode = try XCTUnwrap(converter.renderNode(for: node, at: nil))
44+
45+
// Check heading anchors are encoded
46+
let contentSection = try XCTUnwrap(renderNode.primaryContentSections.first as? ContentRenderSection)
47+
let headings: [RenderBlockContent.Heading] = contentSection.content.compactMap {
48+
if case .heading(let heading) = $0 {
49+
return heading
50+
} else {
51+
return nil
52+
}
53+
}
54+
XCTAssertEqual(headings[0].anchor, "%E3%83%86%E3%82%B9%E3%83%88")
55+
XCTAssertEqual(headings[1].anchor, "Some-heading")
56+
57+
// Check links to them
58+
let testTopic0 = try XCTUnwrap(renderNode.references["doc://unit-test/documentation/Root#%E3%83%86%E3%82%B9%E3%83%88"] as? TopicRenderReference)
59+
XCTAssertEqual(testTopic0.url, "/documentation/root#%E3%83%86%E3%82%B9%E3%83%88")
60+
let testTopic1 = try XCTUnwrap(renderNode.references["doc://unit-test/documentation/Root#Some-heading"] as? TopicRenderReference)
61+
XCTAssertEqual(testTopic1.url, "/documentation/root#Some-heading")
62+
}
63+
}

0 commit comments

Comments
 (0)