W przypadku autocmd w ftplugin, czy powinienem używać dopasowania wzorca czy <bufora>?

14

Mam autocmd dla plików TeX i Markdown, aby automatycznie zapisać plik. Nic niezwykłego:

autocmd CursorHold *.tex,*.md w

Jednak, jak niestandardowe ustawienia dla tych plików wzrosła, podzielić je na ftplugin/tex.vima ftplugin/markdown.vim:

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

Teraz te pliki są pozyskiwane tylko dla odpowiednich plików, więc dopasowanie wzorca jest zbędne. Najwyraźniej autocmds może być lokalnie buforowane. Od :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>

Wydaje się, że jest to przeznaczone do takiego użycia. Teraz, zarówno ftplugin/tex.vimi ftplugin/markdown.vimmoże mieć:

autocmd CursorHold <buffer> w

Nie jestem naprawdę zaniepokojony faktycznym przedłużenie Dopóki filetype jest poprawna, więc to ratuje mnie od konieczności martwienia się o *.mdi *.markdowni cokolwiek innego rozszerzenia są ważne dla promocji cenowych.

Czy to użycie jest <buffer>prawidłowe? Czy są jakieś pułapki, o których powinienem wiedzieć? Czy sprawy się popsują, jeśli wyczyszczę bufor i otworzę inny (mam nadzieję, że liczby się nie zderzą, ale…)?

muru
źródło
1
Jestem prawie pewien, że jeśli wyczyścisz bufor, wszystkie autocmdy lokalne w buforze również zostaną wyczyszczone.
Tumbler41
@ Tumbler41 rzeczywiście. Mówi, że w pomocy kilka akapitów w dół.
muru
1
Nie do końca to, o co prosiłeś, ale up(skrót od :update) byłoby lepsze niż ww twoim autocmd (unikaj niepotrzebnych zapisów).
mMontu
@mMontu Nice. Rozwiązuje nawet problem, który miałem, gdy autocmd aktywował plik, który analizowałem z historii git. Bufor był tylko do odczytu i wnie powiódł się. Wielokrotnie. :upw tym przypadku nic nie robi. :)
muru
Cieszę się, że ci się podobało :) Nawiasem mówiąc, możesz również skorzystać z opcji „autowrite” (w zależności od motywacji możesz upuścić autocmds).
mMontu,

Odpowiedzi:

11

Czy to użycie <bufora> jest prawidłowe?

Myślę, że jest poprawny, ale wystarczy owinąć go w grupę aug i wyczyścić tę ostatnią, aby mieć pewność, że autocmd nie będzie duplikowany za każdym razem, gdy wykonasz polecenie, które ponownie ładuje ten sam bufor.

Jak wyjaśniono, specjalny wzorzec <buffer>pozwala polegać na wbudowanym mechanizmie wykrywania typu pliku, zaimplementowanym w pliku $VIMRUNTIME/filetype.vim.

W tym pliku możesz znaleźć wbudowane autocmdy Vima, które są odpowiedzialne za ustawienie poprawnego rodzaju pliku dla dowolnego bufora. Na przykład dla obniżki cen:

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

Wewnątrz wtyczki typu pliku możesz skopiować te same wzorce dla każdej instalowanej aplikacji autocmd. Na przykład, aby automatycznie zapisać bufor, gdy kursor nie poruszy się w ciągu kilku sekund:

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

Ale <buffer>jest o wiele mniej gadatliwy:

au CursorHold <buffer> update

Poza tym, jeśli któregoś dnia inne rozszerzenie będzie ważne i $VIMRUNTIME/filetype.vimzostanie zaktualizowane, aby je uwzględnić, twoje autocmds nie zostaną poinformowane. Będziesz musiał zaktualizować wszystkie ich wzorce we wtyczkach typu pliku.


Czy sprawy się popsują, jeśli wyczyszczę bufor i otworzę inny (mam nadzieję, że liczby się nie zderzą, ale…)?

Nie jestem pewien, ale nie sądzę, że Vim może ponownie użyć numeru bufora wyczyszczonego bufora. Nie mogłem znaleźć odpowiedniej sekcji z pomocy, ale znalazłem ten akapit z vim.wikia.com :

Nie. Vim nie użyje ponownie numeru bufora usuniętego dla nowego bufora. Vim zawsze przypisze następny numer kolejny do nowego bufora.

Poza tym, jak wyjaśnił @ Tumbler41 , po wyczyszczeniu bufora jego autocmds są usuwane. Od :h autocmd-buflocal:

Oczywiście, kiedy bufor zostanie wyczyszczony, jego lokalne polecenia buforowe również znikną.

Jeśli chcesz się sprawdzić, możesz to zrobić, zwiększając poziom szczegółowości Vima do 6. Możesz to zrobić tymczasowo, tylko dla jednej komendy, używając :verbosemodyfikatora. Tak więc w buforze przecen możesz wykonać:

:6verbose bwipe

Następnie, jeśli sprawdzisz wiadomości Vima:

:messages

Powinieneś zobaczyć linię wyglądającą tak:

auto-removing autocommand: CursorHold <buffer=42>

Gdzie 42był numer bufora przecen.


Czy są jakieś pułapki, o których powinienem wiedzieć?

Istnieją 3 sytuacje, które uznałbym za pułapki i które dotyczą specjalnego wzorca <buffer>. W dwóch z nich <buffer>może być problem, w drugim rozwiązanie.

Pitfall 1

Po pierwsze, powinieneś uważać na sposób, w jaki usuwasz grupy augocd z lokalnych buforów. Musisz znać ten fragment kodu:

augroup your_group_name
    autocmd!
    autocmd Event pattern command
augroup END

Można więc pokusić się o użycie go w przypadku autocmd-buforów lokalnych, niezmodyfikowanych, takich jak to:

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

Ale będzie to miało niepożądany efekt. Przy pierwszym załadowaniu bufora przeceny nazwijmy go A, jego autocmd zostanie poprawnie zainstalowany. Następnie, gdy przeładujesz A, autocmd zostanie usunięty (z powodu autocmd!) i ponownie zainstalowany. Augroup poprawnie zapobiegnie duplikowaniu programu autocmd.

Załóżmy teraz, że ładujesz drugi bufor przeceny, nazwijmy go Bw drugim oknie. Wszystkie autocmds grupy aug zostaną usunięte: to jest autocmd Ai jeden z B. Następnie zostanie zainstalowany POJEDYNCZY autocmd B.

Tak więc, kiedy wprowadzisz jakieś zmiany Bi zaczekasz kilka sekund na uruchomienie CursorHold, zostanie ono automatycznie zapisane. Ale jeśli wrócisz Ai zrobisz to samo, bufor nie zostanie zapisany. Jest tak, ponieważ podczas ostatniego ładowania bufora przeceny istniała nierównowaga między tym, co usunąłeś, a tym, co dodałeś. Usunąłeś więcej niż dodałeś.

Rozwiązaniem jest nie usuwanie WSZYSTKICH autocmds, ale tylko tych z bieżącego bufora, poprzez przekazanie specjalnego wzorca <buffer>do :autocmd!:

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

Pamiętaj, że możesz zastąpić CursorHoldgwiazdką, aby dopasować dowolne zdarzenie w wierszu, które usuwa autocmds:

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

W ten sposób nie musisz określać wszystkich zdarzeń, na które nasłuchują autorzy, gdy chcesz wyczyścić grupę aug.


Pitfall 2

Jest jeszcze jedna pułapka, ale tym razem <buffer>to nie problem, to rozwiązanie.

Po dołączeniu opcji lokalnej do wtyczki typu pliku prawdopodobnie robisz to w następujący sposób:

setlocal option1=value
setlocal option2

Będzie to działać zgodnie z oczekiwaniami dla opcji lokalnych buforów, ale nie zawsze dla opcji lokalnych okien. Aby zilustrować problem, możesz wypróbować następujący eksperyment. Utwórz plik ~/.vim/after/ftdetect/potion.vim, a w nim napisz:

autocmd BufNewFile,BufRead *.pn setfiletype potion

Ten plik automatycznie ustawi typ pliku potiondla każdego pliku, którego rozszerzenie jest .pn. Nie musisz owijać go w augroup, ponieważ dla tego rodzaju pliku Vim zrobi to automatycznie (patrz :h ftdetect).

Jeśli katalogi pośrednie nie istnieją w twoim systemie, możesz je utworzyć.

Następnie utwórz wtyczkę typu pliku ~/.vim/after/ftplugin/potion.vim, a wewnątrz niej napisz:

setlocal list

Domyślnie w potionpliku to ustawienie spowoduje wyświetlanie znaków tabulacji jako ^Ii końca linii jako $.

Teraz stwórz minimum vimrc; w środku /tmp/vimrcnapisz:

filetype plugin on

... aby włączyć wtyczki typu pliku.

Utwórz również plik mikstury /tmp/pn.pni plik losowy /tmp/file. W pliku mikstury napisz coś:

foo
bar
baz

W losowym pliku zapisz ścieżkę do pliku mikstury /tmp/pn.pn:

/tmp/pn.pn

Teraz uruchom Vima z minimum inicjalizacji, po prostu pozyskaj vimrci otwórz oba pliki w rzutniach pionowych:

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

Powinieneś zobaczyć 2 pionowe rzutnie. Plik mikstury po lewej stronie wyświetla koniec linii ze znakami dolara, losowy plik po prawej w ogóle ich nie wyświetla.

Ustaw fokus na losowym pliku i naciśnij, gfaby wyświetlić plik mikstury, którego ścieżka znajduje się pod kursorem. Teraz widzisz ten sam bufor mikstury w prawej rzutni, ale tym razem koniec linii nie jest wyświetlany ze znakami dolara. A jeśli wpiszesz :setlocal list?, Vim powinien odpowiedzieć nolist:

wprowadź opis zdjęcia tutaj

Cały łańcuch wydarzeń:

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

... nie wystąpił, ponieważ pierwszy z nich BufReadnie pojawił się po naciśnięciu gf. Bufor został już załadowany.

Może się to wydawać nieoczekiwane, ponieważ po dodaniu setlocal listdo wtyczki typu pliku mikstury możesz pomyśleć, że włączy ona 'list'opcję w dowolnym oknie wyświetlającym bufor mikstury.

Problem nie jest specyficzny dla tego nowego typu potionpliku. Możesz także doświadczyć tego z markdownplikiem.

Nie jest to również specyficzne dla 'list'opcji. Można wystąpić z innymi ustawieniami okiennych lokalnego, jak 'conceallevel', 'foldmethod', 'foldexpr', 'foldtitle', ...

Nie jest to również specyficzne dla gfpolecenia. Możesz tego doświadczyć z innymi poleceniami, które mogą zmienić bufor wyświetlany w bieżącym oknie: znak globalny, C-o(przesuń się do tyłu w lokalnej liście okien),, :b {buffer_number}...

Podsumowując, opcje lokalne dla okna zostaną poprawnie ustawione, jeśli i tylko jeśli:

  • plik nie został odczytany podczas bieżącej sesji Vima (ponieważ BufReadbędzie musiał zostać uruchomiony )
  • plik jest wyświetlany w oknie, w którym opcje lokalne dla okna zostały już poprawnie ustawione
  • nowe okno jest tworzone za pomocą polecenia, takiego jak :split(w tym przypadku powinno dziedziczyć opcje okna lokalnego z okna, w którym polecenie zostało wykonane)

W przeciwnym razie opcje lokalne dla okna mogą nie być ustawione poprawnie.

Możliwym rozwiązaniem byłoby ustawienie ich nie bezpośrednio z wtyczki typu pliku, ale z autocmd zainstalowanego w tym drugim, który będzie nasłuchiwał BufWinEnter. To zdarzenie powinno być uruchamiane za każdym razem, gdy bufor jest wyświetlany w oknie.

Na przykład zamiast pisać to:

setlocal list

Napisałbyś to:

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

I tutaj znów znajdziesz specjalny wzór <buffer>.

wprowadź opis zdjęcia tutaj


Pitfall 3

Jeśli zmienisz typ pliku bufora, autocmds pozostaną. Jeśli chcesz je usunąć, musisz skonfigurować b:undo_ftplugin(patrz :h undo_ftplugin) i dołączyć do niego następujące polecenie:

exe 'au! my_markdown * <buffer>'

Nie próbuj jednak usuwać samej grupy augroup, ponieważ nadal mogą istnieć bufory przecen, które zawierają w sobie autocmd.

FWIW, to jest fragment UltiSnips, którego używam do ustawienia 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

A oto przykład wartości, którą mam ~/.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>'
                    \  "

Na marginesie rozumiem, dlaczego zadałeś pytanie, ponieważ kiedy szukałem wszystkich wierszy, w których zastosowano specjalny wzór <buffer>w domyślnych plikach Vima:

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

Znalazłem tylko 9 dopasowań (możesz znaleźć mniej więcej, używam Vima w wersji 8.0, z łatami do 134). A spośród 9 dopasowań, 7 znajduje się w dokumentacji, tylko 2 faktycznie pochodzą. Powinieneś je znaleźć w $ VIMRUNTIME / syntax / dircolors.vim :

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

Nie wiem, czy to może powodować problem, ale nie są one wewnątrz augroup, co oznacza, że za każdym razem przeładować bufor, którego filetype jest dircolors(to się dzieje, jeśli edytować plik o nazwie .dircolors, .dir_colorslub których końce Ścieżka z /etc/DIR_COLORS), wtyczka do składni doda nowy autocmd dla bufora lokalnego.

Możesz to sprawdzić w następujący sposób:

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

Ostatnie polecenie powinno wyświetlać to:

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

Teraz ponownie załaduj bufor i ponownie zapytaj, jakie są lokalne autocmds dla bieżącego bufora:

:e
:au * <buffer>

Tym razem zobaczysz:

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

Po każdym przeładunku pliku s:reset_colors()i s:preview_color('.')będzie nazwany jeden dodatkowy czas, każdy czas imprezy CursorHold, CursorHoldI, CursorMoved, CursorMovedIzostaje zwolniony.

Prawdopodobnie nie jest to duży problem, ponieważ nawet po dircolorskilkukrotnym przeładowaniu pliku nie zauważyłem zauważalnego spowolnienia ani nieoczekiwanego zachowania Vima.

Jeśli jest to dla Ciebie problem, możesz skontaktować się z opiekunem wtyczki składni, ale w międzyczasie, jeśli chcesz zapobiec powielaniu autocmds, możesz utworzyć własną wtyczkę składni dircolorsplików, używając tego pliku ~/.vim/syntax/dircolors.vim. Wewnątrz możesz zaimportować zawartość oryginalnej wtyczki składni:

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

Następnie, w tym ostatnim, po prostu owiniesz autocmds wewnątrz grupy aug, którą wyczyścisz. Więc zastąpiłbyś te linie:

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

... z tymi:

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

Zauważ, że jeśli utworzyłeś dircolorswtyczkę składni z plikiem ~/.vim/after/syntax/dircolors.vim, to nie działałoby, ponieważ wcześniej domyślna wtyczka składniowa byłaby pozyskiwana. Korzystając z tej wtyczki, tworzona jest wtyczka składniowa ~/.vim/syntax/dircolors.vimprzed domyślną, i ustawi zmienną lokalną dla bufora b:current_syntax, co zapobiegnie źródłowej wtyczce składni, ponieważ zawiera tę ochronę:

if exists("b:current_syntax")
    finish
endif

Ogólna zasada wygląda następująco: użyj katalogów ~/.vim/ftplugini, ~/.vim/syntaxaby utworzyć niestandardową wtyczkę typu pliku / składni i zapobiec kolejnemu wtyczce (dla tego samego typu pliku) w ścieżce środowiska wykonawczego (w tym domyślnym). I używaj ~/.vim/after/ftplugin, ~/.vim/after/syntaxnie po to, aby zapobiec pozyskiwaniu innych wtyczek, ale po prostu, aby mieć ostatnie słowo na temat wartości niektórych ustawień.

użytkownik852573
źródło
1
Chciałbym móc głosować za tym mocniej.
Bogaty
3
@Rich Głosowałem za tobą mocniej. Moją jedyną skargą jest brak streszczenia „tl; dr”. Strony tekstowych drobiazgów, jakkolwiek istotne dla zrozumienia, bolą moją starzejącą się duszę, aby się przedzierać. Zastąpienie autocmd!ze autocmd! CursorHold <buffer>w augroupblokach jest szczególnie krytyczny haczyka - i powinny być podświetlone góry. Niemniej jednak ... to rażąco niesamowita inwestycja czasu, wysiłku i krwawych łez.
Cecil Curry