Tip 26Using Autocommands to Respond to Events

This tip comes in two parts. In the first part, you’ll try out some examples of autocommands to understand how they work. In the second part, you’ll look at some real examples of how autocommands can be useful, collected from other tips in this book.

Understanding Autocommands

Here’s a short sample Vim script that defines two autocommands:

 augroup demo
  autocmd!
  autocmd BufReadPost * echo ​'Reading: '​ . expand(​'<afile>'​)
  autocmd BufWritePost * echo ​'Writing: '​ . expand(​'<afile>'​)
 augroup END

Before we look at the syntax of these autocommands, let’s see how they behave. Open that script in a fresh instance of Vim:

=> $ cd code/autocmd-demo/
=> $ vim autocmds-01.vim

Then source the file:

=> :source %

Now, Vim prints a message each time you read or write a file:

=> :edit!
<= Reading: autocmds-01.vim
=> :write
<= Writing: autocmds-01.vim
=> :edit! $MYVIMRC
<= Reading: ~/.vim/vimrc

For this demo, you’ll want to use the :edit! variant (note the trailing bang). This always reads the file into a buffer, triggering the BufReadPost event. By contrast, the bang-less :edit command won’t trigger that event if the file has already been read into a buffer. (:h :edit!)

Now, let’s consider how these autocommands work. The general format for defining an autocommand looks like this (:help :autocmd):

 autocmd {event} {pattern} {cmd}

Let’s compare the first of our autocommands with this general format. The {event} is BufReadPost, the {pattern} is *, and everything after the pattern is the {cmd}.

In the {cmd}, the <afile> item stands for the name of the current file. This special item is only defined while the autocommand is executing. (You’ll get a blank line if you run :echo expand(’<afile>’) by hand at Vim’s command line.) Other special items available in this context include <abuf> and <amatch> (:help <afile>).

Vim fires the BufReadPost command after reading a file into a buffer (:h BufReadPost). If the buffer’s filepath matches the pattern defined in our autocommand, then Vim executes the specified {cmd}. Since we’re using the * wildcard here, this autocommand executes for all buffers.

Let’s experiment with changing the {pattern} field to be more selective. This short script defines an autocommand that fires whenever you read a Vim script file:

 augroup demo
  autocmd!
  autocmd BufReadPost *.​vim​,vimrc
  ​ echo ​'Editing Vim script: '​ . expand(​'<afile>'​)
 augroup END

As before, the {event} is BufReadPost, but this time the {pattern} is designed to match any Vim script file. Most Vim script files use the vim extension, but the vimrc file is a special case. When defining an autocommand, you can specify one or more patterns using commas to separate each one (:h autocmd-patterns).

To try out this autocommand, source the script:

=> :source autocmds-02.vim

Now, you’ll see the Editing Vim script message when you open a vim or vimrc file:

=> :edit! autocmds-02.vim
<= Editing Vim script: autocmds-02.vim
=> :edit! $MYVIMRC
<= Editing Vim script: ~/.vim/vimrc

But no message is logged when you open a readme.md file:

=> :edit! readme.md

If you’re paying close attention, you might wonder why you didn’t see the Reading: ... message from the autocommand defined earlier. Hold that thought—we’ll come back to that a little bit later.

It looks like we’ve created an autocommand that detects Vim script files. But watch what happens if you create a new file with the vim extension:

=> :new example.vim

Vim didn’t print any message this time. Why not? Because our autocommand listens for the BufReadPost event, which fires only after a buffer is read from a file on disk. When you use the :new {filename} command, Vim creates the buffer in memory, but until you :write, the buffer has no corresponding file on disk.

You can use another one of Vim’s events to handle this case: BufNewFile (:h BufNewFile). This next Vim script refines the previous autocommand to listen for both events:

 augroup demo
  autocmd!
  autocmd BufNewFile,BufReadPost vimrc,*.​vim
  ​ echo ​'Editing Vim script: '​ . expand(​'<afile>'​)
 augroup END

After sourcing this script, you can create a new Vim script file and the autocommand fires:

=> :source autocmds-03.vim
=> :new example2.vim
<= Editing Vim script: example2.vim

To get the desired behavior, you may sometimes need to use more than one event to trigger an autocommand. Other times, you may find that there’s another event more suitable. For this particular case, you could produce a similar result by listening instead for the FileType event (:h FileType):

 augroup demo
  autocmd!
  autocmd FileType ​vim
  ​ echo ​'Editing Vim script: '​ . expand(​'<afile>'​) . ​' '
  ​ . ​'Filetype: '​. expand(​'<amatch>'​)
 augroup END

To see this autocommand in action, source the file:

=> :source autocmds-04.vim

As before, you’ll see the Editing Vim script... message when you open a Vim script file. This autocommand is triggered whenever you change the ‘filetype’ setting. It doesn’t matter how the file is named, as the following example demonstrates:

=> :edit! readme.md
=> :set filetype?
<= markdown
=> :set filetype=vim
<= Editing Vim script: readme.md
 Filetype: vim

For most {event}s, the {pattern} is tested against the filename of the active buffer. The FileType event is different. Here, the {pattern} is matched against the filetype. In this example, if the filetype is vim, the autocommand fires.

For events such as FileType where the treatment of {pattern} deviates from normal, Vim’s documentation describes the behavior. For more examples, check the documentation for :h Syntax, :h OptionSet, and :h QuickFixCmdPre. If the documentation for an event doesn’t mention what the {pattern} is matched against, you can assume it’s tested against the filename of the active buffer.

Browsing Events

You’ve already seen the BufNewFile and BufReadPost events in action. It’s probably no surprise to learn that Vim also has a BufReadPre event, which fires before reading a file into a buffer. Vim’s events typically offer this level of granularity, which means you can make your autocommand fire at just the right moment.

There are events for all sorts of occasions. You can listen for events each time you read or write a file; you can create a new buffer, window, or tab page (BufNew, WinNew, TabNew); you can launch or quit Vim (VimEnter, VimLeave); change text or move the cursor (TextChanged, CursorMoved). To see an overview of all Vim’s events, look up :help {event}. Whatever you want to do with an autocommand, it’s likely Vim has one or more events you can hook into.

If you’re trying to achieve something that can’t be done with Vim’s events, be assured that this is an area of activity in Vim’s development. For example, the TextYankPost event was added recently (Vim 8.0.1206 and Neovim 0.1.3), as were the CmdlineEnter and CmdlineLeave events (Vim 8.0.1401 and Neovim 0.2.3). If you want to see more events added, get involved with Vim or Neovim.

Tearing Down Autocommands

This next Vim script is almost identical to the first example, with one significant change:

 augroup demo
  autocmd BufReadPost * echo ​'Reading: '​ . expand(​'<afile>'​)
  autocmd BufWritePost * echo ​'Writing: '​ . expand(​'<afile>'​)
 augroup END

Can you spot the difference? The first script called autocmd!, but that line is missing from this script. Let’s see how this changes the behavior of the autocommands. Open that script in a fresh instance of Vim:

=> $ cd code/autocmds-demo/
=> $ vim autocmds-05.vim

Source the file, then use the :edit! command to trigger an autocommand:

=> :source %
=> :edit!
<= Reading: autocmds-05.vim

No surprises so far. But watch what happens if you source the file a second time:

=> :source %
=> :edit!
<= Reading: autocmds-05.vim
 Reading: autocmds-05.vim

The message gets logged twice. If you were to source the file a third time, the message would get logged three times each time you trigger the BufReadPost event, and so on. Each time you source that script, Vim adds the specified autocommands, but it doesn’t remove any existing autocommands. As a result, you end up with multiple autocommands defined for the same {event} and {pattern}.

You can inspect the autocommands in the demo group:

=> :autocmd demo
<= --- Autocommands ---
 demo BufRead
  * echo 'Reading: ' . expand('<afile>')
  echo 'Reading: ' . expand('<afile>')
 demo BufWritePost
  * echo 'Writing: ' . expand('<afile>')
  echo 'Writing: ' . expand('<afile>')

That reveals the duplicate autocommands. You can remove autocommands using the :autocmd! command (:help autocommand-remove). Since all of these autocommands were defined inside the demo group, you can remove them all together by running:

=> :autocmd! demo
=> :autocmd demo
<= --- Autocommands ---

Ideally, you don’t want to be calling that manually. Instead, it’s best to define your autocommands in such a way that they are cleanly removed each time they are sourced. Use this template as a starting point:

 augroup unique_group_name
  autocmd!
 " Define autocommands here:
 augroup END

This ensures you don’t accidentally create multiple autocommands for the same {event} and {pattern}.

Autocommands in Use

The autocommands discussed in this section are taken from other tips in this book. For the sake of brevity, each autocommand is presented without the containing group.

Using BufWritePre to Selectively Disable Persistent Undo

In Disabling Persistent Undo for Temporary Files, we use this autocommand:

 autocmd BufWritePre ​/tmp/​* ​setlocal​ noundofile

The {cmd} disables the ‘undofile’ option. The BufWritePre event is triggered just before the file is written, and the autocommand is only executed if the file matches the {pattern}: /tmp/*.

Responding to User-Defined Events

In Setting Local Variables for Files in a Project, we use this autocommand:

 autocmd User ProjectionistActivate ​call​ s:linters()

This autocommand is triggered by the User event, with ProjectionistActivate as the pattern. The User event is special in the sense that Vim never fires it auto-matically, but you can trigger an event like this one yourself:

=> :doautocmd User ProjectionistActivate

Looking at this example, you might be wondering: Why would I do that? Why not just run :call s:linters() directly? Plugins can use this mechanism to create their own events.

In the case of Projectionist, the plugin configures itself by listening for various different native events. The FileType and VimEnter events both cause Projectionist to do similar work in slightly different ways. In both cases, the User ProjectionistDetect event gets called. Triggering this event means that the user can use this event, instead of listening for the FileType and VimEnter events.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.156.250