Collecting my attempts to improve at tech, art, and life

reStructuredText Basics For Blogging

Tags: python rst text tools

What is it?

reStructuredText is a Lightweight Text Formatting Language with a cumbersome name. You mostly see it in Python docstrings, because it’s the standard format for Python documentation. Through site generators and Sphinx, RST also shows up behind the scenes in blogs, projects, and technical books.

Nothing about RST limits it to technical writing — well except that most nontechnical folks aren’t installing special Python libraries to write Hugo posts.

Anyways. The essentials of the RST format are easy enough that it’s suited for general writing.

How do I use RST in my blog?

If you already blog with Nikola or Pelican, you are all set. Those site generators natively support reStructuredText.

Hugo will build .rst content if you have rst2html.py installed.

pip install docutils

Editor support

Emacs and Vim both include RST support built-in. Visual Studio Code users can find a [useful plugin])(https://marketplace.visualstudio.com/items?itemName=lextudio.restructuredtext). But all you need is a plain text editor, preferably with automatic indentation.

Extracting styles

The HTML generated by rst2html.py has its own special classes. My home-grown Hugo theme supports none of those classes, of course. I couldn’t figure out how to export the Docutils default stylesheet this morning.

So I made a document and grabbed the CSS rules from there for my own nefarious purposes.

"hey\n" | rst2html.py >> sample.html

What does it look like?

Although RST is readable, more folks are familiar with Markdown. I know I was more familiar with Markdown when I started this. Once you get the hang of it, you may find that RST has its charms.

The basics

More than enough to write one of my blog posts.

Paragraphs and inline formatting

It all starts with paragraphs. Plan text, separated by empty lines. The text lines of a paragraph are wrapped together.

Indent your paragraph if you want a nice blockquote.

You can emphasize text in a paragraph using asterisks. Double asterisks give more emphasis. You can wrap multiple words to emphasize all of them. I think doing that dilutes the effect, though. You end up with something that looks more like a conspiracy-themed newsletter. But hey. If that’s the look you’re going for? Have fun!

It all starts with paragraphs. Plain text, separated by empty lines.
The text lines of a paragraph are wrapped together.

     Indent your paragraph if you want a nice blockquote.

You can *emphasize* text in a paragraph using asterisks.
Double asterisks give **more** emphasis.
You can wrap multiple words to *emphasize all of them*.

Use double backticks for inline literals — characters displayed in a monospace font and often used to indicate code. This is a little confusing after Mrkdown, which uses a single backtick for literals. But RST uses those for interpreted text.

Use ``double backticks`` for inline literals — characters displayed in a monospace font and often used to indicate code. This is a little confusing after Mrkdown, which uses a single backtick for literals. But RST uses those for `interpreted text`.

What’s interpreted text? Well, it can mean a few things depending on the context of what’s in and around it. You could even define your own with Python. Not today, though.

rst2html.py transforms a lone bit of interpreted text to <cite>interpreted text</cite>. The citation tag is used in HTML for referencing creative work: books, songs, blog posts.

Bullet lists

We already know what a basic bullet list looks like.

- You have some lines
- Each line starts with a special character and a space
- I used ``-`` but RST allows a few

	- ``*``
	- ``-``
	- ``+``

- The important thing is to be consistent for a list or sublist

	- oh, and you can do sub lists with indentation!
	- but you *need* blank lines between list levels

Links can be simple URL drops, like https://beatrockmusic.bandcamp.com/. Or use some interpreted text for a more readable link. I prefer reference links. It even looks nice for long references, once you get used to it.

Links can be simple URL drops, like https://beatrockmusic.bandcamp.com/.
Or use some interpreted text for a more readable `link <https://bambubeatrock.bandcamp.com/>`_.
I prefer reference_ links.
If even looks nice for `longer references`_, once you get used to it.

.. _reference: https://rockyriverabeatrock.bandcamp.com/
.. _longer references: https://prometheusbrown.bandcamp.com/album/tag-init

See those last couple lines? Those define link targets. The .. at the beginning of the line tells RST this is explicit markup. Explicit markup takes us out of the core document flow, letting us use extensions or define values.

For today’s goal of basic blogging, this explanation is sufficient.

A little more

We’ve got the basics. After these next few items, I have about 80% of everything I ever wrote on this site covered.

Headers and sections

WARNING

Most blog generators demote your headers by at least one level. That way your post title goes at the top of the hierarchy. It also means that my level three section headers generate <h4> tags! So don’t go overboard with subsections.

You’ve been looking at section headers already, so it seems silly to put examples here. Plus it messes up the document structure.

You need two lines to make a section header. The text of the header itself forms the first line. Use the text of the header itself for the first line. In the second line, put enough non-alphanumeric characters to match your header’s length. Pick any you like — well, any from the set of = - : " ~ ^ + * + # < > — as long as you stay consistent.

What does it look like?
=======================

section 3

First symbols picked, so it’s a level one header.

A little more
-------------

Section 3.1

I picked a new symbol for the indicator, so this is a level two header.

Headers and sections
~~~~~~~~~~~~~~~~~~~~

Section 3.1.1

Another new symbol means another level, taking us to a level three header.

Images and figures
~~~~~~~~~~~~~~~~~~

Section 3.2.2

These use the same symbol I used for Headers and sections, so this is another level three header.

Directives
----------

Section 1.2

Oh hey, remember this symbol? We’re back up to level two!

This is the only area where RST feels significantly more cumbersome to me than Markdown or Asciidoctor. At least it’s pretty to look at.

Images and figures

I already have my own shortcodes for images in Hugo. Oh, and the special logic for cover pictures. Jeez I have my work cut out for me if and when I migrate to another generator.

Still, images are a pretty fundamental part of blogging. It would feel strange to skip them.

image:: worst-cat.png
   :alt: Text reads "This is the worst cat." Photo is a baby hippo
   :target: https://worstcats.tumblr.com/post/97243616862/this-is-the-worst-cat

Look, more explicit markup! This calls the image directive with worst-cat.png as an argument, and a few options specified with what RST calls a field list. You can make the image a link with :target:, which is nice.

I prefer the HTML figure for my images. It allows me to add a readable caption, which is a great spot for attribution.

.. figure:: worst-cat.png
	:alt: Text reads "This is the worst cat"; Photo is a baby hippo

	via the `Worst Cats`_ Tumblr blog

This directive is conceptually much closer to what I’m thinking of. You even get a whole paragraph to set the caption. Text after the first paragraph becomes the legend. Interested parties can read the figure documentation for more details about that.

attachments/img/2020/worst-cat.png
via the Worst Cats Tumblr blog

Unfortunately it’s not really a <figure>. This is a div.figure holding an img and a p.caption instead of a <figcaption. As a purist, I recognize that I must eventually fix this.

Simple Tables

Tables are very handy for summarizing information. RST allows extremely complex table formatting. Fortunately for me, I never use extremely complex table formatting. simple-tables work just fine.

========= =================
Generator Supports RST
========= =================
Nikola    Yes
Pelican   Yes
Sphinx    Yes
Hugo      If you install `docutils`
Gatsby    ??
Eleventy  ??
Jekyll    ??
Middleman ??
========= =================

Overflow is okay, as long as the table markers themselves line up. Still. It’s untidy. Excuse me a moment.

========= =========================
Generator Supports RST
========= =========================
Nikola    Yes
Pelican   Yes
Sphinx    Yes
Hugo      If you install `docutils`
Gatsby    ??
Eleventy  ??
Jekyll    ??
Middleman ??
========= =========================

That’s better.

Generator Supports RST
Nikola Yes
Pelican Yes
Sphinx Yes
Hugo If you install docutils
Gatsby ??
Eleventy ??
Jekyll ??
Middleman ??

Table construction can get more elaborate. Check out grid-table if that sort of thing interests you. I can also get simpler, with csv-table and table-listing directives.

Directives

Directive are used to extend RST. They’re written in Python, but you don’t need to understand Python to use them.

Directives share a basic structure:

.. directive-name:: arguments
	:option-name: option-values

	body

The details vary with every directive. Some require a body, some take no options. content generates a full table of contents without requiring argument, options, or a body!


.. content::

We’ve already looked at a couple directives. Do I have a favorite? Strangely enough, I do.

Admonitions

Most of this site’s history has been me talking to myself. Sometimes I talk back. So I’m always looking for good ways to add assorted interjections and comments. Markdown doesn’t officially support that sort of thing, so as a result my .md files have nonstandard components and Hugo shortcodes to accomplish this sort of thing.

Fortunately, these side notes are part of RST as admonitions.

.. note:: Don't forget to mention admonitions!

NOTE

Don’t forget to mention admonitions!

There are several admonition types, from the casual note to the dire alert.

.. warning:: Don't overuse admonitions!

WARNING

Don’t overuse admonitions!

note and warning should suffice for most cases.

Code blocks

This is mostly a coding blog. Of course I’m going to cover the code directive. You give it a language and some code. Pygments handles the highlighting. It handles nearly every language I have handed to, so it should work nice.

How about a little snippet of Python?

.. code:: python

    def main():
        """Create a circle template from command line options"""
        # Get details from command line or use defaults
        parser = argparse.ArgumentParser()
        parser.add_argument("--size", help="length of image side in pixels",
                            type=int, default=DEFAULT_SIZE)
        parser.add_argument("--circles", help="number of circles",
                            type=int, default=DEFAULT_CIRCLES)
        parser.add_argument("--slices", help="number of slices",
                            type=int, default=DEFAULT_SLICES)
        args = parser.parse_args()
        size = args.size
        circle_count = args.circles
        slice_count = args.slices
        circle_template = CircleTemplate(size, circle_count, slice_count)
        circle_template.save()
def main():
	"""Create a circle template from command line options"""
	# Get details from command line or use defaults
	parser = argparse.ArgumentParser()
	parser.add_argument("--size", help="length of image side in pixels",
						type=int, default=DEFAULT_SIZE)
	parser.add_argument("--circles", help="number of circles",
						type=int, default=DEFAULT_CIRCLES)
	parser.add_argument("--slices", help="number of slices",
						type=int, default=DEFAULT_SLICES)
	args = parser.parse_args()
	size = args.size
	circle_count = args.circles
	slice_count = args.slices
	circle_template = CircleTemplate(size, circle_count, slice_count)
	circle_template.save()

Oh my. I’m closing in on two thousand words. That’s far more than I indended. Let’s stop here, with the majority of my regular blog-writing needs covered.

Oh, fine. One little bonus section.

Bonus: CSV Tables

Hand-drawing a table can be labor-intensive — especially when you get fancy. Sometimes that is too much. Sometimes you just want to stuff values in a table.

csv-table serves that perfectly.

Let’s say I have a CSV list of my most important Taskwarrior tasks for this site. I can paste that list under a csv-table directive, give it a caption and the header text — maybe set the widths option to auto, because I dislike the default of equal-width columns.

.. csv-table:: High priority site tasks
	:header: "ID", "Description", "Urgency"

    227,"rst basics for blogging",11.9
    228,"extract rst stylesheet",7.9

And it comes out not too bad!

High priority site tasks
ID Description Urgency
227 rst basics for blogging 11.9
228 extract rst stylesheet 7.9

Another Bonus: List tables

I feel bad. A two row CSV table does not save that much time. Maybe if I had 20 or 30 generated rows. And while it may be easier for stuffing values into a table, CSV is not the most readable format.

I can make it up to you.

I just used list-table while switching a recent post to reStructuredText. It was a lifesaver.

.. list-table:: Emacs text-scale adjustment key bindings
	:header-rows: 1
	:widths: auto

	- - Function
	  - Keys
	  - Description
	- - ``(text-scale-adjust 1)``
	  - ``C-x C-=`` or ``C-x C-+``
	  - Increase text size by one step
	- - ``(text-scale-adjust -1)``
	  - ``C-x C-=`` or ``C-x C--``
	  - Decrease text size by one step
	- - ``(text-scale-adjust 0)``
	  - ``C-x C-=`` or ``C-x C-0``
	  - Reset text size to default

Use nested lists to construct your list table. Each of the top list items represent a row in your table. Each of the items in a row list is a cell in that row. Because I specified :header-rows: 1, the first row gives us a table header.

Emacs text scale adjustment key bindings
Function Keys Description
(text-scale-adjust 1) C-x C-= or C-x C-+ Increase text size by one step
(text-scale-adjust -1) C-x C-- Decrease text-size by one step
(text-scale-adjust 0) C-x C-0 Reset text size to default

I like this. Mind you, I get that simple and grid tables are easier to understanding when reading RST. There are fancy editor extension to draw simple or grid tables. Nevertheless, I’m writing this RST file with the intent of turning it into HTML. In that context — for me — pasting CSV or lines of text is easier than polishing text tables.

Okay I have got to stop now. Clearly I enjoy RST way too much.

What did I miss?

Roles and substitutions. I wanted to cover them today, but no. This will do for now.

I referenced these quite a bit while putting this post together. Maybe they could be useful for you!


Added to vault 2024-01-15. Updated on 2024-02-01