Skip to content

Conversation

@QuietMisdreavus
Copy link
Contributor

Bug/issue #, if applicable: None

Summary

This PR provides an HTML renderer for the Swift-Markdown AST. This is extended from the base HTML renderer in swift-cmark due to two enhancements:

  1. The option to parse block quotes with special markers as Asides instead, and emitting <aside> tags with an appropriate kind.
  2. Support for parsing the attributes of "inline attributes" and including them in the output HTML. As a proof of concept, this includes the ability to populate CSS classes with spans like ^[formatted text](class: "fancy").

The idea behind this is to enable Swift-Markdown to be used as a general-purpose Markdown renderer, a la cmark-gfm. Future enhancements could involve parsing block directives to create custom block constructs, or other enhancements with inline attributes.

Dependencies

None

Testing

As this is a general-purpose tool, testing is somewhat open-ended. As a proof of concept, you can check the output of rendering Everything.md, in the tests directory:

Steps:

  1. swift run markdown-tool print-html Tests/MarkdownTests/Visitors/Everything.md
  2. Check the output and ensure that it is valid and reasonable HTML.

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded (barring check-source)
  • Updated documentation if necessary

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

3 similar comments
@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@QuietMisdreavus
Copy link
Contributor Author

Alright, that finally sated CI. It turns out that i did not know the best way to restrict the JSON5 parsing to the right platforms! 😅 This also means that the implementation is a bit rough on non-Apple platforms, since i'd forgotten that you're required to quote property names in vanilla JSON.

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@Kyle-Ye
Copy link
Contributor

Kyle-Ye commented Jul 6, 2023

This is amazing. Would you mind rebasing it so that we can begin to review and have it merged?

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

/// A ``MarkupWalker`` that prints rendered HTML for a given ``Markup`` tree.
public struct HTMLFormatter: MarkupWalker {
/// The resulting HTML built up after printing.
public var result = ""
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public var result = ""
public private(set) var result = ""

help: "Input file to print (default: standard input)",
completion: .file()
)
var inputFilePath: String?
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit. I see many CLI tool just use input to represent inputFilePath.

Actually, both is fine since most user will use a shorter -i version

Suggested change
var inputFilePath: String?
var input: String?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a positional argument so the caller does't write the argument name. However, a transformed version of this variable name does appear in the help text:

ARGUMENTS:
  <input-file-path>       Input file to print (default: standard input)

This is definitely nitpicking but maybe "" or something like that would be more informative in the help text.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think i can make it print an empty argument label, but i can at least rename the property to input or inputFile and reword the help text.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here's the result i just pushed:

ARGUMENTS:
  <input-file>            Markdown file to print (default: standard input)

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I had written "<markdown-file>" but that got interpreted as an HTML tag and not displayed. I should have looked at the comment after I posted it.

@afoeder
Copy link

afoeder commented Nov 9, 2023

@QuietMisdreavus : would you mind working in @Kyle-Ye 's change suggestions and rebase it? Thank you!

/// or else return an ``Aside`` with a ``Aside/Kind-swift.struct`` of ``Aside/Kind/note``,
/// this function will allow parsers to only parse an aside if there is a single-word aside
/// marker in the first line, and otherwise fall back to a plain ``BlockQuote``.
func isAside() -> Bool {
Copy link

Choose a reason for hiding this comment

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

Should this be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I feel like this API should be carefully considered. The existing Aside initializer is a bit more liberal with its parsing strategy in terms of the aside label, whereas i wanted to be more conservative in the HTML formatter. I think it might be more useful to introduce an optional initializer that allows for finer-grain selection of what counts as an aside label, so that you can optionally allow for multi-word labels, for example, or fall back to the existing permissive conversion where it will use a "Note" label if it doesn't parse one itself.

@mynona
Copy link

mynona commented Aug 20, 2024

Will this been implemented?

Copy link
Contributor

@d-ronnqvist d-ronnqvist left a comment

Choose a reason for hiding this comment

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

LGTM

help: "Input file to print (default: standard input)",
completion: .file()
)
var inputFilePath: String?
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a positional argument so the caller does't write the argument name. However, a transformed version of this variable name does appear in the help text:

ARGUMENTS:
  <input-file-path>       Input file to print (default: standard input)

This is definitely nitpicking but maybe "" or something like that would be more informative in the help text.

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@QuietMisdreavus
Copy link
Contributor Author

@swift-ci Please test

@QuietMisdreavus
Copy link
Contributor Author

It turns out the Swift-Foundation gained JSON5 parsing support in Swift 6.0, so i retooled the inline attribute parsing to enable support on non-Apple platforms when building with a Swift 6.0+ compiler.

@QuietMisdreavus
Copy link
Contributor Author

I'm going to merge this as-is for right now, and leave a couple action items for future enhancements:

  1. Potentially exposing BlockQuote.isAside() or adding a new initializer on Aside that can return nil if it doesn't parse a label.
  2. Adding support to perform custom conversions on block directives, or custom handling of inline attributes beyond the class handling that this PR currently does.

While those enhancements could move this feature to become a highly customizable Markdown renderer, i believe that what we have here is already a great start. Please feel free to file enhancement requests if you start using this feature!

@QuietMisdreavus QuietMisdreavus merged commit 2c126ce into swiftlang:main Aug 30, 2024
@QuietMisdreavus QuietMisdreavus deleted the html branch August 30, 2024 16:58
@lovetodream
Copy link

I'm following this PR silently, great to see it merged. Thanks for the great work everyone! Do you plan to tag a release with the feature soon @QuietMisdreavus ?

@QuietMisdreavus
Copy link
Contributor Author

@lovetodream No. Swift-Markdown creates tagged releases based on Swift's release schedule, which means that this feature won't make it into a tagged release until the release after Swift 6.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants