¿Es correcto este uso de <buffer>?
Creo que es correcto, pero solo necesita envolverlo dentro de un grupo de aug, y borrar el último, para asegurarse de que el autocmd no se duplicará cada vez que ejecute un comando que recargue el mismo búfer.
Como explicó, el patrón especial le <buffer>
permite confiar en el mecanismo de detección de tipo de archivo incorporado, implementado dentro del archivo $VIMRUNTIME/filetype.vim
.
En este archivo, puede encontrar los autocmds integrados de Vim que son responsables de establecer el tipo de archivo correcto para cualquier búfer dado. Por ejemplo, para rebajas:
" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
Dentro de su complemento de tipo de archivo, puede copiar los mismos patrones para cada autocmd que instale. Por ejemplo, para guardar automáticamente el búfer cuando el cursor no se ha movido durante unos segundos:
au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md update
Pero <buffer>
es mucho menos detallado:
au CursorHold <buffer> update
Además, si algún día otra extensión es válida y $VIMRUNTIME/filetype.vim
se actualiza para incluirla, sus autocmds no serán informados. Y tendrá que actualizar todos sus patrones dentro de sus complementos de tipo de archivo.
¿Se complicarán las cosas si borro un búfer y abro otro (espero que los números no choquen, pero ...)?
No estoy seguro, pero no creo que Vim pueda reutilizar el número de búfer de un búfer borrado. No pude encontrar la sección relevante de la ayuda, pero encontré este párrafo en vim.wikia.com :
No. Vim no reutilizará el número de búfer de un búfer eliminado para un nuevo búfer. Vim siempre asignará el siguiente número secuencial para un nuevo búfer.
Además, como explicó @ Tumbler41 , cuando limpia un búfer, se eliminan sus autocmds. De :h autocmd-buflocal
:
Cuando se borra un búfer, sus autocomandos locales de búfer también desaparecen, por supuesto.
Si desea comprobarlo usted mismo, puede hacerlo aumentando el nivel de verbosidad de Vim a 6. Puede hacerlo temporalmente, solo para un comando, utilizando el :verbose
modificador. Entonces, dentro de su búfer de reducción, puede ejecutar:
:6verbose bwipe
Luego, si revisas los mensajes de Vim:
:messages
Deberías ver una línea como esta:
auto-removing autocommand: CursorHold <buffer=42>
¿Dónde 42
estaba el número de su búfer de descuento?
¿Hay alguna dificultad que deba tener en cuenta?
Hay 3 situaciones que consideraría como escollos y que involucran el patrón especial <buffer>
. En dos de ellos, <buffer>
puede ser un problema, en el otro es una solución.
Trampa 1
Primero, debe tener cuidado con la forma en que borra los grupos de sus autocmds de búfer local. Debe estar familiarizado con este fragmento:
augroup your_group_name
autocmd!
autocmd Event pattern command
augroup END
Por lo tanto, podría tener la tentación de usarlo para sus autocmds locales de búfer, sin modificar, como este:
augroup my_markdown
autocmd!
autocmd CursorHold <buffer> update
augroup END
Pero esto tendrá un efecto no deseado. La primera vez que cargue un búfer de A
reducción, llamémoslo, su autocmd se instalará correctamente. Luego, cuando vuelva a cargar A
, el autocmd se eliminará (debido a autocmd!
) y se volverá a instalar. Entonces, el augroup evitará correctamente la duplicación del autocmd.
Ahora, suponga que carga un segundo búfer de reducción, llamémoslo B
, en una segunda ventana. TODOS los autocmds del augroup se borrarán: ese es el autocmd de A
y el de B
. Luego, se instalará un solo autocmd para B
.
Por lo tanto, cuando realice algún cambio B
y espere unos segundos para CursorHold
que se dispare, se guardará automáticamente. Pero si vuelve A
y hace lo mismo, el búfer no se guardará. Esto se debe a que la última vez que cargó un búfer de reducción, hubo un desequilibrio entre lo que eliminó y lo que agregó. Eliminaste más de lo que agregaste.
La solución es no eliminar TODOS los autocmds, sino solo los del búfer actual, pasando el patrón especial <buffer>
a :autocmd!
:
augroup my_markdown
autocmd! CursorHold <buffer>
autocmd CursorHold <buffer> update
augroup END
Tenga en cuenta que puede reemplazar CursorHold
con una estrella para que coincida con cualquier evento en la línea que elimine autocmds:
augroup my_markdown
autocmd! * <buffer>
autocmd CursorHold <buffer> update
augroup END
De esta manera, no tiene que especificar todos los eventos a los que están escuchando sus autocmds cuando desea borrar el augroup.
Pitfall 2
Hay otra trampa, pero esta vez <buffer>
no es el problema, es la solución.
Cuando incluye una opción local en un complemento de tipo de archivo, probablemente lo haga así:
setlocal option1=value
setlocal option2
Esto funcionará como se espera para las opciones locales de búfer, pero no siempre para las locales de ventana. Para ilustrar el problema, puede intentar el siguiente experimento. Crea el archivo ~/.vim/after/ftdetect/potion.vim
, y dentro de él escribe:
autocmd BufNewFile,BufRead *.pn setfiletype potion
Este archivo configurará automáticamente el tipo potion
de archivo para cualquier archivo cuya extensión sea .pn
. No necesita envolverlo dentro de un augroup porque, para este tipo particular de archivo, Vim lo hará automáticamente (ver :h ftdetect
).
Si los directorios intermedios no existen en su sistema, puede crearlos.
A continuación, cree el complemento de tipo de archivo ~/.vim/after/ftplugin/potion.vim
y, dentro, escriba:
setlocal list
De manera predeterminada, en un potion
archivo, esta configuración hará que los caracteres de tabulación se muestren como ^I
y el final de las líneas como $
.
Ahora, crea un mínimo vimrc
; /tmp/vimrc
escritura interior :
filetype plugin on
... para habilitar los complementos de tipo de archivo.
Además, cree un archivo de pociones /tmp/pn.pn
, y un archivo aleatorio /tmp/file
. En el archivo de pociones, escribe algo:
foo
bar
baz
En el archivo aleatorio, escriba la ruta al archivo de la poción /tmp/pn.pn
:
/tmp/pn.pn
Ahora, inicie Vim con un mínimo de inicializaciones, solo obtenga vimrc
y abra ambos archivos en ventanas verticales:
$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file
Debería ver 2 ventanas verticales. El archivo de pociones a la izquierda muestra el final de las líneas con signos de dólar, el archivo aleatorio a la derecha no los muestra en absoluto.
Centra la atención en el archivo aleatorio y presiona gf
para mostrar el archivo de pociones cuya ruta está debajo del cursor. Ahora verá el mismo búfer de pociones en la ventana gráfica correcta, pero esta vez, el final de las líneas no se muestra con signos de dólar. Y si escribe :setlocal list?
, Vim debería responder con nolist
:
Toda la cadena de eventos:
BufRead event → set 'filetype' option → load filetype plugins
... no ocurrió, porque el primero de ellos, BufRead
no ocurrió cuando presionaste gf
. El búfer ya estaba cargado.
Puede parecer inesperado, porque cuando agregó setlocal list
dentro de su plugin de tipo de archivo de poción, puede haber pensado que habilitaría la 'list'
opción en cualquier ventana que muestre un búfer de poción.
El problema no es específico de este nuevo tipo de potion
archivo. También puedes experimentarlo con un markdown
archivo.
Tampoco es específico de la 'list'
opción. Se puede experimentar con otras configuraciones de ventana local, como 'conceallevel'
, 'foldmethod'
, 'foldexpr'
, 'foldtitle'
, ...
Tampoco es específico del gf
comando. Puede experimentarlo con otros comandos que pueden cambiar el búfer que se muestra en la ventana actual: marca global, C-o
(retroceder en el jumplista local de la ventana) :b {buffer_number}
, ...
Para resumir, las opciones locales de la ventana se establecerán correctamente, si y solo si:
- el archivo no ha sido leído durante la sesión actual de Vim (porque
BufRead
tendrá que ser despedido)
- el archivo se muestra en una ventana donde las opciones locales de la ventana ya estaban configuradas correctamente
- la nueva ventana se crea con un comando como
:split
(en este caso, debe heredar las opciones locales de la ventana donde se ejecutó el comando)
De lo contrario, las opciones locales de la ventana pueden no estar configuradas correctamente.
Una posible solución sería establecerlos no directamente desde un complemento de tipo de archivo, sino desde un autocmd instalado en este último, que escucharía BufWinEnter
. Este evento debe activarse cada vez que se muestra un búfer en una ventana.
Entonces, por ejemplo, en lugar de escribir esto:
setlocal list
Escribirías esto:
augroup my_potion
au! * <buffer>
au BufWinEnter <buffer> setlocal list
augroup END
Y aquí, encuentras de nuevo el patrón especial <buffer>
.
Trampa 3
Si cambia el tipo de archivo de su búfer, los autocmds permanecerán. Si desea eliminarlos, debe configurar b:undo_ftplugin
(ver :h undo_ftplugin
) e incluir este comando en él:
exe 'au! my_markdown * <buffer>'
Sin embargo, no intente eliminar el augroup en sí, porque aún podría haber algunos buffers de rebajas que tienen autocmds en su interior.
FWIW, este es el fragmento de UltiSnips que estoy usando para configurar 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
Y aquí hay un ejemplo de valor que tengo en ~/.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 nota al margen, entiendo por qué hizo la pregunta, porque cuando busqué todas las líneas donde <buffer>
se utilizó el patrón especial en los archivos predeterminados de Vim:
:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*
Solo encontré 9 coincidencias (puede encontrar más o menos, estoy usando Vim versión 8.0, con parches hasta 134
). Y entre las 9 coincidencias, 7 están en la documentación, solo 2 son de origen. Debería encontrarlos en $ VIMRUNTIME / syntax / dircolors.vim :
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
No sé si puede causar un problema, pero no están dentro de un augroup, lo que significa que cada vez que se vuelve a cargar un búfer cuyo tipo de archivo es dircolors
(sucede si edita un archivo llamado .dircolors
, .dir_colors
o cuyos extremos trayectoria con /etc/DIR_COLORS
), el complemento de sintaxis agregará un nuevo autocmd local de búfer.
Puedes verificarlo así:
$ vim ~/.dir_colors
:au * <buffer>
El último comando debería mostrar esto:
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('.')
Ahora, vuelva a cargar el búfer y vuelva a preguntar cuáles son los autocmds locales del búfer para el búfer actual:
:e
:au * <buffer>
Esta vez, verás:
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('.')
Después de cada carga de los archivos, s:reset_colors()
y s:preview_color('.')
será llamado una vez adicional, cada vez que uno de los eventos CursorHold
, CursorHoldI
, CursorMoved
, CursorMovedI
es despedido.
Probablemente no sea un gran problema, porque incluso después de volver a cargar un dircolors
archivo varias veces, no vi una desaceleración notable o un comportamiento inesperado de Vim.
Si es un problema para usted, puede contactar al responsable del complemento de sintaxis, pero mientras tanto, si desea evitar la duplicación de autocmds, puede crear su propio complemento de sintaxis para los dircolors
archivos, utilizando el archivo ~/.vim/syntax/dircolors.vim
. Dentro, importaría el contenido del complemento de sintaxis original:
$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim
Luego, en este último, simplemente envolvería los autocmds dentro de un augroup que borraría. Entonces, reemplazarías estas líneas:
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
... con estos:
augroup my_dircolors_syntax
autocmd! * <buffer>
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
augroup END
Tenga en cuenta que si creó su dircolors
complemento de sintaxis con el archivo ~/.vim/after/syntax/dircolors.vim
, no funcionaría, porque el complemento de sintaxis predeterminado se obtendría antes. Al usarlo ~/.vim/syntax/dircolors.vim
, su complemento de sintaxis se generará antes que el predeterminado, y establecerá la variable local del búfer b:current_syntax
, lo que evitará que se obtenga el complemento de sintaxis predeterminado porque contiene esta protección:
if exists("b:current_syntax")
finish
endif
La regla general parece ser: use los directorios ~/.vim/ftplugin
y ~/.vim/syntax
para crear un complemento de tipo de archivo / sintaxis personalizado y evitar que se obtenga el siguiente complemento (para el mismo tipo de archivo) en la ruta de tiempo de ejecución (incluidos los predeterminados). Y use ~/.vim/after/ftplugin
, ~/.vim/after/syntax
no para evitar que se obtengan otros complementos, sino solo para tener la última palabra sobre el valor de algunas configuraciones.