Learn Vimscript the Hard Way

Autocommand Groups

A few chapters ago we learned about autocommands. Run the following command:

:autocmd BufWrite * :echom "Writing buffer!"

Now write the current buffer with :write and run :messages to view the message log. You should see the Writing buffer! message in the list.

Now write the current buffer again and run :messages to view the message log. You should see the Writing buffer! message in the list twice.

Now run the exact same autocommand again:

:autocmd BufWrite * :echom "Writing buffer!"

Write the current buffer one more time and run :messages. You will see the Writing buffer! message in the list four times. What happened?

When you create an autocommand like this Vim has no way of knowing if you want it to replace an existing one. In our case, Vim created two separate autocommands that each happen to do the same thing.

The Problem

Now that you know it's possible to create duplicate autocommands, you may be thinking: "So what? Just don't do that!"

The problem is that sourcing your ~/.vimrc file rereads the entire file, including any autocommands you've defined! This means that every time you source your ~/.vimrc you'll be duplicating autocommands, which will make Vim run slower because it executes the same commands over and over.

To simulate this, try running the following command:

:autocmd BufWrite * :sleep 200m

Now write the current buffer. You may or may not notice a slight sluggishness in Vim's writing time. Now run the command three more times:

:autocmd BufWrite * :sleep 200m
:autocmd BufWrite * :sleep 200m
:autocmd BufWrite * :sleep 200m

Write the file again. This time the slowness will be more apparent.

Obviously you won't have any autocommands that do nothing but sleep, but the ~/.vimrc of a seasoned Vim user can easily reach 1,000 lines, many of which will be autocommands. Combine that with autocommands defined in any installed plugins and it can definitely affect performance.

Grouping Autocommands

Vim has a solution to the problem. The first step is to group related autocommands into named groups.

Open a fresh instance of Vim to clear out the autocommands from before, then run the following commands:

:augroup testgroup
:    autocmd BufWrite * :echom "Foo"
:    autocmd BufWrite * :echom "Bar"
:augroup END

The indentation in the middle two lines is insignificant. You don't have to type it if you don't want to.

Write a buffer and check :messages. You should see both Foo and Bar. Now run the following commands:

:augroup testgroup
:    autocmd BufWrite * :echom "Baz"
:augroup END

Try to guess what will happen when you write the buffer again. Once you have a guess in mind, write the buffer and check :messages to see if you were correct.

Clearing Groups

What happened when you wrote the file? Was it what you expected?

If you thought Vim would replace the group, you can see that you guessed wrong. Don't worry, most people think the same thing at first (I know I did).

When you use augroup multiple times Vim will combine the groups each time.

If you want to clear a group you can use autocmd! inside the group. Run the following commands:

:augroup testgroup
:    autocmd!
:    autocmd BufWrite * :echom "Cats"
:augroup END

Now try writing your file and checking :messages. This time Vim only echoed Cats when you wrote the file.

Using Autocommands in Your Vimrc

Now that we know how to group autocommands and clear those groups, we can use this to add autocommands to ~/.vimrc that don't add a duplicate every time we source it.

Add the following to your ~/.vimrc file:

augroup filetype_html
    autocmd!
    autocmd FileType html nnoremap <buffer> <localleader>f Vatzf
augroup END

We enter the filetype_html group, immediately clear it, define an autocommand, and leave the group. If we source ~/.vimrc again the clearing will prevent Vim from adding duplicate autocommands.

Exercises

Go through your ~/.vimrc file and wrap every autocommand you have in groups like this. You can put multiple autocommands in the same group if it makes sense to you.

Try to figure out what the mapping in the last example does.

Read :help autocmd-groups.