icyleaf / markd

Yet another markdown parser, Compliant to CommonMark specification, written in Crystal.
MIT License
109 stars 31 forks source link

Revisit `Markd::Renderer` implementation #56

Open devnote-dev opened 1 year ago

devnote-dev commented 1 year ago

Per the README:

If you want to use a custom renderer, it can!


class CustomRenderer < Markd::Renderer

  def strong(node, entering)
  end

  # more methods following in render.
end

options = Markd::Options.new(time: true)
document = Markd::Parser.parse(markdown, options)
renderer = CustomRenderer.new(options)

html = renderer.render(document)

IMO I don't think this is the best approach, Markd::Renderer is centered around the HTML renderer specifically, so an unsuspecting user may implement a custom renderer that only overrides #strong (for example), expecting the rest of the markdown to be rendered as text—except it's rendered HTML now! It's also not clear what methods can be overridden unless you look into the source code (partially related to #55). I think it would be better if Markd::Renderer was implemented as an abstract class with abstract methods, like an interface:

module Markd
  abstract class Renderer
    abstract def heading(node : Node, entering : Bool) : Nil
    abstract def list(node : Node, entering : Bool) : Nil
    abstract def item(node : Node, entering : Bool) : Nil
    abstract def block_quote(node : Node, entering : Bool) : Nil
    # ...
  end
end

This way it's clear what methods you can/have to implement. If users just want to override one or more methods like in the README example, they still can do so by extending Markd::HTMLRenderer. WDYT?

straight-shoota commented 1 year ago

Introducing a completely abstract interface seems like a good idea. This would also allow to document the interface independently of a specific implementation.