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.