Найти тему
Блокнот математика

Рубрика "Секреты Вим". Автокоманды

Привет! Сегодня тема посложнее, и рядовому пользователю Вим, возможно, и не нужна. Но упомянуть о ней стоит. Можно использовать готовые автокоанды, и полезно хотя бы представлять, как они устроены, чтобы не нарваться на шутку, как минимум.

Оглавление рубрики

Итак, автокоманды. Это команды, которые Вим выполняет автоматически при наступлении того или иного события: список прилагается.

Мышеловка — типичная автокоманда в реальном мире! При входе объекта типа мышь закрыть дверцу.
Мышеловка — типичная автокоманда в реальном мире! При входе объекта типа мышь закрыть дверцу.

Понятно, что средство очень мощное! Что можно делать? Ну, вот пример из прошлой заметки: можно сразу перейти на последнюю позицию курсора при открытии файла. При открытии файла выполнится автокоманда перехода на метку ", в которой сохранена эта позиция (а сохранена она в файле .viminfo).

  • Можно редактировать сжатые файлы, пример в конце заметки.
  • Можно, например, просто сохранять файл в какой-нибудь recycle.bin на всякий случай.
  • Можно проверить кодировку и переключить ее, если она cp1251, например.
  • Да мало ли что можно.

К хорошему быстро привыкаешь, поэтому держите копию своего .vimrc в надежном месте, потому что в случае чего дико не хвататет настроенных удобств!

Создание, удаление и просмотр автокоманд

Создается автокоманда командой

au[tocmd] [group] event pattern [nested] cmd.

В квадратных скобках, как обычно, необязательные элементы. Имя команды можно сократить до au. Обязательны только событие event, файловый шаблон pattern и команда cmd. Команда может быть составной через конвейер | или включать вызовы функций, а там уж что угодно можно делать.

Если cmd не указывать, команда выведет список определенных автокоманд: вообще, или для данного типа файлов или события.

Автокоманды загружаются без проверки на существование. Могут загрузиться дважды! Версия autocmd! перезаписывает или удаляет автокоманду. Если указана команда cmd, перезаписывает; если нет, удаляет. Для шаблона, события, или вообще все (из данной группы! — о них далее).

События

Событий довольно много. В принципе, всё, что происходит в Вим, подпадает под то или иное событие. Изменение текста, вставка текста, отсутствие нажатия на клавишу в течение некоторого времени (!), не говоря уж об открытии/закрытии файлов, различных ошибках (файл не найден и т.п.), открытии и закрытии вкладок и т.д.

При открытии файла различаются разные варианты:

  1. BufNewFile — создан новый файл
  2. BufReadPre и BufReadPost — открытие существующего файла; одно событие ДО чтения, второе ПОСЛЕ.
  3. FilterReadPre и FilterReadPost — перенаправление входного потока (временный файл). Такое бывает в ситуациях ls | vim -
  4. FileReadPre и FileReadPost — все прочие ситуации чтения файла. Например, какой-нибудь /dev/random

Аналогично для записи, причем различаются события записи всего файла или его части.

Задание опций — это тоже события. Вход в Вим: VimEnter. Выход из Вим: до решения, надо ли выходить, перед записью файла .viminfo и после этого...

Можно отловить применение пользователем неизвестной команды.

Есть даже юмористическое событие UserGettingBored (пользователь заскучал/раздражен), которое наступает после 42 нажатий на одну и ту же клавишу подряд.

Список событий event в команде autocmd содержит имена событий, разделенные запятыми. Обычно там одно событие или два, скажем, для буфера и файла — это не одно и то же, но обычно идут в паре.

Файловые шаблоны pattern содержат файловые шаблоны через запятую: команда будет применяться только к этим файлам. Если команда от файла не зависит, ставим *. Но удобно, например, распознать, что файл — исходник tex и поменять кодировку, а с другими файлами этого делать не нужно. Шаблоны могут быть простыми, от *.tex или * до disser.tex (да, одиночный файл), а могут быть сложными, с путями, масками * и ?, классами [abc] и т.п.

Группировка

Автокоманды можно группировать. Это удобно для массового уничтожения автокоманд, например. Все команды по умолчанию принадлежат безымянной группе.

Создаем группу командой augroup, указав имя. Эта группа станет текущей. А augroup END делает текущей группой безымянную. Это удобно, чтобы удалить автокоманды перед их повторным созданием:

augroup mygroup
au!
au BufEnter * ...
augroup END

Команда au! удалит все автокоманды группы mygroup, если они есть, а потом мы создадим новые.

Доступная информация

При выполнении автокоманды вам доступно все, что и обычно, но есть кое-что еще: <afile> содержит имя файла, с которым делается то, что вызвало событие, или просто имя текущего файла (которое доступно также как %); <abuf> содержит номер буфера.

Прочее

Автокоманду можно выполнить вручную командой doautocmd.

Есть события, имена которых кончаются на Cmd. Такие команды должны сами выполнять действие, с которым связано событие: чтение, запись и т.п. Это очень опасно! Используйте только в том случае, если точно знаете, чего хотите. Но может быть очень полезно, позволяя изменить поведение Вим до неузнаваемости.

Переменная eventignore перечисляет события, которые игнорируются. А команда noautocmd cmd позволяет выполнить указанную после нее команду cmd без выполнения автокоманд.

Ключевое слово nested в определении автокоманды означает, что она допускает вложение: вызов автокоманд при выполнении автокоманды. По умолчанию этого нет, то есть если вы записываете файл при наступлении некоторого события, то событие "запись файла" не отрабатывается. Если должно, ставьте nested. вложенность ограничена 10 уровнями.

Можно даже поменять определение автокоманды при ее выполнении. Это позволяет делать одноразовые команды, которые срабатывают один раз.

Если создано несколько команд на одно и то же событие и один и тот же шаблон, они выполняются в том порядке, в каком определены.

Примеры

1. Из справки Вим: группа команд для редактирования сжатых файлов.

:augroup gzip
: autocmd!
: autocmd BufReadPre,FileReadPre *.gz set bin
: autocmd BufReadPost,FileReadPost *.gz '[,']!gunzip
: autocmd BufReadPost,FileReadPost *.gz set nobin
: autocmd BufReadPost,FileReadPost *.gz execute ":doautocmd BufReadPost " . expand("%:r")
: autocmd BufWritePost,FileWritePost *.gz !mv <afile> <afile>:r
: autocmd BufWritePost,FileWritePost *.gz !gzip <afile>:r
: autocmd FileAppendPre *.gz !gunzip <afile>
: autocmd FileAppendPre *.gz !mv <afile>:r <afile>
: autocmd FileAppendPost *.gz !mv <afile> <afile>:r
: autocmd FileAppendPost *.gz !gzip <afile>:r
:augroup END

2. Команда для восстановления позиции курсора:

augroup vimrc_au
au!
autocmd BufReadPost *
\ if line("'\"") > 0 && line("'\"") <= line("$") |
\ exe "normal g`\"" |
\ endif
augroup END

3. Из справки Вим. Создать сокращения специально для Си:

:autocmd BufEnter *.c,*.h abbr FOR for (i = 0; i < 3; ++i)<CR>{<CR>}<Esc>O
:autocmd BufLeave *.c,*.h unabbr FOR

Открыли файл на Си — создали удобное сокращение. Вышли — удалили.

4. Аналогично удобно включать замену "" на <<>> в техе, и потом удалять, потому что нигде больше это не нужно и будет мешать:

:autocmd BufEnter *.tex abbr "" <<>>
:autocmd BufLeave *.tex unabbr ""


5. А еще можно открыть шаблон вместо пустого файла:

:autocmd BufNewFile *.tex 0r ~/template.tex

6. Вот пример, как автоматически менять строку "Last Modified":

:autocmd BufWritePre,FileWritePre *.html ms|call LastMod()|'s
:fun LastMod()
: if line("$") > 20
: let l = 20
: else
: let l = line("$")
: endif
: exe "1," . l . "g/Last modified: /s/Last modified: .*/Last modified: " .
: \ strftime("%Y %b %d")
:endfun

Обратите внимание на использование метки s для запоминания позиции и возврата к ней.

Можно делать сложные вещи, например: при открытии тех-файла отыскать там строку \usepackage[...]{babel}, выяснить, какой там язык (есть ли russian) в квадратных скобках и включить соответствующую проверку орфографии.

Удачи, и будьте осторожны!

Путеводитель по каналу