Vim Tip #18: Basic Functions

2018-12-04(Tue)

tags: Vim

Vim Tips

Vim and NeoVim can be extended using a problematic language called VimScript, the single best source for which is Learn Vimscript the Hard Way, a book - and website - by Steve Losh. Today I'm going to cover writing functions in VimScript (although probably not as well as Losh did).

To re-indent an entire file, we would do this: gg=G. Which is great, but ... gg takes you to the top of the file, so I just lost my place. If you search on Google, you'll see a lot of answers to this problem that look like this:

msHmtgg=G'tzt`s

This is marking your cursor location and your window position, and then returning to it later (note the gg=G trapped in the middle). Ignore all of these, as Vim introduced a couple wonderful functions to replace all that maneuvering: winsaveview() and winrestview(). So we can write a function:

function! ReindentFile()
    let l:winview = winsaveview()
    :normal! gg=G
    call winrestview(l:winview)
endfunction

Saves the current view (both the cursor position and what portion of the file is visible in the window) to local variable l:winview (where the 'l:' indicates a variable local to the current function - see Vimscript Variable Scoping and Pseudovariables). Invokes Normal Mode, but the "!" forces a state that ignores the user's over-ridden key mappings. (People end up making a lot of mappings - this forces default behaviour in case they overrode any commands we use here. This is also why I called VimScript "problematic" earlier: the behaviour of accepting user modifications to the language as a default is ... perverse and bad.) Then restore the view.

You can enter this at the command line, although I wouldn't particularly recommend it. You can add it to your ~/.vimrc (better) and then call it at the command line with :call ReindentFile(), but better would be to map it:

nnoremap <leader>r :call ReindentFile()<cr>

See Map Leader and Normal mode mappings if you're not familiar with mapping.

Similarly, let's write another function to delete all trailing white space in a document:

function! DeleteTrailingWhiteSpace()
    let l:winview = winsaveview()
    :%s/\s\+$//
    call winrestview(l:winview)
endfunction

As before, save (and restore) the window state, then search the entire file ('%' in the regex) for all whitespace ('s+') at the end of the line ('$') and replace it with nothing ('//'). Then restore your window view.

To make it useful, attach it to a mapped keyset:

nnoremap <leader>dw :call DeleteTrailingWhiteSpace()<CR>