Jekyll Code Highlight And Line Numbers Problem Solved
Setting up my new webpage and blog, thanpol.as, using Jekyll proved to be a bit more cumbersome than expected. Specifically with syntax highlighting. To get it just right, I had to create a custom plugin just to wrap the generated markup in a block container.
As a consequence i had to let go of directly deploying the Jekyll repository to Github pages and resort to pushing the static generated website instead. It strikes me as odd that, what I believed to be a trivial task, proved to be so hard and what I discovered that was required for highlighting to work, was not offered as an option from Jekyll. So finally, i ended up fixing the problem with Javascript.
Syntax Highlighting Options
Although I know there are many solutions for Syntax Highlighting out there, I wanted to go with Jekyll’s “native” way, pygments and Liquid Templates. Syntax looked ok, codeblocks are required to be inside {% highlight html %}
and {% endhighlight %}
tags.
This codeblock will generate pygmented markup like the following Javascript sample:
Pretty straightforward… Now how about adding some line numbers?
Adding line numbers to codeblocks in Jekyll
Again, it looked simple, just add the key linenos
in the liquid tags and line numbers are added!
1
2
3
4
// so this codeblock has been generated
// by adding the property 'linenos' to the
// syntaxhighlight tag
for (var i = 0, len = ar.length; i < 0; i++) {
However, the line numbers are inline with the code which creates a “Select and Copy” problem… You get the line numbers in the clipboard, essentially making the codeblock not copy-pasteable.
The solution for that is to use one additional parameter, the table
. So the opening liquid tag would look like this {% highlight language linenos=table %}
. Which produces this result:
1
2
3
for (var i = 0, len = ar.length; i < 0; i++) {
newAr.push(ar[i].shift());
}
So far so good, but what happens at the odd cases where a line might need to wrap?
The problems of Jekyll’s Syntax Highlighting
The codeblock is rendered as a table with two columns, the first containing the line numbers and the second the actual code. Each column has only one row, which in its turn has the <pre><code> ... </code></pre>
elements. It looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<table class="highlighttable">
<tr>
<td class="linenos">
<div class="linenodiv">
<pre>
<code class="javascript"> 1
2
</code>
</pre>
</div>
</td>
<td class="code">
<div class="highlight"><pre> ... </pre></div>
</td>
</tr>
</table>
So, naturally, when a line is exceeding its width and wrapped this is what happens:
The second column of the table, where the code existed, has a width
of 100%
. Having a fixed width is out of the question, so there were little to go with for handling this situation.
Rather than trying to somehow properly align the line numbers with the wrapped lines, I decided to force the code to not wrap and have horizontal scroll bars if the text overflowed.
However, with no fixed width on the table cell, there is no proper solution to have horizontal bars for a child element. In the case of a no-wrapping pre
things get nasty. Challenge yourself in this Dabblet.
A solution to this problem, is to wrap the table into a containing block. Then the block can have horizontal bars if the table overflowed. And this option, plainly does not exist for Jekyll. The {% highlight %}...
tag is Jekyll’s native plugin for the liquid templates. It takes the codeblock and runs it through albino, a ruby wrapper for pygmentize, before it is pushed upwards the stack, to whatever engine the document is (md, html, textile).
There is no option or way to define a wrapper for the generated code. There is also no way to wrap around a tag
in the liquid templates… This left me with three choices:
- Manually wrap codeblocks with the containing block. Very verbose way of expressing, not elegant, turned me down.
- Create a custom plugin for Jekyll that would do the job. Loosing the ability to push Jekyll sources to Github for auto-generation.
- Wrap the elements with Javascript sensible, not perfect but effective.
I chose to create a custom plugin for Jekyll
I wanted to experiment so I gave it a try. It worked, but not without issues. Essentially I overwrote the highlight
tag defined by Jekyll, and wrapped its output with the proper container block:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module Jekyll
class WrapHighlightBlock < Jekyll::HighlightBlock
def initialize(tag_name, markup, tokens)
super
end
def render(context)
'<figure class="code"><figcaption></figcaption>' + super + '</figure>'
end
end
end
Liquid::Template.register_tag('highlight', Jekyll::WrapHighlightBlock)
Custom plugins == no Github love
Once you step to the other side, using custom plugins for Jekyll’s generation, you loose the option to directly push to Github on the [username].github.com
repo.
So I switched the origin
of my Jekyll repo to point to, the new “blog repo” and created pushed the _site
folder of Jekyll to github as a separate repo.
But, Jekyll had one last trick up its’ sleeve to slam me, it completely erased everything in the _site
folder before each new file generation, along with the .git
folder that kept the repo’s actual data (!). Grunt came to the rescue, with the copy task on a grunt watch I was able to copy the newly generated files into a mirror folder that the .git
file was preserved.
One small script to commit & push both repos and the saga was finished.
But, I was not satisfied with this setup so I reverted to solution 3 and using Javascript to wrap the codeblocks with the <figure>
container.
After loosing so much time with this, I am left wondering at which point I took the wrong turn. I’d love to hear a Jekyll’s expert feedback on this, so please share your thoughts.
blog comments powered by Disqus