Skip to content

Add row and column directives #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ extension RenderBlockContent: TextIndexing {
return $0.term.inlineContent.rawIndexableTextContent(references: references)
+ ( definition.isEmpty ? "" : " \(definition)" )
}.joined(separator: " ")
case .row(let row):
return row.columns.map { column in
return column.content.rawIndexableTextContent(references: references)
}.joined(separator: " ")
default:
fatalError("unknown RenderBlockContent case in rawIndexableTextContent")
}
Expand Down
11 changes: 8 additions & 3 deletions Sources/SwiftDocC/Model/DocumentationMarkup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,17 @@ struct DocumentationMarkup {
abstractSection = AbstractSection(paragraph: firstParagraph)
return
} else if let directive = child as? BlockDirective {
// Found deprecation notice in the abstract.
if directive.name == DeprecationSummary.directiveName {
// Found deprecation notice in the abstract.
deprecation = MarkupContainer(directive.children)
return
} else if directive.name == Comment.directiveName || directive.name == Metadata.directiveName {
// These directives don't affect content so they shouldn't break us out of
// the automatic abstract section.
return
} else {
currentSection = .discussion
}
// Skip other block like @Comment and so on.
return
} else if let _ = child as? HTMLBlock {
// Skip HTMLBlock comment.
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public enum RenderBlockContent: Equatable {
case termList(TermList)
/// A table that contains a list of row data.
case table(Table)

case row(Row)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're missing docs here, same for the public APIs below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Addressed here: d0c2e31.


// Warning: If you add a new case to this enum, make sure to handle it in the Codable
// conformance at the bottom of this file, and in the `rawIndexableTextContent` method in
Expand Down Expand Up @@ -403,6 +405,28 @@ public enum RenderBlockContent: Equatable {
/// The definition in the term-list item.
public let definition: Definition
}

/// A row in a grid-based layout system that describes a collection of columns.
public struct Row: Codable, Equatable {
/// The number of columns that should be rendered in this row.
///
/// This may be different then the count of ``columns`` array. For example, there may be
/// individual columns that span multiple columns (specified with the column's
/// ``Column/size`` property) or the row could be not fully filled with columns.
public let numberOfColumns: Int

/// The columns that should be rendered in this row.
public let columns: [Column]

/// A column with a row in a grid-based layout system.
public struct Column: Codable, Equatable {
/// The number of columns in the parent row this column should span.
public let size: Int

/// The content that should be rendered in this column.
public let content: [RenderBlockContent]
}
}
}

// Codable conformance
Expand All @@ -412,6 +436,7 @@ extension RenderBlockContent: Codable {
case inlineContent, content, caption, style, name, syntax, code, level, text, items, media, runtimePreview, anchor, summary, example, metadata
case request, response
case header, rows
case numberOfColumns, columns
}

public init(from decoder: Decoder) throws {
Expand Down Expand Up @@ -457,11 +482,18 @@ extension RenderBlockContent: Codable {
))
case .termList:
self = try .termList(.init(items: container.decode([TermListItem].self, forKey: .items)))
case .row:
self = try .row(
Row(
numberOfColumns: container.decode(Int.self, forKey: .numberOfColumns),
columns: container.decode([Row.Column].self, forKey: .columns)
)
)
}
}

private enum BlockType: String, Codable {
case paragraph, aside, codeListing, heading, orderedList, unorderedList, step, endpointExample, dictionaryExample, table, termList
case paragraph, aside, codeListing, heading, orderedList, unorderedList, step, endpointExample, dictionaryExample, table, termList, row
}

private var type: BlockType {
Expand All @@ -477,6 +509,7 @@ extension RenderBlockContent: Codable {
case .dictionaryExample: return .dictionaryExample
case .table: return .table
case .termList: return .termList
case .row: return .row
default: fatalError("unknown RenderBlockContent case in type property")
}
}
Expand Down Expand Up @@ -523,6 +556,9 @@ extension RenderBlockContent: Codable {
try container.encodeIfPresent(t.metadata, forKey: .metadata)
case .termList(items: let l):
try container.encode(l.items, forKey: .items)
case .row(let row):
try container.encode(row.numberOfColumns, forKey: .numberOfColumns)
try container.encode(row.columns, forKey: .columns)
default:
fatalError("unknown RenderBlockContent case in encode method")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,11 @@ struct RenderContentCompiler: MarkupVisitor {
return docCommentContent + [code]
}
default:
return []
guard let renderableDirective = DirectiveIndex.shared.renderableDirectives[blockDirective.name] else {
return []
}

return renderableDirective.render(blockDirective, with: &self)
}
}

Expand Down
52 changes: 52 additions & 0 deletions Sources/SwiftDocC/Model/Rendering/RenderContentConvertible.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2022 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation
import Markdown

/// A directive that can be directly rendered within markup content.
///
/// This protocol is used by the `RenderContentCompiler` to render arbitrary directives
/// that conform to renderable.
protocol RenderableDirectiveConvertible: AutomaticDirectiveConvertible {
func render(with contentCompiler: inout RenderContentCompiler) -> [RenderContent]
}

extension RenderableDirectiveConvertible {
static func render(
_ blockDirective: BlockDirective,
with contentCompiler: inout RenderContentCompiler
) -> [RenderContent] {
guard let directive = Self.init(
from: blockDirective,
for: contentCompiler.bundle,
in: contentCompiler.context
) else {
return []
}

return directive.render(with: &contentCompiler)
}
}

struct AnyRenderableDirectiveConvertibleType {
var underlyingType: RenderableDirectiveConvertible.Type

func render(
_ blockDirective: BlockDirective,
with contentCompiler: inout RenderContentCompiler
) -> [RenderContent] {
return underlyingType.render(blockDirective, with: &contentCompiler)
}

var directiveName: String {
return underlyingType.directiveName
}
}
2 changes: 1 addition & 1 deletion Sources/SwiftDocC/Model/Rendering/RenderSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public enum RenderSectionKind: String, Codable {
case hero, intro, tasks, assessments, volume, contentAndMedia, contentAndMediaGroup, callToAction, tile, articleBody, resources

// Symbol render sections
case discussion, content, taskGroup, relationships, declarations, parameters, sampleDownload
case discussion, content, taskGroup, relationships, declarations, parameters, sampleDownload, row

// Rest symbol sections
case restParameters, restResponses, restBody, restEndpoint, properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ extension AutomaticDirectiveConvertible {

Semantic.Analyses.HasOnlyKnownDirectives<Self>(
severityIfFound: .warning,
allowedDirectives: reflectedDirective.childDirectives.map(\.name)
allowedDirectives: reflectedDirective.childDirectives.map(\.name),
allowsStructuredMarkup: reflectedDirective.allowsStructuredMarkup
)
.analyze(
directive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ protocol _ChildMarkupProtocol {

var index: Int? { get }

var supportsStructuredMarkup: Bool { get }

func setProperty<T>(
on containingDirective: T,
named propertyName: String,
Expand Down Expand Up @@ -61,6 +63,10 @@ public struct ChildMarkup<Value>: _ChildMarkupProtocol {

var numberOfParagraphs: _ChildMarkupParagraphs

/// Returns true if the child markup can contain structured markup content like
/// rows and columns.
var supportsStructuredMarkup: Bool

public var wrappedValue: Value {
get {
parsedValue!
Expand Down Expand Up @@ -89,21 +95,28 @@ public struct ChildMarkup<Value>: _ChildMarkupProtocol {
}

extension ChildMarkup where Value == MarkupContainer {
init(numberOfParagraphs: _ChildMarkupParagraphs = .oneOrMore, index: Int? = nil) {
init(
numberOfParagraphs: _ChildMarkupParagraphs = .oneOrMore,
index: Int? = nil,
supportsStructure: Bool = false
) {
self.parsedValue = MarkupContainer()
self.numberOfParagraphs = numberOfParagraphs
self.index = index
self.supportsStructuredMarkup = supportsStructure
}
}

extension ChildMarkup where Value == Optional<MarkupContainer> {
init(
value: Value,
numberOfParagraphs: _ChildMarkupParagraphs = .zeroOrMore,
index: Int? = nil
index: Int? = nil,
supportsStructure: Bool = false
) {
self.parsedValue = value
self.numberOfParagraphs = numberOfParagraphs
self.index = index
self.supportsStructuredMarkup = supportsStructure
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct DirectiveIndex {
Redirect.self,
Snippet.self,
DeprecationSummary.self,
Row.self,
]

private static let topLevelTutorialDirectives: [AutomaticDirectiveConvertible.Type] = [
Expand All @@ -39,6 +40,8 @@ struct DirectiveIndex {

let indexedDirectives: [String : DirectiveMirror.ReflectedDirective]

let renderableDirectives: [String : AnyRenderableDirectiveConvertibleType]

static let shared = DirectiveIndex()

private init() {
Expand Down Expand Up @@ -78,6 +81,14 @@ struct DirectiveIndex {
}

self.indexedDirectives = indexedDirectives

self.renderableDirectives = indexedDirectives.compactMapValues { directive in
guard let renderableDirective = directive.type as? RenderableDirectiveConvertible.Type else {
return nil
}

return AnyRenderableDirectiveConvertibleType(underlyingType: renderableDirective)
}
}

func reflection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,15 @@ extension DirectiveMirror {
}
}

var allowsStructuredMarkup: Bool {
switch childMarkupSupport {
case .supportsMarkup(let markupRequirements):
return markupRequirements.first?.markup.supportsStructuredMarkup ?? false
case .disallowsMarkup:
return false
}
}

var requiresMarkup: Bool {
switch childMarkupSupport {
case .supportsMarkup(let markupRequirements):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2022 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Markdown

/// A directive convertible that contains markup.
protocol MarkupContaining: DirectiveConvertible {
/// The markup contained by this directive.
///
/// This property does not necessarily return the markup contained only by this directive, it may
/// be the concatenated markup contained by all of this directive's directive children.
var childMarkup: [Markup] { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,24 @@ extension Semantic.Analyses {
let severityIfFound: DiagnosticSeverity?
let allowedDirectives: [String]
let allowsMarkup: Bool
public init(severityIfFound: DiagnosticSeverity?, allowedDirectives: [String], allowsMarkup: Bool = true) {
public init(
severityIfFound: DiagnosticSeverity?,
allowedDirectives: [String],
allowsMarkup: Bool = true,
allowsStructuredMarkup: Bool = false
) {
self.severityIfFound = severityIfFound
self.allowedDirectives = allowedDirectives
var allowedDirectives = allowedDirectives
/* Comments are always allowed because they are ignored. */
+ [Comment.directiveName]

if allowsStructuredMarkup {
allowedDirectives += DirectiveIndex.shared.renderableDirectives.values.map {
return $0.directiveName
}
}
self.allowedDirectives = allowedDirectives

self.allowsMarkup = allowsMarkup
}

Expand Down
Loading