Automate boring writing with Vim

Constructing obscure keyboard maps a.k.a. shortcuts in Vim to make repetitive tasks easier

Two of my most used markup languages these days are Markdown and reStructuredText. For instance, a common operation is two add hyperlinks while blogging. Both languages provide concise syntaxes to make links, but they tend to need a lot more keystrokes to get it right. Another pain-point is the need for escaping certain special characters while searching and replacing text.

Well, not anymore!

Requirements

vim-surround, pynvim + Neovim / Vim compiled with Python bindings

How it works

Vim can be configured to behave differently based on the state it is in (reading to buffer, file type, vim mode etc.). Keyboard shortcuts can be mapped for such scenarios either of these commands map, nmap, imap, vmap, noremap, nnoremap, inoremap, vnoremap.

Markdown

The following maps Shift+Enter \a in the normal mode:

""Markdown: inline-link maker
"surround with [], find ], append ()
au FileType markdown,pandoc nmap <leader>a ysiW]f]a()<ESC>i

to add an inline link:

> before
A sample sentence with a link

> after
A sample sentence with a [link]()

The following maps Enter in the normal mode:

""Markdown: link maker
"yank inner word, surround with [], find ], append [], paste word, move right
"mark l, go to end of file, add a line, append [], paste, move right, append:
au FileType markdown,pandoc nmap <CR> yiWysiW]f]a[]<ESC>PlmlGo<ESC>a[]<ESC>Pla:<SPACE>

to add a link with a handle:

> before
A sample passage with a link and containing
a lot of text.

> after
A sample passage with a [link][link] and containing
a lot of text.
[link]:

You can return back to the text by visiting the mark with l.

The following maps h1, h2, h3, h4 to create headings in the normal mode:

""Markdown: heading maker
"mark h, go to beginning of the line, add #/##/###/### , go back to mark h
au FileType markdown,pandoc nmap h1 mh^i#<SPACE><ESC>`h
au FileType markdown,pandoc nmap h2 mh^i##<SPACE><ESC>`h
au FileType markdown,pandoc nmap h3 mh^i###<SPACE><ESC>`h
au FileType markdown,pandoc nmap h4 mh^i####<SPACE><ESC>`h
> before
A Heading

> after
# A Heading

reStructuredText

The following maps Shift+Enter \a in the normal mode:

""rST: inline-link maker
"surround with ``, find `, append __, move left by 2 characters, insert <>
au FileType rst nmap <leader>a ysiW`f`a__<ESC>2hi<SPACE><lt>><ESC>i

to add an inline link:

.. before
A sample sentence with a link

.. after
A sample sentence with a `link <>`__

The following maps Enter in the normal mode:

""rST: link maker
"yank inner word, go to end of word, append _, mark l, end of document, add a
"new line, insert .. _, paste, append:
au FileType rst nmap <CR> yiWEa_<ESC>mlGo<ESC>i..<SPACE>_<ESC>pa:

to add a link with a handle:

.. before
A sample passage with a link and containing
a lot of text.

.. after
A sample passage with a link_ and containing
a lot of text.
.. _link:

You can return back to the text by visiting the mark with l.

The following maps h1, h2, h3, h4 to create headings in the normal mode:

""rST: heading maker
au FileType rst nmap h1 yypVr#
au FileType rst nmap h2 yypVr=
au FileType rst nmap h3 yypVr-
au FileType rst nmap h4 yypVr~
.. before
A Heading

.. after
A Heading
#########

Search and replace

In most editors, you could use Ctrl+f to search text and Ctrl+r to search and replace text. While searching in Vim is a piece of cake with / key, search and replace is often a bit more painful.

"{{{ Select (visual mode) and search / replace
""""""""""""""""""""""""""""""""""""""""""
if has('python3')
python3 << endpython3
import re
import vim

def py_regex_escape(string=None):
    h = string if string else vim.eval('@h')
    h = re.escape(h).replace("'", "''")
    if string:
      print(h)
    else:
      vim.command("let @h='{}'".format(h))

endpython3

command! -nargs=? RegexEscape :py3 py_regex_escape(<f-args>)

" http://stackoverflow.com/questions/676600/#676619
" Below h is used as a register to yank into
" Also search with \v prefix for searching with very magic and
" if needed \V prefix with very nomagic. See `:help magic`
vnoremap <C-f> "hy:RegexEscape<CR>/\v<C-r>h
vnoremap <C-r> "hy:RegexEscape<CR>:%s/\v<C-r>h//gc<left><left><left>
endif
"}}}

This powerful mapping would automatically try to do a Python regular expression escape and fill the search or search-and-replace command with a sample text you selected in visual mode. An obvious caveat is that Python regex and Vim regex are not identical. I have found the very-magic mode to be close to Python regex.

Improvements?

Before you say it, I agree these solutions are far from elegant. Perhaps UltiSnips can come in handy to create links. Let me know you find it useful or you can improve it.

Ashwin Vishnu Mohanan

About the author

Ashwin Vishnu Mohanan, Ph.D. in Fluid mechanics

Posted by on in Tech Talk. updated Tags: software, markdown, rst, vim, writing.

Interactions

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can: