Skip to content

Support Ruby 4 in the cmark version#430

Closed
davidcelis wants to merge 1 commit intogjtorikian:c-api-stablefrom
davidcelis:ruby-4
Closed

Support Ruby 4 in the cmark version#430
davidcelis wants to merge 1 commit intogjtorikian:c-api-stablefrom
davidcelis:ruby-4

Conversation

@davidcelis
Copy link
Copy Markdown

I’m still using the cmark version (which is hopefully still being maintained 😅), but that version’s gemspec remains locked to < 4.0. I believe we can just bump that to < 5.0 and that the gem will work on Ruby 4 as is, though I’m still testing that out a bit. I’ve tried to update my application to the new comrak wrapper but I’ve been reliant on the ability to use a custom CommonMarker::HTMLRenderer subclass that adds attributes to link nodes beyond the standard href and title attributes. Either custom renderers aren’t possible in the comrak version or I haven’t been able to figure out how to port this custom logic over based on available documentation (I’m happy to open a separate issue about this as well)

@gjtorikian
Copy link
Copy Markdown
Owner

Hiya!

The short answer is that I'm loathe to maintain the C-backed version.

For this specific case, I can suggest one of two things:

  1. I can bring back the ability to define a custom HtmlRenderer in Ruby. That would enable code that looked something like this:
class CustomRenderer < Commonmarker::HtmlRenderer
  def link(node)
    out('<a href="', escape_href(node.url), '"')
    out(' title="', escape_html(node.title), '"') unless node.title.empty?
    out(' rel="nofollow" target="_blank"')  # Add custom attributes
    with_node(node) { out(">", :children, "</a>") }
  end
end

doc = Commonmarker.parse("[click here](https://example.com)")
renderer = CustomRenderer.new
html = renderer.render(doc)
# => "<p><a href=\"https://example.com\" rel=\"nofollow\" target=\"_blank\">click here</a></p>\n"

I don't think it'll take very long to implement, since the code was available in the old library anyhow, and won't require me to write new Rust code.

  1. Part of the reason I ripped out the ability to add custom renderers is that I've been trying to push people to use html-pipeline instead. It supports more complex transformations, is a much more performant option, and, frankly, just looks nicer. For this case, that might look something like this:
require "html_pipeline"
require "commonmarker"

class LinkAttributeFilter < HTMLPipeline::NodeFilter
  SELECTOR = Selma::Selector.new(match_element: "a")

  def selector
    SELECTOR
  end

  def handle_element(element)
    element["rel"] = "nofollow"
    element["target"] = "_blank"
  end
end

pipeline = HTMLPipeline.new(
  # yeah, this is incorrectly named, but it uses `commonmarker` underneath 
  convert_filter: HTMLPipeline::ConvertFilter::MarkdownFilter.new,
  node_filters: [LinkAttributeFilter.new]
)

result = pipeline.call("[Example](https://example.com)")
puts result[:output]

Would this work?

@davidcelis
Copy link
Copy Markdown
Author

The short answer is that I'm loathe to maintain the C-backed version.

That is extremely fair 😂 Your suggestion to use html-pipeline makes sense! I’ll give that a shot when I have some time and, in the meantime, my fork is working for me. Thank you for pointing me in the right direction!

@davidcelis davidcelis closed this Jan 7, 2026
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.

2 participants