Я уже восемь лет работаю в Vim и постоянно открываю что-то новое.
Принято считать это достоинством Vim. Как по мне, так это недостаток
открытости: куча скрытых функций спрятаны слишком глубоко.
Вот говорят о красоте модального редактирования и текстовых объектах, но
мне кажется, что суть Vim не в этом. Vim — это лоскутное одеяло из
подсистем, под завязку забитых дополнительными инструментами. Только в
обычном режиме редактирования более сотни комбинаций клавиш! Такая
плотность инструментария в значительной степени объясняет, почему Vim
настолько полезен. Если «показать все теги для ключевого слова» — это
просто g], то этой командой будут пользоваться гораздо чаще.
В системах с недостатком открытости приходится полагаться на
руководства. Но для Vim их не так уж много. Есть статьи для новичков,
такие как ciw (не путать с CIA, мануалом ЦРУ по Vim)
и тому подобное. И есть статьи экспертов, которые погружаются в
подсистемы. Но никто на самом деле не говорит об этих особых трюках,
которые заставляют воскликнуть: чёрт побери, как мне это было нужно в
течение последних шести лет!
Эта статья о некоторых маленьких трюках, которые я использую в Vim. Ни
один из них не разобран во всех деталях, так что если что-то
заинтересовало, рекомендую покопать дополнительную информацию. Они также
не связаны друг с другом. Но это нормально. В общем, их более чем
достаточно, чтобы реально помочь практически каждому.
Структура статьи
Очень грубо, пользователи Vim делятся на две категории. Пуристы
ценят небольшие размеры и вездесущность. Как правило, они сводят
конфигурацию к минимуму на случай, если придётся работать на незнакомом
компьютере (например, по ssh). С другой стороны, расширяльщики
наполняют Vim плагинами, функциями и доморощенными сопоставлениями в
тщетной попытке притвориться, что они используют Emacs. Если забрать у
них vimrc, то ребята останутся совершенно беспомощными.
Как вы, наверное, догадываетесь, мне гораздо ближе расширяльщики, чем
пуристы. Я разделил трюки на два раздела в зависимости от того,
требуются ли изменения в базовом Vim.
Пуристы
Для модальных команд используются стандартные представления из справки, т. е. <cr> означает нажатие клавиши Enter. Когда нужно получить справку :h для определённой строки, например, :h E676, то строка будет в скобках.
Различные команды в обычном режиме
«: и @:
": является регистром, хранящим последнюю выполненную команду. Вы можете набрать ":p, чтобы распечатать его в буфер. @:повторяет последнюю команду.
«=
Регистр для «выражений». Здесь вы можете ввести любое выражение vimL и
вставить его, использовать с ctrl-R и т. д. Таким образом, например,
локальная метка времени вставляется вводом "=strftime("%c")<cr>p.
mA, ‘A
m{letter} устанавливает метку на месте курсора. Тогда '{letter}
перейдёт к этой строке. Для строчных букв действует в пределах на
буфера, поэтому подходит для навигации. Для прописных букв действует
глобально: даже если вы находитесь в другом файле, 'A перейдёт к файлу с меткой А. Можете посмотреть все свои метки командой :marks:.
ctrl-A и ctrl-X
Увеличивает и уменьшает следующее число в строке на месте курсора или
справа от него. Поскольку сразу переходит к числу, то сочетание можно
использовать из любого места. 10c-A намного проще, чем wwwwwciw20.
q:
Открывает историю предыдущих команд. Можете работать с ней как с любым
текстом Vim, но изменения не сохраняются. Однако вы можете запустить
изменённую команду с помощью <CR>. Это позволяет очень быстро изменять и перезапускать команды или искать старые для повторного использования.
q/, q?
То же, что и q:, за исключением поиска.
ctrl-I, ctrl-O
Перемещает к следующему или предыдущему местоположению в джамплисте.
Полезно для быстрой проверки, а затем возврата назад. Очень приятно
читать файлы справки.
Макросы
См. этот пост для глубокого погружения в использование макросов.
Визуальный режим
gv
Выбирает предыдущий визуальный элемент.
v_o
Переходит на другую сторону визуального блока. Полезно, если вы начали
одну строку слишком низко или что-нибудь такое. В блочном режиме
переходит в противоположный угол по диагонали, а для перехода в
противоположный угол по горизонтали используйте v_O.
g ctrl-A / ctrl-X
В визуальном режиме ctrl-A просто увеличивает первое число на каждой строке. С другой стороны, g ctrl-A будет с каждой строкой наращивать увеличение на единицу. Это намного проще объяснить в таблице:
| выбрано | ctrl-A | g ctrl-A | 2 g ctrl-A |
|---|---|---|---|
| a 0 b 0 c d 0 | a 1 b 1 c d 1 | a 1 b 2 c d 3 | a 2 b 4 c d 6 |
Операторы: v, V, c-v (:h o_v)
Вероятно, вы знаете, что в визуальном режиме можно выделять символы (v),
строки (V) и блоки (ctrl-V). Но эти три сочетания можно использовать в
качестве операторов движения по соответствующему фрагменту. Например, у
вас есть такой текст:abc
abc
abc
Если поместить курсор на верхнюю b и нажать d2j, он удалит все три строки, потому что j двигается построчно. Если вместо этого нажать d<c-V>2j, движение становится поблочным и удаляется только средний столбец с тремя буквами b.
Один вариантов использования — удаление в поиске. Обычный d/ перемещается посимвольно. Поэтому я использую dV/ для построчного движения с удалением. Есть и другой способ сделать это:
/regex/{n}
Движение на n строк ниже совпадения или на столько же строк вверх, если
значение отрицательно. В качестве побочного эффекта движение происходит
построчно. Таким образом, если вы хотите удалить первую строку,
соответствующую regex, можете ввести d/regex//0.
Ex-команды
Ex-команды вы вводите в командном режиме, например, команда :s. Кроме замены, есть много других полезных команд. Для всех этих примеров требуется диапазон, такой как %.
:g/regex/ex
Выполняет команду только в строках, соответствующих регулярному выражению. Например, вы можете ввести g/regex/d для удаления всех строк, соответствующих regex. Команда v похожа на g, но работает на всех строках, которые не соответствуют регулярному выражению.
Трюки становятся более мощными с применением norm и некоторых других.
:norm {Vim}
Действует так, словно вы запустили {Vim} на каждой строке диапазона. Например, g/regex/norm f dw
удалит первое слово после первого пробела в каждой строке,
соответствующей регулярному выражению regex. Это часто намного проще,
чем макрос.norm подчиняется всем вашим сопоставлениям. Например, если вы назначили клавиши jk на <esc> в режиме вставки, то norm I jk$diw добавит пробел к началу строки, покинет режим вставки,
а затем удалит последнее слово в строке. Мне очень нравится эта
функциональность, но если вы предпочитаете не использовать свои
сопоставления, вы можете тогда применять norm!.
:co .
Копирует диапазон в текущую строку. Можно указывать и произвольные значения вместо точки, например, +3 или 'a. mv для перемещения.
:y {reg}
Копирует диапазон в регистр {reg}. Если {reg} обозначить заглавной буквой, то он добавляется к существующему регистру. т. е. такая командаlet @a = '' | %g/regex/y A
скопирует в a все строки, соответствующие regex во всём файле. Это помогает извлечь из файла разбитый текст и скопировать его в системный буфер обмена (с помощью let @+ = @a).
:windo {ех}
Запускает команду во всех окнах. Например, :windo $ прокрутит все окна вниз. Есть ещё bufdo, cdo, tabdo и другие.
Очень хорошо работает с g и s. Чтобы заменить все сочетания AA на BB с предварительным просмотром замен, можно ввести vimgrep AA, загрузив все совпадения в quickfix, а затем cdo s/AA/BB/cge для поиска/замены всех совпадений.
Vim для расширятелей
Здесь перечислены трюки, которые требуют сохранения в настройках или
изменения сеанса Vim. Гипотетически их можно использовать в
«пуританском» режиме, просто введя команды, но некоторые влекут довольно
серьёзные изменения, противоречащие духу пуризма.
Здесь только самое необычное. Многие люди назначают H на крышечку ^, поэтому такие вещи не стоит упоминать. Также нет смысла говорить о vim-sensible или vim-surround, а лишь о более экзотических плагинах.
Если вы постоянно настраиваете свой vimrc, сделайте себе приятно и добавьте для этого отдельную команду:command! Vimrc :vs $MYVIMRC
Настройки
У меня все настройки, привязки клавиш и функции хранятся в одном файле vimrc. Разбиение на множество файлов затрудняет поиск.
Большинство настроек на самом деле не являются какими-то «трюками». Лучше всего посмотреть на vim-sensible: почти все настройки оттуда подойдут вашему vimrc.
set lazyredraw
Не перерисовывать экран посреди макроса (для повышения производительности).
set smartcase/ignorecase
С двумя этими настройками поиск без заглавных букв становится
нечувствителен к регистру, а поиск с заглавными буквами чувствителен к
регистру.
set undofile
Сохранение действий, даже если вы закрываете и открываете Vim, так что
всегда доступна отмена действий. Очень удобно в сочетании с плагином
undotree.
set foldcolumn={n}
Боковая колонка со свёрнутыми блоками. Чем больше n, тем больше свёрнутых блоков показано в колонке, а для остальных указано число.
set suffixesadd={str}
gf обычно означает «перейти к файлу под курсором», но требует наличия расширения файла в строке. suffixesadd добавляет указанное расширение. Если установить suffixesadd=.md, то команда gf на строке ‘foo’ будет искать файлы foo и foo.md.
set inccommand=nosplit
Только для Neovim. Настройка incommand показывает в реальном времени, какие изменения внесёт команда. Сейчас поддерживается только s, но даже это невероятно полезно. Если ввести :s/regex, выделятся все соответствия. Если затем добавить /change, он покажет все замены. Работает со всеми свойствами регулярного выражения, включая обратные ссылки и группы.
set statusline (:h statusline)
Определяет, что отображать на панели в нижней части каждого окна. Здесь
форматирование намного сложнее и привередливее, чем в других настройках,
так что придётся потратить время на объяснение. Есть некоторые простые
трюки. Во-первых, посмотрим на строку состояния Vim по умолчанию::set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
Тут проще всего заменить %P (процент файла над курсором).
Формат строки состояния — значение после знака процента в фигурных
скобках. Поэтому для файлов Markdown можно написать такое::set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %{wordcount()[\"words\"]}
И заменить процент файла на количество слов в документе.
Или установить tabline. Если вы не используете вкладки, то эту строку можно сделать «глобальной статусной строкой». Например,set tabline=%{strftime('%c')}
всегда будет показывать дату сверху.
Привязки клавиш
У меня много привязок.
Очень много удобных клавиш в Vim по умолчанию назначено бестолково. Например, сохранение нажатия s — это синоним cl (экономия одного нажатия), а U — то же самое, что и u, за исключением записи undo как нового изменения, что функционально бесполезно. Q идентично gQ и в любом случае является колоссальной ловушкой. Z используется только для ZZ и ZQ. Чёрт возьми, даже руководство Vim рекомендует переназначать клавиши _ и ,
на какие-нибудь функции, поскольку, «вероятно, вы, никогда их не
используете». Я бы предпочёл не экономить одно нажатие, а добавить на
клавиатуру совершенно новые функции. Вот некоторые из моих привязок:
nnoremap Q @@
Не замедляясь на переход в ex-режим, повторяет последний макрос.
nnoremap s «_d
Заставляет клавишу s (с соответствующими назначениями для ss и S) работать как d, только без сохранения удалённого текста в регистре. Полезно для того, чтобы не засорять регистр.
nnoremap <c-j> <c-w>j
Перейти к окну ниже. Соответствующие назначения для h, k, l. Работа с окнами становится гораздо проще.
nnoremap <leader>e :exe getline(line(‘.’))<cr>
Выполнить текущую строку, как будто она представляет собой команду. При экспериментах часто более удобно, чем q:.
Специальные аргументы (:h map-arguments)
Команда map <buffer> lhs rhs активирует назначение
клавиш только для данного буфера. Это действительно удобно работает с
автокомандами как временное сочетание клавиш или при определении
назначений через функцию. Буферные назначения имеют приоритет над
глобальными, то есть вы можете переопределить общую команду более
полезной в конкретной ситуации.
Команда map <expr> {lhs} {expr} проверяет {expr}
и использует возвращаемое значение в качестве финального переназначения
клавиш. Один простой вариант использования — привязка в зависимости от
условий. У меня есть такие:nnoremap <expr> k (v:count == 0 ? 'gk' : 'k')
nnoremap <expr> j (v:count == 0 ? 'gj' : 'j')
Что заставляет j и k двигаться по строке до тех пор,
пока не встретится число, а после этого назначение клавиши отменяется.
Поэтому я могу перемещаться по длинным абзацам прозы, не нарушая такие
сочетания, как 10j.
Аргумент <silent> помогает, если какие-то привязки запускают ex-команды.
inoremaps
Благодаря inoremap привязки работают в режиме вставки. Там они начинают работать, поэтому inoremap ;a aaaa введёт ‘aaaa’ вместо ‘;a’. Если вы хотите сделать что-то в обычном режиме, используйте <c-O>. Например, если у нас естьinoremap ;1 <c-o>ma
то ;1 установит в данной точке метку 'a.
Я люблю указывать использовать точки с запятой в качестве ключа для
переназначений, потому что в нормальных текстах после точки с запятой
практически всегда идёт пробел или новая строка.
autocmd
Автокоманды отлично подходят для конфигурации. Обычно вы их настраиваете в таком виде:
augroup {name}
autocmd! " Prevents duplicate autocommands
au {events} {file regex} {command}
augroup END
Затем, если в файле {file regex} происходит какое-либо из событий
{events}, то срабатывает команда {command}. События перечислены в списке
:h event. Например, если записать
augroup every
autocmd!
au InsertEnter * set norelativenumber
au InsertLeave * set relativenumber
augroup END
то Vim отключит relativenumber только для режима вставки.
Команда au {event} <buffer> {ex} применяет
автокоманду только к текущему буферу. Иногда я использую это для
добавления краткосрочных обработчиков событий в конкретный файл.
BufNewFile, BufRead
BufnewFile запускается при создании нового файла, BufRead —
при первом открытии буфера. Их обычно используют для добавления
параметров и переназначений в конкретные типы файлов. У меня есть одно
такое:
augroup md
autocmd!
au BufNewFile,BufRead *.md syntax keyword todo TODO
au BufNewFile,BufRead *.md inoremap <buffer> ;` ```<cr><cr>```<Up><Up>
augroup END
Только в файлах Markdown подсвечивается строка TODO, а символы ;` в режиме вставки добавляет обозначение кода.
Автокоманды позволяют делать гораздо более сложные вещи. Например, au для BufWriteCmd
переопределяет стандартное сохранение, позволяя реализовать
нестандартную логику. Это выходит за рамки «трюков» и переходит в
область «тёмной магии».
Плагины
Большинство знает о популярных плагинах, таких как vim-surround и NERDtree. Вот список некоторых малоизвестных, которые я считаю очень полезными.
Undotree
В большинстве текстовых редакторов отмена действий происходит линейно.
Если вы внесёте изменение A, отмените его, а затем внесёте изменение B,
то A потеряно навсегда. Однако Vim хранит всё дерево отменённых
действий. Команда u откатывает действие в текущей ветке дерева, а g переходит к предыдущей хронологической версии. Можете просмотреть список отменённых действий командой :undolist.
Но этот формат не очень наглядный. Гораздо лучше увидеть фактическое дерево. Именно это делает Undotree: выкладывает хорошую ASCII-репрезентацию дерева отменённых действий с удобной навигацией.
vim.swap
Плагин предоставляет команды для обмена аргументами, поэтому вы можете в пару нажатий клавиш заменить (a, f(b, c)) на (f(b, c), a). Мне регулярно приходится делать такие правки, так что это сильное улучшение качества жизни.
Neoterm
Подключает API более высокого уровня к встроенному терминалу neo/vim. Например, :T {text} отправляет {text} в консоль. Хорошо подходит для создания интерактивной среды.
» TODO {{{
В этой статье не охвачены многие темы, потому что они слишком
технические или нуждаются в подробном объяснении, как написание функций
или синтаксическая система. А ещё я многого не знаю. Хотелось бы
детальнее изучить следующие темы:
Окна Preview, Quickfix и List
Я иногда использую инструменты с этими окнами, но не знаю, как ими манипулировать. Хотелось бы добавить ошибки quickfix в мой плагин TLA+.
Ещё мне нравится идея поместить вспомогательную информацию и команды
обратного вызова в окно предварительного просмотра. Это открывает
некоторые возможности, которые трудно воспроизвести в IDE.
Neovim API
Neovim предлагает продвинутый API для интеграции Vim с внешними
программами. Ваш скрипт Python может отправлять команд инстансу Neovim, а
вы можете управлять редактором через сервер, например. Я видел
некоторые классные концептуальные демонстрации, где автозаполнение
происходит на основе информации в браузере. Кажется, это очень классно!
Текстовые объекты
Никогда таких не создавал.
Оригинал данной статьи можно прочитать на хабре: https://habr.com/ru/post/454742/