Issues of doautoall command of Vim and changes in 8.2.2596

itchyny
3 min readMar 22, 2021

The doautoall command of Vim has been a problematic for me. But by the changes of 8.2.2596, all my concerns have been gone away. This article explains the issues of the command and what the patch 8.2.2596 fixes. Note that the commit message says :doautocmd but it is a mistake of :doautoall.

The doautoall command is used for emitting some event against all the buffers. For example, doautoall FileType emits the FileType event on all the buffers.

This command confuses event handlers in various ways. For example, let’s use statusline to debug the list of buffers shown in the current tab page on the WinEnter event.

autocmd WinEnter * let &statusline = join(
\ map(
\ range(1, winnr('$')),
\ 'v:val . ": " . bufname(winbufnr(v:val))'
\ ), ' | ')

Let’s see what happens with doautoall WinEnter.

edit foo.txt | vnew bar.txt | tabe baz.txt | tabp
doautoall WinEnter

In older (than 8.2.2596) versions of Vim, the statusline will show as follows.

1: baz.txt | 2: bar.txt | 3: foo.txt

Since there are only two windows in the current tab page, this result looks wired.

Let’s look into another example. We improve the previous event handler to show the active window using winnr().

autocmd WinEnter * let &statusline = '%t / ' . join(
\ map(
\ range(1, winnr('$')),
\ 'v:val . ": " . (v:val == winnr() ? "[" : "") .' .
\ 'bufname(winbufnr(v:val)) . (v:val == winnr() ? "]" : "")'
\ ), ' | ')

Now open 3 windows and switch back to the previous window.

edit foo.txt | vnew bar.txt | vnew baz.txt | wincmd p
doautoall WinEnter

In the older versions of Vim, the result will be as follows.

bar.txt / 1: [baz.txt] | 2: bar.txt | 3: foo.txt

The current window is opening bar.txt but it shows baz.txt as the active window.

These behaviors happen in any events and not limited to the statusline. The doautoall command does not limit the event type, so even if you might think doautoall WinEnter is not appropriate, plugin authors should take care of the command as long as Vim allows. Any scripts using winnr() , bufname() , winrestcmd() , winsaveview(), and etc. in event handlers will fail to behave well when the event is emitted by the command. I used statusline not echomsg because the latest window state is most important in plugins.

The first problem of doautoall is autocommand window. When Vim emits an event for each buffers, it activates the associated window. For buffers not shown in any windows in the current tab page, it uses a special window called autocommand window (aucmd_win in source code) instead. Many Vim plugin authors may not expect their event handlers run with this special autocommand window. Since 8.2.0996 (after 8.2.0991), we can detect this special window by win_gettype(). The autocommand window makes the first problem happen; the buffer name in the another tab page can appear. So skip using win_gettype() if you want.

The second problem is the order of the buffers. The doautoall command used to emit the event simply in the order of buffer numbers. When the last buffer is shown in an inactive window of the current tab page, all event handlers will misjudges the active window. Since 8.2.2596, it emits the event on the current buffer last. This change fixes various problems caused by the command.

I am the author of lightline.vim and have been annoyed by these behaviors of doautoall. Especially, the session file contains doautoall SessionLoadPost, I constantly receive bug reports due to the session (#435, #444, #447, #448, #556). But the problem is not limited to the session feature, but the doautoall command. I’m now grad I fixed the real problem of the command without introducing any dirty workaround in my plugin; updates on cursor moves or on timers.

You may not invoke the command by hand, but we plugin authors often face bugs with such features. Most of the fixes of software are done in incremental way and it is sometimes easy to introduce dirty workaround. Investigating the real cause and fixing them will grow the software better. Thanks for reading.

--

--