Writing and highlighting source code in reStructured Text (RST)

I use reStructured Text for my own documentation and notes as it makes it easier to use a single text mark-up language for everything. For source code listings or records of interactive sessions it's really helpful to have syntax highlighting. When I'm writing code, highlighting the structure aids with correctness, and it's much more readable to have highlighting in the documentation when you refer back later. It's also just prettier!

However, the mark-up for source code in reStructuredText is a bit confusing, and I couldn't find good information for getting Pygments working well with Docutils. Consequently, this post covers how to represent code in RST, and how to get it rendering with HTML/CSS using Pygments.

Source directive

Version 0.9 of Docutils and above recognise the code directive natively. Previously, you had to set-up your own directive, which is why most of the information on the Web tells you how to set-up a custom source-code directive. However, this is not required any more.

To use the builtin code mark-up in a source reStructuredText file:

Source content Rendered content
.. code:: python

  for i in range(10)
      print("Hello World")

  A clear line before continuing is required
for i in range(10)
 print("Hello World")

A clear line before continuing is required

  .. code:: python
     :number-lines:

    for i in range(10)
        print("Hello World")

A clear line before continuing is required
1 for i in range(10)
2  print("Hello World")

A clear line before continuing is required

The option :number-lines: will number lines in the code, it can also take an optional start value to begin numbering from.

To render the source code with highlights Docutils uses the Pygments library. Consequently, it can do syntax highlighting for any language that Pygments knows about. The ones I most commonly use are console (for interactive sessions), bash (for shell scripts), css, html, make, python3 (for python code), pycon (for python console) and restructuredtext. There are a lot more listed on the Pygments Lexers page - use the short-name field in your RST file. This Language demo shows the highlighting in action.

Source code highlight style

An advantage of pygments is that there are a lot of different styles you can choose to render your source code in. Pygments comes with a set of styles built-in, there are also more out on the Web. The Pygments site has a demo area that lets you try different languages and alter the rendering style to see the effect: for example some Python source rendered using the native style. There's also a good gallery of pygments CSS styles available on Richelands site.

To render the source reStructuredText file into HTML with highlights for the code we need a document converter and suitable CSS file. For document conversion I am using Docutils, the other commonly used option is Pandoc. Docutils comes with most distributions, but it can be a bit fiddly to get working with Pygments.

  • Make sure docutils and pygments are installed

Assuming that you're using a Debian or Ubuntu-based system then you do:

$ dpkg --list python-docutils python-pygments
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                          Version             Architecture        Description
+++-=============================-===================-===================-================================================================
ii  python-docutils               0.11-3              all                 text processing system for reStructuredText (implemented in Pyth
ii  python-pygments               1.6+dfsg-1ubuntu1   all                 syntax highlighting package written in Python

Check that the status of both packages is installed (i) and that there are no errors.

  • Check which Pygments styles are available

Pygments provides the pygmentize command (yes, with an American English z in there) to interact with the library. To list the available styles do:

$ pymentize -L styles
Pygments version 1.6, (c) 2006-2013 by Georg Brandl.

Styles:
~~~~~~~
* monokai:
    This style mimics the Monokai color scheme.
* manni:
    A colorful style, inspired by the terminal highlighting style.
* rrt:
    Minimalistic "rrt" theme, based on Zap and Emacs defaults.

The list should continue and be the same as the one available at the Pygments site.

  • Choose one and convert the style into a CSS file

I chose monokai after dallying with emacs, to convert to a CSS file the command is.

$ pymentize -S monokai -f html -a pre.code > pygments.css

It's fairly obvious but the -S switch is the style we want and the -f switch is which formatter we're dealing with, in this case the HTML one. The -a switch tells the formatter that our source HTML uses the pre.code class to wrap code, so the CSS has to be output using that. We're using pre.code because that's what docutils uses to wrap the source code in - as this pygments docutils stylesheet shows .

  • Form your HTML using short classnames

I couldn't get rst2html and pygments to agree on the CSS. The issue seems to be that rst2html defaults to long names for code classes, while pygments defaults to short names. The solution is to switch rst2html to using short ones with --syntax-highlight=short. Unfortunately, I couldn't figure out how to get Pygments to generate long ones to suit the rst2html default!

This means that a typical command to create HTML becomes:

$ rst2html --verbose --stylesheet=/path/to/pygment.css --syntax-highlight=short <input.rst> <output.html>

If you're already using a style sheet and want to add the Pygments one you can tell rst2html to render using multiple stylesheets. Where there are multiple stylesheets later ones over-ride anything in previous ones.

$ rst2html --stylesheet=/path/to/main/css/sheet/main.css,/path/to/pygments/pygments.css \
--syntax-highlight=short <input.rst> <output.html>

In-line code

Another situation where we want code highlights is when we write code within the body of other text. To do this we use the code directive from reStructuredText Interpreted Text Roles. This lets us define specific roles and then mark the text with them.

Source content Rendered content
.. role:: pyth(code)
  :language: python

Then we can do, :pyth:`print("Hello World!")`
examples in-line, :pyth:`for i in range(10)`
Then we can do, print("Hello World!") examples in-line, for i in range(10)

We can define any name (here we used pyth) for our code class, and the language can be any short-name that Pygments recognises.

Unfortunately, the stylesheet we generated above won't highlight this code without some alterations. This text role is wrapped in the code class in the HTML, whereas our other source code is wrapped in the pre.code class. The solution is to add the code class in the CSS file we generated earlier (pygments.css).

Currently, it should look like this:

pre.code .hll { background-color: #49483e }
pre.code, code { background: #272822; color: #f8f8f2 }
pre.code .c, comment { color: #75715e } /* Comment */
pre.code .err, error { color: #960050; background-color: #1e0010 } /* Error */
pre.code .k, keyword { color: #66d9ef } /* Keyword */
pre.code .l, literal { color: #ae81ff } /* Literal */
pre.code .n, name { color: #f8f8f2 } /* Name */
pre.code .o, operator { color: #f92672 } /* Operator */
<lines continue>

The easiest way is to simply add onto the bottom of the CSS file we generated earlier:

$ cd <location/of/pygments.css>
$ pymentize -S monokai -f html -a code >> pygments.css

Note the use of the double >> to append the new class onto the end of the existing pygments.css. Alternatively, to be neat and reduce the amount of CSS we're sending we can edit each line to look like this:

code .hll, pre.code .hll { background-color: #49483e }
code, pre.code  { background: #272822; color: #f8f8f2 }
code .c, pre.code .c { color: #75715e } /* Comment */
code .err, pre.code .err { color: #960050; background-color: #1e0010 } /* Error */
code .k, pre.code .k  { color: #66d9ef } /* Keyword */
code .l, pre.code .l { color: #ae81ff } /* Literal */
code .n, pre.code .n { color: #f8f8f2 } /* Name */
code .o, pre.code .o { color: #f92672 } /* Operator */
code .p, pre.code .p { color: #f8f8f2 } /* Punctuation */

There's some good information in this in-line code highlighting question on StackOverflow.

Including code files

The two previous methods are useful for short code listings which are part of the reStructuredText source file. More substantial pieces can be kept in a separate file and integrated with the include directive.

Source content
.. include:: HelloWorld.py
  :language: python
  :number-lines:
Rendered content
 #!/usr/bin/env python
 """ Sets up a simple window with a Hello World label """

 # Check that pygtk 2.0 is being used
 import pygtk
 pygtk.require('2.0')
 import gtk

 class GUI:
    """ The GUI class sets up our app and does all the work """
    def __init__(self):
        """ init is run when an instance is formed """
        """ in this case all the apps startup code is here """
        self.root = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
        self.root.set_title("Hello World")
        self.root.set_default_size(150,350)
        self.root.connect("destroy", self.destroy_cb)
        # Message for the window
        self.label1 = gtk.Label("Hello world!")
        self.root.add(self.label1)

        self.root.show_all()

    def run(self):
        """ This function runs the application """
        gtk.mainloop()

    def destroy_cb(self, *args):
        """ The destroy callback shuts down the app """
        args[0].hide()
        gtk.mainquit()


if __name__ == '__main__':
    myGUI = GUI()
    myGUI.run()

Finishing

That covers the whole thing! If you know of any improvements I could make or have a comment please leave it below.


Posted in Tech Friday 07 August 2015
Tagged with reStructuredText rst docutils