Tools for Writing reStructuredText in Vim

22:43 Sun 07 Oct 2012. Updated: 21:55 25 Oct 2012
[, , , , , , , , , ]

I switched to writing in reStructuredText in mid-2009, and to writing in Vim in early 2010. Since then I’ve made a lot of tweaks to improve editing efficiency, and eventually collected these in a Vim plugin (and a Python script). The following discussion of that plugin might be of interest to anyone concerned with writing efficiency and/or editor customization.

I write entirely in Unicode (saving all files as UTF-8), so some of the functionality is aimed at making it it easier to enter Unicode characters on a standard keyboard.

Insert-Mode Delimiters

In reStructuredText, sections of text can be marked as strong, emphasis, link, code, etc., by surrounding them with certain characters. For example *word* produces an italicized word, i.e. word. This approach is common to most lightweight markup languages[1].

The problem with it is that the characters used are in awkward places on the keyboard. To make entry easier I created simple shortcuts[2]—to enter emphasized text, I hit e<Tab>, hitting <Tab> again to exit; this approach is about as efficient as hitting <Control-I> in a word processor. All the reStructuredText inline markup that I commonly use is covered, including links.

I also wanted typographically-correct quotation marks, but I wanted them to work only at my discretion—one of the problem with “smart quote” functionality, is that it can’t tell when you mean to have straight quotation marks and when you don’t. Since I write about code reasonably often, I need to make sure that explicit direction is required. But I don’t want to have to hit <Alt-[> and <Alt-Shift-[> to open and close double quotation marks[3]. The answer is to treat them like the other delimiters: now I enter q<Tab>, followed by <Tab> again when I’m finished quoting; using an uppercase Q instead gives single instead of double quotation marks.

Other Insert-Mode Elements

I commonly use footnotes (symbolic and numeric), headings, horizontal rules, and lists (bulleted, definition, and numeric); I assume anyone who writes a lot does also.

To make reStructuredText headings, I enter h[1-6]<Tab>, where the number is the level of heading I want[4].

Horizontal rules are simple: hr<Tab>.

i<Tab> produces a correctly-indented bulleted list item, and n<Tab> does the same for numeric list items.

Footnotes are a special case, because of restrictions in snippet functionality—you can’t hit <Tab> to complete a “snippet trigger” that’s part of a word. So while nf<Tab> is fine as a way to trigger the insertion of a numeric footnote, it’s not that useful when you want the footnote to go right at the end of a word (which is where I tend to put them). To get around this I used Vim’s native ability to turn any key into a “leader” for special entry. I used the semicolon as this “leader” because I don’t hit it that often in writing and would be most likely to follow it with a space (which I won’t create a combination for). So to create a numeric footnote, I enter ;n[5].

Insert-Mode Unicode Glyphs

I also use the semicolon for a variety of Unicode characters. For example, ;d emits an en dash, ;m emits a minus sign, ;; emits an ellipsis, and ;e emits the Euro symbol.

For the em dash, I cheat a little and use the hyphen as another “leader”, so that -- emits .

Surround Customization

surround.vim is a Vim plugin that lets you change the characters surrounding a text object. I created some of these to match the insert-mode delimiters discussed above.

Syntax Highlighting

I created highlighting that works on things overlooked in other syntax files for reStructuredText that I’ve seen[6], and that also makes it easier to eliminate confusion between similar-looking glyphs—such as the various dashes and quotation marks. My highlighting also highlights parenthetical and quoted passages, so that it’s very difficult to forget to close them[7]. I also conceal the escaped spaces that reStructuredText sometimes makes necessary. Here’s what a sample paragraph looks like with my syntax highlighting (and my own Vim color scheme):

Syntax highlighting from rest_tools

(Most passages of text have far fewer elements and would look rather less garish; when I wrote the passage for that image I tried to cover all the bases.)

reStructuredText References

One of reStructuredText’s strengths is something I really didn’t like when I started using it: the ability to mark something as a reference inline and then define it later in the document. The most common example is with links. Here’s what I wrote when I first tried reStructuredText five years ago:

I’ll try reStructuredText out, but I have a hard time seeing myself writing

I visit BoingBoing_ too often.
.. _BoingBoing: http://boingboing.net

instead of

I visit <a href="http://boingboing.net">BoingBoing</a> too often.
—Tadhg O’Higgins. “reStructuredText”. tadhg.com, 11 Oct 2007.

I was complaining about the awkwardness of the reStructuredText syntax, as compared to the very familiar HTML, as well as what I perceived at the time as the need to fill out the reference (the .. _BoingBoing: http://boingboing.net part) immediately, which was harder to do than write the HTML.

But the whole point is that there’s no need to do it immediately. Writing HTML made me afraid of forgetting the href parameter later and leaving a dead link in the document, so whenever I wrote a link I would go find the URL I was referring to and insert it then and there. With reStructuredText, however, there’s no need to do that; the natural approach is the better workflow, i.e. finish writing and worry about the detail of the URL later on.

It took me a while to realize that because I continued to find it awkward to fill in the reStructuredText references. It was harder to search a document for them than to search for href="". And once I did adapt to worrying about them later, it became annoying to have to write out the repetitive syntax.

The answer to that is not to do it manually.

I wrote a script to do this, one that will go through a document and insert the references at the bottom. At that stage, all I need to do is fill out the URLs. It also works for footnotes and substitutions[8]. Once my rest_tools plugin is installed, it can be invoked in Vim with :Grefs—however, it can also be run from the command line on any file[9].

In the three years I’ve been using it steadily, I’ve put a lot of work into making reStructuredText easier for me to use every day. Much of that work is contained in this plugin, which is available, along with documentation, at <https://github.com/erisian/rest_tools>.

[1] And their antecedents; I think that surrounding a word with asterisks to make it stand out preceded the notion of a lightweight markup language.

[2] Any snippet functionality would be able to do this, and this is available in most text editors. I use snipMate.vim, but may switch to UltiSnips at some point.

[3] On OS X; I also don’t want to enter the Windows equivalents, <Alt-0147> and <Alt-0148>.

[4] reStructuredText doesn’t exactly pay attention to this; it regards the characters you use for the first heading as indicative of what means “level one header” in that document. So I could use “h6” first and “h3” second, but the end result would be that the “h6” heading would be treated as “h1”. See <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections> for more details.

[5] Which emits \ [#]_, because reStructuredText doesn’t like footnotes to be right up against words, so you have to put an escaped space at the end of the word before the footnote. This was rather annoying, but since adopting the shortcut for it is completely fine. UltiSnips allows “in-word” triggers, so switching to it might change my approach to this, but the semicolon-leader works well.

[6] Such as definition lists, which I use quite often, and have for years; I think they’re a semantic element that doesn’t get enough love.

[7] This does not work for multi-paragraph quotations, where the convention is to open quotation marks at the start of each paragraph but only close them at the end of final paragraph. However, I don’t use this style, instead using indented block quotations.

[8] reStructuredText has a fairly powerful system for replacing text with other text or with other constructs: <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#substitution-references>.

[9] It requires Python 2.6 or 2.7.

One Response to “Tools for Writing reStructuredText in Vim”

  1. jbremnant Says:

    Nice, thanks for the post. I just started using reStructuredText and also a heavy vim user. Looking forward to more posts on this subject.

Leave a Reply