Using Prism for syntax highlighting with Sculpin

Posted on

I have been playing with the idea of starting my own blog for a while, but until now my website consisted of just a static index.html (along with some Sass file and images). Writing the HTML manually worked fine for a single landing page, but I realised that for a blog this would quickly become cumbersome. In order to keep the management of my site as easy as possible, without having to run a fully-fledged, database-backed content management system, I decided to go for a static site generator.

When it comes to static site generators, there are a lot of options available, written in many different programming languages. I decided to go with Sculpin, a static site generator written in PHP and based on Symfony. Just as most static site generators, Sculpin allows converting lightweight markup languages such as Markdown and Textile to HTML. Together with a template (written in Twig) this is rendered into a full HTML page. If you want to read more about Sculpin, my former colleague Matthias Noback has written some excellent blog posts about it.

After having set up Sculpin, a basic layout based on Bootstrap and some pages, the result was starting to look like a proper blog. However, obviously a blog about programming is not complete without code samples and syntax highlighting. At first I installed highlight.js for syntax highlighting, as I already had some limited experience with it from creating a presentation with code samples using reveal.js. However, after playing around with it for a bit, I discovered that the functionalities of highlight are a bit limited as it lacks support for certain syntax elements such as variable and method names.

Disappointed, I started searching for an alternative, and found Prism. Like highlight.js, Prism is a JavaScript library that offers syntax highlighting for a lot of different languages and supports multiple themes. However, the syntax highlighting offered by Prism seems far more complete than that of highlight.js, and when using the Darcula theme it displays my piece of test PHP code almost exactly the same as PhpStorm does:

<?php
declare(strict_types=1);

namespace NicWortel\Demo;

class Foo
{
    private const MY_CONST = 1;

    /**
     * @var string
     */
    private $bar = '';

    public function __construct(string $bar)
    {
        $this->bar = $bar;
    }

    public function getBar(): string
    {
        return $this->bar;
    }

    public function getFoo(): string
    {
        echo self::MY_CONST;

        return $this->getBar();
    }
}

But in order to get Prism working with my Sculpin blog, there was one more hurdle to take. The Markdown parser used by Sculpin surrounds code blocks (either indented or "fenced") with <pre><code> tags. When using fenced code blocks, you can add the language name after the three backticks which will translate into a class being set on the <code> tag:

```javascript
var s = "JavaScript syntax highlighting";
```
<pre><code class="javascript">var s = "JavaScript syntax highlighting";</code></pre>

While this works fine for highlight.js (which will even try to detect the language automatically if the class is omitted), Prism is a lot more strict and only works when code blocks have a language-xxx (or lang-xxx) class. Unfortunately, this doesn't play nicely with Markdown. I could decide to use ```language-xxx in my Markdown files, but this would disable all syntax highlighting within my Markdown files in PhpStorm, GitHub, etc.

Luckily PHP Markdown, the Markdown parser used by Sculpin, supports configuring a class prefix for fenced code blocks by setting the code_class_prefix property on the MarkdownExtra class. So the only remaining question was how to change the value of this property on the instance of MarkdownExtra used by Sculpin. And because Sculpin is based on the Symfony framework, this was actually quite easy. Just like Symfony, Sculpin can be configured using YAML files. In order to change the value of the code_class_prefix property, all I had to do was to create a file app/config/sculpin_services.yml and override the service definition of the Markdown parser:

# app/config/sculpin_services.yml
services:
  sculpin_markdown.parser:
    class: '%sculpin_markdown.parser.class%'
    properties:
      code_class_prefix: language-

And that was it! The Markdown parser now prepends all code classes in the generated HTML with language-, enabling syntax highlighting with Prism while keeping syntax highlighting inside the source Markdown files intact.