gettalong / kramdown

kramdown is a fast, pure Ruby Markdown superset converter, using a strict syntax definition and supporting several common extensions.
http://kramdown.gettalong.org
Other
1.72k stars 274 forks source link

Better support for custom elements #694

Closed jaredcwhite closed 3 years ago

jaredcwhite commented 3 years ago

We're using Kramdown in the Bridgetown project (originally a fork of Jekyll), and of course it's super awesome, but I did run into a minor issue and a new feature request.

The minor issue is I seem not to be able to use markdown="1" to process Markdown within an HTML tag if the tag itself is a custom element. So, for example, this works:

<div markdown="1">
**Strong** _emphasis_
</div>

but this does not:

<custom-element markdown="1">
**Strong** _emphasis_
</custom-element>

And then for a feature request, I'd like to be able to specify custom elements to use instead of divs in places where they're inserted automatically—for example, the wrappers around code blocks, and endnotes, etc.

FYI, some context around all this: https://www.bridgetownrb.com/showcase/custom-html-elements-everywhere-for-page-layout/

I might be able to put together a PR if you could steer me in the right direction. I'm guessing a configuration hash to map specific tags to specific usages, with a frozen hash maping divs by default, would be a worthwhile solution.

gettalong commented 3 years ago

If you need to process markdown inside custom elements, you need to specify which type of content you have inside the element:

<custom-element markdown="block">
**Strong** _emphasis_
</custom-element>

or

<custom-element markdown="span">
**Strong** _emphasis_
</custom-element>

Since <custom-element> isn't a known HTML element, kramdown can't know what you mean by just using markdown="1".

As for the second request: Yes, this might be done via a configuration option but I don't see the general usefulness. If you need this I would recommend creating a custom HTML converter which replaces the usages of div/span.

jaredcwhite commented 3 years ago

Thanks @gettalong! I missed the notification of your reply. That all sounds good. 😃

xaviershay commented 1 year ago

Have to imagine this is messing with a private API, but as a proof of concept I was able to "configure the defaults" for my tag thusly:

Kramdown::Parser::Html::HTML_CONTENT_MODEL_BLOCK << "x-spoiler"
Kramdown::Parser::Html::HTML_CONTENT_MODEL["x-spoiler"] = :block
Kramdown::Parser::Html::HTML_BLOCK_ELEMENTS << "x-spoiler"
Kramdown::Parser::Html::HTML_ELEMENT["x-spoiler"] = true

These constants are used in Kramdown::Parser::Kramdown and not easy to override or modify behaviour with otherwise (outside of recreating large code blocks.)

creating a custom HTML converter which replaces the usages of div/span.

I'm not totally clear how that helps ... if the content isn't parsed as block content then the converter doesn't have access to the parsed block content.

EDIT: Unless you meant something like the following? It's not technically using an HTML5 custom element and isn't really relevant to parsing block html, but gets me what I need:

class Kramdown::Converter::BookHtml < Kramdown::Converter::Html
  def convert_html_element(el, indent)
    if el.value == "x-spoiler"
      summary = Kramdown::Element.new(:html_element, "summary", nil)
      summary.children << Kramdown::Element.new(:text, "Spoiler", nil)
      el.children.unshift summary
      el.value = "details"
      super(el, indent)
    else
      super
    end
  end
end