Вот говорят о красоте модального редактирования и текстовых объектах, но мне кажется, что суть 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, а вы можете управлять редактором через сервер, например. Я видел некоторые классные концептуальные демонстрации, где автозаполнение происходит на основе информации в браузере. Кажется, это очень классно!
Текстовые объекты
Никогда таких не создавал.
Итак, это был краткий обзор некоторых неявных функций Vim. Надеюсь, вы узнали что-то полезное!
Комментарии (15)
inferrna
04.06.2019 18:56Использую Vim, но так глубоко не зарываюсь. Интуиция подсказывает, что если я выучу все эти заклинания и буду постоянно держать их в подкорке, то там останется меньше места для осознания собственно того, что я редактирую.
Чего мне хватает:— редактировать (i)
— выход из редактирования(esc)
— отмена (u)
— поиск вперёд / назад (/abc+enter, n, shift+n)
— поиск с заменой (:%s/abc/def/g)
— визуальный режим (shift+v, ctrl+v, потом ещё можно shift+i, отредактировать, потом esc+^)
— вырезать / вставить (dd, shift+p)
— переход на линию (:99+enter)
— перейти наверх, вниз (gg, shift+g)
— выход / выход с записью (:q, :wq)dannk
05.06.2019 08:50я бы еще добавил в этот минималистический набор * (поиск слова под курсором), cw (удаление до конца слова и переход в режим вставки),. (повтор предыдущего действия).
CryptoPirate
06.06.2019 09:29выход с записью можно ещё так ":x"
Я ещё часто пользуюсь «V» — выделить строку и «y» — скопировать.
slamon
05.06.2019 09:52Самый лучший трюк, который можно проделать с Vim — это установить другой нормальный редактор
istepan
05.06.2019 10:21Честно пытался перейти на vim/nvim. Пробовал кучу плагинов и сборок. Перепробовал все доступные анализаторы и плагины под PHP.
В итоге купил лицензию phpstorm.
Vim идеален для правки одиночных файлов. Заменить IDE не сможет.x_user
06.06.2019 20:49Думаю вам следует взглянуть на vim-lsp и плагин автокомплита, который умеет с ним работать например ncm2 + ncm2-vim-lsp. Будет вам и автокомплит ваших функций и классов, проверка синтаксиса итд.
ne_zabudka
06.06.2019 19:13Пять копеек в копилку пуристов. Попробуйте поработать с файловым менеджером ranger и вы возможно забудете о плагине NERDtree.
lanseg
"Минимум один трюк Vim, про который вы не знали"
saboteur_kiev
да, можно через ребут!