Esse uso do <buffer> está correto?
Eu acho que está correto, mas você só precisa envolvê-lo dentro de um augroup e limpar o último, para garantir que o autocmd não seja duplicado toda vez que você executar um comando que recarrega o mesmo buffer.
Como você explicou, o padrão especial <buffer>permite confiar no mecanismo de detecção de tipo de arquivo interno, implementado dentro do arquivo $VIMRUNTIME/filetype.vim.
Nesse arquivo, você pode encontrar os autocmds internos do Vim, responsáveis por definir o tipo de arquivo correto para qualquer buffer. Por exemplo, para remarcação:
" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
Dentro do seu plugin de tipo de arquivo, você pode copiar os mesmos padrões para cada autocmd instalado. Por exemplo, para salvar automaticamente o buffer quando o cursor não se mover durante alguns segundos:
au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md update
Mas <buffer>é bem menos detalhado:
au CursorHold <buffer> update
Além disso, se algum dia outra extensão for válida e $VIMRUNTIME/filetype.vimfor atualizada para incluí-la, seus autocmds não serão informados. E você terá que atualizar todos os seus padrões dentro dos plugins do tipo de arquivo.
As coisas ficarão confusas se eu limpar um buffer e abrir outro (espero que os números não colidam, mas ...)?
Não tenho certeza, mas não acho que o Vim possa reutilizar o número do buffer de um buffer limpo. Não encontrei a seção relevante na ajuda, mas encontrei este parágrafo em vim.wikia.com :
Não. O Vim não reutilizará o número do buffer de um buffer excluído para um novo buffer. O Vim sempre atribuirá o próximo número seqüencial para um novo buffer.
Além disso, como o @ Tumbler41 explicou, quando você limpa um buffer, seus autocmds são removidos. De :h autocmd-buflocal:
Quando um buffer é eliminado, os comandos automáticos locais do buffer também desaparecem, é claro.
Se você quiser verificar a si mesmo, poderá fazê-lo aumentando o nível de verbosidade do Vim para 6. Você pode fazê-lo temporariamente, apenas para um comando, usando o :verbosemodificador. Portanto, dentro do seu buffer de remarcação, você pode executar:
:6verbose bwipe
Então, se você verificar as mensagens do Vim:
:messages
Você deve ver uma linha parecida com esta:
auto-removing autocommand: CursorHold <buffer=42>
Onde 42estava o número do seu buffer de remarcação.
Existem armadilhas das quais devo estar ciente?
Existem três situações que eu consideraria armadilhas e que envolvem um padrão especial <buffer>. Em dois deles, <buffer>pode ser um problema, no outro, é uma solução.
Armadilha 1
Primeiro, você deve ter cuidado com a maneira de limpar os augroups de seus autocmds locais de buffer. Você deve estar familiarizado com este trecho:
augroup your_group_name
autocmd!
autocmd Event pattern command
augroup END
Portanto, você pode ser tentado a usá-lo para seus autocmds locais de buffer, sem modificação, assim:
augroup my_markdown
autocmd!
autocmd CursorHold <buffer> update
augroup END
Mas isso terá um efeito indesejado. Na primeira vez que você carregar um buffer de redução, vamos chamá-lo A, seu autocmd será instalado corretamente. Então, quando você recarregar A, o autocmd será excluído (por causa de autocmd!) e reinstalado. Portanto, o augroup impedirá corretamente a duplicação do autocmd.
Agora, suponha que você carregue um segundo buffer de redução, vamos chamá-lo B, em uma segunda janela. TODOS os autocmds do augroup serão limpos: esse é o autocmd de Ae esse de B. Em seguida, um único autocmd será instalado para B.
Portanto, quando você fizer alguma alteração Be esperar alguns segundos para CursorHoldser acionado, ele será salvo automaticamente. Mas se você voltar Ae fizer a mesma coisa, o buffer não será salvo. Isso ocorre porque na última vez em que você carregou um buffer de redução, houve um desequilíbrio entre o que você removeu e o que adicionou. Você removeu mais do que adicionou.
A solução é não remover TODOS os autocmds, mas apenas os do buffer atual, passando o padrão especial <buffer>para :autocmd!:
augroup my_markdown
autocmd! CursorHold <buffer>
autocmd CursorHold <buffer> update
augroup END
Observe que você pode substituir CursorHoldpor uma estrela para corresponder a qualquer evento na linha que remova autocmds:
augroup my_markdown
autocmd! * <buffer>
autocmd CursorHold <buffer> update
augroup END
Dessa forma, você não precisa especificar todos os eventos que seus autocmds estão ouvindo quando deseja limpar o augroup.
Armadilha 2
Há outra armadilha, mas desta vez <buffer>não é o problema, é a solução.
Ao incluir uma opção local em um plug-in de tipo de arquivo, você provavelmente faz o seguinte:
setlocal option1=value
setlocal option2
Isso funcionará conforme o esperado para opções locais de buffer, mas nem sempre para opções locais de janelas. Para ilustrar o problema, você pode tentar o seguinte experimento. Crie o arquivo ~/.vim/after/ftdetect/potion.vime, dentro dele, escreva:
autocmd BufNewFile,BufRead *.pn setfiletype potion
Este arquivo definirá automaticamente o tipo potionde arquivo para qualquer arquivo cuja extensão seja .pn. Você não precisa agrupá-lo dentro de um augroup porque, para esse tipo específico de arquivo, o Vim o fará automaticamente (consulte :h ftdetect).
Se os diretórios intermediários não existirem no seu sistema, você poderá criá-los.
Em seguida, crie o plugin do tipo de arquivo ~/.vim/after/ftplugin/potion.vime, dentro dele, escreva:
setlocal list
Por padrão, em um potionarquivo, essa configuração fará com que os caracteres de tabulação sejam exibidos como ^Ie o final das linhas como $.
Agora, crie um mínimo vimrc; dentro de /tmp/vimrcescrever:
filetype plugin on
... para ativar os plugins de tipo de arquivo.
Além disso, crie um arquivo de poção /tmp/pn.pn, e um arquivo aleatório /tmp/file. No arquivo de poções, escreva algo:
foo
bar
baz
No arquivo aleatório, escreva o caminho para o arquivo de poção /tmp/pn.pn:
/tmp/pn.pn
Agora, inicie o Vim com um mínimo de inicializações, apenas localizando vimrce abra os dois arquivos em viewports verticais:
$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file
Você deve ver duas viewports verticais. O arquivo de poção à esquerda exibe o final das linhas com cifrões, o arquivo aleatório à direita não os exibe.
Concentre o foco no arquivo aleatório e pressione gfpara exibir o arquivo de poção cujo caminho está sob o cursor. Agora você vê o mesmo buffer de poção na janela de exibição correta, mas desta vez o final das linhas não é exibido com cifrões. E se você digitar :setlocal list?, o Vim deverá responder com nolist:

Toda a cadeia de eventos:
BufRead event → set 'filetype' option → load filetype plugins
... não ocorreu, porque o primeiro deles, BufReadnão ocorreu quando você pressionou gf. O buffer já estava carregado.
Pode parecer inesperado, porque quando você adicionou setlocal listdentro do seu plug-in de tipo de arquivo de poção, pode ter pensado que ele habilitaria a 'list'opção em qualquer janela que exibisse um buffer de poção.
O problema não é específico para este novo potiontipo de arquivo. Você pode experimentar isso com um markdownarquivo também.
Também não é específico para a 'list'opção. Você pode experimentá-lo com outras definições da janela-local, como 'conceallevel', 'foldmethod', 'foldexpr', 'foldtitle', ...
Também não é específico para o gfcomando. Você pode experimentar com outros comandos que podem alterar o buffer exibido na janela atual: marca global, C-o(retroceder no jumplist local da janela),, :b {buffer_number}...
Para resumir, as opções locais da janela serão definidas corretamente, se e somente se:
- o arquivo não foi lido durante a sessão atual do Vim (porque
BufReadprecisará ser acionado)
- o arquivo está sendo exibido em uma janela em que as opções locais da janela já estavam definidas corretamente
- a nova janela é criada com um comando como
:split(nesse caso, ele deve herdar as opções locais da janela da janela em que o comando foi executado)
Caso contrário, as opções locais da janela podem não estar definidas corretamente.
Uma solução possível seria configurá-los não diretamente a partir de um plug-in de tipo de arquivo, mas a partir de um autocmd instalado no último, que seria escutado BufWinEnter. Este evento deve ser acionado toda vez que um buffer for exibido em uma janela.
Então, por exemplo, em vez de escrever isso:
setlocal list
Você escreveria isto:
augroup my_potion
au! * <buffer>
au BufWinEnter <buffer> setlocal list
augroup END
E aqui, você encontra novamente o padrão especial <buffer>.

Armadilha 3
Se você alterar o tipo de arquivo do seu buffer, os autocmds permanecerão. Se você deseja removê-los, é necessário configurar b:undo_ftplugin(consulte :h undo_ftplugin) e incluir este comando:
exe 'au! my_markdown * <buffer>'
No entanto, não tente remover o augroup em si, porque ainda pode haver alguns buffers de remarcação que possuem autocmds dentro dele.
FWIW, este é o trecho UltiSnips que estou usando para definir b:undo_ftplugin:
snippet undo "undo ftplugin settings" bm
" teardown {{{1
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
\ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\ ."${1:
\ setl ${2:option}<}${3:
\ | exe '${4:n}unmap <buffer> ${5:lhs}'}${6:
\ | exe 'au! ${7:group_name} * <buffer>'}${8:
\ | unlet! b:${9:variable}}${10:
\ | delcommand ${11:Cmd}}
\ "
$0
endsnippet
E aqui está um exemplo de valor que eu tenho ~/.vim/after/ftplugin/awk.vim:
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
\ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\ ."
\ setl cms< cocu< cole< fdm< fdt< tw<
\| exe 'nunmap <buffer> K'
\| exe 'au! my_awk * <buffer>'
\| exe 'au! my_awk_format * <buffer>'
\ "
Como observação, entendo por que você fez a pergunta, porque quando procurei todas as linhas em que o padrão especial <buffer>foi usado nos arquivos padrão do Vim:
:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*
Encontrei apenas 9 correspondências (você pode encontrar mais ou menos, estou usando o Vim versão 8.0, com correções de até 134). E entre as 9 correspondências, 7 estão na documentação, apenas 2 são realmente fornecidas. Você deve encontrá-los em $ VIMRUNTIME / syntax / dircolors.vim :
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
Eu não sei se isso pode causar um problema, mas eles não estão dentro de um augroup, o que significa que cada vez que você recarregar um tampão cujo filetype é dircolors(acontece se você editar um arquivo chamado .dircolors, .dir_colorsou cujas extremidades caminho com /etc/DIR_COLORS), o plug-in de sintaxe adicionará um novo autocmd local de buffer.
Você pode verificá-lo assim:
$ vim ~/.dir_colors
:au * <buffer>
O último comando deve exibir isso:
CursorHold
<buffer=1>
call s:reset_colors()
CursorHoldI
<buffer=1>
call s:reset_colors()
CursorMoved
<buffer=1>
call s:preview_color('.')
CursorMovedI
<buffer=1>
call s:preview_color('.')
Agora, recarregue o buffer e pergunte novamente quais são os autocmds locais do buffer para o buffer atual:
:e
:au * <buffer>
Desta vez, você verá:
CursorHold
<buffer=1>
call s:reset_colors()
call s:reset_colors()
CursorHoldI
<buffer=1>
call s:reset_colors()
call s:reset_colors()
CursorMoved
<buffer=1>
call s:preview_color('.')
call s:preview_color('.')
CursorMovedI
<buffer=1>
call s:preview_color('.')
call s:preview_color('.')
Após cada recarga do arquivo, s:reset_colors()e s:preview_color('.')será chamado uma vez adicional, cada vez que um dos eventos CursorHold, CursorHoldI, CursorMoved, CursorMovedIé acionado.
Provavelmente não é um grande problema, porque mesmo depois de recarregar um dircolorsarquivo várias vezes, não vi uma desaceleração perceptível ou comportamento inesperado do Vim.
Se é um problema para você, você poderia entrar em contato com o mantenedor do plugin sintaxe, mas ao mesmo tempo, se você queria evitar a duplicação de autocmds, você pode criar seu próprio plugin sintaxe para dircolorsarquivos, usando o arquivo ~/.vim/syntax/dircolors.vim. Dentro dele, você importaria o conteúdo do plug-in de sintaxe original:
$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim
Então, no último, você apenas colocaria os autocmds dentro de um augroup que você limparia. Então, você substituiria estas linhas:
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
... com estes:
augroup my_dircolors_syntax
autocmd! * <buffer>
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
augroup END
Observe que, se você criou seu dircolorsplug-in de sintaxe com o arquivo ~/.vim/after/syntax/dircolors.vim, ele não funcionaria, porque o plug-in de sintaxe padrão seria originário antes. Ao usar ~/.vim/syntax/dircolors.vim, seu plug-in de sintaxe será originado antes do padrão e definirá a variável buffer-local b:current_syntax, o que impedirá que o plug-in de sintaxe padrão seja originado porque contém esta proteção:
if exists("b:current_syntax")
finish
endif
A regra geral parece ser: use os diretórios ~/.vim/ftplugine ~/.vim/syntaxpara criar um plug-in de tipo de arquivo / sintaxe personalizado e impedir que o próximo plug-in (para o mesmo tipo de arquivo) no caminho de tempo de execução seja originado (incluindo os padrão). E uso ~/.vim/after/ftplugin, ~/.vim/after/syntaxe não para evitar que outros plugins de ser originária, mas apenas para ter a última palavra sobre o valor de alguns ajustes.