Questo uso di <buffer> è corretto?
Penso che sia corretto, ma devi solo avvolgerlo all'interno di un augroup e cancellare quest'ultimo, per assicurarti che autocmd non venga duplicato ogni volta che esegui un comando che ricarica lo stesso buffer.
Come spiegato, il modello speciale <buffer>
consente di fare affidamento sul meccanismo di rilevamento del tipo di file incorporato, implementato all'interno del file $VIMRUNTIME/filetype.vim
.
In questo file, puoi trovare gli autocmds integrati di Vim che sono responsabili dell'impostazione del tipo di file corretto per ogni dato buffer. Ad esempio, per il markdown:
" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
All'interno del plug-in del tipo di file, è possibile copiare gli stessi schemi per ogni autocmd che si installa. Ad esempio, per salvare automaticamente il buffer quando il cursore non si è mosso per alcuni secondi:
au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md update
Ma <buffer>
è molto meno prolisso:
au CursorHold <buffer> update
Inoltre, se un giorno un altro interno è valido e $VIMRUNTIME/filetype.vim
viene aggiornato per includerlo, i tuoi autocmds non verranno informati. E dovrai aggiornare tutti i loro pattern all'interno dei tuoi plugin di tipo file.
Le cose diventeranno confuse se pulisco un buffer e ne apro un altro (speriamo che i numeri non si scontrino, ma ...)?
Non sono sicuro ma non credo che Vim possa riutilizzare il numero di buffer di un buffer cancellato. Non sono riuscito a trovare la sezione pertinente dall'aiuto, ma ho trovato questo paragrafo da vim.wikia.com :
No. Vim non riutilizzerà il numero di buffer di un buffer eliminato per un nuovo buffer. Vim assegnerà sempre il numero sequenziale successivo per un nuovo buffer.
Inoltre, come ha spiegato @ Tumbler41 , quando si cancella un buffer, i suoi autocmds vengono rimossi. Da :h autocmd-buflocal
:
Quando un buffer viene cancellato, anche i suoi autocomandi locali del buffer scompaiono, ovviamente.
Se vuoi controllare te stesso, puoi farlo aumentando il livello di verbosità di Vim a 6. Puoi farlo temporaneamente, solo per un comando, usando il :verbose
modificatore. Quindi, all'interno del buffer di markdown, è possibile eseguire:
:6verbose bwipe
Quindi, se controlli i messaggi di Vim:
:messages
Dovresti vedere una linea simile a questa:
auto-removing autocommand: CursorHold <buffer=42>
Dov'era 42
il numero del buffer di markdown.
Ci sono delle insidie di cui dovrei essere a conoscenza?
Ci sono 3 situazioni che considererei insidie e che coinvolgono il modello speciale <buffer>
. In due di essi, <buffer>
potrebbe essere un problema, nell'altro è una soluzione.
Pitfall 1
Innanzitutto, dovresti stare attento al modo in cui cancelli i gruppi di file degli autocmds buffer-locali. Devi conoscere questo frammento:
augroup your_group_name
autocmd!
autocmd Event pattern command
augroup END
Quindi, potresti essere tentato di usarlo per i tuoi autocmds buffer-locali, non modificati, in questo modo:
augroup my_markdown
autocmd!
autocmd CursorHold <buffer> update
augroup END
Ma questo avrà un effetto indesiderato. La prima volta che carichi un buffer A
markdown, chiamiamolo , il suo autocmd verrà installato correttamente. Quindi, quando ricarichi A
, l'autocd verrà eliminato (a causa di autocmd!
) e reinstallato. Pertanto, l'augroup impedirà correttamente la duplicazione di autocmd.
Supponiamo ora di caricare un secondo buffer di markdown, chiamiamolo B
, in una seconda finestra. TUTTI gli autocmd di augroup verranno cancellati: questo è autocmd di A
e quello di B
. Quindi, verrà installato un SINGOLO autocmd per B
.
Quindi, quando effettui qualche modifica B
e attendi qualche secondo per CursorHold
essere licenziato, verrà automaticamente salvato. Ma se torni a A
fare la stessa cosa, il buffer non verrà salvato. Questo perché l'ultima volta che hai caricato un buffer markdown, c'era uno squilibrio tra ciò che hai rimosso e ciò che hai aggiunto. Hai rimosso più di quello che hai aggiunto.
La soluzione è quella di non rimuovere TUTTI gli autocmds, ma solo quelli del buffer corrente, passando lo schema speciale <buffer>
a :autocmd!
:
augroup my_markdown
autocmd! CursorHold <buffer>
autocmd CursorHold <buffer> update
augroup END
Si noti che è possibile sostituire CursorHold
con una stella per abbinare qualsiasi evento nella riga che rimuove autocmds:
augroup my_markdown
autocmd! * <buffer>
autocmd CursorHold <buffer> update
augroup END
In questo modo, non è necessario specificare tutti gli eventi a cui i vostri autocmds stanno ascoltando quando si desidera cancellare l'augroup.
Pitfall 2
C'è un'altra trappola, ma questa volta <buffer>
non è il problema, è la soluzione.
Quando includi un'opzione locale in un plug-in di tipo di file, probabilmente lo fai in questo modo:
setlocal option1=value
setlocal option2
Funzionerà come previsto per le opzioni buffer-local, ma non sempre per quelle window-local. Per illustrare il problema, puoi provare il seguente esperimento. Crea il file ~/.vim/after/ftdetect/potion.vim
e al suo interno scrivi:
autocmd BufNewFile,BufRead *.pn setfiletype potion
Questo file imposterà automaticamente il tipo di file potion
per qualsiasi file la cui estensione è .pn
. Non è necessario avvolgerlo all'interno di un augroup perché, per questo particolare tipo di file, Vim lo farà automaticamente (vedi :h ftdetect
).
Se le directory intermedie non esistono sul tuo sistema, puoi crearle.
Quindi, crea il plugin filetype ~/.vim/after/ftplugin/potion.vim
e al suo interno scrivi:
setlocal list
Per impostazione predefinita, in un potion
file, questa impostazione farà apparire i caratteri della scheda come ^I
e la fine delle righe come $
.
Ora crea un minimo vimrc
; dentro /tmp/vimrc
scrivi:
filetype plugin on
... per abilitare i plugin del tipo di file.
Inoltre, crea un file pozione /tmp/pn.pn
e un file casuale /tmp/file
. Nel file di pozioni, scrivi qualcosa:
foo
bar
baz
Nel file casuale, scrivi il percorso nel file pozione /tmp/pn.pn
:
/tmp/pn.pn
Ora, avvia Vim con un minimo di inizializzazioni, procurando semplicemente il vimrc
, e apri entrambi i file nelle finestre verticali:
$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file
Dovresti vedere 2 finestre verticali. Il file di pozione a sinistra mostra la fine delle linee con segni di dollaro, il file casuale a destra non le mostra affatto.
Dai lo stato attivo al file casuale e premi gf
per visualizzare il file di pozione il cui percorso è sotto il cursore. Ora vedi lo stesso buffer di pozioni nella vista corretta, ma questa volta la fine delle linee non viene visualizzata con i simboli del dollaro. E se digiti :setlocal list?
, Vim dovrebbe rispondere con nolist
:
L'intera catena di eventi:
BufRead event → set 'filetype' option → load filetype plugins
... non si è verificato, perché il primo di essi BufRead
, non si è verificato quando hai premuto gf
. Il buffer era già caricato.
Può sembrare inaspettato, perché quando hai aggiunto setlocal list
il plug-in del tipo di file di pozioni, potresti aver pensato che avrebbe abilitato l' 'list'
opzione in qualsiasi finestra che mostra un buffer di pozioni.
Il problema non è specifico per questo nuovo tipo di potion
file. Puoi provarlo anche con un markdown
file.
Non è nemmeno specifico per l' 'list'
opzione. Si può sperimentare con altre impostazioni della finestra locale, come 'conceallevel'
, 'foldmethod'
, 'foldexpr'
, 'foldtitle'
, ...
Non è nemmeno specifico per il gf
comando. È possibile sperimentarlo con altri comandi che possono modificare il buffer visualizzato nella finestra corrente: segno globale, C-o
(spostarsi indietro nella jumplist locale della finestra) :b {buffer_number}
, ...
Per riassumere, le opzioni locali della finestra saranno impostate correttamente, se e solo se:
- il file non è stato letto durante l'attuale sessione di Vim (perché
BufRead
dovrà essere attivato)
- il file viene visualizzato in una finestra in cui le opzioni locali della finestra erano già impostate correttamente
- la nuova finestra viene creata con un comando come
:split
(in questo caso, dovrebbe ereditare le opzioni finestra locale dalla finestra in cui è stato eseguito il comando)
Altrimenti, le opzioni locali della finestra potrebbero non essere impostate correttamente.
Una possibile soluzione sarebbe quella di impostarli non direttamente da un plug-in di tipo di file, ma da un autocmd installato in quest'ultimo, che ascolterebbe BufWinEnter
. Questo evento deve essere generato ogni volta che viene visualizzato un buffer in una finestra.
Quindi, ad esempio, invece di scrivere questo:
setlocal list
Scriveresti questo:
augroup my_potion
au! * <buffer>
au BufWinEnter <buffer> setlocal list
augroup END
E qui trovi di nuovo lo schema speciale <buffer>
.
Pitfall 3
Se si modifica il tipo di file del buffer, rimarranno gli autocmds. Se vuoi rimuoverli, devi configurare b:undo_ftplugin
(vedi :h undo_ftplugin
) e includere questo comando al suo interno:
exe 'au! my_markdown * <buffer>'
Tuttavia, non tentare di rimuovere l'ugroup stesso, perché potrebbero esserci ancora alcuni buffer di markdown che contengono autocmds al suo interno.
FWIW, questo è lo snippet UltiSnips che sto usando per impostare 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
Ed ecco un esempio di valore che ho in ~/.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>'
\ "
Come nota a margine, capisco perché hai posto la domanda, perché quando ho cercato tutte le linee in cui è <buffer>
stato utilizzato lo schema speciale nei file predefiniti di Vim:
:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*
Ho trovato solo 9 corrispondenze (potresti trovare più o meno, sto usando Vim versione 8.0, con patch fino a 134
). E tra le 9 partite, 7 sono nella documentazione, solo 2 sono effettivamente di provenienza. Dovresti trovarli in $ VIMRUNTIME / syntax / dircolors.vim :
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
Non so se può causare un problema, ma non sono all'interno di un augroup, il che significa che ogni volta che si ricarica un buffer il cui tipo di file è dircolors
(succede se si modifica un file di nome .dircolors
, .dir_colors
o le cui estremità percorso con /etc/DIR_COLORS
), il plugin di sintassi aggiungerà un nuovo autocmd buffer-local.
Puoi verificarlo in questo modo:
$ vim ~/.dir_colors
:au * <buffer>
L'ultimo comando dovrebbe visualizzare questo:
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('.')
Ora ricaricare il buffer e chiedere di nuovo quali sono gli autocmds buffer-locali per il buffer corrente:
:e
:au * <buffer>
Questa volta vedrai:
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('.')
Dopo ogni ricarico del file, s:reset_colors()
e s:preview_color('.')
si chiamerà una volta in più, ogni volta che uno degli eventi CursorHold
, CursorHoldI
, CursorMoved
, CursorMovedI
viene licenziato.
Probabilmente non è un grosso problema, perché anche dopo aver ricaricato un dircolors
file più volte, non ho visto un rallentamento evidente o un comportamento inaspettato da Vim.
Se si tratta di un problema per te, puoi contattare il manutentore del plug-in di sintassi, ma nel frattempo, se vuoi impedire la duplicazione di autocmds, puoi creare il tuo plug-in di sintassi per i dircolors
file, utilizzando il file ~/.vim/syntax/dircolors.vim
. Al suo interno verrai importato il contenuto del plugin di sintassi originale:
$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim
Quindi, in quest'ultimo caso, avresti semplicemente avvolto gli autocmds all'interno di un augroup che elimineresti. Quindi, sostituiresti queste righe:
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
... con questi:
augroup my_dircolors_syntax
autocmd! * <buffer>
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
augroup END
Nota che se hai creato il tuo dircolors
plugin di sintassi con il file ~/.vim/after/syntax/dircolors.vim
, non funzionerebbe, perché il plugin di sintassi predefinito sarebbe stato fornito in precedenza. Usando ~/.vim/syntax/dircolors.vim
, il tuo plug-in di sintassi verrà fornito prima di quello predefinito e imposterà la variabile buffer-local b:current_syntax
, che impedirà di ottenere il plug-in di sintassi predefinito perché contiene questo guard:
if exists("b:current_syntax")
finish
endif
La regola generale sembra essere: utilizzare le directory ~/.vim/ftplugin
e ~/.vim/syntax
per creare un plug-in di tipo file / sintassi personalizzato e impedire che il plug-in successivo (per lo stesso tipo di file) nel percorso di runtime venga fornito (inclusi quelli predefiniti). E usa ~/.vim/after/ftplugin
, ~/.vim/after/syntax
non per impedire la provenienza di altri plugin, ma solo per avere l'ultima parola sul valore di alcune impostazioni.