Return false if you are not interested in rendering the given inline. Use Context.inline and Context.block on the given context if you need to invoke the renderer recursively.
Return false if you are not interested in rendering the given block. Use Context.inline and Context.block with the given context if you need to invoke the renderer recursively.
Return false if you are not interested in rendering the given document. Use Context.inline, Context.block and Context.doc with the given context if you need to invoke the renderer recursively.
make ?init_context ?inline ?block ?doc () is a renderer using inline, block, doc to render documents. They all default to (fun _ _ -> false), which means that by default they defer to next renderer (see compose).
init_context is used to initialize the context for the renderer before a document render. It defaults to fun _ _ -> ().
compose g f renders first with f and if a renderer returns false, falls back on its counterpart in g.
The init_context of the result calls g's initialization context function first, followed by the one of f. This means f's initialization function can assume the context is already setup for g.
Accessors
Normally you should not need these but you may want to peek into other renderers.
This example extends the Cmarkit_html.renderer but it applies mutatis mutandis to the other backend document renderers.
Let's assume you want to:
Extend the abstract syntax tree with a Doc block which allows to splice documents in another one (note that splicing is already built-in via the Cmarkit.Block.Blocks block case).
Change the rendering of Cmarkit.Inline.Image inlines to render HTML video or audio elements depending on the link's destination suffix.
Define a custom_html renderer which treats Cmarkit.Inline.Image and the new Doc case the way we see it fit and return false otherwise to use the built-in renderer.
type Cmarkit.Block.t += Doc of Cmarkit.Doc.t (* 1 *)
let media_link c l =
let has_ext s ext = String.ends_with ~suffix:ext s in
let is_video s = List.exists (has_ext s) [".mp4"; ".webm"] in
let is_audio s = List.exists (has_ext s) [".mp3"; ".flac"] in
let defs = Cmarkit_renderer.Context.get_defs c in
match Cmarkit.Inline.Link.reference_definition defs l with
| Some Cmarkit.Link_definition.Def (ld, _) ->
let start_tag = match Cmarkit.Link_definition.dest ld with
| Some (src, _) when is_video src -> Some ("<video", src)
| Some (src, _) when is_audio src -> Some ("<audio", src)
| None | Some _ -> None
in
begin match start_tag with
| None -> false (* let the default HTML renderer handle that *)
| Some (start_tag, src) ->
(* More could be done with the reference title and link text *)
Cmarkit_renderer.Context.string c start_tag;
Cmarkit_renderer.Context.string c {| src="|};
Cmarkit_html.pct_encoded_string c src;
Cmarkit_renderer.Context.string c {|" />|};
true
end
| None | Some _ -> false (* let the default HTML renderer that *)
let custom_html =
let inline c = function
| Cmarkit.Inline.Image (l, _) -> media_link c l
| _ -> false (* let the default HTML renderer handle that *)
in
let block c = function
| Doc d ->
(* It's important to recurse via Cmarkit_renderer.Context.block *)
Cmarkit_renderer.Context.block c (Cmarkit.Doc.block d); true
| _ -> false (* let the default HTML renderer handle that *)
in
Cmarkit_renderer.make ~inline ~block () (* 2 *)
let custom_html_of_doc ~safe doc =
let default = Cmarkit_html.renderer ~safe () in
let r = Cmarkit_renderer.compose default custom_html in (* 3 *)
Cmarkit_renderer.doc_to_string r doc
The custom_html_of_doc function performs your extended renderings.