-
Notifications
You must be signed in to change notification settings - Fork 143
Add support for options directive #368
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
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 | ||
*/ | ||
|
||
extension RenderNode { | ||
/// The rendering style of the topics section. | ||
public enum TopicsSectionStyle: String, Codable { | ||
/// A list of the page's topics, including their full declaration and abstract. | ||
case list | ||
|
||
/// A grid of items based on the card image for each page. | ||
/// | ||
/// Includes each page’s title and card image but excludes their abstracts. | ||
case compactGrid | ||
|
||
/// A grid of items based on the card image for each page. | ||
/// | ||
/// Unlike ``compactGrid``, this style includes the abstract for each page. | ||
case detailedGrid | ||
|
||
/// Do not show child pages anywhere on the page. | ||
case hidden | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected, | |
let markup: Markup? | ||
/// An optional container for metadata that's unrelated to the article's content. | ||
private(set) var metadata: Metadata? | ||
/// An optional container for options that are unrelated to the article's content. | ||
private(set) var options: [Options.Scope : Options] | ||
/// An optional list of previously known locations for this article. | ||
private(set) public var redirects: [Redirect]? | ||
|
||
|
@@ -30,10 +32,11 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected, | |
/// - markup: The markup that makes up this article's content. | ||
/// - metadata: An optional container for metadata that's unrelated to the article's content. | ||
/// - redirects: An optional list of previously known locations for this article. | ||
init(markup: Markup?, metadata: Metadata?, redirects: [Redirect]?) { | ||
init(markup: Markup?, metadata: Metadata?, redirects: [Redirect]?, options: [Options.Scope : Options]) { | ||
let markupModel = markup.map { DocumentationMarkup(markup: $0) } | ||
|
||
self.markup = markup | ||
self.options = options | ||
self.metadata = metadata | ||
self.redirects = redirects | ||
self.discussion = markupModel?.discussionSection | ||
|
@@ -46,7 +49,7 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected, | |
} | ||
|
||
convenience init(title: Heading?, abstractSection: AbstractSection?, discussion: DiscussionSection?, topics: TopicsSection?, seeAlso: SeeAlsoSection?, deprecationSummary: MarkupContainer?, metadata: Metadata?, redirects: [Redirect]?, automaticTaskGroups: [AutomaticTaskGroupSection]? = nil) { | ||
self.init(markup: nil, metadata: metadata, redirects: redirects) | ||
self.init(markup: nil, metadata: metadata, redirects: redirects, options: [:]) | ||
self.title = title | ||
self.abstractSection = abstractSection | ||
self.discussion = discussion | ||
|
@@ -139,7 +142,61 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected, | |
} | ||
|
||
var optionalMetadata = metadata.first | ||
|
||
let options: [Options] | ||
(options, remainder) = remainder.categorize { child -> Options? in | ||
guard let childDirective = child as? BlockDirective, childDirective.name == Options.directiveName else { | ||
return nil | ||
} | ||
return Options( | ||
from: childDirective, | ||
source: source, | ||
for: bundle, | ||
in: context, | ||
problems: &problems | ||
) | ||
} | ||
|
||
let allCategorizedOptions = Dictionary(grouping: options, by: \.scope) | ||
|
||
for (scope, options) in allCategorizedOptions { | ||
let extraOptions = options.dropFirst() | ||
guard !extraOptions.isEmpty else { | ||
continue | ||
} | ||
|
||
let extraOptionsProblems = extraOptions.map { extraOptionsDirective -> Problem in | ||
let diagnostic = Diagnostic( | ||
source: source, | ||
severity: .warning, | ||
range: extraOptionsDirective.originalMarkup.range, | ||
identifier: "org.swift.docc.HasAtMostOne<\(Article.self), \(Options.self), \(scope)>.DuplicateChildren", | ||
summary: "Duplicate \(scope) \(Options.directiveName.singleQuoted) directive", | ||
explanation: """ | ||
An article can only contain a single \(Options.directiveName.singleQuoted) \ | ||
directive with the \(scope.rawValue.singleQuoted) scope. | ||
Comment on lines
+176
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment here regarding explaining which will be dropped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first one will be kept and all others dropped. I updated the diagnostics to only apply to those that are dropped. |
||
""" | ||
) | ||
|
||
guard let range = extraOptionsDirective.originalMarkup.range else { | ||
return Problem(diagnostic: diagnostic) | ||
} | ||
|
||
let solution = Solution( | ||
summary: "Remove extraneous \(scope) \(Options.directiveName.singleQuoted) directive", | ||
replacements: [ | ||
Replacement(range: range, replacement: "") | ||
] | ||
) | ||
|
||
return Problem(diagnostic: diagnostic, possibleSolutions: [solution]) | ||
} | ||
|
||
problems.append(contentsOf: extraOptionsProblems) | ||
} | ||
|
||
let relevantCategorizedOptions = allCategorizedOptions.compactMapValues(\.first) | ||
|
||
let isDocumentationExtension = title.child(at: 0) is AnyLink | ||
if !isDocumentationExtension, let metadata = optionalMetadata, let displayName = metadata.displayName { | ||
let diagnosticSummary = """ | ||
|
@@ -164,7 +221,12 @@ public final class Article: Semantic, MarkupConvertible, Abstracted, Redirected, | |
optionalMetadata = Metadata(originalMarkup: metadata.originalMarkup, documentationExtension: metadata.documentationOptions, technologyRoot: metadata.technologyRoot, displayName: nil) | ||
} | ||
|
||
self.init(markup: markup, metadata: optionalMetadata, redirects: redirects.isEmpty ? nil : redirects) | ||
self.init( | ||
markup: markup, | ||
metadata: optionalMetadata, | ||
redirects: redirects.isEmpty ? nil : redirects, | ||
options: relevantCategorizedOptions | ||
) | ||
} | ||
|
||
/// Visit the article using a semantic visitor and return the result of visiting the article. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a warning, it would be good to describe which one of these global options will be dropped, if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're all dropped currently but I can update the diagnostic to clarify that. Picking a single one deterministically is somewhat non-trivial and didn't seem worth it to me since it's an unsupported workflow.