Pour un autocmd dans un ftplugin, dois-je utiliser la correspondance de modèle ou <buffer>?

14

J'ai un autocmd pour les fichiers TeX et Markdown pour enregistrer le fichier automatiquement. Rien d'inhabituel:

autocmd CursorHold *.tex,*.md w

Cependant, à mesure que les paramètres personnalisés de ces fichiers augmentaient, je les ai séparés ftplugin/tex.vimet ftplugin/markdown.vim:

" ftplugin/tex.vim
autocmd CursorHold *.tex w
" ftplugin/markdown.vim
autocmd CursorHold *.md w

Maintenant, ces fichiers proviennent uniquement des fichiers appropriés, donc la correspondance de modèle est redondante. Apparemment, autocmds peut être tampon local. De :h autocmd-buffer-local:

Buffer-local autocommands are attached to a specific buffer.  They are useful
if the buffer does not have a name and when the name does not match a specific
pattern.  But it also means they must be explicitly added to each buffer.

Instead of a pattern buffer-local autocommands use one of these forms:
        <buffer>        current buffer
        <buffer=99>     buffer number 99
        <buffer=abuf>   using <abuf> (only when executing autocommands)
                        <abuf>

Cela semble être destiné à une telle utilisation. Maintenant, les deux ftplugin/tex.vimet ftplugin/markdown.vimpeuvent avoir:

autocmd CursorHold <buffer> w

Je ne suis pas vraiment préoccupé par l'extension réelle tant que le type de fichier est correct, donc cela me permet d'avoir à se soucier *.mdet *.markdownet quelles que soient les autres extensions sont valables pour Markdown.

Cette utilisation est-elle <buffer>correcte? Y a-t-il des pièges dont je devrais être conscient? Est-ce que les choses vont mal tourner si j'essuie un tampon et en ouvre un autre (j'espère que les chiffres ne se heurteront pas, mais…)?

muru
la source
1
Je suis sûr que si vous effacez un tampon, tous les autocmds de tampon local sont également effacés.
Tumbler41
@ Tumbler41 en effet. Cela dit que dans l'aide, quelques paragraphes plus bas.
muru
1
Pas exactement ce que vous avez demandé, mais up(abréviation de :update) serait mieux que wdans votre autocmd (évitez les écritures inutiles).
mMontu
@mMontu Nice. Il résout même un problème que j'ai eu lorsque l'autocmd a été activé pour un fichier que j'examinais à partir de l'historique git. Le tampon était en lecture seule et a wéchoué. À plusieurs reprises. :upne fait rien dans ce cas. :)
muru
Heureux que vous ayez aimé :) Par ailleurs, vous pouvez également trouver l'option 'autowrite' utile (en fonction de votre motivation, vous pouvez supprimer les autocmds).
mMontu

Réponses:

11

Cette utilisation de <buffer> est-elle correcte?

Je pense que c'est correct, mais vous avez juste besoin de l'envelopper dans un augroup, et effacer ce dernier, pour vous assurer que l'autocmd ne sera pas dupliqué à chaque fois que vous exécutez une commande qui recharge le même tampon.

Comme vous l'avez expliqué, le modèle spécial <buffer>vous permet de vous fier au mécanisme de détection de type de fichier intégré, implémenté à l'intérieur du fichier $VIMRUNTIME/filetype.vim.

Dans ce fichier, vous pouvez trouver les autocmds intégrés de Vim qui sont responsables de la définition du type de fichier correct pour tout tampon donné. Par exemple, pour le démarque:

" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  setf markdown

Dans votre plugin de type de fichier, vous pouvez copier les mêmes modèles pour chaque autocmd que vous installez. Par exemple, pour enregistrer automatiquement le tampon lorsque votre curseur n'a pas bougé pendant quelques secondes:

au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  update

Mais <buffer>est beaucoup moins verbeux:

au CursorHold <buffer> update

De plus, si un jour une autre extension est valide et $VIMRUNTIME/filetype.vimest mise à jour pour l'inclure, vos autocmds ne seront pas informés. Et vous devrez mettre à jour tous leurs modèles dans vos plugins de type de fichier.


Est-ce que les choses vont mal tourner si j'essuie un tampon et j'ouvre un autre (j'espère que les chiffres ne se heurteront pas, mais…)?

Je ne suis pas sûr mais je ne pense pas que Vim puisse réutiliser le numéro de tampon d'un tampon effacé. Je n'ai pas trouvé la section pertinente de l'aide, mais j'ai trouvé ce paragraphe sur vim.wikia.com :

Non. Vim ne réutilisera pas le numéro de tampon d'un tampon supprimé pour un nouveau tampon. Vim attribuera toujours le prochain numéro séquentiel pour un nouveau tampon.

De plus, comme @ Tumbler41 l'a expliqué, lorsque vous effacez un tampon, ses autocmds sont supprimés. De :h autocmd-buflocal:

Lorsqu'un tampon est effacé, ses autocommandes de tampon local ont également disparu, bien sûr.

Si vous voulez vous vérifier, vous pouvez le faire en augmentant le niveau de verbosité de Vim à 6. Vous pouvez le faire temporairement, juste pour une commande, en utilisant le :verbosemodificateur. Ainsi, à l'intérieur de votre tampon de démarque, vous pouvez exécuter:

:6verbose bwipe

Ensuite, si vous consultez les messages de Vim:

:messages

Vous devriez voir une ligne ressemblant à ceci:

auto-removing autocommand: CursorHold <buffer=42>

42était le numéro de votre tampon de démarque.


Y a-t-il des pièges dont je devrais être conscient?

Il y a 3 situations que je considérerais comme des pièges et qui impliquent le schéma spécial <buffer>. Dans deux d'entre eux, <buffer>peut être un problème, dans l'autre, c'est une solution.

Piège 1

Tout d'abord, vous devez être prudent avec la façon dont vous effacez les augroups de vos autocmds tampon-local. Vous devez être familiarisé avec cet extrait:

augroup your_group_name
    autocmd!
    autocmd Event pattern command
augroup END

Donc, vous pourriez être tenté de l'utiliser pour vos autocmds tampon-local, non modifié, comme ceci:

augroup my_markdown
    autocmd!
    autocmd CursorHold <buffer> update
augroup END

Mais cela aura un effet indésirable. La première fois que vous chargez un tampon de démarque, appelons-le A, son autocmd sera correctement installé. Ensuite, lorsque vous rechargerez A, l'autocmd sera supprimé (à cause de autocmd!) et réinstallé. Ainsi, l'augroup empêchera correctement la duplication de l'autocmd.

Supposons maintenant que vous chargiez un 2e tampon de démarque, appelons-le B, dans une 2e fenêtre. TOUS les autocmds du augroup seront effacés: c'est l'autocmd de Aet celui de B. Ensuite, un seul autocmd sera installé pour B.

Ainsi, lorsque vous apportez des modifications Bet attendez quelques secondes pour CursorHoldêtre renvoyé, il sera automatiquement enregistré. Mais si vous revenez à Aet faites la même chose, le tampon ne sera pas enregistré. En effet, la dernière fois que vous avez chargé un tampon de démarque, il y avait un déséquilibre entre ce que vous avez supprimé et ce que vous avez ajouté. Vous avez supprimé plus que ce que vous avez ajouté.

La solution est de ne pas supprimer TOUS autocommandes, mais seulement ceux du tampon courant, en passant le motif spécial <buffer>à :autocmd!:

augroup my_markdown
    autocmd! CursorHold <buffer>
    autocmd CursorHold <buffer> update
augroup END

Notez que vous pouvez remplacer CursorHoldpar une étoile pour correspondre à n'importe quel événement de la ligne qui supprime les autocmds:

augroup my_markdown
    autocmd! * <buffer>
    autocmd CursorHold <buffer> update
augroup END

De cette façon, vous n'avez pas besoin de spécifier tous les événements que vos autocmds écoutent lorsque vous souhaitez effacer le groupe.


Piège 2

Il y a un autre écueil, mais cette fois <buffer>- ci ce n'est pas le problème, c'est la solution.

Lorsque vous incluez une option locale dans un plugin de type de fichier, vous le faites probablement comme ceci:

setlocal option1=value
setlocal option2

Cela fonctionnera comme prévu pour les options locales du tampon, mais pas toujours pour les options locales de la fenêtre. Pour illustrer le problème, vous pouvez essayer l'expérience suivante. Créez le fichier ~/.vim/after/ftdetect/potion.vimet écrivez à l'intérieur:

autocmd BufNewFile,BufRead *.pn setfiletype potion

Ce fichier définira automatiquement le type potionde fichier pour tout fichier dont l'extension est .pn. Vous n'avez pas besoin de l'envelopper dans un augroup car, pour ce type de fichier particulier, Vim le fera automatiquement (voir :h ftdetect).

Si les répertoires intermédiaires n'existent pas sur votre système, vous pouvez les créer.

Ensuite, créez le plugin de type de fichier ~/.vim/after/ftplugin/potion.vim, et à l'intérieur, écrivez:

setlocal list

Par défaut, dans un potionfichier, ce paramètre entraînera l'affichage des caractères de tabulation en tant que ^Iet la fin des lignes en tant que $.

Maintenant, créez un minimum vimrc; /tmp/vimrcécrire à l' intérieur :

filetype plugin on

... pour activer les plugins de type de fichier.

Créez également un fichier de potion /tmp/pn.pnet un fichier aléatoire /tmp/file. Dans le fichier de potion, écrivez quelque chose:

foo
bar
baz

Dans le fichier aléatoire, écrivez le chemin d'accès au fichier de potion /tmp/pn.pn:

/tmp/pn.pn

Maintenant, démarrez Vim avec un minimum d'initialisations, juste en recherchant le vimrc, et ouvrez les deux fichiers dans des fenêtres verticales:

$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file

Vous devriez voir 2 fenêtres verticales. Le fichier de potion à gauche affiche la fin des lignes avec des signes dollar, le fichier aléatoire à droite ne les affiche pas du tout.

Donnez le focus au fichier aléatoire et appuyez sur gfpour afficher le fichier de potion dont le chemin se trouve sous le curseur. Vous voyez maintenant le même tampon de potion dans la fenêtre de droite, mais cette fois, la fin des lignes ne s'affiche pas avec des signes dollar. Et si vous tapez :setlocal list?, Vim devrait répondre avec nolist:

entrez la description de l'image ici

Toute la chaîne d'événements:

BufRead event → set 'filetype' option → load filetype plugins

... ne s'est pas produit, car le premier d'entre eux BufRead, ne s'est pas produit lorsque vous avez appuyé gf. Le tampon était déjà chargé.

Cela peut sembler inattendu, car lorsque vous avez ajouté à l' setlocal listintérieur de votre plugin de type de fichier de potion, vous avez peut-être pensé qu'il activerait l' 'list'option dans n'importe quelle fenêtre affichant un tampon de potion.

Le problème n'est pas spécifique à ce nouveau type de potionfichier. Vous pouvez également en faire l'expérience avec un markdownfichier.

Ce n'est pas spécifique à l' 'list'option non plus. Vous pouvez faire l' expérience avec d' autres paramètres de la fenêtre locale, comme 'conceallevel', 'foldmethod', 'foldexpr', 'foldtitle', ...

Ce n'est pas spécifique à la gfcommande non plus. Vous pouvez en faire l'expérience avec d'autres commandes qui peuvent changer le tampon affiché dans la fenêtre courante: marque globale, C-o(reculer dans la liste des fenêtres locale),, :b {buffer_number}...

Pour résumer, les options locales de la fenêtre seront correctement définies, si et seulement si:

  • le fichier n'a pas été lu pendant la session Vim en cours (car il BufReaddevra être viré)
  • le fichier est affiché dans une fenêtre où les options locales de la fenêtre étaient déjà correctement définies
  • la nouvelle fenêtre est créée avec une commande telle que :split(dans ce cas, elle doit hériter des options window-local de la fenêtre où la commande a été exécutée)

Sinon, les options locales de la fenêtre risquent de ne pas être correctement définies.

Une solution possible serait de les définir non pas directement à partir d'un plugin de type de fichier, mais à partir d'un autocmd installé dans ce dernier, qui l'écouterait BufWinEnter. Cet événement doit être déclenché chaque fois qu'un tampon est affiché dans une fenêtre.

Ainsi, par exemple, au lieu d'écrire ceci:

setlocal list

Vous écririez ceci:

augroup my_potion
    au! * <buffer>
    au BufWinEnter <buffer> setlocal list
augroup END

Et ici, vous retrouvez le motif spécial <buffer>.

entrez la description de l'image ici


Piège 3

Si vous modifiez le type de fichier de votre tampon, les autocmds resteront. Si vous souhaitez les supprimer, vous devez configurer b:undo_ftplugin(voir :h undo_ftplugin) et y inclure cette commande:

exe 'au! my_markdown * <buffer>'

Cependant, n'essayez pas de supprimer l'augroup lui-même, car il pourrait toujours y avoir des tampons de démarque contenant des autocmds.

FWIW, voici l'extrait UltiSnips que j'utilise pour définir 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

Et voici un exemple de valeur que j'ai dans ~/.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>'
                    \  "

En remarque, je comprends pourquoi vous avez posé la question, car lorsque j'ai cherché toutes les lignes où le motif spécial <buffer>était utilisé dans les fichiers par défaut de Vim:

:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*

Je n'ai trouvé que 9 correspondances (vous pouvez en trouver plus ou moins, j'utilise la version 8.0 de Vim, avec des correctifs jusqu'à 134). Et parmi les 9 correspondances, 7 sont dans la documentation, seulement 2 sont réellement originaires. Vous devriez les trouver dans $ VIMRUNTIME / syntax / dircolors.vim :

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

Je ne sais pas si cela peut causer un problème, mais ils ne sont pas à l' intérieur d' un augroup, ce qui signifie que chaque fois que vous rechargerez un tampon dont le type de fichier est dircolors(il arrive si vous modifiez un fichier nommé .dircolors, .dir_colorsou dont les extrémités de chemin avec /etc/DIR_COLORS), le plugin de syntaxe ajoutera un nouvel autocmd tampon local.

Vous pouvez le vérifier comme ceci:

$ vim ~/.dir_colors
:au * <buffer>

La dernière commande devrait afficher ceci:

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('.')

Maintenant, rechargez le tampon et demandez à nouveau quels sont les autocmds tampon-local pour le tampon actuel:

:e
:au * <buffer>

Cette fois, vous verrez:

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('.')

Après chaque rechargement du fichier, s:reset_colors()et s:preview_color('.')sera appelé un temps supplémentaire, chaque fois que l' un des cas CursorHold, CursorHoldI, CursorMoved, CursorMovedIest tiré.

Ce n'est probablement pas un gros problème, car même après avoir rechargé un dircolorsfichier plusieurs fois, je n'ai pas vu de ralentissement notable ou de comportement inattendu de Vim.

Si c'est un problème pour vous, vous pouvez contacter le responsable du plugin de syntaxe, mais en attendant, si vous souhaitez empêcher la duplication des autocmds, vous pouvez créer votre propre plugin de syntaxe pour les dircolorsfichiers, en utilisant le fichier ~/.vim/syntax/dircolors.vim. À l'intérieur, vous importeriez le contenu du plugin de syntaxe d'origine:

$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim

Ensuite, dans ce dernier, vous emballeriez simplement les autocmds dans un augroup que vous effaceriez. Donc, vous remplaceriez ces lignes:

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

... avec ceux-ci:

augroup my_dircolors_syntax
    autocmd! * <buffer>
    autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
    autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()
augroup END

Notez que si vous avez créé votre dircolorsplug-in de syntaxe avec le fichier ~/.vim/after/syntax/dircolors.vim, cela ne fonctionnerait pas, car le plug-in de syntaxe par défaut proviendrait auparavant. En utilisant ~/.vim/syntax/dircolors.vim, votre plugin de syntaxe sera sourced avant celui par défaut, et il définira la variable buffer-local b:current_syntax, ce qui empêchera le plugin de syntaxe par défaut d'être sourced car il contient cette garde:

if exists("b:current_syntax")
    finish
endif

La règle générale semble être: utiliser les répertoires ~/.vim/ftpluginet ~/.vim/syntaxpour créer un plugin de type / syntaxe de fichier personnalisé et empêcher que le prochain plugin (pour le même type de fichier) dans le chemin d'exécution soit sourcé (y compris ceux par défaut). Et l' utilisation ~/.vim/after/ftplugin, ~/.vim/after/syntaxnon pas pour empêcher les autres plug - ins d'être source, mais juste pour avoir le dernier mot sur la valeur de certains paramètres.

user852573
la source
1
J'aimerais pouvoir voter plus fort.
Rich
3
@Rich, j'ai voté plus fort pour vous. Ma seule plainte est l'absence d'un résumé "tl; dr". Des pages de détails textuels, pourtant essentielles à la compréhension, font souffrir mon âme vieillissante. Le remplacement de autocmd!with autocmd! CursorHold <buffer>in augroupblocks est un piège particulièrement critique - et aurait dû être souligné dès le départ. Néanmoins ... c'est un investissement manifestement incroyable de temps, d'efforts et de larmes sanglantes.
Cecil Curry du