Skip to content

Commit 7c13a8e

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 3099317 commit 7c13a8e

File tree

11 files changed

+357
-11
lines changed

11 files changed

+357
-11
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: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ public enum RenderBlockContent: Equatable {
6161
/// A table that contains a list of row data.
6262
case table(Table)
6363

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

6670
// Warning: If you add a new case to this enum, make sure to handle it in the Codable
6771
// conformance at the bottom of this file, and in the `rawIndexableTextContent` method in
@@ -427,6 +431,15 @@ public enum RenderBlockContent: Equatable {
427431
public let content: [RenderBlockContent]
428432
}
429433
}
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+
}
430443
}
431444

432445
// Codable conformance
@@ -489,11 +502,15 @@ extension RenderBlockContent: Codable {
489502
columns: container.decode([Row.Column].self, forKey: .columns)
490503
)
491504
)
505+
case .small:
506+
self = try .small(
507+
Small(inlineContent: container.decode([RenderInlineContent].self, forKey: .inlineContent))
508+
)
492509
}
493510
}
494511

495512
private enum BlockType: String, Codable {
496-
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
497514
}
498515

499516
private var type: BlockType {
@@ -510,6 +527,7 @@ extension RenderBlockContent: Codable {
510527
case .table: return .table
511528
case .termList: return .termList
512529
case .row: return .row
530+
case .small: return .small
513531
default: fatalError("unknown RenderBlockContent case in type property")
514532
}
515533
}
@@ -559,6 +577,8 @@ extension RenderBlockContent: Codable {
559577
case .row(let row):
560578
try container.encode(row.numberOfColumns, forKey: .numberOfColumns)
561579
try container.encode(row.columns, forKey: .columns)
580+
case .small(let small):
581+
try container.encode(small.inlineContent, forKey: .inlineContent)
562582
default:
563583
fatalError("unknown RenderBlockContent case in encode method")
564584
}

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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,5 +1057,15 @@ class RenderNodeTranslatorTests: XCTestCase {
10571057
XCTAssertEqual(row.columns.first?.content.count, 1)
10581058
XCTAssertEqual(row.columns.last?.size, 5)
10591059
XCTAssertEqual(row.columns.last?.content.count, 3)
1060+
1061+
guard case let .small(small) = discussion.content.last else {
1062+
XCTFail("Expected to find small as last child.")
1063+
return
1064+
}
1065+
1066+
XCTAssertEqual(
1067+
small.inlineContent,
1068+
[.text("Copyright (c) 2022 Apple Inc and the Swift Project authors. All Rights Reserved.")]
1069+
)
10601070
}
10611071
}

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
}

Tests/SwiftDocCTests/Semantics/RowTests.swift renamed to Tests/SwiftDocCTests/Semantics/Reference/RowTests.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ class RowTests: XCTestCase {
2929
["1: warning – org.swift.docc.HasAtLeastOne<Row, Column>"]
3030
)
3131

32+
XCTAssertEqual(renderBlockContent.count, 1)
3233
XCTAssertEqual(
33-
renderBlockContent,
34+
renderBlockContent.first,
3435
.row(.init(numberOfColumns: 0, columns: []))
3536
)
3637
}
@@ -65,8 +66,9 @@ class RowTests: XCTestCase {
6566
]
6667
)
6768

69+
XCTAssertEqual(renderBlockContent.count, 1)
6870
XCTAssertEqual(
69-
renderBlockContent,
71+
renderBlockContent.first,
7072
.row(RenderBlockContent.Row(
7173
numberOfColumns: 6,
7274
columns: [
@@ -129,8 +131,9 @@ class RowTests: XCTestCase {
129131
]
130132
)
131133

134+
XCTAssertEqual(renderBlockContent.count, 1)
132135
XCTAssertEqual(
133-
renderBlockContent,
136+
renderBlockContent.first,
134137
.row(RenderBlockContent.Row(
135138
numberOfColumns: 0,
136139
columns: []
@@ -159,8 +162,9 @@ class RowTests: XCTestCase {
159162
]
160163
)
161164

165+
XCTAssertEqual(renderBlockContent.count, 1)
162166
XCTAssertEqual(
163-
renderBlockContent,
167+
renderBlockContent.first,
164168
.row(RenderBlockContent.Row(
165169
numberOfColumns: 1,
166170
columns: [
@@ -187,8 +191,9 @@ class RowTests: XCTestCase {
187191
]
188192
)
189193

194+
XCTAssertEqual(renderBlockContent.count, 1)
190195
XCTAssertEqual(
191-
renderBlockContent,
196+
renderBlockContent.first,
192197
.row(RenderBlockContent.Row(numberOfColumns: 0, columns: []))
193198
)
194199
}
@@ -212,8 +217,9 @@ class RowTests: XCTestCase {
212217
XCTAssertNotNil(row)
213218
XCTAssertEqual(problems, [])
214219

220+
XCTAssertEqual(renderBlockContent.count, 1)
215221
XCTAssertEqual(
216-
renderBlockContent,
222+
renderBlockContent.first,
217223
.row(RenderBlockContent.Row(
218224
numberOfColumns: 5,
219225
columns: [
@@ -252,8 +258,9 @@ class RowTests: XCTestCase {
252258
XCTAssertNotNil(row)
253259
XCTAssertEqual(problems, [])
254260

261+
XCTAssertEqual(renderBlockContent.count, 1)
255262
XCTAssertEqual(
256-
renderBlockContent,
263+
renderBlockContent.first,
257264
.row(RenderBlockContent.Row(
258265
numberOfColumns: 1,
259266
columns: [

0 commit comments

Comments
 (0)