Writing Prose in Vim

2021-10-06

If you know me at all, you probably know I am a fan of vim. I have written more than a few blog posts on it already.

Most vim users use vim for programming. That is, after all, what most people use plain text editors for. However, since learning vim, I have tried to use it for as much as I can. Everything from emails to blog posts to letters. Of course, programming as well, but certainly not only that.

“Why?”, you might ask. Because it makes my life easier. I have said before that Vim is not as difficult as many vim users would like you to think it is.

Spellcheck

I need a spell check. My spelling is atrocious. This is not helped by the fact that I am dyslexic. Having a decent spell checker is essential if I am writing anything that is going to be read by another human. Incidently, I count future me as another human in this regard.

Vim has a built-in spell check.

set spell
set spelllang=en_gb

How it will look will depend on your colour scheme but in my case it is an underline.

Basic usage consists of the following keyboard shortcuts:

  • [s or ]s to move between misspelled words
  • z= to get a list of suggestions
  • zg to add the current word to your custom dictionary

As with anything in vim, these shortcuts can be changed, although I have never felt the need to.

Vim’s insert completion can be used to look up words in the internal dictionary as well. To use this, start writing a word and complete it with ctrl-x ctrl-k.

This will present you with a list of words that start with what you have typed. You can select one, as normal, with ctrl-n and ctrl-p.

Finally, on the topic of spell checking, I have added an insert mode mapping that is used to correct the previous misspelled word.

The mapping is as follows:

inoremap <C-l> <c-g>u<Esc>[s1z=`]a<c-g>u
  1. <c-g>u - break undo sequence (new change)
  2. <Esc> - go into normal mode
  3. [s - go to previous spelling mistake
  4. 1z= - change to the top spelling suggestion
  5. `] - go to the end of the last changed word
  6. a - enter insert mode
  7. <c-g>u - break undo sequence (new change)

This was shamelessly stolen from Gilles Castel’s excellent blog post on using vim for LaTeX

Undo Points

When programming in vim, a lot of time is spent in both normal mode and insert mode. However, when writing prose, much more time is spent in insert mode. As a result, each undo point tends to be much larger. This can result in the rather annoying behaviour that undoing anything can remove a huge amount of writing.

To minimize this annoyance, I add undo points whenever I type punctuation points.

    inoremap ! !<C-g>u
    inoremap , ,<C-g>u
    inoremap . .<C-g>u
    inoremap : :<C-g>u
    inoremap ; ;<C-g>u
    inoremap ? ?<C-g>u
    inoremap ( <C-g>u(
    inoremap ) )<C-g>u

The only point I really need to elaborate on here is that for the opening bracket: I add the undo-point before inserting the character. This results in what I consider preferable behaviour whereby a whole bracket group is removed at once when undoing, rather than leaving the opening bracket.

Distraction Free Writing

Distraction free writing seems to be quite a fashionable / desirable feature in a lot of editors at the moment. I have tried and used Goyo and limelight which go some way to reproducing the distraction free environment of many other editors. However, I no longer use them. Both worked well, but vim doesn’t have that many distractions without them. When I am writing, I open vim in a full screen terminal. I don’t even have a clock visible. Being able to highlight the paragraph I am currently working on or putting the text in the middle of the screen wasn’t worth it for me.

Proselint

Proselint, as the name suggests, is a linter for prose. It points out areas where my writing could be better. To use it with vim, I have the following autofunction:

function! mine#functions#proselint() abort
    let oldmakeprg = &l:makeprg
    " set new value of makeprg and call the function
    set makeprg=proselint\ %
    make
    copen
    " set makeprg back to old value
    let &l:makeprg = oldmakeprg
endfunction

and a mapping:

nnoremap <leader>p :call mine#functions#proselint()<CR>

This allows me to fill the quickfix list with a list of suggestions from proselint.

LanguageTool

LanguageTool is similar in concept to Proselint although seems to have a lot more checks under its hood. It can be intergrated into vim in a similar way to Proselint, with an autofunction.

function! mine#functions#languagetool() abort
    let oldmakeprg = &l:makeprg
    let olderrformat = &l:errorformat
    " set new value of makeprg and call the function
    set makeprg=languagetool\ -l\ en-GB\ %
    let &l:errorformat =
          \ '%-GPicked up _JAVA_OPTIONS: %.%#,' .
          \ '%-GExpected text language: %.%#,' .
          \ '%-PWorking on %f...,' .
          \ '%-I%.%# [main] DEBUG %.%#,' .
          \ '%+IUsing %.%# for file %.%#,' .
          \ '%I%\d%\+.) Line %l\, column %c\, Rule ID: %m,' .
          \ '%-CMessage%m,' .
          \ '%-CSuggestion%m,' .
          \ '%-CMore info%m,' .
          \ '%-C%\s%#^%\+%\s%#,' .
          \ '%-C%.%#,' .
          \ '%-Z%\s%#,' .
          \ '%-Q,' .
          \ '%-GTime: %.%#'
    make
    copen
    " set makeprg back to old value
    let &l:makeprg = oldmakeprg
    let &l:makeprg = olderrformat
endfunction

You will notice that this is slightly longer because the error format used by Proselint is compatible with Vim’s default. LanguageTool’s is not.