Skip to content

Commit cad3718

Browse files
committed
Add authoring support for the @Small directive
Adds support for the `@Small` directive as described here: https://forums.swift.org/t/supporting-more-dynamic-content-in-swift-docc-reference-documentation/59527#small-18 A new `@Small` directive based on HTML’s small tag. This directive can be used to specify small print like legal, license, or copyright text that should be rendered in a smaller font size. `@Small` accepts no parameters and accepts inline DocC markup body content. Example: You can create a sloth using the ``init(name:color:power:)`` initializer, or create randomly generated sloth using a ``SlothGenerator``: let slothGenerator = MySlothGenerator(seed: randomSeed()) let habitat = Habitat(isHumid: false, isWarm: true) // ... @Small { Licensed under Apache License v2.0 with Runtime Library Exception } Dependencies: - swiftlang/swift-docc-render#405 Resolves rdar://97744845
1 parent e4a6fc4 commit cad3718

File tree

9 files changed

+360
-1
lines changed

9 files changed

+360
-1
lines changed

Sources/SwiftDocC/Indexing/RenderBlockContent+TextIndexing.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ extension RenderBlockContent: TextIndexing {
6262
return row.columns.map { column in
6363
return column.content.rawIndexableTextContent(references: references)
6464
}.joined(separator: " ")
65+
case .small(let small):
66+
return small.inlineContent.rawIndexableTextContent(references: references)
6567
default:
6668
fatalError("unknown RenderBlockContent case in rawIndexableTextContent")
6769
}

Sources/SwiftDocC/Model/Rendering/Content/RenderBlockContent.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public enum RenderBlockContent: Equatable {
6363

6464
/// A row in a grid-based layout system that describes a collection of columns.
6565
case row(Row)
66+
67+
/// A paragraph of small print content that should be rendered in a small font.
68+
case small(Small)
6669

6770
// Warning: If you add a new case to this enum, make sure to handle it in the Codable
6871
// conformance at the bottom of this file, and in the `rawIndexableTextContent` method in
@@ -428,6 +431,15 @@ public enum RenderBlockContent: Equatable {
428431
public let content: [RenderBlockContent]
429432
}
430433
}
434+
435+
/// A paragraph of small print content that should be rendered in a small font.
436+
///
437+
/// Small is based on HTML's `<small>` tag and could contain content like legal,
438+
/// license, or copyright text.
439+
public struct Small: Codable, Equatable {
440+
/// The inline content that should be rendered.
441+
public let inlineContent: [RenderInlineContent]
442+
}
431443
}
432444

433445
// Codable conformance
@@ -490,11 +502,15 @@ extension RenderBlockContent: Codable {
490502
columns: container.decode([Row.Column].self, forKey: .columns)
491503
)
492504
)
505+
case .small:
506+
self = try .small(
507+
Small(inlineContent: container.decode([RenderInlineContent].self, forKey: .inlineContent))
508+
)
493509
}
494510
}
495511

496512
private enum BlockType: String, Codable {
497-
case paragraph, aside, codeListing, heading, orderedList, unorderedList, step, endpointExample, dictionaryExample, table, termList, row
513+
case paragraph, aside, codeListing, heading, orderedList, unorderedList, step, endpointExample, dictionaryExample, table, termList, row, small
498514
}
499515

500516
private var type: BlockType {
@@ -511,6 +527,7 @@ extension RenderBlockContent: Codable {
511527
case .table: return .table
512528
case .termList: return .termList
513529
case .row: return .row
530+
case .small: return .small
514531
default: fatalError("unknown RenderBlockContent case in type property")
515532
}
516533
}
@@ -560,6 +577,8 @@ extension RenderBlockContent: Codable {
560577
case .row(let row):
561578
try container.encode(row.numberOfColumns, forKey: .numberOfColumns)
562579
try container.encode(row.columns, forKey: .columns)
580+
case .small(let small):
581+
try container.encode(small.inlineContent, forKey: .inlineContent)
563582
default:
564583
fatalError("unknown RenderBlockContent case in encode method")
565584
}

Sources/SwiftDocC/Semantics/DirectiveInfrastructure/DirectiveIndex.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct DirectiveIndex {
1818
DeprecationSummary.self,
1919
Row.self,
2020
Options.self,
21+
Small.self,
2122
]
2223

2324
private static let topLevelTutorialDirectives: [AutomaticDirectiveConvertible.Type] = [
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2022 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 Markdown
13+
14+
/// A directive for specifying small print text like legal, license, or copyright text that
15+
/// should be rendered in a smaller font size.
16+
///
17+
/// The `@Small` directive is based on HTML's small tag (`<small>`). It supports any inline markup
18+
/// formatting like bold and italics but does not support more structured markup like ``Row``
19+
/// and ``Row/Column``.
20+
///
21+
/// ```md
22+
/// You can create a sloth using the ``init(name:color:power:)``
23+
/// initializer, or create randomly generated sloth using a
24+
/// ``SlothGenerator``:
25+
///
26+
/// let slothGenerator = MySlothGenerator(seed: randomSeed())
27+
/// let habitat = Habitat(isHumid: false, isWarm: true)
28+
///
29+
/// // ...
30+
///
31+
/// @Small {
32+
/// _Licensed under Apache License v2.0 with Runtime Library Exception._
33+
/// }
34+
/// ```
35+
public final class Small: Semantic, AutomaticDirectiveConvertible, MarkupContaining {
36+
public let originalMarkup: BlockDirective
37+
38+
/// The inline markup that should be rendered in a small font.
39+
@ChildMarkup(numberOfParagraphs: .oneOrMore)
40+
public private(set) var content: MarkupContainer
41+
42+
static var keyPaths: [String : AnyKeyPath] = [
43+
"content" : \Small._content,
44+
]
45+
46+
override var children: [Semantic] {
47+
return [content]
48+
}
49+
50+
var childMarkup: [Markup] {
51+
return content.elements
52+
}
53+
54+
@available(*, deprecated,
55+
message: "Do not call directly. Required for 'AutomaticDirectiveConvertible'."
56+
)
57+
init(originalMarkup: BlockDirective) {
58+
self.originalMarkup = originalMarkup
59+
}
60+
}
61+
62+
extension Small: RenderableDirectiveConvertible {
63+
func render(with contentCompiler: inout RenderContentCompiler) -> [RenderContent] {
64+
// Render the content normally
65+
let renderBlockContent = content.elements.flatMap { markupElement in
66+
return contentCompiler.visit(markupElement) as! [RenderBlockContent]
67+
}
68+
69+
// Transform every paragraph in the render block content to a small paragraph
70+
let transformedRenderBlockContent = renderBlockContent.map { block -> RenderBlockContent in
71+
guard case let .paragraph(paragraph) = block else {
72+
return block
73+
}
74+
75+
return .small(RenderBlockContent.Small(inlineContent: paragraph.inlineContent))
76+
}
77+
78+
return transformedRenderBlockContent
79+
}
80+
}

Sources/SwiftDocC/SwiftDocC.docc/Resources/RenderNode.spec.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@
434434
{
435435
"$ref": "#/components/schemas/Aside"
436436
},
437+
{
438+
"$ref": "#/components/schemas/Small"
439+
},
437440
{
438441
"$ref": "#/components/schemas/Heading"
439442
},
@@ -619,6 +622,25 @@
619622
}
620623
}
621624
},
625+
"Small": {
626+
"type": "object",
627+
"required": [
628+
"type",
629+
"inlineContent"
630+
],
631+
"properties": {
632+
"type": {
633+
"type": "string",
634+
"enum": ["small"]
635+
},
636+
"inlineContent": {
637+
"type": "array",
638+
"items": {
639+
"$ref": "#/components/schemas/RenderInlineContent"
640+
}
641+
}
642+
}
643+
},
622644
"Heading": {
623645
"type": "object",
624646
"required": [

Tests/SwiftDocCTests/Rendering/RenderNodeTranslatorTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,4 +1058,37 @@ class RenderNodeTranslatorTests: XCTestCase {
10581058
XCTAssertEqual(row.columns.last?.size, 5)
10591059
XCTAssertEqual(row.columns.last?.content.count, 3)
10601060
}
1061+
1062+
func testSmall() throws {
1063+
let (bundle, context) = try testBundleAndContext(named: "BookLikeContent")
1064+
let reference = ResolvedTopicReference(
1065+
bundleIdentifier: bundle.identifier,
1066+
path: "/documentation/BestBook/MyArticle",
1067+
sourceLanguage: .swift
1068+
)
1069+
let article = try XCTUnwrap(context.entity(with: reference).semantic as? Article)
1070+
var translator = RenderNodeTranslator(
1071+
context: context,
1072+
bundle: bundle,
1073+
identifier: reference,
1074+
source: nil
1075+
)
1076+
let renderNode = try XCTUnwrap(translator.visitArticle(article) as? RenderNode)
1077+
1078+
let discussion = try XCTUnwrap(
1079+
renderNode.primaryContentSections.first(
1080+
where: { $0.kind == .content }
1081+
) as? ContentRenderSection
1082+
)
1083+
1084+
guard case let .small(small) = discussion.content.last else {
1085+
XCTFail("Expected to find small as last child.")
1086+
return
1087+
}
1088+
1089+
XCTAssertEqual(
1090+
small.inlineContent,
1091+
[.text("Copyright (c) 2022 Apple Inc and the Swift Project authors. All Rights Reserved.")]
1092+
)
1093+
}
10611094
}

Tests/SwiftDocCTests/Semantics/DirectiveInfrastructure/DirectiveIndexTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class DirectiveIndexTests: XCTestCase {
3333
"Options",
3434
"Redirected",
3535
"Row",
36+
"Small",
3637
"Snippet",
3738
"Stack",
3839
"TechnologyRoot",
@@ -50,6 +51,7 @@ class DirectiveIndexTests: XCTestCase {
5051
DirectiveIndex.shared.renderableDirectives.keys.sorted(),
5152
[
5253
"Row",
54+
"Small",
5355
]
5456
)
5557
}

0 commit comments

Comments
 (0)