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.vim
for 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 :verbose
modificador. 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 42
estava 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 A
e esse de B
. Em seguida, um único autocmd será instalado para B
.
Portanto, quando você fizer alguma alteração B
e esperar alguns segundos para CursorHold
ser acionado, ele será salvo automaticamente. Mas se você voltar A
e 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 CursorHold
por 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.vim
e, dentro dele, escreva:
autocmd BufNewFile,BufRead *.pn setfiletype potion
Este arquivo definirá automaticamente o tipo potion
de 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.vim
e, dentro dele, escreva:
setlocal list
Por padrão, em um potion
arquivo, essa configuração fará com que os caracteres de tabulação sejam exibidos como ^I
e o final das linhas como $
.
Agora, crie um mínimo vimrc
; dentro de /tmp/vimrc
escrever:
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 vimrc
e 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 gf
para 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, BufRead
não ocorreu quando você pressionou gf
. O buffer já estava carregado.
Pode parecer inesperado, porque quando você adicionou setlocal list
dentro 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 potion
tipo de arquivo. Você pode experimentar isso com um markdown
arquivo 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 gf
comando. 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
BufRead
precisará 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_colors
ou 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 dircolors
arquivo 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 dircolors
arquivos, 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 dircolors
plug-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/ftplugin
e ~/.vim/syntax
para 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/syntax
e não para evitar que outros plugins de ser originária, mas apenas para ter a última palavra sobre o valor de alguns ajustes.