Это использование <buffer> правильно?
Я думаю, что это правильно, но вам просто нужно обернуть его внутри группы и очистить последнюю, чтобы убедиться, что autocmd не будет дублироваться при каждом выполнении команды, которая перезагружает один и тот же буфер.
Как вы объяснили, специальный шаблон <buffer>
позволяет вам полагаться на встроенный механизм обнаружения типов файлов, реализованный внутри файла $VIMRUNTIME/filetype.vim
.
В этом файле вы можете найти встроенные в Vim autocmds, которые отвечают за установку правильного типа файла для любого данного буфера. Например, для уценки:
" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
Внутри вашего плагина filetype вы можете копировать одни и те же шаблоны для каждого устанавливаемого вами 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
Во-первых, вы должны быть осторожны с тем, как очищать augroups ваших локальных буферов 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 augroup будут очищены: это autocmd из A
и один из B
. Затем будет установлен одиночный 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
Таким образом, вам не нужно указывать все события, которые слушает ваш autocmds, когда вы хотите очистить группу.
Ловушка 2
Есть еще одна ловушка, но на этот раз <buffer>
это не проблема, а решение.
Когда вы включаете локальный параметр в плагин filetype, вы, вероятно, делаете это следующим образом:
setlocal option1=value
setlocal option2
Это будет работать, как и ожидалось, для локальных параметров буфера, но не всегда для локальных окон. Чтобы проиллюстрировать проблему, вы можете попробовать следующий эксперимент. Создайте файл ~/.vim/after/ftdetect/potion.vim
и внутри него напишите:
autocmd BufNewFile,BufRead *.pn setfiletype potion
Этот файл автоматически устанавливает тип potion
файла для любого файла с расширением .pn
. Вам не нужно заключать его в группу augroup, потому что для этого конкретного типа файлов Vim сделает это автоматически (см. :h ftdetect
).
Если промежуточные каталоги не существуют в вашей системе, вы можете их создать.
Затем создайте плагин filetype ~/.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
Вы должны увидеть 2 вертикальных видовых экрана. Файл зелья слева отображает конец строк со знаками доллара, случайный файл справа не отображает их вообще.
Установите фокус на случайный файл и нажмите, 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
(в этом случае оно должно наследовать локальные параметры окна от окна, в котором была выполнена команда)
В противном случае локальные параметры окна могут быть установлены неправильно.
Возможным решением было бы установить их не напрямую из плагина filetype, а из 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>'
Однако не пытайтесь удалить саму группу, потому что все еще могут быть некоторые буферы уценки, в которых есть autocmds.
Кстати, это фрагмент кода 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
and ~/.vim/syntax
для создания пользовательского подключаемого модуля типа / синтаксиса и предотвращайте выбор следующего подключаемого модуля (для того же типа файла) в пути времени выполнения (включая стандартные). И используйте ~/.vim/after/ftplugin
, ~/.vim/after/syntax
не для того, чтобы другие источники плагинов были получены, а просто чтобы иметь последнее слово в значении некоторых настроек.