Search code examples
cssmarkdownjekyllpygmentskramdown

markdown display incorrect when add code block in list


I am using pygments and kramdown in my jekyll blog.

I tried to add code block to markdown list, but display incorrect.

1. first

2. second

    {% highlight ruby %}
    def foo
      puts 'foo'
    end
    {% endhighlight %}

3. third

2015-10-29 6 39 28

generated html:

<ol>
  <li>
    <p>first</p>
  </li>
  <li>
    <p>second</p>
  </li>
</ol>

<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
  <span class="nb">puts</span> <span class="s1">&#39;foo&#39;</span>
<span class="k">end</span></code></pre></div>

<ol>
  <li>third</li>
</ol>

but if I write like this, it is no problem.

1. first

2. second

    ```
    def foo
      puts 'foo'
    end
    ```

3. third

2015-10-29 6 41 37

Is this pygments or kramdown problem?


Solution

  • The problem is not with Liquid or kramdown individually, but how they operate together. Jekyll seems to be processing the files with Liquid first, followed by passing the result to kramdown to be parsed as markdown.

    This means that kramdown is seeing something like this:

    1. first
    
    2. second
    
    
    <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby">    <span class="k">def</span> <span class="nf">foo</span>
          <span class="nb">puts</span> <span class="s1">'foo'</span>
        <span class="k">end</span>
        </code></pre></figure>
    
    
    3. third
    

    Liquid isn’t keeping the indentation of the code section, and so when processed as markdown it is causing the list to close and the third item to be seen as a new list.

    In order to use the highlight Liquid tag here you need to ensure the result of Liquid processing is the appropriately indented markdown. I don’t know if this is possible with plain Jekyll, but you could do this fairly simply with a plugin (so this won’t work if you are using Github pages).

    Create a file named something like _plugins/indent_filter.rb with these contents:

    module IndentFilter
      def indent(input)
        input.gsub(/\n/, "\n    ")
      end
    end
    
    Liquid::Template.register_filter(IndentFilter)
    

    Now you can use it like this:

    1. first
    
    2. second
    
    {% capture the_code %}
    {% highlight ruby %}
    def foo
      puts 'foo'
    end
    {% endhighlight %}
    {% endcapture %}
    {{ the_code | indent }}
    
    3. third
    

    Note that you need to use capture first in order to use the indent filter (you could probably create a custom tag to use instead of highlight if you prefer). Also note that the Liquid tags aren’t indented at all, that is handled by the filter.

    The result of this after Liquid processing but before markdown is something like this:

    1. first
    
    2. second
    
        <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
          <span class="nb">puts</span> <span class="s1">'foo'</span>
        <span class="k">end</span></code></pre></figure>
    
    3. third 
    

    Now the code block is correctly indented so that markdown sees it as the content of the second list item. Since it is already HTML kramdown doesn’t try to process it further, but also it doesn’t cause the list to be closed. The result after markdown processing is:

    <ol>
      <li>
        <p>first</p>
      </li>
      <li>
        <p>second</p>
    
        <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
       <span class="nb">puts</span> <span class="s1">'foo'</span>
     <span class="k">end</span></code></pre></figure>
      </li>
      <li>
        <p>third</p>
      </li>
    </ol>