Чи потрібно використовувати autocmd у ftplugin, чи відповідність шаблонів чи <buffer>?


14

У мене є autocmd для файлів TeX і Markdown, щоб автоматично зберігати файл. Нічого незвичайного:

autocmd CursorHold *.tex,*.md w

Однак, як призначені для користувача настройки для цих файлів збільшуються, я розділив їх на ftplugin/tex.vimі ftplugin/markdown.vim:

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

Тепер ці файли отримуються лише для відповідних файлів, тому відповідність шаблонів є зайвою. Мабуть, autocmds може бути буферно-локальним. Від :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>

Це, мабуть, призначене для такого використання. Тепер і те, ftplugin/tex.vimі інше ftplugin/markdown.vim:

autocmd CursorHold <buffer> w

Я не дуже стурбований фактичне розширення до тих пір , як Filetype правильно, так що це позбавляє мене від необхідності турбуватися про *.mdі *.markdownі будь-яких інших розширеннях дійсні для Markdown.

Це <buffer>правильне використання ? Чи є якісь підводні камені, про які я маю знати? Чи стануть брудні речі, якщо я витерю буфер і відкрию інший (сподіваюся, цифри не зіткнуться, але ...)?


1
Я впевнений, що якщо ви стерте буфер, будь-які автоматичні локальні буфери також будуть витерти.
Tumbler41

@ Tumbler41 справді. Це говорить про те, що в довідці кілька пунктів вниз.
muru

1
Не зовсім те, про що ви запитували, але up(скорочується :update) було б краще, ніж wу вашому autocmd (уникайте зайвих записів).
mMontu

@mMontu Nice. Це навіть вирішує проблему, яку я мав, коли autocmd активувався для файлу, який я вивчав з історії git. Буфер було прочитано лише та wвийшов з ладу. Неодноразово. :upв цьому випадку нічого не робить. :)
muru

Радий, що вам сподобалось :) До речі, ви також можете знайти корисний варіант "автозаписати" (залежно від вашої мотивації, ви можете скинути autocmds).
mMontu

Відповіді:


11

Чи правильне використання <buffer> правильного?

Я думаю, що це правильно, але вам просто потрібно загорнути його всередину augroup і очистити останню, щоб переконатися, що autocmd не буде дублюватися кожного разу, коли ви виконуєте команду, яка перезавантажує той самий буфер.

Як ви пояснили, спеціальна схема <buffer>дозволяє покластися на вбудований механізм виявлення файлів, реалізований всередині файлу $VIMRUNTIME/filetype.vim.

У цьому файлі ви можете знайти вбудовані autocmds Vim, які відповідають за встановлення правильного файлу для будь-якого буфера. Наприклад, для розмітки:

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

Всередині вашого модуля файлів ви можете скопіювати однакові шаблони для кожного встановленого autocmd. Наприклад, щоб автоматично зберегти буфер, коли курсор не перемістився протягом декількох секунд:

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

Але <buffer>чи менш дослівно:

au CursorHold <buffer> update

Крім того, якщо якийсь день діє інше розширення та $VIMRUNTIME/filetype.vimоновиться для його включення, ваші autocmds не будуть повідомлені. І вам доведеться оновити всі їх візерунки всередині ваших плагінів файлів.


Чи стануть брудні речі, якщо я витерю буфер і відкрию інший (сподіваюся, цифри не зіткнуться, але ...)?

Я не впевнений, але не думаю, що Vim може повторно використовувати номер буфера стертого буфера. Я не зміг знайти відповідний розділ у довідці, але я знайшов цей пункт на сайті vim.wikia.com :

Ні. Vim не повторно використовувати номер буфера видаленого буфера для нового буфера. Vim завжди присвоює наступний послідовний номер для нового буфера.

Крім того, як пояснив @ Tumbler41 , коли ви стираєте буфер, його autocmds видаляються. Від :h autocmd-buflocal:

Коли буфер видаляється, його локальні автокоманди також втрачаються, звичайно.

Якщо ви хочете перевірити себе, ви можете зробити це, збільшивши рівень багатомовності Vim до 6. Ви можете це зробити тимчасово, лише за одну команду, використовуючи :verboseмодифікатор. Отже, всередині вашого буфера відмітки ви можете виконати:

:6verbose bwipe

Потім, якщо ви перевірите повідомлення Vim:

:messages

Ви повинні побачити такий рядок:

auto-removing autocommand: CursorHold <buffer=42>

Де 42була номер вашого буфера відмітки.


Чи є якісь підводні камені, про які я маю знати?

Є 3 ситуації, які я б розглядав як підводні камені і які передбачають особливий зразок <buffer>. У двох з них це <buffer>може бути проблемою, в іншому - рішенням.

Падіння 1

По-перше, ви повинні бути обережними з тим, як очистити аугрупи своїх буферних локальних autocmds. Ви повинні бути знайомі з цим фрагментом:

augroup your_group_name
    autocmd!
    autocmd Event pattern command
augroup END

Отже, ви можете спокуситись використовувати його для локальних autocmds буфера, незмінених, як це:

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

Але це матиме небажаний ефект. Перший раз, коли ви завантажуєте буфер Aрозмітки , назвемо його , його autocmd буде правильно встановлено. Потім, коли ви перезавантажите A, autocmd буде видалений (через autocmd!) і знову встановлений. Отже, augroup правильно запобіжить дублювання autocmd.

Тепер, припустимо, ви завантажуєте другий буфер розмітки, давайте назвемо його B, у другому вікні. ВСІ autocmds аугрупи будуть очищені: це autocmd Aта one of B. Потім буде встановлено SINGLE autocmd для B.

Отже, коли ви внесете певні зміни Bта зачекаєте кілька секунд, CursorHoldщоб вас звільнили, це буде автоматично збережено. Але якщо ви повернетесь Aі зробите те саме, буфер не буде збережено. Це тому, що востаннє, коли ви завантажували буфер відмітки, спостерігався дисбаланс між тим, що ви видалили, і тим, що ви додали. Ви видалили більше, ніж додали.

Рішення полягає в тому, щоб не видалити ВСЕ autocmds, але тільки ті з поточного буфера, передаючи спеціальний шаблон <buffer>для :autocmd!:

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

Зауважте, що ви можете замінити CursorHoldзірочку, щоб відповідати будь-якій події в рядку, який видаляє autocmds:

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

Таким чином, вам не потрібно вказувати всі події, до яких слухаються ваші автосмс, коли ви хочете очистити аугруппу.


Падіння 2

Є ще одна помилка, але на цей раз <buffer>це не питання, це рішення.

Коли ви додаєте локальний параметр у плагін файлу, ви, ймовірно, робите це так:

setlocal option1=value
setlocal option2

Це буде працювати, як очікувалося, для локальних буферних параметрів, але не завжди для локальних вікон. Для ілюстрації проблеми можна спробувати наступний експеримент. Створіть файл ~/.vim/after/ftdetect/potion.vim, а всередині нього напишіть:

autocmd BufNewFile,BufRead *.pn setfiletype potion

Цей файл автоматично встановить тип файлу potionдля будь-якого файлу, розширенням якого є .pn. Вам не потрібно загортати його всередину augroup, тому що для цього конкретного типу файл Vim зробить це автоматично (див. :h ftdetect).

Якщо проміжних каталогів у вашій системі немає, ви можете їх створити.

Далі створіть плагін файлу ~/.vim/after/ftplugin/potion.vim, а всередині нього напишіть:

setlocal list

За замовчуванням у potionфайлі цей параметр призведе до того, що символи вкладки відображатимуться як, ^Iа кінці рядків як $.

Тепер створіть мінімум vimrc; всередині /tmp/vimrcнапишіть:

filetype plugin on

... щоб увімкнути плагіни файлів.

Також створіть файл зілля /tmp/pn.pnта довільний файл /tmp/file. У файл зілля напишіть щось:

foo
bar
baz

У випадковий файл запишіть шлях до файлу зілля /tmp/pn.pn:

/tmp/pn.pn

Тепер почніть Vim з мінімальними ініціалізаціями, просто знайдіть vimrcі відкрийте обидва файли у вертикальних вікнах перегляду:

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

Ви повинні побачити два вертикальних вікна перегляду. Файл зілля зліва відображає кінець рядків із знаками долара, випадковий файл праворуч взагалі не відображає їх.

Наведіть фокус на випадковий файл і натисніть, gfщоб відобразити файл зілля, шлях якого знаходиться під курсором. Тепер ви бачите той самий буфер зілля у правій області перегляду, але цього разу кінець рядків не відображається із знаками долара. А якщо ви введете :setlocal list?, Vim повинен відповісти nolist:

введіть тут опис зображення

Весь ланцюжок подій:

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

... не сталося, тому що перший із них BufReadне відбувся при натисканні gf. Буфер вже завантажений.

Це може здатися несподіваним, оскільки, додавши setlocal listвсередину плагін філейного зілля, ви, можливо, подумали, що він включить 'list'параметр у будь-якому вікні, де відображається буфер зілля.

Питання не характерне для цього нового potionфайлетипу. Ви можете також відчути це з markdownфайлом.

Це також не є специфічним для 'list'варіанту. Ви можете випробувати його з іншого віконної локальної налаштуванням, як 'conceallevel', 'foldmethod', 'foldexpr', 'foldtitle', ...

Це не характерно і для gfкоманди. Ви можете відчути це з іншими командами, які можуть змінити буфер, відображений у поточному вікні: глобальна позначка, C-o(переміщення назад у вікні переліку вікон),, :b {buffer_number}...

Підводячи підсумок, параметри локальних вікон будуть правильно встановлені, якщо і лише якщо:

  • файл не був прочитаний під час поточного сеансу Vim (тому що BufReadйого потрібно буде випустити)
  • файл відображається у вікні, де параметри локальних вікон вже були правильно встановлені
  • нове вікно створюється з такою командою, як :split(у цьому випадку воно має успадкувати параметри локального вікна з вікна, де виконувалася команда)

Інакше параметри локальних вікон можуть бути неправильно встановлені.

Можливим рішенням було б встановити їх не безпосередньо з плагіну філейного типу, а з встановленого в останньому autocmd, який би слухав BufWinEnter. Цю подію потрібно запускати кожного разу, коли буфер відображається у вікні.

Так, наприклад, замість цього писати:

setlocal list

Ви б написали це:

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

І тут ви знову знайдете особливий візерунок <buffer>.

введіть тут опис зображення


Падіння 3

Якщо ви зміните тип файлу буфера, autocmds залишиться. Якщо ви хочете їх видалити, вам потрібно налаштувати b:undo_ftplugin(див. :h undo_ftplugin) І включити в нього цю команду:

exe 'au! my_markdown * <buffer>'

Однак не намагайтеся видалити саму augroup, оскільки все ще можуть бути буфери для розмітки, у яких всередині є autocmds.

FWIW, це фрагмент UltiSnips, який я використовую для встановлення 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

Ось приклад цінності, яку я маю ~/.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>'
                    \  "

Як зауваження, я розумію, чому ви задали це питання, тому що коли я шукав усі рядки, де використовується спеціальний шаблон <buffer>у файлах за замовчуванням Vim:

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

Я знайшов лише 9 збігів (ви можете знайти більше чи менше, я використовую Vim версії 8.0, з виправленнями до 134). І серед 9 матчів 7 - у документації, лише 2 - насправді. Ви повинні знайти їх у $ VIMRUNTIME / syntax / dircolors.vim :

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

Я не знаю , якщо це може викликати проблеми, але вони не всередині augroup, що означає кожен раз , коли ви перезавантажувати буфер якого є Filetype dircolors(це відбувається , якщо ви редагуєте файл з ім'ям .dircolors, .dir_colorsабо чий шлях кінці з /etc/DIR_COLORS), синтаксичний плагін додасть новий буфер-локальний autocmd.

Ви можете перевірити так:

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

Остання команда повинна відображати це:

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

Тепер перезавантажте буфер і знову запитайте, що таке локальні autocmds для поточного буфера:

:e
:au * <buffer>

Цього разу ви побачите:

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

Після кожної перезавантаження файлу, s:reset_colors()і s:preview_color('.')буде називатися ще один раз, кожен раз , коли один з події CursorHold, CursorHoldI, CursorMoved, CursorMovedIобпалюють.

Це, мабуть, не є великою проблемою, тому що навіть після dircolorsдекількох завантажень файлу я не бачив помітного уповільнення або несподіваної поведінки від Vim.

Якщо це проблема для вас, ви можете зв’язатися з обслуговуючим модулем синтаксису, але тим часом, якщо ви хочете запобігти дублюванню autocmds, ви можете створити власний синтаксичний плагін для dircolorsфайлів, використовуючи файл ~/.vim/syntax/dircolors.vim. Всередині нього ви імпортуєте вміст вихідного синтаксичного плагіна:

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

Потім, в останньому, ви просто загорнете autocmds всередину augroup, яку ви очистите. Отже, ви б замінили ці рядки:

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

... з цими:

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

Зауважте, що якщо ви створили свій dircolorsсинтаксичний плагін з файлом ~/.vim/after/syntax/dircolors.vim, він не працюватиме, тому що плагін синтаксису за замовчуванням був би використаний раніше. Використовуючи ~/.vim/syntax/dircolors.vimваш синтаксичний плагін, буде встановлено раніше за замовчуванням, і він встановить локальну змінну буфера b:current_syntax, що запобіжить виведенню модуля синтаксису за замовчуванням, оскільки він містить цей захист:

if exists("b:current_syntax")
    finish
endif

Загальним правилом здається: використовувати каталоги ~/.vim/ftpluginта ~/.vim/syntaxкаталоги для створення користувальницького модуля файлів / синтаксису та запобігати появі наступного плагіна (для того ж типу файлів) у шляху виконання (включаючи типові). І використовуйте ~/.vim/after/ftplugin, ~/.vim/after/syntaxне для того, щоб не використовувати інші плагіни, а просто мати останнє слово про значення деяких налаштувань.


1
Я б хотів, щоб я міг посилити цю заяву.
Багатий

3
@Rich Я більш важко підтримав це. Моя єдина скарга - відсутність резюме "tl; dr". Сторінки текстових дрібниць, як би життєво важливі для розуміння, болять моєї старечої душі. Заміна autocmd!з autocmd! CursorHold <buffer>в augroupблоках є особливо важливим Гоча - і повинні були бути виділені заздалегідь. Тим не менш ... це очевидно дивовижна інвестиція часу, зусиль і кривавих сліз.
Сесіль Карі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.