Using Kramdown instead of Maruku

Update January 23rd, 2015: Paul Robert Lloyd improved the converter with his Pypedown plugin. It also sports the Typogruby extension for some visual juice.

Update July 24th, 2014: Matthijs van den Bos improved Juan’s plugin to work with Jekyll 2.x.

Update May 7th, 2014: As of Jekyll version 2.0.0, Kramdown is Jekyll’s default Markdown converter. However, it’s still using CodeRay.

Update August 14th, 2013: The code as shown below is not working with Jekyll versions >= 1.0. See this post how to update your Jekyll installation to version 1.0. Juan Antonio Navarro Pérez kindly put a fixed version of the code on GitHub.


After I messed around with Maruku for some time now, I became fed up with its limitations such as missing fenced code blocks. Without these, you have to use the unwieldy Liquid tags to denote a piece of code. Fortunuately, there are many alternative Markdown converters. To decide which one to use in the future, I came up with this (incomplete) feature matrix:

FeatureMarukuRedcarpetKramdownPandoc
Fenced code blocks
Footnotes
Jekyll integration
Header IDs
Inline math
Abbreviations
Pygments

At first glance, Kramdown wins hands down (even ahead of my beloved Pandoc). But there is one slight problem: When blogging with Jekyll, you have to use CodeRay for syntax highlighting or none at all. Because I favor Pygments due to the broader range of supported languages, I had to update the Markdown converter plugin that I presented in the last post.

For this, we have to subclass Kramdown’s HTML converter to use Pygments on code blocks:

require 'kramdown'
require 'pygments'
require 'typogruby'

module Kramdown
  module Converter
    class Pygs < Html
      def convert_codeblock(el, indent)
        attr = el.attr.dup
        lang = extract_code_language!(attr)
        if lang
          add_code_tags(
            Pygments.highlight(el.value,
                               :lexer => lang,
                               :options => { :encoding => 'utf-8' })
          )
        else
          "<pre><code>#{el.value}</code></pre>"
        end
      end

      def add_code_tags(code)
        code = code.sub(/<pre>/,'<pre><code>')
        code = code.sub(/<\/pre>/,"</code></pre>")
      end
    end
  end
end

Now, we can replace the Maruku converter with our customized Kramdown converter:

module Jekyll
  class MarkdownConverter < Jekyll::Converter
    def convert(content)
      setup

      html = Kramdown::Document.new(content, {
          :auto_ids         => @config['kramdown']['auto_ids'],
          :footnote_nr      => @config['kramdown']['footnote_nr'],
          :entity_output    => @config['kramdown']['entity_output'],
          :toc_levels       => @config['kramdown']['toc_levels'],
          :smart_quotes     => @config['kramdown']['smart_quotes']
      }).to_pygs
      return Typogruby.improve(html)
    end
  end
end

Note, that Kramdown uses weird dynamic name resolution (the method call to_pygs in this case) to hook the subclassed Pygs class into the conversion process. For my taste, this goes a bit too far but that’s the way it is.