Syntax highlighting plugin for PicoCMS

PicoPygments

A PicoCMS plugin that adds Pygments syntax highlighting to code blocks that have a language definition.

Requirements

You need

  • to have PicoCMS up and running.
  • the DOM module for PHP (possibly also the SimpeXML and XML modules; on Debian, these are packaged into php-xml.
  • Python3 and a Python3 version of Pygments. I tested with both python3-pygments from Debian stable repos (2.3.1) and a much newer, pip-installed version - both worked.

Tested on Debian buster (stable), PicoCMS 2.1.4 and PHP 7.3.

Installation

There are two ways - cloning the git repository, or manually downloading the plugin.

Git clone

  • Change into your PicoCMS install's /plugin directory, open a terminal there.
  • Execute git clone https://$site.org/ohnonot/PicoPygments, where $site is either framagit or notabug.

If you follow a few simple rules, you can always cd PicoPygments; git pull to the newest version:

  • Don't edit any files inside the repository.
  • Copy the picopygments.yml config file to your PicoCMS install's /config directory.
  • Create custom files inside the repository that have the word custom in their name (see .gitignore) and edit /config/picopygments.yml to point to those. Currently this only applies to the stylesheet configuration.

Manual download

  • Download the master.zip into your PicoCMS install's /plugin directory.
  • Extract it in place - you should now have a PicoPygments folder that immediately contains PicoPygments.php and some other files. In other words, from your PicoCMS install's base directory, you should have /plugins/PicoPygments/PicoPygments.php (and some other files and directories).

That should be enough.

As usual, you should copy the picopygments.yml configuration file to your PicoCMS installation's /config folder and make your adjustments there.

Configuration

The plugin should be enabled by default, and its own caching should also work by default.
If you want to make sure, set debug: true in your PicoCMS' config.yml, you will see some messages at the top of the page. After first loading a page all code blocks on that page should be cached, greatly reducing loading times. They are cached with a unique filename representing the code itself and PicoPygments' configuration. If anything changes there, new versions are generated.

Additionally you can explicitely disable PicoPygments for a particular page by adding picopygments: false to its YAML header.

Styles, Colors, CSS

Base16

If the page's already existing stylesheet declares the CSS variables --base00 through --base0f in its :root (see the TagBlog theme's README.md (mirror) for details), PicoPygments will automatically apply them.

This happens through the default css/base16var_with_fallback.css stylesheet. If these variables are not defined, then the fallbacks following them will be applied . They come from the Base16 Equilibrium Light scheme (and the TagBlog theme currently uses the same as fallback).

As the names of the variables suggest they should adhere to the [Base16 Styling Guidelines][bsg].

However, it is still possible to use a different stylesheet with the stylesheet setting in picopygments.yml. Options are provided in the css/generated folder.

BTW, you can preview many Pygments styles here, and most Base16 styles here. Those Pygments styles that are not Base16 styles can be generated with scripts/styles.py (be sure to read scripts/README.md first).

Additional to colors, there's a _common.css file with overrides for all styles.

Line numbers

It is possible by editing the formatteroptions configuration under the DANGER ZONE in /config/picopygments.yml.

I have tried to integrate line numbers, but this introduces too many quirks and (what I consider) inconsistencies in Pygments' behaviour. They work fine as such, but possibly

  • your code blocks will get even larger, with empty elements.
  • the code block will look weird, with double borders etc (fixable with CSS).
  • you have to decide if you want line numbers for either all code blocks or none.

Usage

You have a markdown article with a fenced code block. If you append a language keyword to the opening fence, this plugin will become active. Example:

md
## This is what I coded in Lua: ```lua -- try to find themes in $HOME themedir = os.getenv("HOME") .. '/.local/share/themes/' ```

will use Lua syntax highlighting for this block of code.

If you're unsure which language to choose for your code you can run the included guess.py script, enter the raw code in question, and Pygments will tell you what it thinks about it.

The plugin activates after PicoCMS has converted your Markdown articles to HTML, it replaces code blocks therein if they have a language class, like this: <code class="language-lua">...</code>. PicoCMS uses Parsedown and Parsedown Extra which is supposed to implement Markdown Extra, but I found that this particular feature is not as rich as outlined here:

  • It won't handle more than one class added.
  • If you don't use the simple syntax above, but try to add multiple classes or dots or brackets (e.g. ~~~ .lua, ~~~{.lua .my-code}, ~~~{.lua #my-code} etc.) the extra characters show up in the langauge class and you end up with something like <code class="language-{.lua">.

This plugin sanitizes these strings, but multiple classes won't work.

Highlighting inline code (not a block of code)

Neither Markdown Extra nor Parsedown Extra have a means to put a CSS class on inline code, but you can write it out as HTML in your Markdown text. Instead of

I typed `while true; do echo Yes; done` without looking.

you have to write

I typed <code class="language-bash">while true; do echo Yes; done</code> without looking.

Background

The plugin does all its work server-side.
If you care about keeping your site javascript free, you might like this.
If not - I would recommend other solutions.

It is written in PHP, just like PicoCMS itself, but Pygments is written in Python and there is no PHP version of it.
Therefore it needs to execute an external program with exec, which is just python3 -I with an ad-hoc generated script (it does not call the pygmentize script included with python-pygmentize).
This increases loading times much more than any pure PHP solution (but IMO Pygments is still the best, most up-to-date solution for non-javascript syntax highlighting).

To minimise this impact the plugin

  • considers only code blocks that are marked with a language class (it does not guess at the language)
  • executes python at most once per page (and not once per code block)
  • caches Pygments' output and prefers to re-use that.

So the best-case scenario is that all code blocks have been cached peviously and will be read from (RAM)disk, and there's no need to call python at all.
I made some (unscientific, anecdotal) comparisons with Firefox' developer tools - the result is clear enough:

  • Without internal caching this plugin adds between 66% and 91% to the loading time (the HTML of the page only, no assets like images or CSS).
  • With internal caching (and all code blocks loaded from cache) the plugin adds between 2% and 5% to the loading time.

Get it here

framagit.org/ohnonot/PicoPygments / notabug.org/ohnonot/PicoPygments