-
Notifications
You must be signed in to change notification settings - Fork 166
Experimental markdown output #1303
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
base: main
Are you sure you want to change the base?
Experimental markdown output #1303
Conversation
…ert feature flags
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.
There's a lot to unpack here and I've only had the time to skim thought the code but these are a few things that stand out to me:
-
If this is intended as experimental feature, then it can't add actual
publicAPI because consumers of SwiftDocC would have no way of knowing what API is stable and what is experimental and could still have breaking changes or be removed.If a goal of this API is that clients can use it while it's still experimental then it needs to be marked as SPI (using
@_spi(SomeName)so that those clients make a deliberate decision to use it and acknowledge that it may have breaking changes. -
The added tests follow a pattern not seen elsewhere in the repo. I left some more specific comments about that.
Sources/SwiftDocC/Model/MarkdownOutput/Translation/MarkdownOutputSemanticVisitor.swift
Show resolved
Hide resolved
Tests/SwiftDocCTests/Rendering/Markdown/MarkdownOutputTests.swift
Outdated
Show resolved
Hide resolved
Tests/SwiftDocCTests/Rendering/Markdown/MarkdownOutputTests.swift
Outdated
Show resolved
Hide resolved
Tests/SwiftDocCTests/Test Bundles/MarkdownOutput.docc/MarkdownOutput.symbols.json
Outdated
Show resolved
Hide resolved
Tests/SwiftDocCTests/Test Bundles/MarkdownOutput.docc/MarkdownOutput.symbols.json
Outdated
Show resolved
Hide resolved
|
I have removed or @_spi-decorated all new public API, with the exception of the feature flags themselves, I assumed that would be OK |
…are rendering links-with-abstracts
d-ronnqvist
left a comment
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.
There's quite a lot going on here. To name a few, there's:
- logic bugs or bugs where the values are based of incorrect data
- "public" properties that are very loosely typed (either
StringorData) without accompanying documentation that describe how the caller can decode and use that information. - several symbol sections that's silently excluded from the output
- a fair amount of supported markdown content that's never tested against (and AFAICT isn't defined what the expected output would be)
- some supported tutorial content that's silently excluded from the output
- some terminology that's not seen elsewhere in the project
- a few design choices that could be worth revisiting
- quite a lot of formatting that's different from existing code in this projected
- some unclear "public" SPI where it's not clear to me what the use-case is and there's also no documentation to give the caller/client context to how and when they should use this "public" SPI.
| let fileSafePath = NodeURLGenerator.fileSafeReferencePath( | ||
| markdownNode.identifier, | ||
| lowercased: true | ||
| ) | ||
|
|
||
| // The path on disk to write the markdown file at. | ||
| let markdownNodeTargetFileURL = renderNodeURLGenerator | ||
| .urlForReference( | ||
| markdownNode.identifier, | ||
| fileSafePath: fileSafePath | ||
| ) | ||
| .appendingPathExtension("md") | ||
|
|
||
| let markdownNodeTargetFolderURL = markdownNodeTargetFileURL.deletingLastPathComponent() | ||
|
|
||
| // On Linux sometimes it takes a moment for the directory to be created and that leads to | ||
| // errors when trying to write files concurrently in the same target location. | ||
| // We keep an index in `directoryIndex` and create new sub-directories as needed. | ||
| // When the symbol's directory already exists no code is executed during the lock below | ||
| // besides the set lookup. | ||
| try directoryIndex.sync { directoryIndex in | ||
| let (insertedMarkdownNodeTargetFolderURL, _) = directoryIndex.insert(markdownNodeTargetFolderURL) | ||
| if insertedMarkdownNodeTargetFolderURL { | ||
| try fileManager.createDirectory( | ||
| at: markdownNodeTargetFolderURL, | ||
| withIntermediateDirectories: true, | ||
| attributes: nil | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| try fileManager.createFile(at: markdownNodeTargetFileURL, contents: markdownNode.nodeData, options: nil) |
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.
minor: This is duplicating some non-trivial code with only a few minor differences. Can we extract the common code into a private helper function that both write(_:) methods can call?
| extension MarkdownOutputNode { | ||
| public struct Metadata: Codable, Sendable { | ||
|
|
||
| static let version = "0.1.0" |
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.
Please use SemanticVersion rather than strings for version information. See for example DiagnosticFile, SerializableLinkResolutionInformation, and RenderIndex.
| // If no introduced, we are unavailable | ||
| self.unavailable = unavailable || introduced == nil |
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.
I don't think this logic is correct. A framework can have availability information configured in its Info.plist that doesn't include an introduced version. That configuration specifies that the framework is available for that platform.
| // Can't have deprecated without an introduced | ||
| self.introduced = introduced ?? deprecated |
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.
I don't know if this is correct. It's possible to specify @available(macOS, deprecated: 26, message: "This symbol is deprecated") as an availability attribute in Swift.
Generally, no introduced version means that a symbol is available as far back as the framework itself supports.
| } | ||
|
|
||
| public struct Symbol: Codable, Sendable { | ||
| public let kind: String |
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.
Question: Is this representing the tooling facing kind identifier (for example "struct" or "enum.case") or is this representing a display name (for example "Structure" or "Enumeration Case")?
If it's the former; is there a more precise type—for example SymbolKit/SymbolGraph/Symbol/KindIdentifier —that we could use here?
If it's the latter; please rename this to kindDisplayName or something similar that clarifies what this value represents.
| } | ||
|
|
||
| var markdown = "" | ||
| var outgoingReferences: Set<MarkdownOutputManifest.Relationship> = [] |
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.
Question: what does this represent? Based on the implementation it looks like it's "topic curation" (documentation organization) but this property name sounds broader than that. It would be good to clarify this name and maybe add a documentation comment if the name alone isn't clear.
| @@ -1,4 +1,5 @@ | |||
| { | |||
| "originHash" : "6a2fba12d9d8cf682094fa377600d56c196ce947a70bf82d12a7faff1b81c530", | |||
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.
Question: what does this hash represent?
|
|
||
| } | ||
|
|
||
| // Merge into ConvertOutputMarkdownConsumer when no longer SPI |
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.
What is this comment referring to? This is the ConvertOutputMarkdownConsumer protocol.
|
|
||
| // Merge into ConvertOutputMarkdownConsumer when no longer SPI | ||
| @_spi(MarkdownOutput) | ||
| public protocol ConvertOutputMarkdownConsumer { |
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.
Question: Is there a use case for callers/clients to consume the in-memory representations of this information as opposed to decoding the output files? If not, can we make this internal only?
|
|
||
| public let sourceURI: String | ||
| public let relationshipType: RelationshipType | ||
| public let subtype: String? |
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.
Question: what does this represent? Is there a more precise type—either existing or new—that this could use rather than a string value?
Another way to look at it; what is the caller expected to do with this information? It's public but just a string so it could represent anything. With an enumeration there would be a known list of cases the the caller could check against and make decisions based on.
Issue #1301
Summary
This PR addresses the changes proposed in #1301. It involves a new translator / semantic visitor / markup walker to create the new outputs from
DocumentationNode. This is done for each node, after the render JSON is generated.Dependencies
None
Testing
There are extensive unit tests in
MarkdownOutputTests.swift. Testers can also run the convert command against any catalog with the--enable-experimental-markdown-outputand--enable-experimental-markdown-output-manifestflags set.Checklist
Make sure you check off the following items. If they cannot be completed, provide a reason.
./bin/testscript and it succeeded