In this post, I would like to present vim-mail-refs, a new Vim plugin that I wrote. It automatically inserts URL references into emails.
Since I started to use Vim as the sole text editor, I have found myself pressing Vim shortcuts outside Vim all the time. Especially when writing emails. Of course, it did nothing useful since I was writing them directly in Thunderbird. Thanks to the External Editor plugin that integrates Vim into Thunderbird, writing emails become easier. However, one thing was still missing. Creating emails with references was tedious.
Imagine all the work that had to be done to create an email with references. I had to write a reference to a URL in the email body, then move to the end of the email and write the reference with the URL on one line separated by a space and, finally, move to the original position. Admittedly, this sequence of actions can be done relatively quickly in Vim. Nevertheless, I still had to pay attention not to include one URL twice in the reference list. Also, after rewriting the email, references needed to be renumbered by their order in the email body or completely removed when they were no longer used.
To simplify writing of emails with references, I implemented a Vim plugin that does all of this automatically. In the remainder of this post, I would like to introduce this plugin to you.
The plugin provides three features. The first one is implemented by the
AddMailRef command. After executing
:AddMailRef, you will be asked to enter a URL. Then, a reference to this URL will be added into the current cursor position in the email body, including adding the URL to the end of the email body before the signature (if any). If the current cursor position is inside a word (anything with letters, numbers, underscores and hyphens), the reference will be added after this word and separated by a single space.
The second feature is represented by the
AddMailRefFromMenu command. It is useful if you want to reuse an already existing reference. After executing
:AddMailRefFromMenu, you will be asked to select a reference from the shown menu. To select a reference, either write just the reference number (e.g.
1) or put it inside square brackets (e.g.
The last command, called
FixMailRefs, normalizes all references used in the email. It performs the following actions:
- unused references are removed,
- references are renumbered by their order of appearance in the buffer (
To simplify the use of the plugin, it is recommended to create mappings for these commands. For example:
au FileType mail nnoremap <buffer> <Leader>ar :AddMailRef<CR> au FileType mail nnoremap <buffer> <Leader>aR :AddMailRefFromMenu<CR> au FileType mail nnoremap <buffer> <Leader>fr :FixMailRefs<CR>
The first autocommand allows using
,ar (assuming that your
,) as an abbreviation for typing
:AddMailRef. The other autocommands create similar mappings.
The plugin consists of two parts. The first one is written in Vimscript and serves as an interconnection between Vim and the second part. The second part is written in Python and contains the core of the plugin. The Python part is covered by unit tests. Since this plugin is implemented in Python 3, Vim has to be compiled with Python 3 support. To check it, run
During the implementation of this plugin, I stumbled across a few problems. The most subtle one was calculating cursor position. Initially, I used
vim.current.windows.cursor to get the cursor position. It is a tuple containing two numbers: row and column. At least so I thought. At first, everything seemed to work all right and my friend started to test the plugin. After a short time, he reported a bug. Sometimes, references were inserted at wrong position. It turned out that the problem happened only for emails written in Czech. Since Czech uses accents, some letters need more than one byte to be stored. From this stackoverflow post, I grasped that column from
vim.current.windows.cursor is byte offset on the current line.
For correct functioning of the plugin, I need to use the
virtcol function. However, using
virtcol has its own drawbacks. One of them is that the result of
virtcol depends on the
linebreak settings. Before calling
virtcol, these settings have to be remembered and set to appropriate values and after the call to
virtcol they have to be set to their original values. For a more thorough explanation, look at this blog post.
For an introduction to writing of Vim plugins in Python, you can take a look at this video. However, be aware that it does not include proper handling of cursor positions when writing non-ASCII text. For other tips how to write Vim plugins, I highly recommend this article.
The plugin can be downloaded here. If you find a bug in the plugin or have some ideas for enhancing or extending plugin, please let me know. I will also be happy to receive any opinions and comments on this plugin.