Build Status

cr-cmark-gfm

API Documentation

Crystal C bindings for cmark-gfm, with support for the latest version of Github Flavored Markdown (v.29). For more information regarding the available options and extensions see the official GFM spec documentation.

Features:

Installation

Add the dependency to your shard.yml and then run shards install:

dependencies:
  cmark:
    github: amauryt/cr-cmark-gfm

This will automatically clone the cmark-gfm repository and compile both libcmark-gfm and libcmark-gfm-extensions, which will be then statically linked.

Usage

The Cmark module offers high-level parsing and rendering of markdown content, be it Commonmark-only, full GFM, or partially GFM.

If the content is Commonmark-only use the respective commonmark parsing and HTML rendering methods for the best performance. If all of the GFM extensions must be enabled use the gfm methods. For partial support of GFM extensions use the more generic document methods.

require "cmark"

options = Option.flags(Nobreaks, ValidateUTF8) # deafult is Option::None
extensions = Extension.flags(Table, Tasklist)  # default is Extension::None

commonmark_only = File.read("commonmark_only.md")
full_gfm = File.read("full_gfm.md")
partially_gfm = File.read("partially_gfm.md")

# Direct parsing and rendering

commonmark_only_html  = Cmark.commonmark_to_html(commonmark_only, options)
full_gfm_html         = Cmark.gfm_to_html(full_gfm, options)
partially_gfm_html    = Cmark.document_to_html(partially_gfm, options, extensions)

# Parse to obtain a document node for further processing

commonmark_only_node  = Cmark.parse_commonmark(commonmark_only, options)
full_gfm_node         = Cmark.parse_gfm(full_gfm, options)
partially_gfm_node    = Cmark.parse_document(partially_gfm, options, extensions)

Node creation and rendering

require "cmark"
include Cmark

contents = NodeMaker.strong
contents.append_child NodeMaker.text("click here")
node = NodeMaker.link(url: "http://example.com", title: "Example")
node.append_child(contents)

node.type.link? # => true
node.url # => http://example.com
node.title # => "Example"
node.title = "My Example"

node.render_commonmark # => [**click here**](http://example.com "My Example")
node.render_plaintext # => click here

Tree traversal and manipulation

# Using the node from the previous example

node.previous # => nil
node.first_child # => <Cmark::Node::Strong>
last_child =  node.last_child.not_nil!
last_child == node.first_child # => true
last_child.next # => <Cmark::Node::Text>

iterator = EventIterator.new(node)

iterator.each do |event|
  node = event.node
  if event.enter?
    puts "entering node"
  end
  if event.modifiable? && node.type.strong?
    # Transform strong text into unformatted text
    text_node = node.first_child.not_nil!
    node.parent.not_nil!.prepend_child(text_node)
    node.unlink
  end
end

node.render_commonmark # => [click here](http://example.com "My Example")

Custom rendering

class MyHTMLRenderer < Cmark::HTMLRenderer
  # Container nodes receive `node` and `entering`
  def strong(node, entering)
    if entering
      out "<b>"
    else
      out "</b>"
    end
  end

  # Leaf nodes only receive `node`
  def text(node)
    out node.literal.gsub("click", "don't click")
  end
end

# Using the node from the "Node creation and rendering" example

options = Option.flags(Nobreaks, ValidateUTF8) # default is Option::None
extensions = Extension.flags(Table, Tasklist) # default is Extension::None

renderer = MyHTMLRenderer.new(options, extensions)
html = renderer.render(node)
html # => <a href="http://example.com" title="My Example"><b>don't click here</b></a>

Alternatives

Now that that the Crystal language (subset of) Markdown is no longer part of the public API in the standard library, other alternatives (from which this shard took inspiration) are:

Contributing

  1. Fork it (<https://github.com/amauryt/cr-cmark-gfm/fork>)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors