vim-galore logo

Всё, что нужно знать о Vim

Перевод с английского © Гиви Хакеридзе. Оригинал: https://github.com/mhinz/vim-galore

Введение

Основные составляющие

Использование

Полезные советы

Команды

Решение проблем

Разное

Распространённые проблемы

Технические особенности

Список цветовых схем

Список плагинов


Введение

Что есть Vim?

Vim это — текстовый редактор с длинной чередой предков, которая начинается с qed. Его выпустил в 1991 году Брэм Мооленаар (Bram Moolenaar).

Адрес проекта в Интернет: vim.org.

Получение Vim: Используйте ваш любимый менеджер пакетов или посетите страницу загрузки на vim.org.

Участвовать в обсуждении и задавать вопросы лучше всего через рассылку vim_use или на IRC-канале (Freenode) #vim.

Разработка находится на Github, а её обсуждение — в списке рассылки vim_dev.

Прочитайте Why, oh WHY, do those #?@! nutheads use vi?, чтобы узнать об основных заблуждениях относительно Vim (перевод на русский).

Философия Vim

Vim придерживается философии модального редактирования. Это означает, что у него есть несколько режимов, и значения клавиш меняются в зависимости от режима. Вы перемещаетесь по файлу в нормальном режиме, вставляете текст в режиме вставки, выделяете строки в визуальном режиме, выполняете команды в режиме командной строки, и т.д. Поначалу это может показаться сложным, но имеет огромное преимущество: вам не нужно калечить свои пальцы, нажимая несколько клавиш сразу, вы просто нажимаете их одну за другой. Чем чаще задача встречается, тем меньше понадобится нажатий.

Другая концепция, которая хорошо проявляет при модальном редактировании, это — операторы и перемещения. Оператор начинает некое действие, например, изменение, удаление или выделение текста. Затем при помощи перемещения вы указываете область текста, с которой вы хотите работать. Чтобы изменить всё, что находится между скобками, используйте ci( (читайте это как change inner parentheses — изменить внутри скобок). Чтобы удалить целый параграф текста, используйте dap (delete around paragraph — удалить вокруг параграфа).

Глядя на то, как работают продвинутые пользователи Vim, можно заметить, что они как бы разговаривают на языке Vim или похожи на пианиста, играющего на своём инструменте. Сложные операции производятся небольшим количеством нажатий клавиш. Более того, они вообще не думают о клавишах, т.к. об этом позаботилась мышечная память. Это уменьшает умственную нагрузку, помогая сосредоточиться на основной задаче.

Первые шаги

Vim поступает с интерактивным учебником, при помощи которого можно научиться основным вещам, которые вам необходимо знать. Его можно запустить командой в оболочке (добавьте параметр ru, чтобы читать учебник на русском — пер.):

$ vimtutor [ru]

Не откладывайте, даже если он покажется вам скучным, и пройдите упражнения. Редакторы или IDE, которые вы использовали ранее, скорее всего, были немодальными, поэтому работать, переключая режимы, поначалу будет казаться очень неудобным, но чем больше использовать Vim, тем сильнее он закрепится в мышечной памяти.

Vim основан на Stevie, клоне vi, и поддерживает два режима работы: «совместимый» и «несовместимый». Использование Vim в режиме совместимости означает использование для настроек умолчаний vi, а не Vim. Пока вы не создадите пользовательский vimrc или запускаете Vim командой vim -N, подразумевается режим совместимости! Не используйте Vim в режиме совместимости. Просто не делайте этого.

Далее:

  1. Создайте свой vimrc.
  2. Первые недели держите наготове читшиты (шпаргалки).
  3. Прочитайте секцию Основные составляющие, чтобы узнать об имеющихся возможностях.
  4. Учитесь по мере необходимости! Изучение Vim невозможно закончить (как ремонт — пер.). Если вы столкнулись с проблемой, просто загляните в Интернет. Будьте уверены, ваша проблема уже решена. Vim имеет прекрасную документацию, и знание навигации по ней — обязательно: Получение справки офлайн.
  5. Посмотрите дополнительные ресурсы.

И ещё один совет: Пожалуйста, научитесь использовать Vim правильно, прежде чем начать добавлять разрекламированные плагины, которые лишь воплощают функциональность, которая изначально есть в Vim.

Минимальный vimrc

Пользовательский vimrc можно сохранить как ~/.vimrc или в целях лучшей изоляции как ~/.vim/vimrc. В последнем случае будет проще поместить всю конфигурацию в систему контроля версий и загрузить её, скажем, на Github.

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

В конце концов, вам нужно прочитать про все упомянутые настройки и сделать выбор для себя. :-)

Итак, он — здесь: минимальный vimrc.

Если вам интересно, то здесь — мой vimrc.

СОВЕТ: Большинство авторов плагинов поддерживают не по одному плагину и дополнительно публикуют свои vimrc на Github (часто в репозитории с названием «vim-config» или «dotfiles»), поэтому, найдя интересный плагин, посмотрите также страницу автора на Github и пробегитесь по репозиториям.

Какой именно Vim у меня работает?

Команда :version даёт всю необходимую информацию о том, как была скомпилирована версия Vim, работающая в данный момент.

В первой строке выводится время компиляции и версия двоичного файла, например, 7.4. Одна из следующих строк содержит что-то вроде Included patches: 1-1051, это — номер патча (правки). Таким образом, полная версия Vim — 7.4.1051.

Далее следует строка вроде Tiny version without GUI (малая версия без GUI) или Huge version with GUI (огромная версия с GUI). Отсюда можно узнать включена ли поддержка графического интерфейса (GUI), например, для запуска gvim в оболочке или командой :gui в Vim, запущенном в эмуляторе терминала. Другая важная часть — слова Tiny (малая) или Huge (огромная). Vim может быть скомпилирован с различным набором функциональностей: tiny (малый), small (небольшой), normal (нормальный), big (большой) и huge (огромный).

Основная часть вывода команды :version занимает список функциональностей. +clipboard говорит, что при компиляции была включена функциональность работы с буфером обмена, а -clipboard — что она не была включена.

Часть функциональности можно включить только во время компиляции. Например, для того, чтобы работала команда :prof, нужен Vim с установленным набором huge, т.к. этот набор включает функциональность +profile.

Если это не ваш случай, и вы устанавливали Vim при помощи менеджера пакетов, попробуйте установить пакет vim-x, vim-x11, vim-gtk, vim-gnome или похожий, т.к. эти пакеты обычно идут с включенным набором huge.

Проверить версию или наличие функциональности можно и программно:

" Сделать что-то, если версия Vim не меньше 7.4.42 и включена фунциональность +profile.
if (v:version > 704 || v:version == 704 && has('patch42')) && has('profile')
  " сделать что-то
endif

Справка:

:h :version
:h feature-list
:h +feature-list
:h has-patch

Читшиты (шпаргалки)

Или быстро откройте читшит прямо в Vim: vim-cheat40.

Основные составляющие

Буферы, окна, табы

Vim — текстовый редактор. Текст, который виден на экране, является частью буфера. Каждый файл открывается в своём собственном буфере. Плагины отображают свою работу в своих собственных буферах. И т.д.

Буферы имеют много атрибутов, например, можно ли изменять текст в буфере, или связан ли буфер с неким файлом, и его нужно синхронизировать при сохранении.

Окна — область просмотра буфера. Окна используются, если необходимо просматривать сразу несколько файлов или один в разных местах.

И, пожалуйста, не называйте их разбиениями. Можно разбить окно на два, но это не сделает их разбиениями.

Окна можно разделить вертикально или горизонтально, можно изменять их высоту или ширину. Т.е. расположить их так, как вам нужно.

Страница с вкладкой (или просто таб) это — коллекция окон. Таким образом, если нужно иметь несколько раскладок окон, используйте табы.

В общем, если запустить Vim без аргументов, у вас будет один таб, в котором будет одно окно с одним буфером.

Кстати говоря, список буферов — глобален, и у вас есть доступ к любому буферу из любого таба.

Активные, загруженные, списковые, именованные буферы

Запустите Vim примерно так: vim file1. Содержимое файла будет загружено в буфер. Теперь у вас есть загруженный буфер. Содержимое буфера синхронизируется с диском (пишется обратно в файл) только при сохранении его в Vim.

Т.к. содержимое буфера показано в окне, то этот буфер ещё и активный буфер. Теперь, если загрузить другой файл командой :e file2, file1 станет скрытым буфером, а file2 — активным.

Оба буфера также — списковые, потому что они будут показаны в выводе команды :ls. Буферы плагинов или справки часто помечены как несписковые, т.к. это не обычные файлы, которые вы обычно редактируете в текстовом редакторе. И списковые и несписковые буферы вместе можно посмотреть командой :ls!.

Неименованные буферы, также часто используемые плагинами, это — буферы, которые не ассоциированы с именем файла. Например, :enew создаст неименованный пустой буфер. Добавьте некий текст и запишите его на диск командой :w /tmp/foo, и он станет именованным буфером.

Список аргументов

Глобальный список буферов — вещь, специфичная для Vim. До него, в vi, использовался список аргументов, который в Vim тоже есть.

Каждое имя файла, переданное Vim в командной строке оболочки, запоминается в списке аргументов. Таких списков может быть несколько: по умолчанию все аргументы помещаются в глобальный список аргументов, но при помощи команды :arglocal можно создать новый список аргументов, который будет локальным для данного окна.

Просмотреть текущий список аргументов можно при помощи команды :args. Переключиться между файлами в списке — при помощи :next, :previous, :first, :last и др. Изменить — при помощи :argadd, :argdelete или :args со списком файлов.

Какой список использовать для работы с файлами, буферов или аргументов, — дело вкуса. По моему мнению, большинство использует исключительно список буферов.

И, тем не менее, есть один важный приём с использованием списка аргументов: пакетная обработка при помощи :argdo! Вот пример простой переработки кода:

:args **/*.[ch]
:argdo %s/foo/bar/ge | update

Эта команда заменяет все вхождения «foo» на «bar» во всех исходных и заголовочных файлах на C в текущем каталоге и ниже.

Справка: :h argument-list

Мапинги

(«Отображения», «переназначение клавиш» — слишком длинно — прим. пер.)

С помощью команд семейства :map можно определять мапинги клавиш. Каждая команда из этого семейства определяет мапинги для определённого набора режимов. Технически говоря, у Vim-а 12 режимов, для 6 из которых можно использовать мапинг. Кроме того, некоторые команды действуют для нескольких режимов.

Рекурсивный Не рекурсивный Отмена Режимы
:map :noremap :unmap нормальный, визуальный, ожидание-оператора
:nmap :nnoremap :nunmap нормальный
:xmap :xnoremap :xunmap визуальный
:cmap :cnoremap :cunmap командная-строка
:omap :onoremap :ounmap ожидание-оператора
:imap :inoremap :iunmap вставка

Например, эта команда определяет мапинг только для нормального режима:

:nmap <space> :echo "foo"<cr>

Отменим мапинг при помощи :nunmap <space>.

Чтобы узнать о некоторых более редко используемых режимах (и их комбинациях), см. :h map-modes.

Дальше — больше. Есть одна проблема, которая может смутить новичков: :nmap — рекурсивна! Т.е. правая часть команды также участвует в мапинге.

Допустим, был определён мапинг, который просто печатает «Foo»:

:nmap b :echo "Foo"<cr>

Но, что если мы замапим умолчальное поведение b (переход на одно слово влево) на другую клавишу?

:nmap a b

Нажимая a, мы ожидаем перехода к началу предыдущего слова, но вместо этого в командной строке печатается «Foo»! Это происходит потому, что до этого правая часть уже была переназначена на :echo "Foo"<cr>.

Правильный способ решить эту проблему — использовать нерекурсивный мапинг:

:nnoremap a b

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

Проверяйте мапинги, запуская команду, не передавая ей правой стороны. И для примера, :nmap показывает все мапинги для нормального режима, а :nmap <Leader> — для мапингов, которые начинаются мап-лидера.

Если захотите запретить стандартные мапинги, переназначьте их на специальный символ <nop>, например, :noremap <left> <nop>.

Справка:

:h key-notation
:h mapping
:h 05.3

Мап-лидер

Мап-лидер это — просто заглушка, которая может использоваться для создания дополнительных мапингов, по умолчанию он назначен на \.

nnoremap <leader>h :helpgrep<space>

Такой мапинг вызывается командой \h. Если вместо этого вы хотите использовать <space>h:

let mapleader = ' '
nnoremap <leader>h :helpgrep<space>

Кроме того, есть <LocalLeader>, напарник <Leader>, который предназначен для мапингов локальных для буфера, например, для использования в плагинах для конкретных типов файлов. По умолчанию он тоже назначен на \.

NB: Изменяйте мап-лидеры до остальных мапингов! При назначении мапинга с мап-лидером действует текущее значение <Leader>, которое не изменяется впоследствии при изменении самого мап-лидера. :nmap <Leader> покажет мапинги нормального режима с лидером, который установлен в данный момент. Поэтому дважды проверяйте ваши мапинги.

См. также :h mapleader и :h maplocalleader.

Регистры

Регистры это — ячейки, в которых сохраняется текст. Копирование в регистр называется yanking (вытаскивание, выдёргивание; далее будет употребляться «копирование» — прим. пер.), а извлечение из регистра — pasting (вставка).

У Vim-а есть следующие регистры:

Тип Символ Кем заполняется? Только для чтения? Содержит текст откуда?
Безымянный " vim [ ] Последнее копирование или удаление. (d, c, s, x, y)
Нумерованный 0 to 9 vim [ ] Регистр 0: последнее копирование. Регистр 1: последнее удаление. Регистр 2: предыдущее перед последним удалением. И т.д. Представляйте регистры 1-9 как очередь с 9 элементами только для чтения.
Малое удаление - vim [ ] Последнее удаление, которое захватило меньше одной строки.
Именованный a to z, A to Z пользователь [ ] Копирование в регистр a замещает его текст. Копирование в регистр A добавляет текст в регистр a.
Только для чтения :, ., % vim [x] :: последняя команда, .: последний вставленный текст, %: имя текущего файла.
Альтернативный буфер # vim [ ] Обычно — имя буфера, который просматривался ранее в этом окне. См. :h alternate-file
Вычисление выражения = пользователь [ ] Результат вычисления выражения VimL. Например, если в режиме вставки набрать: <c-r>=5+5<cr>, то в этом буфере окажется «10».
Выделение +, * vim [ ] * и + — регистры буфера обмена.
Сброс ~ vim [x] Результат последней операции «drag'n'drop».
Чёрная дыра _ vim [ ] Предназначен для того, чтобы не влиять неявно на другие регистры. Например, "_dd удаляет текущую строку, не изменяя регистры ", 1, +, *.
Последний шаблон поиска / vim [ ] Последний шаблон, который использовался с /, ?, :global и т.д.

Пользователь может изменить любой регистр, который не «только для чтения»:

:let @/ = 'register'

После этой команды n переместит курсор к следующему вхождению «register».

Есть некоторое количество исключений, когда регистры заполняются неявно, поэтому не забудьте прочитать :h registers.

Копируйте при помощи y и вставляйте при помощи p/P, но помните, что Vim различает посимвольное и построчное визуальное выделение. См. :h linewise.

Пример: построчно

yy (или просто Y) скопирует текущую строку. Переместите курсор в другое место и используйте p, чтобы вставить скопированное под текущей строкой или P, чтобы над.

Пример: посимвольно

Скопируйте первое слово в строке при помощи 0yw, переместите курсор в другое место, вставьте скопированное после курсора в текущей строке при помощи p или перед ним при помощи P.

Пример: явное указание регистра

"aY копирует текущую строку в регистр a. Перейдите на другую строку. "AY добавит эту строку в регистр a.

Предлагаю немного поиграться со всеми этими регистрами, постоянно сверяясь с :reg, чтобы видеть что на самом деле происходит.

Забавный факт: В Emacs «yanking» соответствует вставке (или повторная вставка ранее прибитого текста, ориг. — reinserting previously killed text), а не копированию.

Диапазоны

Диапазоны достаточно просты для понимания, но многие вимеры не полностью осознают весь их потенциал.

Использование диапазонов достаточно интуитивно, поэтому вот несколько примеров (:d используется в качестве сокращения :delete):

Команда Строки, на которые воздействует
:d Текущая строка.
:.d Текущая строка.
:1d Первая строка.
:$d Последняя строка.
:1,$d Все строки.
:%d Все строки (синтаксический сахар для 1,$).
:.,5d С текущей строки по 5.
:,5d Также с текущей строки по 5.
:,+3d Текущая строка и следующие 3.
:1,+3d От первой строки до текущей + 3.
:,-3d Текущая строка и предыдущие 3. (Vim сделает запрос, потому что это перевёрнутый диапазон.)
:3,'xdelete С 3 строки до строки, помеченной меткой x.
:/^foo/,$delete Со строки, начинающейся с «foo» до конца.
:/^foo/+1,$delete Со строки, следующей за строкой, которая начинается с «foo», до конца.

Заметьте, что в качестве разделителя вместо , можно использовать ;. Разница заключается в том, что в случае от,до до будет относительно текущей строки, а при использовании от;до до будет относительно от! Допустим, мы находимся на строке 5, :1,+1d удалит строки с 1 по 6, а :1;+1d удалит только строки 1 и 2.

Адресу / может предшествовать другой адрес. Это позволяет составлять стек шаблонов, например:

:/foo//bar//quux/d

Эта команда удалит первую строку, содержащую «quux» после первой строки, содержащей «bar», которая находится после первой строки, содержащей «foo», которая находится после текущей строки. (Как «В доме, который построил Джек» — пер.)

Иногда Vim вставляет диапазон в начале командной строки автоматически. Например, начните визуальное выделение строк при помощи V, выделите несколько строк и наберите :. Командная строка будет начинаться диапазоном '<,'>, что означает, что команда будет использовать ранее выделенные строки в качестве диапазона. (Поэтому иногда можно встретить мапинги вроде :vnoremap foo :<c-u>command. Здесь <c-u> используется для удаления диапазона, потому что Vim сообщит об ошибке при передаче диапазона команде, которая его не использует.)

Другой пример — использование !! в нормальном режиме. Эта команда заполнит командную строку символами :.!. Если это дополнить именем внешней программы, то её вывод заменит текущую строку. Таким приёмом можно заменить текущий абзац выводом команды ls, используя команду :?^$?+1,/^$/-1!ls. Фантастика!

Справка:

:h cmdline-ranges
:h 10.3

Метки

Метки используются для запоминания позиции в файле — номера строки и колонки.

Метки Кем устанавливаются Использование
a - z Пользователь Локальные для файла, т.е. имеют смысл только в одном файле. Прыжок к метке в нижнем регистре означает прыжок внутри текущего файла.
A - Z Пользователь Глобальные, т.е. имеют смысл на уровне нескольких файлов. Другое название — файловые метки. Прыжок к файловой метке может переключить на другой буфер.
0 - 9 viminfo 0 — позиция во время последней записи файла viminfo. На практике это означает момент последнего завершения процесса Vim. 1 — позиция во время второго с краю завершения процесса Vim. И т.д.

Поместите '/g' или `/g` перед меткой, чтобы переместиться к метке.

Используйте mm, чтобы запомнить позицию в метке «m». Переместитесь в другое место в файле и прыгните обратно при помощи 'm (первый непробельный символ) или `m (с точностью до колонки). Метки в нижнем регистре запоминаются при выходе из Vim, если вы скажете об этом вашему файлу viminfo, см. :h viminfo-'.

Чтобы запомнить позицию в файловой метке «M», используйте mM. Переключитесь на другой буфер и вернитесь при помощи 'M или `M.

Другие перемещения:

Перемещение Прыжок к...
'[, `[ Первая строка или символ ранее изменённого или скопированного текста.
'], `] Последняя строка или символ ранее изменённого или скопированного текста.
'<, `< Начальная строка или символ последнего визуального выделения.
'>, `> Конечная строка или символ последнего визуального выделения.
'', `` Позиция до последнего прыжка.
'", `" Позиция в момент последнего выхода из текущего буфера.
'^, `^ Позиция, где закончилась последняя вставка.
'., `. Позиция, где производились последние изменения.
'(, `( Начало текущего предложения.
'), `) Конец текущего предложения.
'{, `{ Начало текущего параграфа.
'}, `} Конец текущего параграфа.

Метки можно использовать в диапазонах. Возможно, раньше вы уже видели такое и были удивлены: после визуального выделения текста и нажатия : командная строка начинается с :'<,'>, что означает, что последующая команда получит диапазон, обозначенный выделением.

Для просмотра списка меток используйте :marks. Подробности см. :h mark-motions.

Автозавершение

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

Типичные виды автозавершения это — теги, функции, импортированные из модулей или библиотек, имена файлов, словарь или просто слова из текущего буфера.

Для всех видов автозавершения назначены сочетания клавиш, и все они начинаются с <c-x> (помните, что использовать их необходимо в режиме вставки).

Мапинг Вид Соответствующая тема справки
<c-x><c-l> целые строки :h i^x^l
<c-x><c-n> ключевые слова из текущего файла :h i^x^n
<c-x><c-k> ключевые слова согласно опции 'dictionary' :h i^x^k
<c-x><c-t> ключевые слова согласно опции 'thesaurus' :h i^x^t
<c-x><c-i> ключевые слова из текущего и включенных файлов :h i^x^i
<c-x><c-]> теги :h i^x^]
<c-x><c-f> имена файлов :h i^x^f
<c-x><c-d> определения и макро :h i^x^d
<c-x><c-v> команды Vim :h i^x^v
<c-x><c-u> определённое пользователем (как указано в опции 'completefunc') :h i^x^u
<c-x><c-o> omni-завершение (как указано в опции 'omnifunc') :h i^x^o
<c-x>s предложения проверки орфографии :h i^Xs

Людей может смутить разница между автозавершением, определённым пользователем, и omni, но технически они делают одно дело. Они используют функцию, которая исследует текущую позицию и возвращает список предложений для завершения. Автозавершение определённое пользователем, соответствует его персональным задачам. (Сюрприз!) И может быть всем, чем угодно. Omni-завершение предназначено для целей специфичных для типа файла, например, для завершения членов структур или методов класса, и часто устанавливается плагинами для типа файла.

Vim позволяет производить автозавершение сразу нескольких видов согласно установка опции 'complete'. По умолчанию эта опция содержит достаточно много, поэтому вы можете укоротить её по своему вкусу. Вызывается такое автозавершение нажатием <c-n> (next, следующий) или <c-p> (previous, предыдущий), эти же клавиши используются для выбора пунктов во всплывающем меню. Дополнительно см. :h i^n и :h 'complete'.

Просмотрите :h 'completeopt', чтобы настроить поведение всплывающего окна. Настройки по умолчанию достаточно разумны, но я предпочитаю добавлять туда «noselect».

Справка:

:h ins-completion
:h popupmenu-keys
:h new-omni-completion

Перемещения, операторы, текстовые объекты

Перемещение сдвигает курсор. Всем известны h/j/k/l. Или w и b. Даже / — перемещение. Перед ними можно указать счётчик. 2?the<cr> перепрыгнет ко второму предыдущему вхождению «the».

См. :h navigation и всё, что ниже, обо всех доступных перемещениях.

Операторы воздействуют на регион текста, например, d, ~, gU, >. Их можно использовать в двух контекстах, в нормальном или визуальном режиме. В нормальном режиме сначала идёт оператор, за которым следует перемещение, например, >j. В визуальном режиме оператор просто воздействует на выделение, например, Vjd.

Как и перемещениям, операторам тоже можно указывать счётчик, например, 2gUw приведёт к верхнему регистру остаток текущего слова и следующее за ним. Т.к. и перемещения, и операторы принимают счётчики, 2gU2w работает так же, как два вызова gU2w.

См. :h operator обо всех доступных операторах. Используйте :set tildeop, чтобы ~ работала как оператор.

Текстовые объекты воздействуют на окружающее пространство в противоположность перемещениям, которые действуют только в одном направлении. На самом деле, они действуют на объекты, например, на целое слово, целое предложение, на всё, что внутри скобок и т.д.

Текстовые объекты нельзя использовать для перемещения курсора в нормальном режиме, т.к. даже самые многоопытные курсоры не могут прыгать в двух направлениях одновременно. Хотя это работает в визуальном режиме, потому что одна сторона объекта уже выбрана, и курсор просто прыгает к другой стороне.

Текстовые объекты начинаются либо с i (представляйте как inner — внутри), либо с a (around — вокруг), за которым следует символ, обозначающий объект. С i он действует на сам объект, а с a — на объект и последующее пробельное пространство (пропуск). Например, diw удаляет текущее слово, а ci( изменяет всё, что находится внутри скобок.

Текстовые объекты принимают счётчик. Допустим, имеем ((( ))), и курсор находится между самыми внутренними скобками, тогда d2a( удалит 2 внутренние пары скобок и всё, что находилось между ними.

См. :h text-objects обо всех доступных текстовых объектах.

Автокоманды

Во многих случаях Vim возбуждает события. Эти события можно обработать при помощи автокоманд.

Vim невозможно было бы использовать без автокоманд. Они используются постоянно, даже, если вы о них ничего не знаете. Не верите? Посмотрите :au, но не пугайтесь её вывода. Это все те автокоманды, которые в действии прямо сейчас!

См. :h {event} с обзором всех доступных событий и :h autocmd-events-abc, где содержится больше деталей.

Типичный пример — установка настроек для конкретного типа файла:

autocmd FileType ruby setlocal shiftwidth=2 softtabstop=2 comments-=:#

Но каким образом буфер вообще узнает, что он содержит код Ruby? Потому что другая автокоманда определила это и установила соответствующий тип файла, что в свою очередь вызвало событие FileType.

Первое, что все добавляют в свой vimrc, — filetype on. Это просто означает, что во время запуска читается файл filetype.vim, который настраивает автокоманды почти для всех типов файлов, существующих под солнцем.

Если вы достаточно смелый, посмотрите на это: :e $VIMRUNTIME/filetype.vim. Поищите «Ruby» и вы увидите, что Vim определяет тип файла как Ruby просто по расширению:

NB: Автокоманды для одинаковых событий исполняются по мере создания. :au показывает их в правильной очерёдности.

au BufNewFile,BufRead *.rb,*.rbw  setf ruby

В этом случае события BufNewFile и BufRead наглухо закодированы в C-коде Vim и вызываются всякий раз, когда вы открываете файл при помощи :e и подобными командами. После этого в filetype.vim тестируются ещё сотни типов файлов.

Подытожим. Vim интенсивно использует события и автокоманды, предоставляя при этом удобный интерфейс для работы с управляемой событиями системой и пользовательской настройки.

Справка: :h autocommand

Список изменений, список прыжков

Позиции последних 100 изменений сохраняются в списке изменений. Несколько небольших изменений в одной строке объединяются, но сохраняется позиция последнего изменения (в случае, если вы добавили что-то посреди строки).

При каждом прыжке в списке прыжков сохраняется позиция до прыжка. Список прыжков содержит до 100 записей. Каждое окно имеет свой список прыжков. При разбиении окна список прыжков копируется.

Прыжок это — одна из следующих команд: ', `, G, /, ?, n, N, %, (, ), [[, ]], {, }, :s, :tag, L, M, H и команды, которые начинают редактирование нового файла.

Список Список всех записей К предыдущей позиции К следующей позиции
список прыжков :jumps [счётчик]<c-o> [счётчик]<c-i>
список изменений :changes [счётчик]g; [счётчик]g,

В списке всех записей маркер > указывает текущую позицию. Обычно он находится ниже текущей позиции с номером 1.

Для того чтобы оба списка восстанавливались после перезапуска Vim, необходимо использовать файл viminfo и :h viminfo-'.

NB: Позиция перед последним прыжком дополнительно сохраняется как метка, к ней можно перейти при помощи `` или ''.

Справка:

:h changelist
:h jumplist

Дерево отмены

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

Важная деталь для понимания этого заключается в том, что структура данных, которая содержит последние изменения, представляет собой не очередь, а дерево! Ваши изменения это — узлы дерева, и каждый (кроме самого верхнего) имеет родителя. Каждый узел хранит информацию об изменённом тексте и времени изменения. Ветка это — последовательность узлов, которая начинается с любого узла и поднимется к самому верхнему узлу. Новые ветки создаются, когда вы отменяете изменение, а затем вставляете что-то ещё.

ifoo<esc>
obar<esc>
obaz<esc>
u
oquux<esc>

Теперь у вас есть 3 строки, и дерево отмены выглядит примерно так:

     foo(1)
       /
    bar(2)
   /      \
baz(3)   quux(4)

Дерево отмены имеет 4 изменения. Числа представляют время, когда созданы узлы.

Есть два способа обхода этого дерева, назовём их веточный и повременной.

Отмена (u) и повтор (<c-r>) работают веточно. Они поднимаются и спускаются по текущей ветке. u восстановит состояние текста, соответствующее узлу «bar». Следующее u — «foo». Теперь <c-r> вернёт к состоянию узла «bar», а очередное <c-r> — к «quux». (Способа достигнуть узла «baz» веточными командами не существует.)

В противоположность этому, g- и g+ работают повремённо. Так g- не вернёт к состоянию «bar», как это делает u, а вернёт к хронологически предыдущему состоянию, узлу «baz». Следующее g- вернёт к состоянию узла «bar» и так далее. Таким образом, g- и g+ переходят вперёд и назад по состояниям во времени.

Мапинги / команды Действие
[счётчик]u, :undo [счётчик] Отменить [счётчик] изменений.
[счётчик]<c-r>, :redo Повторить [счётчик] изменений.
U Отменить все изменения в строке, последующее отменяет отмену.
[счётчик]g-, :earlier [счётчик]? Вернуться во времени [счётчик] раз. «?» может быть одним из «s», «m», «h», «d» или «f». Например, :earlier 2d вернёт состояние текста, которое было двое суток назад. :earlier 1f вернёт к состоянию, которое было в момент последнего сохранения файла.
[счётчик]g+, :later [счётчик]? То же самое, но в другом направлении.

Дерево отмены хранится в памяти и теряется при выходе из Vim. См. Файлы отмены, где описано как организовать сохраняемую отмену.

Если дерево отмены кажется вам запутанным, то undotree сделает для вас отличную работу по его визуализации.

Справка:

:h undo.txt
:h usr_32

Списки quickfix и местоположений

Список quickfix это — это структура данных, которая содержит позиции в файле. В частности, список quickfix состоит из элементов, которые содержат имя файла, номер строки, возможно, колонку и описание.

Типичный пример применения — сохранение сообщений об ошибках при компиляции или результатов работы grep.

Для показа списка quickfix в Vim есть специальный тип буфера: буфер quickfix. Каждая строка показывает один элемент из списка quickfix.

Для просмотра списка quickfix обычно открывают новое окно: окно quickfix. При этом предыдущее окно связывается с окном quickfix.

<cr> в буфере quickfix открывает выбранный элемент в связанном окне, а <c-w><cr> — в новом окне.

Список quickfix назван в честь функции «quick fix» компилятора Aztec C.

На самом деле, существует два типа списков: quickfix и список местоположений. В основном, они ведут себя одинаково, но имеют некоторые различия:

Действие Quickfix Местоположение
открыть окно :copen :lopen
закрыть окно :cclose :lclose
следующее вхождение :cnext :lnext
предыдущее вхождение :cprevious :lprevious
первое вхождение :cfirst :lfirst
последнее вхождение :clast :llast

Помните, что для того, чтобы эти команды работали, сами окна quickfix и местоположений открывать не обязательно.

Дополнительную информацию и полный список команд см. :h quickfix.

Для краткости, слова quickfix и location часто сокращаются до qf и loc соответственно.

Пример:

Давайте используем нашего старого доброго друга grep для рекурсивного поиска файлов в текущем каталоге по некоторому запросу и поместим результат в список quickfix.

:let &grepprg = 'grep -Rn $* .'
:grep! foo
<вывод grep - нажимаем enter>
:copen

Любые файлы, содержащие строку «foo», будут показаны в окне quickfix.

Макро

Vim позволяет записывать в регистр набранные символы. Это отличный способ для автоматизации некоторых задач на лету. (Для более сложных задач вместо этого можно использовать скрипты Vim)

Пример 1:

Вставить строку и повторить её 10 раз:

qq
iabc<cr><esc>
q
10@q

(То же самое можно сделать и без макро: oabc<esc>10.. o — начать новую строку ниже текущей, вставить символы «abc», завершить ввод, 10. — повторить ввод 10 раз.)

Пример 2:

Для добавления номеров всех строк в начале строки, начнём на первой строке и добавим к ней вручную «1. ». Увеличим число под курсором при помощи <c-a>, которая будет показана как ^A.

qq
0yf jP0^A
q
1000@q

Используя 1000@q, мы просто надеемся, что файл содержит не более 1000 строк, но мы также можем использовать рекурсивное макро, которое будет исполняться, пока есть строки, к которым его можно применить:

qq
0yf jP0^A@q
q
@q

(То же самое можно сделать без макро при помощи подстановки: :%s/^/\=line('.') . '. ')

Обратите внимание, что я опять показал способ, как сделать то же самое без макро, но в основном, это работает только в случае таких простых примеров. Для сложной автоматизации макро — просто бомба!

См. также: Быстрое редактирование макро

Справка:

:h recording
:h 'lazyredraw'

Цветовые схемы

Цветовые схемы это — способ стилизовать ваш Vim. Vim состоит из многих компонентов, и для каждого из них могут быть подобраны цвет переднего плана, фона, а также несколько других атрибутов вроде полужирного текста и т.д. Их можно задавать примерно так:

:highlight Normal ctermbg=1 guibg=red

Это приведет к прорисовке фона красным цветом. См. :h :highlight для дополнительной информации.

Таким образом, цветовые схемы это в основном — набор команд :highlight.

На самом деле, большинство цветовых схем это — 2 цветовых схемы! Пример выше задаёт цвета при помощи ctermbg и guibg. Первое (cterm*) будет использоваться, когда Vim запущен в эмуляторе терминала, например в xterm. Второе (gui*) будет использоваться в графическом окружении, таком как gvim или MacVim.

Если так получилось, что вы используете цветовую схему в терминальном Vim-е, а цвета совсем не похожи на те, которые были на скриншоте, то, скорее всего, эта схема определяет цвета только для GUI. И наоборот, если при использовании графического Vim (например, gvim или MacVim) цвета выглядят «слетевшими», возможно, в цветовой схеме определены цвета только для терминала.

Последний вариант может быть «исправлен» разрешением использования полноцветной палитры (true colors) в Neovim или в Vim версии 7.4.1830 и новее. В этом случае терминальный Vim будет использовать определения цветов для GUI, но это потребует того, чтобы соответствующий эмулятор терминала, а так же всё остальное программное обеспечение (например, tmux) могли работать с полноцветной палитрой. (Вот эта статья содержит хороший обзор на тему цвета в терминале.)

Справка:

Свёртка

Каждый текст (или исходный код) имеет некоторую структуру. Если у вас есть структура, это значит, что у вас есть диапазоны логически разделённого текста. Свёртка позволяет «завернуть» такой диапазон в одну строку и показывать вместо него короткое описание. Есть много команд, которые работают с такими диапазонами, называемыми свёртками. Свёртки могут быть вложенными.

В Vim есть несколько методов свёртки:

'foldmethod' Использование
diff Используется в окнах diff для свёртки неизменённого текста.
expr Использует 'foldexpr', на основе которого создаётся новый метод свёртки.
indent Свёртки на основе отступов.
manual Ручное создание свёртки при помощи команд zf, zF и :fold.
marker Свёртки основанные на маркерах в тексте (часто в комментариях).
syntax Свёртки основанные на синтаксисе, например, свёртка блоков if.

NB: Свёртка может потребовать интенсивных вычислений! Если вы испытываете снижение производительности (небольшие задержки при наборе текста), посмотрите FastFold, который предотвращает Vim от обновления свёрток, когда это не нужно.

Справка:

:h usr_28
:h folds

Сессии

Если сохранить вид (:h :mkview), текущее состояние окна (плюс опции и мапинги) будут сохранены для дальнейшего использования (:h :loadview).

Сессия сохраняет вид всех окон плюс глобальные настройки. Такое сохранение создаёт снимок текущего состояния Vim и сохраняет его в файл сессии. Необходимо подчеркнуть: она сохраняет состояние на данный момент; всё, что будет сделано после сохранения сессии, не станет частью файла сессии. Для «обновления» сессии просто сохраните её заново.

Это создаёт удобство для сохранения ваших проектов и простого переключения между ними.

Попробуйте прямо сейчас! Откройте несколько окон и табов и дайте команду :mksession! Foo.vim. Если опустить имя файла, будет использовано Session.vim. Файл будет сохранён в текущем рабочем каталоге, посмотрите результат :pwd. Перезапустите Vim и выполните :source Foo.vim, и, вуаля, список буферов, раскладка окон, мапинги, текущий каталог и т.д. станут такими же, как и в момент сохранения сессии. Поработайте ещё немного и обновите сессию, перезаписав существующий файл сессии при помощи :mksession! Foo.vim.

Нужно заметить, что файл сессии на самом деле — просто набор команд Vim, предназначенных для восстановления текущего состояния Vim, поэтому его можно запросто просмотреть: :vs Foo.vim.

Можно сказать Vim что конкретно нужно сохранять в сессии, настроив 'sessionoptions'.

Для использования при написании скриптов Vim сохраняет имя последнего прочитанного или сохранённого файла сессии во внутренней переменной v:this_session.

Справка:

:h Session
:h 'sessionoptions'
:h v:this_session

Локальность

Многие понятия, упоминаемые выше, имеют локальных партнёров:

Глобальный Локальный Видимость Справка
:set :setlocal буфер или окно :h local-options
:map :map <buffer> буфер :h :map-local
:autocmd :autocmd * <buffer> буфер :h autocmd-buflocal
:cd :lcd окно :h :lcd
<leader> <localleader> буфер :h maplocalleader

Переменные тоже имеют различные области видимости.

Использование

Получение справки офлайн

Vim поставляется с великолепной документацией в виде одиночных текстовых файлов в специальном формате. Для доступа к конкретным местам этих файлов справки Vim использует систему тегов.

Прежде всего прочитайте вот это: :help :help. Эта команда откроет файл $VIMRUNTIME/doc/helphelp.txt в новом окне и перепрыгнет в этом файле к тегу :help.

Несколько простых правил:

Для просмотра списка всех тегов, соответствующих запросу, набранному в данный момент, можно использовать <c-d> (это ctrl+d).

Хотите посмотреть список всех функций VimL? Просто наберите :h ()<c-d>. Хотите посмотреть функции для работы с окнами? :h win*()<c-d>.

Скоро это станет вашей второй натурой, но, особенно в начале, иногда вы не будете знать даже части тега того, что вы ищете. В голову вам могут прийти только некоторые ключевые слова, которые к этому относятся. И тогда вам на помощь придет :helpgrep!

:helpgrep backwards

Эта команда будет искать слово «backwards» во всех файлах документации и перепрыгнет к первому совпадению. Совпадения будут собраны в список quickfix. Используйте :cn/:cp для перехода к следующему/предыдущему совпадению. Или откройте окно quickfix при помощи :copen, перейдите к нужной записи и нажмите <cr> для перехода к этому совпадению. Всю правду об этом см. в :h quickfix.

Получение справки офлайн (альтернатива)

Этот список был подобран @chrisbra, одним из самых активных разработчиков Vim, и размещён в vim_dev.

Здесь он размещён с небольшими изменениями.


Если вы знаете что ищете, то обычно проще искать это в справочной системе. Потому что она организована в определённом стиле.

Кроме того, справка имеет преимущество в том, что она относится к конкретной версии Vim, поэтому вам не будут попадаться устаревшие или добавленные позже темы.

Очень важно научиться пользоваться справкой и понимать её язык. Здесь приводится несколько примеров (это далеко неполный список, и, возможно, я что-то и забыл).

  1. Опции заключаются в одиночные кавычки. Поэтому вы можете использовать :h 'list', чтобы открыть тему, посвящённую опции list. Если нужно найти какую-то опцию, то можно командой :h options.txt открыть страницу помощи, на которой описывается работа со всеми опциями, а затем найти нужную при помощи регулярных выражений, например, /width. Некоторые опции имеют своё пространство имён, например, :h cpo-a, :h cpo-A, :h cpo-b и т.д.

  2. Команды нормального режима используются как есть. Чтобы перейти к описанию команды «gt», используйте :h gt.

  3. Элементы регулярных выражений всегда начинаются с «/», поэтому :h /\+ выдаст вам справку о квантификаторе «+» в регулярных выражениях Vim. Если вы хотите узнать всё о регулярных выражениях, начните с чтения :h pattern.txt.

  4. Комбинации клавиш. Обычно они начинаются с буквы, обозначающей режим, в котором комбинация используется. Например, :h i_CTRL-X приведёт к семейству команд CTRL-X режима вставки, которые используются для различных способов автодополнения. Нужно отметить, что одна и та же клавиша пишется всегда одинаково, т.е., Control всегда будет CTRL. Кроме того, для команд нормального режима буква «n» не добавляется, как в :h CTRL-A. В противоположность этому :h c_CTRL-R описывает то, что делает CTRL-R при вводе команд, :h v_CTRL-A говорит об увеличении чисел в визуальном режиме, а :h g_CTRL-A — о команде g<C-A> (т.е. после нажатия «g» нажимается <Ctrl-A>). Здесь «g» относится к команде нормального режима «g», которая всегда, чтобы что-то выполнить, ожидает вторую клавишу, как и команды, которые начинаются с «z».

  5. Регистры всегда начинаются с «кавычки», поэтому для поиска информации о специальном регистре «:» используйте :h quote.

  6. Описание языка сценариев Vim (VimL) находится в :h eval.txt. Некоторые аспекты языка рассматриваются в :h expr-X, где 'X' — одиночный символ, например, :h expr-! приведёт вас к топику, в котором описывается оператор VimL '!' (логическое отрицание). Не менее важна тема :h function-list для поиска краткого описания всех доступных функций.

  7. Мапинг обсуждается на странице справки :h map.txt. Чтобы найти информацию о команде :imap, используйте :h mapmode-i. Кроме того, чтобы найти информацию по некоторой подтеме, относящейся к мапингу, используйте map-topic, например, для мапинга, локального для буфера, — :h :map-local или :h map_bar, чтобы узнать, как в мапинге обрабатывается '|'.

  8. Определения команд обсуждаются в :h command-*, поэтому используйте :h command-bang, чтобы узнать об аргументе '!' в пользовательских командах.

  9. Команды управления окнами начинаются с CTRL-W, поэтому и соответствующая справка находится в :h CTRL-W_* (например, :h CTRL-W_p о переходе к ранее активному окну). Если вы ищете команду управления окнами, можно открыть :h window.txt и поискать её там.

  10. Команды Ex начинаются с «:», следовательно :h :s содержит описание команды «:s».

  11. Используйте CTRL-D после набора названия топика и позвольте Vim завершить набор из названий всех доступных топиков.

  12. Для поиска по всем страницам справки используйте :helpgrep (обычно в этот поиск попадает и справка по установленным плагинам). Как этим пользоваться см. :h :helpgrep. После того как будет найдена какая-то тема, все совпадения будут доступны в окне quickfix (или местоположений), которое можно открыть при помощи :copen или :lopen. Там можно использовать / для дополнительной фильтрации.

  13. Как использовать справку, описано в :h helphelp.

  14. Руководство пользователя. В нём в дружественной манере описывается содержимое тем справки для начинающих. Начните с :h usr_toc.txt, чтобы посмотреть содержание (наверное, вы так и подумали). Быстро проглядев эту страницу, можно найти, например, вхождение «Digraphs» и «Entering special characters» в части 24 (для перехода к этой странице справки используйте :h usr_24.txt).

  15. Группы подсветки синтаксиса начинаются с hl-*. Например, в :h hl-WarningMsg рассказывается о группе подсветки «WarningMsg».

  16. У подсветки синтаксиса есть своё пространство имён в «:syn-topic», например, :h syn-conceal говорит о скрытых аргументах команды :syn.

  17. Команды quickfix обычно начинаются с «:c», а команды списка местоположений — с «:l».

  18. Об автокоманде BufWinLeave говорится в :h BufWinLeave. Кроме того, все возможные события описаны в :h autocommands-events.

  19. Аргументы командной строки начинаются с «-», соответственно :h -f приведёт вас к справке о ключе командной строки «-f» Vim.

  20. Дополнительные функциональности, включенные при компиляции, начинаются с «+», поэтому :h +conceal расскажет о поддержке опции conceal.

  21. Описания кодов ошибок можно искать как они есть. :h E297 приведёт вас точно к описанию этого сообщения об ошибке. Но иногда описания кода ошибки нет, и она описывается вместе с командой, которая может её вызвать. Например, :h E128 приведёт нас к описанию команды :function.

  22. Документация по включенным файлам подсветки синтаксиса обычно доступна как :h ft-*-syntax. Например, в :h ft-c-syntax говорится о подсветке синтаксиса языка C и возможных его настройках. Иногда приводятся описания omni-функций автодополнения (например, :h ft-php-omni) или плагинов типов файлов (:h ft-tex-plugin).

В начале страницы помощи могут приводиться ссылки на документацию пользователя, если она доступна (в ней команды описываются с точки зрения пользователя и менее детально). Например, в :h pattern.txt упоминаются темы из руководства пользователя :h 03.9 и :h usr_27.

Получение справки онлайн

Если у вас есть проблема, которую вы не можете разрешить самостоятельно, или вы нуждаетесь в подсказке для её решения, посмотрите список рассылки vim_use. Ещё один отличный ресурс — IRC. Канал #vim на Freenode — очень большой и обычно полон людьми, готовыми помочь.

Если вы хотите сообщить об ошибке, используйте список рассылки vim_dev.

Автокоманды на практике

Любое событие можно вызвать прямо сейчас: :doautocmd BufRead.

Пользовательские события

Для плагинов полезно создавать свои, «Пользовательские» события:

function! Chibby()
  " Здесь много чего происходит.
  " И наконец...
  doautocmd User ChibbyExit
endfunction

Теперь пользователи вашего плагина смогут сделать всё, что угодно, когда Chibby закончит свою работу:

autocmd User ChibbyExit call ChibbyCleanup()

Кстати, если «перехватывающая» :autocmd отсутствует, :doautocmd будет выводить надоедливое сообщение «No matching autocommands». Вот почему многие плагины используют silent doautocmd. Но у этого есть недостаток в том, что вы не сможете использовать простое echo "foo" в :autocmd, вместо этого придётся использовать unsilent echo "foo"...

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

if exists('#User#ChibbyExit')
  doautocmd User ChibbyExit
endif

Справка: :h User

Вложенные автокоманды

По умолчанию автокоманды не вкладываются друг в друга! Если autocmd исполняет команду, которая в свою очередь может создать другое событие, то это событие не будет создано.

Допустим, при каждом запуске Vim вы хотите автоматически открывать ваш vimrc:

autocmd VimEnter * edit $MYVIMRC

Теперь, когда вы запустите Vim, он откроет ваш vimrc, но первое, что вы заметите, это то, что не будет никакой подсветки синтаксиса, хотя раньше она была.

Проблема состоит в том, что :edit в вашей невложенной autocmd не вызовет события «BufRead», поэтому тип файла не получит значения «vim», а$VIMRUNTIME/syntax/vim.vim не будет прочитан. См. :au BufRead *.vim. Вместо этого нужно использовать следующее:

autocmd VimEnter * nested edit $MYVIMRC

Справка: :h autocmd-nested

Буфер обмена

Требует включения функциональности: +clipboard и, возможно, +xterm_clipboard, если вам понадобится использовать опцию 'clipboard' в Unix-системе без поддержки GUI.

Справка:

:h 'clipboard'
:h gui-clipboard
:h gui-selections

См. также: Вставка в скобках (или почему мне всё время приходится переустанавливать 'paste'?).

Использование буфера обмена (Windows, macOS)

В Windows есть буфер обмена, а в macOS — pasteboard.

Оба работают так, как этого ожидает большинство пользователей. Вы копируете выделенный текст при помощи ctrl+c/cmd+c и вставляете его в другом приложении при помощи ctrl+v/cmd+v.

Нужно отметить, что скопированный текст реально копируется в буфер обмена, поэтому приложение, в котором вы скопировали текст, можно закрыть перед тем, как без проблем вставить в другом.

Всякий раз, когда вы работаете с буфером обмена, его регистр * заполнен выделением. В Vim вы используете команды "*y и "*p для копирования в и вставки из буфера.

Если вам не нравится каждый раз указывать регистр *, то добавьте это в ваш vimrc:

set clipboard=unnamed

Обычно все операции копирования/удаления/вставки работают с регистром ", но после этого в этих же целях будет использоваться регистр *, поэтому будет достаточно простых y и p.

Позвольте повторить: использование вышеупомянутой опции означает, что каждое копирование/вставка, даже, если оно делается в границах одного окна Vim, будет изменять системный буфер обмена. Вам решать, полезно это для вас или нет.

Если вам даже y набирать лениво, вы можете отправлять в буфер обмена любое визуальное выделение при помощи такой настройки:

set clipboard=unnamed,autoselect
set guioptions+=a

Справка:

:h clipboard-unnamed
:h autoselect
:h 'go_a'

Использование буфера обмена (Linux, BSD...)

Если в вашей системе используется X, то тут всё работает немного по-другому. X реализует протокол X Window System Protocol, который был введён в мажорной версии 11 в 1987 году, поэтому X часто называют X11.

До этого, в X10, использовались буферы вырезки, которые работали подобно буферу обмена в том смысле, что скопированный текст хранился самими X, и был доступен всем остальным приложениям. Этот механизм в X существует до сих пор, но сейчас его использование не приветствуется, и большинство программ его вообще не используют.

В настоящее время данные передаются между приложениями в терминах выделений. Из 3 определённых атомов выделения, на практике используются только 2: ОСНОВНОЕ и БУФЕР ОБМЕНА.

Выделения работают примерно так:

Программа A: <ctrl+c>
Программа A: заявляет о том, что захватила владение БУФЕРОМ ОБМЕНА
Программа B: <ctrl+v>
Программа B: узнала о том, БУФЕРОМ ОБМЕНА владеет Программа A
Программа B: делает запрос к Программе A
Программа A: отвечает на запрос и отправляет данные Программе B
Программа B: получает данные из Программы A и вставляет их в своё окно
Выделение Когда используется? Как вставить? Как получить доступ из Vim?
ОСНОВНОЕ Выделение текста средняя-кнопка-мыши, shift+insert регистр *
БУФЕР ОБМЕНА Выделение текста плюс ctrl+c ctrl+v регистр +

NB: Выделения (и даже выделения БУФЕРА ОБМЕНА) никогда не хранятся самим сервером X! Поэтому вы потеряете данные, скопированные при помощи ctrl+c, если закроете приложение.

Используйте для вставки ОСНОВНОГО выделения "*p, а для копирования всего файла в БУФЕР ОБМЕНА — "+y1G.

Если вам захочется пользоваться одним из этих регистров постоянно, попробуйте использовать:

set clipboard^=unnamed      " регистр *
" или
set clipboard^=unammedplus  " регистр +

(Присваивание вида ^= используется, чтобы сначала подставить значение по умолчанию, см. :h :set^=.)

Такая настройка сделает так, что все операции копирования/удаления/вставки будут вместо неименованного регистра " использовать либо регистр *, либо +. Кроме того, при помощи простых нажатий y и p у вас будет доступ к выделениям в X.

Справка:

:h clipboard-unnamed
:h clipboard-unnamedplus

Восстановление позиции курсора при повторном открытии файла

При открытии файла курсор будет находиться в 1-й колонке 1-й строки. К счастью, файл viminfo запоминает метки. Метка " содержит позицию в буфере при выходе из него.

autocmd BufReadPost *
    \ if line("'\"") > 1 && line("'\"") <= line("$") |
    \   execute "normal! g`\"" |
    \ endif

Читайте это так: Если метка " содержит номер строки больше, чем 1, и не больше, чем номер последней строки в файле, то перепрыгнуть к ней.

:h viminfo-'
:h `quote
:h g`

Временные файлы

Файлы резервной копии

Перед сохранением файла Vim создаёт его резервную копию. Если запись на диск произошла успешно, резервная копия удаляется.

Если установить :set backup, резервная копия будет сохранена. Это означает, что она будет содержать то же, что содержал оригинальный файл до последнего сохранения.

При помощи :set nobackup nowritebackup можно полностью отменить сохранение резервной копии, но даже в нынешнее время этого делать не нужно. 'writebackup' — функция безопасности, которая не позволит вам потерять оригинальный файл при сохранении, что рано или поздно может произойти, и не важно, сохраните ли вы резервную копию после записи или нет.

Если вы часто используете Vim для редактирования очень больших файлов, чего, возможно, делать не стоит, то для них можно отключить создание резервной копии при помощи 'backupskip'.

Vim различает два способа создания резервной копии: копирование и переименование.

Мельчайшие подробности см. :h 'backupcopy'.


Демо:

:set backup backupskip= backupdir=. backupext=-backup
:e /tmp/foo
ifoo<esc>
:w
" Создаётся оригинальный файл, необходимости в резервной копии нет
obar<esc>
:w
" создаётся резервная копия, а оригинальный файл обновляется
$ diff -u /tmp/foo-backup /tmp/foo
--- /tmp/foo-backup     2017-04-22 15:05:13.000000000 +0200
+++ /tmp/foo    2017-04-22 15:05:25.000000000 +0200
@@ -1 +1,2 @@
 foo
+bar

:h backup
:h write-fail

Своп-файлы

При редактировании файла несохранённые изменения записываются в своп-файл.

Узнать имя текущего своп-файла можно при помощи :swapname. Отключить его — при помощи :set noswapfile.

Своп-файл обновляется либо после набора 200 символов, либо когда ничего не печатается в течение 4 секунд. По окончании редактирования файла своп-файл удаляется. Как изменить периодичность см. :h 'updatecount' и :h 'updatetime'.

При «вылете» Vim-а (например, при отключении электричества) вы потеряете все изменения с момента последней записи файла на диск, но своп-файл при этом удалён не будет. Теперь, при редактировании файла вновь, Vim предоставит шанс восстановить файл из своп-файла.

При попытке двух людей редактировать один и тот же файл второй из них будет извещён, что своп-файл уже существует. Это предотвратит людей от сохранения различных версий файла. Если вам не нравится такое поведение, см. :h 'directory'.

:h swap-file
:h usr_11

Файлы отмены

Дерево отмены хранится в памяти и теряется при выходе их Vim. Если вы хотите, чтобы оно сохранялось, используйте :set undofile. Эта команда сохранит файл отмены для ~/foo.c в ~/foo.c.un~.

:h 'undofile'
:h undo-persistence

Файлы viminfo

В то время, как файлы резервной копии, свопа и отмены используются для сохранения информации о состоянии редактируемых файлов, файл viminfo используется для сохранения всего остального, что будет утеряно при выходе из Vim. Он содержит историю (командной строки, поиска, ввода), регистры, метки, список буферов, состояние глобальных переменных и т.д.

По умолчанию файл viminfo записывается как ~/.viminfo.

:h viminfo
:h 'viminfo'

Пример конфигурации временных файлов

Поместить все временные файлы в свои собственные каталоги в ~/.vim/files:

" при необходимости создать каталог
if !isdirectory($HOME.'/.vim/files') && exists('*mkdir')
  call mkdir($HOME.'/.vim/files')
endif

" файлы резервной копии
set backup
set backupdir   =$HOME/.vim/files/backup/
set backupext   =-vimbackup
set backupskip  =
" своп-файлы
set directory   =$HOME/.vim/files/swap//
set updatecount =100
" файлы отмены
set undofile
set undodir     =$HOME/.vim/files/undo/
" viminfo-файлы
set viminfo     ='100,n$HOME/.vim/files/info/viminfo

Редактирование файлов на удалённом компьютере

В поставку Vim входит плагин netrw, который позволяет редактировать удалённые файлы. На самом деле он делает временную копию удалённого файла при помощи scp, открывает её в буфере, а при сохранении файла копирует его обратно на удалённый компьютер.

Это очень удобно, если вы хотите использовать свою локальную конфигурацию, а не подключаться к серверу по ssh и пользоваться тем, что считает нужным администратор сервера.

:e scp://bram@awesome.site.com/.vimrc

Если у вас есть настроенный ~/.ssh/config, это происходит автоматически:

Host awesome
    HostName awesome.site.com
    Port 1234
    User bram

Допустим, что выше приведено содержимое ~/.ssh/config, тогда эта команда тоже сработает:

:e scp://awesome/.vimrc

Похожим образом это можно сделать при помощи ~/.netrc, см. :h netrw-netrc.

Обязательно посмотрите :h netrw-ssh-hack и :h g:netrw_ssh_cmd.


Ещё одна возможность — использовать sshfs, которая для локального монтирования удалённой файловой системы использует FUSE.

Управление плагинами

Pathogen был первым популярным инструментом для управления плагинами. На самом деле он просто настраивает runtimepath (см. :h 'rtp'), чтобы подключались файлы из определённого каталога. Клонировать репозитории плагинов вам нужно вручную.

Настоящие менеджеры плагинов предоставляют команды, которые помогают вам устанавливать и обновлять плагины прямо в Vim. Далее в алфавитном порядке перечисляются наиболее часто используемые менеджеры плагинов.

Список менеджеров плагинов

Блочная вставка

Этот приём позволяет вставить определённый текст на нескольких смежных строках одним движением. См. вот это демо.

Переключитесь в режим выделения визуального блока при помощи <c-v>. После этого перейдите на несколько строк вниз. Нажмите I или A и начинайте набор нужного текста.

Поначалу будет казаться странным, но текст будет вставляться только в текущей строке, и только по окончании ввода этот же текст появится во всех остальных сроках, которые были отмечены при визуальном выделении.

Простейший пример: <c-v>3jItext<esc>.

Если у вас строки различной длины, и вы хотите добавить некий текст в конце каждой, то используйте это: <c-v>3j$Atext<esc>.

Возможно, вам понадобиться поместить курсор где-то за пределами конца текущей строки. По умолчанию этого сделать нельзя, но можно установить опцию virtualedit:

set virtualedit=all

С этой настройкой $10l и 90| позволят работать за пределами конца строки.

Дополнительно см. :h blockwise-examples. Поначалу это будет выглядеть сложновато, но очень быстро станет второй натурой.

Если вам хочется реальной фантастики, посмотрите множественные курсоры.

Запуск внешних программ и использование фильтров

Предупреждение: Vim однопоточен, поэтому запуск внешней программы будет блокировать всё остальное. Конечно, можно использовать программные интерфейсы Vim, например Lua, и использовать их многопоточность, но в настоящее время процесс Vim будет заблокирован всегда. В Neovim это исправлено при помощи специального API.

(По всей видимости, Bram собирается добавить в Vim управление заданиями. Если у вас одна из свежих версий, см. :helpgrep startjob.)

Для запуска задания используйте :!. Для просмотра списка файлов в текущем рабочем каталоге используйте :!ls. Для создания канала (конвейера), как в оболочке, используйте |, например, :!ls -1 | sort | tail -n5.

Без указания диапазона вывод :! будет показан в прокручиваемом окне. Но если диапазон указать, то строки будут отфильтрованы. Это значит, что они будут переданы по каналу в стандартный ввод программы–фильтра, а после обработки будут заменены стандартным выводом фильтра. Например, чтобы пронумеровать следующие 5 строк, используйте такую команду:

:.,+4!nl -ba -w1 -s' '

Т.к. набор диапазона — не всегда простое занятие, в Vim для удобства предусмотрены некоторые вспомогательные приёмы. Как всегда при работе с диапазонами, можно выделить строки в визуальном режиме и нажать :. Кроме того, есть ещё оператор !, который в качестве параметра принимает перемещение. Например, !ip!sort отсортирует строки в текущем параграфе.

Хорошим примером применения фильтрации служит язык программирования Go. У него достаточно жёсткие требования к форматированию, такие, что с компилятором для правильной расстановки отступов идёт фильтр, который называется gofmt. Поэтому плагины для Go часто предоставляют вспомогательную команду :Fmt, которая просто выполняет :%!gofmt, чем расставляет отступы во всём файле.

Люди часто используют :r !prog для вставки вывода prog, после текущей строки, что удобно для скриптов, но делая это по ходу набора, я понял для себя, что вместо этого проще использовать !!ls, которая заменяет текущую строку.

:h filter
:h :read!

Cscope

Cscope делает больше, чем ctags, но поддерживает только C (и в некоторой степени C++ и Java).

В то время, как файлу с тегами известно только то, где символ определён, база данных cscope о ваших данных знает значительно больше:

1. Создайте базу данных

В корне вашего проекта сделайте следующее:

$ cscope -bqR

В текущем рабочем каталоге создадутся 3 файла: cscope{,.in,.po}.out. Думайте о них как о вашей базе данных.

К сожалению, cscope по умолчанию анализирует только файлы *.[c|h|y|l]. Если вам нужно использовать cscope для проекта на Java, сделайте так:

$ find . -name "*.java" > cscope.files
$ cscope -bq

2. Добавьте базу данных

Соединитесь с вашей свежесозданной базой данных:

:cs add cscope.out

Проверьте созданное соединение:

:cs show

(Да, вы можете добавлять несколько соединений.)

3. Сделайте запрос к базе данных

:cs find <тип> <запрос>

Например, :cs find d foo покажет список всех функций, которые вызывает foo(...).

Тип Описание
s symbol: найти все ссылки на символ
g global: найти глобальное(ные) определение(ния) символа
c calls: найти все вызовы функции
t text: найти все вхождения текста
e egrep: поиск слова при помощи egrep
f file: открыть файл по имени
i includes: найти файлы, которые включают этот файл
d depends: найти функции, которые вызывает эта функция

Предложу некоторые полезные мапинги, например:

nnoremap <buffer> <leader>cs :cscope find s  <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cg :cscope find g  <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cc :cscope find c  <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>ct :cscope find t  <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>ce :cscope find e  <c-r>=expand('<cword>')<cr><cr>
nnoremap <buffer> <leader>cf :cscope find f  <c-r>=expand('<cfile>')<cr><cr>
nnoremap <buffer> <leader>ci :cscope find i ^<c-r>=expand('<cfile>')<cr>$<cr>
nnoremap <buffer> <leader>cd :cscope find d  <c-r>=expand('<cword>')<cr><cr>

Таким образом, если :tag (или <c-]>) переходит к определению, используя файл с тегами, то :cstag делает то же самое, но при этом использует присоединённую базу данных cscope. Опция 'cscopetag' заставляет :tag действовать подобно :cstag автоматически. Это очень удобно, если у вас есть мапинги для тегов.

Справка: :h cscope

MatchIt

Т.к. Vim написан на C, многие приёмы подразумевают синтаксис похожий на C. По умолчанию, если курсор находится на { или #endif, можно использовать % для прыжка к соответствующему } или #ifdef.

Vim поставляется в комплекте с плагином matchit.vim, который по умолчанию не включен. Он позволяет % переходить также по тегам HTML, конструкциям if/else/endif языка VimL и добавляет несколько команд.

Установка для Vim 8

" vimrc
packadd! matchit

Установка для Vim 7 и более ранних

" vimrc
runtime macros/matchit.vim

Документация matchit довольно обширна, поэтому я рекомендую единожды проделать следующее:

:!mkdir -p ~/.vim/doc
:!cp $VIMRUNTIME/macros/matchit.txt ~/.vim/doc
:helptags ~/.vim/doc

Небольшое введение

Теперь плагин готов к использованию. См. :h matchit-intro, чтобы узнать о поддерживаемых командах, и :h matchit-languages, чтобы узнать о поддерживаемых языках.

Там говорится о том, как просто определить свои пары соответствий:

autocmd FileType python let b:match_words = '\<if\>:\<elif\>:\<else\>'

После этого в любом файле на Python вы сможете обходить эти 3 выражения при помощи % (вперёд) и g% (назад).

Help:

:h matchit-install
:h matchit
:h b:match_words

Полноцветная палитра

Использование полноцветной палитры (True color) в эмуляторе терминала означает возможность использования 24 бит для RGB-цвета. Это даёт 16777216 (2^24) цветов вместо обычных 256-и.

Как говорится здесь, цветовые схемы на самом деле могут состоять из двух цветовых схем, одна с определениями для терминала (xterm), другая — для GUI (gvim). Это имело смысл, пока эмуляторы терминала не научились работать с полноцветной палитрой.

После :set termguicolors Vim начинает выдавать escape-последовательности, которые понимает только эмулятор терминала, поддерживающий полноцветную палитру. Если ваши цвета выглядят странно, есть все шансы, что либо ваш эмулятор терминала не поддерживает полноцветную палитру, либо ваша цветовая схема не содержит определений цветов для GUI.

Многие люди используют терминальный мультиплексор tmux, который по существу находится между эмулятором терминала и Vim. Чтобы tmux начал пропускать полноцветные escape-последовательности, которые выдаёт Vim, необходимо в пользовательский .tmux.conf добавить следующее:

set-option -g  default-terminal 'tmux-256color'
set-option -ga terminal-overrides ',xterm-256color:Tc'

Подытожим, вот чеклист для включения полноцветной палитры:

Популярный справочник по цветам в терминале: https://gist.github.com/XVilka/8346728

Полезные советы

Разумное поведение n и N

Направление, куда ведут n и N, зависит от того, какая команда использовалась, / или ?, для начала поиска вперёд или назад соответственно. Меня это немного путает.

Если вы хотите, чтобы n всегда искало вперёд, а N назад, используйте это:

nnoremap <expr> n  'Nn'[v:searchforward]
nnoremap <expr> N  'nN'[v:searchforward]

Более умная история командной строки

Если вы похожи на меня, то используете <c-n> и <c-p> для перехода между предыдущим и следующим элементами соответственно. По умолчанию это работает и в командной строке и вызывает предыдущие и следующие команды из истории.

Что ж, неплохо. Но <up> и <down> ещё полезнее! Они вызывают команды из истории, чьё начало соответствует содержимому командной строки. Например, :echo <up> могут заменить на :echo "Vim рулит!".

Конечно же, я не хочу, чтобы вам приходилось тянуться к клавишам стрелок, поэтому замапьте их вот так:

cnoremap <c-n>  <down>
cnoremap <c-p>  <up>

Мне такое поведение пригождается помногу раз в день.

Улучшите CTRL-L

По умолчанию <c-l> очищает и перерисовывает содержимое экрана (как :redraw!). Следующий мапинг делает то же самое, плюс сбрасывает подсветку соответствий найденных путём /, ? и т.д., исправляет подсветку синтаксиса (иногда Vim теряется в ней из-за сложности её правил) и вызывает обновление подсветки синтаксиса в режиме diff:

nnoremap <leader>l :nohlsearch<cr>:diffupdate<cr>:syntax sync fromstart<cr><c-l>

Выключите аудио и визуальные звонки

set noerrorbells
set novisualbell
set t_vb=

См. Vim Wiki: Disable beeping.

Быстрое перемещение текущей строки

Иногда мне бывает нужен быстрый способ переместить текущую строку выше или ниже:

nnoremap [e  :<c-u>execute 'move -1-'. v:count1<cr>
nnoremap ]e  :<c-u>execute 'move +'. v:count1<cr>

Эти мапинги принимают счётчик, поэтому 2]e переместит текущую строку на 2 строки ниже.

Быстрое добавление пустых строк

nnoremap [<space>  :<c-u>put! =repeat(nr2char(10), v:count1)<cr>'[
nnoremap ]<space>  :<c-u>put =repeat(nr2char(10), v:count1)<cr>

Теперь 5[<space> вставит пять пустых строк над текущей.

Быстрое редактирование макро

Это — просто жемчужина! Мапинг берёт регистр (или * по умолчанию) и открывает его в окне командной строки. Нажмите <cr> по окончании редактирования для установки содержимого регистра.

Я часто использую это для исправления опечаток, сделанных при записи макро.

nnoremap <leader>m  :<c-u><c-r><c-r>='let @'. v:register .' = '. string(getreg(v:register))<cr><c-f><left>

Используйте примерно так: <leader>m или "q<leader>m.

Обратите внимание на использование <c-r><c-r>, с целью убедиться, что <c-r> вставляется литерально. См. :h c_^R^R.

Быстрый прыжок к заголовочному файлу или исходнику

Наверняка, эта техника может применяться ко многим типам файлов. Она устанавливает файловые метки (см. :h marks) при покидании заголовочного файла или исходника, поэтому вы можете быстро вернуться к файлу, который редактировали последним, при помощи 'C или 'H (см. :h 'A).

autocmd BufLeave *.{c,cpp} mark C
autocmd BufLeave *.h       mark H

NB: Эта информация сохраняется в файле viminfo, поэтому убедитесь, что :set viminfo? включает :h viminfo-'.

Быстрая смена размера шрифта в GUI

Кажется, это было взято из конфига tpope:

command! Bigger  :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)+1', '')
command! Smaller :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)-1', '')

Изменение стиля курсора в зависимости от режима

Мне нравится использовать курсор в форме блока в нормальном режиме, в форме буквы I в режиме вставки и в виде подчёркивания в режиме замены. В том числе и при работе в tmux.

if empty($TMUX)
  let &t_SI = "\<Esc>]50;CursorShape=1\x7"
  let &t_EI = "\<Esc>]50;CursorShape=0\x7"
  let &t_SR = "\<Esc>]50;CursorShape=2\x7"
else
  let &t_SI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=1\x7\<Esc>\\"
  let &t_EI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=0\x7\<Esc>\\"
  let &t_SR = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=2\x7\<Esc>\\"
endif

Это просто сообщит Vim, что при входе/выходе из режима вставки нужно напечатать определённую последовательность символов (управляющая последовательность). Терминал, в котором работает Vim, или програма, которая находится между ним и терминалом (например, tmux), обработает и применит её.

Однако здесь есть одна проблема: существует множество реализаций эмуляторов терминала, и не все они используют одинаковые последовательности для выполнения одних и тех же действий. Последовательности, которые использованы выше, возможно, не заработают в вашей конфигурации. Она может вообще не поддерживать различные стили курсора. Обратитесь к документации.

Вышеприведённый пример работает в iTerm2.

Не теряйте выделение при сдвиге в сторону

После выделения одной или более строк вы можете использовать < и > для сдвига и в сторону. К сожалению, после этого вы сразу же потеряете выделение.

Для восстановления выделения можно использовать gv, поэтому проблему можно обойти примерно так:

xnoremap <  <gv
xnoremap >  >gv

Теперь с вашим визуальным выделением без проблем можно использовать >>>>>.

NB: Того же самого можно достигнуть при помощи команды ., которая повторяет последнее изменение.

Перезагрузка файла при сохранении

При помощи автокоманд с файлом можно сделать всё, что угодно, например, запустить в случае дот-файла (настроек) или пропустить через линтер для нахождения синтаксических ошибок в вашем исходном коде.

autocmd BufWritePost $MYVIMRC source $MYVIMRC
autocmd BufWritePost ~/.Xdefaults call system('xrdb ~/.Xdefaults')

Более умная линия курсора

Мне нравится линия курсора, но мне нравится использовать её только в активном окне, когда я не нахожусь в режиме вставки:

autocmd InsertLeave,WinEnter * set cursorline
autocmd InsertEnter,WinLeave * set nocursorline

Более быстрое дополнение ключевых слов

Дополнение ключевых слов (<c-n>/<c-p>) пытается сделать дополнение из всего, что перечислено в опции complete. По умолчанию она включает теги (которые могут надоедать) и сканирование всех включенных файлов (что может быть очень медленным). Если вы сможете прожить без всего этого, отключите:

set complete-=i   " запретить сканирование включаемых файлов
set complete-=t   " запретить поиск тегов

Косметические изменения в цветовых схемах

Всегда используйте тёмно-серую строку статуса, какая бы ни была выбрана цветовая схема:

autocmd ColorScheme * highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE

Это будет работать при любом использовании :colorscheme .... При желании воздействовать только на определённую цветовую схему:

autocmd ColorScheme desert highlight StatusLine ctermbg=darkgray cterm=NONE guibg=darkgray gui=NONE

Это будет работать только для :colorscheme desert.

Команды

Полезные команды, которые неплохо бы знать. Используйте :h :<command name>, чтобы узнать о них больше, например, :h :global.

:global и :vglobal

Выполнение команды во всех соответствующих условию строках. Например, :global /regexp/ print будет использовать :print для всех строк, которые содержат «regexp» (регулярное выражение).

Забавный факт: Возможно, всем известен старый добрый grep, программа–фильтр, которую написал Ken Thompson. Что же она делает? Она печатает все строки, которые соответствуют некоторому регулярному выражению! Теперь давайте угадаем, как выглядит короткая форма :global /regexp/ print? Правильно! Это — :g/re/p. Ken Thompson был вдохновлён командой :global редактора vi, когда писал свой grep.

Вопреки своему имени, :global только по умолчанию работает во всех строках, но может принимать и диапазон. Допустим, вы хотите использовать :delete для всех строк, содержащих «foo», до первой пустой (которая соответствует регулярному выражению ^$):

:,/^$/g/foo/d

Для выполнения команд на всех строках, которые не соответствуют шаблону, используйте :global! или его псевдоним :vglobal (представляйте inVerse — инверсно).

:normal и :execute

Эти команды часто используются в скриптах Vim.

При помощи :normal вы можете выполнять команды нормального режима в командной строке. Например, :normal! 4j переместит курсор на 4 строки вниз (без использования каких либо дополнительных мапингов для «j» из-за присутствия «!»).

Не забывайте, что :normal также может принимать диапазон, поэтому :%norm! Iabc добавит «abc» в начало всех строк файла.

При помощи :execute можно смешивать команды и выражения. Допустим, что вы редактируете файл с исходным кодом на C и хотите переключиться на его заголовочный файл:

:execute 'edit' fnamemodify(expand('%'), ':r') . '.h'

Обе команды часто используются вместе. Например, вы хотите переместить курсор вниз на «n» строк:

:let n = 4
:execute 'normal!' n . 'j'

:redir и execute()

Многие команды выводят сообщения, а :redir позволяет перенаправить этот вывод. Можно сделать перенаправление в файл, регистр или переменную.

:redir => var
:reg
:redir END
:echo var
:" Ради шутки давайте также поместим это в текущий буфер.
:put =var

В Vim 8 есть ещё более короткий путь:

:put =execute('reg')

Справка:

:h :redir
:h execute()

Решение проблем

Общие рекомендации

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

vim -u NONE -N

Эта команда запустит Vim без vimrc (т.е. с настройками по умолчанию), но в несовместимом режиме (когда используются настройки по умолчанию Vim, а не vi). (См. :h --noplugin о других вариантах того, что загружать при запуске.)

Если проблема всё ещё воспроизводится, то, скорее всего, это баг в самом Vim! Сообщите об этом в рассылке vim_dev. В большинстве случаев проблема не будет решена сразу, и вам придется продолжить исследование.

Часто плагины привносят новое/изменённое/неправильное поведение. Например, если что-то происходит при сохранении, проверьте :verb au BufWritePost, чтобы получить список потенциальных виновников.

Если вы используете менеджер плагинов, отключайте их по очереди, пока не найдёте причину.

Проблема до сих пор не решена? Если это не плагин, то она должна скрываться в ваших настройках, таких как опции или автокоманды и т.д.

Пришло время двоичного поиска. Многократно разбивайте пространство поиска надвое, пока не найдёте виновную строку. Согласно природе деления на два, многих шагов это не потребует .

На практике это работает примерно так: поместите посередине вашего vimrc команду :finish. Vim пропустит всё, что находится после неё, Если проблема всё ещё возникает, то её причина находится в верхней половине. Переместите :finish в середину этой половины. В противном случае проблема кроется в неактивной нижней половине. Переместите :finish в её середину. И так далее.

Подробность сообщений

Ещё один полезный способ наблюдать за тем, что в данный момент делает Vim, это — увеличение уровня подробности сообщений. В настоящее время Vim поддерживает 9 различных уровней. Полный список см. :h 'verbose'.

:e /tmp/foo
:set verbose=2
:w
:set verbose=0

При таком уровне будут показаны все прочитанные файлы, например, файл отмены или плагины, которые участвуют в сохранении.

Если вы хотите увеличить уровень подробности только для одной команды, то для этого есть :verbose, которую нужно просто поставить перед любой другой командой. В качестве счётчика она принимает уровень подробности, который по умолчанию равен 1.

:verb set verbose
"  verbose=1
:10verb set verbose
"  verbose=10

Команда часто используется с уровнем по умолчанию 1 для просмотра, где была установлена опция в последний раз:

:verb set ai?
"      Last set from ~/.vim/vimrc

Естественно, что чем больше уровень подробности, тем большим он делает вывод. Но не нужно пугаться, вывод можно перенаправить в файл:

:set verbosefile=/tmp/foo | 15verbose echo "foo" | vsplit /tmp/foo

Можно выбрать уровень подробности при запуске при помощи опции -V. По умолчанию используется уровень 10. Например, vim -V5.

Профилирование времени запуска

Кажется, что Vim медленно запускается? Время похрустеть числами:

vim --startuptime /tmp/startup.log +q && vim /tmp/startup.log

Первая колонка — самая важная, потому что в ней показано абсолютное затраченное время. Если между двух строк имеется большая разница во времени, то вторая строка — либо очень большой файл, либо файл с ошибочным кодом на VimL, с которым стоит разобраться.

Профилирование во время работы

функциональность: +profile должна быть включена

Vim предоставляет встроенную функциональность для профилирования во время выполнения, которая является отличным инструментом для поиска медленного кода в вашем окружении.

Команда :profile имеет целый набор подкоманд для указания что нужно профилировать.

Если вы хотите профилировать всё подряд, сделайте так:

:profile start /tmp/profile.log
:profile file *
:profile func *
<сделайте что-нибудь в Vim>
<выйдите из Vim>

Vim хранит информацию о профилировании в памяти и сохраняет журнал работы только при выходе. (В Neovim это исправлено при помощи :profile dump).

Давайте взглянем на /tmp/profile.log. Тут показан весь код, который выполнялся во время профилирования. Каждая строка, как долго она выполнялась, и сколько времени на это ушло.

В большинстве случаев это будет код плагина, который не знаком пользователю, но если вы разбираетесь с какой-то проблемой, перейдите вниз журнала. Там есть два раздела: FUNCTIONS SORTED ON TOTAL TIME (функции, отсортированные по полному времени работы) и FUNCTIONS SORTED ON SELF TIME (функции, отсортированные по собственному времени), оба они на вес золота. Быстрого взгляда достаточно, чтобы определить, какая функция работала слишком долго.

Отладка скриптов Vim

Если вы раньше пользовались отладчиком в командной строке, то очень быстро освоите :debug.

Просто добавьте :debug перед любой командой, и вы попадёте в режим отладки. То есть, выполнение остановится перед первой строкой, которая должна быть выполнена, и эта строка будет показана.

Информацию о 6 доступных командах отладчика см. :h >cont и ниже, и заметьте, что, как и в gdb и похожих отладчиках, можно использовать короткие формы, т.е. c, q, n, s, i, и f.

Кроме этих команд, можно использовать любую другую команду Vim, например, :echo myvar, которая исполнится в контексте текущей позиции в коде.

При помощи простой команды :debug 1 вы попадаете в среду REPL.

Было бы мучительно, если бы для прохода каждой строки нужно было бы использовать отдельный шаг, поэтому, конечно, мы можем определять точки останова. (Точки останова потому так и называются, что выполнение останавливается при их достижении, поэтому вы можете запросто пропустить код, который вам не интересен.) Детальнее см. :h :breakadd, :h :breakdel, и :h :breaklist.

Допустим, вам захотелось узнать, какой код исполняется каждый раз, когда вы сохраняете файл:

:au BufWritePost
" signify  BufWritePost
"     *         call sy#start()
:breakadd func *start
:w
" Breakpoint in "sy#start" line 1
" Entering Debug mode.  Type "cont" to continue.
" function sy#start
" line 1: if g:signify_locked
>s
" function sy#start
" line 3: endif
>
" function sy#start
" line 5: let sy_path = resolve(expand('%:p'))
>q
:breakdel *

Как можно видеть, использование <cr> повторяет предыдущую команду отладчика, в данном примере — s.

:debug можно комбинировать с настройкой подробности сообщений.

Отладка файлов синтаксиса

Файлы синтаксиса часто бывают причиной замедления из-за ошибочных и/или сложных регулярных выражений. Если при компиляции включена функциональность +profile, Vim предоставляет очень полезную команду :syntime.

:syntime on
" нажмите несколько раз <c-l> для обновления содержимого окна, которое вызовет повторное применение правил раскраски синтаксиса
:syntime off
:syntime report

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

См. :h :syntime.

Разное

Дополнительные источники

Источник Описание
Seven habits of effective text editing «Семь привычек эффективного редактирования текстов», статья Брэма Мооленаара, автора Vim.
Seven habits of effective text editing 2.0 (PDF) См. выше. Презентация.
IBM DeveloperWorks: Scripting the Vim editor Серия из пяти частей, посвящённая написанию скриптов Vim.
Learn Vimscript the Hard Way Разработка плагинов Vim с нуля.
Practical Vim (2nd Edition) Просто лучшая книга о Vim.
Vimcasts.org Скринкастинги Vim.
Скринкасты wincent на YouTube Скринкастинги Vim.
Why, oh WHY, do those #?@! nutheads use vi? «Почему, ну почему, эти #?@! придурки используют vi?» — Развенчание некоторых заблуждений относительно Vim.
Your problem with Vim is that you don't grok vi «Ваша проблема с Vim заключается в том, что вы не въехали в vi» — Лаконично, информативно и корректно. Просто — жемчужина.

Дистрибутивы Vim

Дистрибутивы Vim это — готовые наборы настроек и плагинов для Vim.

Продвинутые пользователи знают, как настроить свой редактор, поэтому дистрибутивы предназначены в основном для начинающих. Задумайтесь над этим, и вам в голову придёт парадоксальная мысль: неужели добавление чего-то, с чем тоже нужно будет разбираться, сделает жизнь проще?

Я понимаю, что многие люди не хотят тратить многие часы на настройку редактора (на самом деле, однажды начав, вы никогда не закончите настраивать ваш vimrc), но использовать Vim эффективно можно только потратив время на то, чтобы хорошо его изучить.

Повторяйте за мной: «Программист обязан знать свои инструменты».

В любом случае, если вы конечно, понимаете, что делаете, вы вдохновитесь некоторыми дистрибутивами:

Стандартные плагины

Для многих это будет сюрпризом, но с Vim идёт набор плагинов, которые загружаются по умолчанию. Посмотрите :scriptnames после запуска Vim, чтобы увидеть все прочитанные файлы.

Большинство из них никогда не будет использоваться, поэтому отключите их, если посчитаете нужным. Они всё ещё будут показаны как загруженные, но прочитаны будут только первые несколько строк. Весь остальной код (мапинги, команды, логика) обработаны не будут.

Плагин Отключите его при помощи... Справка
2html let g:loaded_2html_plugin = 1 :h 2html
getscript let g:loaded_getscriptPlugin = 1 :h pi_getscript
gzip let g:loaded_gzip = 1 :h pi_gzip
logipat let g:loaded_logipat = 1 :h pi_logipat
matchparen let g:loaded_matchparen = 1 :h pi_paren
netrw let g:loaded_netrwPlugin = 1 :h pi_netrw
rrhelper let g:loaded_rrhelper = 1 :e $VIMRUNTIME/plugin/rrhelper.vim
spellfile let g:loaded_spellfile_plugin = 1 :h spellfile.vim
tar let g:loaded_tarPlugin = 1 :h pi_tar
vimball let g:loaded_vimballPlugin = 1 :h pi_vimball
zip let g:loaded_zipPlugin = 1 :h pi_zip

Переназначьте CapsLock на Control

CapsLock — самая бесполезная клавиша на клавиатуре, но, т.к. она находится в домашнем ряду, до неё гораздо проще дотянуться, чем до клавиши Control. Если вы много программируете, переназначение CapsLock на Control — отличный способ предотвратить или, хотя бы, уменьшить RSI.

Внимание! Как только вы начнёте этим пользоваться, жить без этого вы уже не сможете.

macOS:

System Preferences -> Keyboard -> Keyboard Tab -> Modifier Keys. Измените «CapsLock» на «Control».

Linux:

Для того чтобы изменить назначение клавиш в X, добавьте это в ваш ~/.xmodmap:

remove Lock = Caps_Lock
keysym Caps_Lock = Control_L
add Control = Control_L

После этого запустите его при помощи $ xmodmap ~/.xmodmap.

В качестве альтернативы можно использовать caps2esc или xcape.

Windows:

См. superuser.com: Map Caps-Lock to Control in Windows 8.1.

Пасхальные яйца

Команда Сообщение
:Ni! Do you demand a shrubbery?Ты ищешь кустарник?
:h 'sm' NOTE: Use of the short form is rated PG).NB: Использование краткой формы попало в рейтинги PGродительского контроля. (sm это — как бы «садо-мазо». Но, это таки пасхалка.)
:h 42 What is the meaning of life, the universe and everything? Douglas Adams, the only person who knew what this question really was about is now dead, unfortunately. So now you might wonder what the meaning of death is...Главный вопрос жизни, вселенной и всего такого. К сожалениею, Дуглас Адамс, единственный, кто на самом деле знал, в чём именно состоит этот вопрос, теперь не с нами. Теперь, возможно, вы задумаетесь о смысле смерти о том, ради чего стоит прожить жизнь...
:h UserGettingBored When the user presses the same key 42 times. Just kidding! :-)(Автокоманда ПользовательЗаскучал.) Когда пользователь нажимает одну и ту же клавишу 42 раза. Просто прикол! :-)
:h bar Ceci n'est pas une pipe.Это не перенаправление (канал) в смысле shell.
:h holy-grail You found it, Arthur!Артур! Ты его нашёл!
:h map-modes :nunmap can also be used outside of a monastery.:nunmap можно использовать даже за пределами монастыря.
:help! E478: Don't panic!Без паники! (Не получается? При использовании в буфере справки (buftype=help) это работает как :h help.txt.)
:smile Попробуйте сами. ;-) Добавлено в версии 7.4.1005.

(Перевёл, как понял, с помощью автора (особенно про родительский контроль) — пер.)

Почему для перемещения используются клавиши hjkl?

Билл Джой писал vi, предшественника Vim, за ADM-3A, у которого не было дополнительных клавиш со стрелками, а вместо них, как вы наверно догадались, использовались hjkl.

Раскладка клавиатуры: кликните

Это так же объясняет, почему для обозначения домашнего каталога в Unix используется ~.

Распространённые проблемы

Замедленное редактирование небольших файлов

Есть две вещи, которые могут очень сильно повлиять на производительность:

  1. Сложные регулярные выражения. В частности, ранее люди наблюдали замедление из-за синтаксиса Ruby. (См. также Отладка файлов синтаксиса.)
  2. Обновление экрана. Для некоторых функций необходимо обновление всех экранных строк.
Типичная причина Почему? Решение?
:set cursorline Причина перерисовки всех строк. :set nocursorline
:set cursorcolumn Причина перерисовки всех строк. :set nocursorcolumn
:set relativenumber Причина перерисовки всех строк. :set norelativenumber
:set foldmethod=syntax Если файл подсветки синтаксиса уже медленно работает, то это ещё более ухудшает положение. :set foldmethod=manual, :set foldmethod=marker или FastFold
:set synmaxcol=3000 Из-за внутреннего представления Vim вообще имеет проблемы с длинными строками. Эта настройка говорит, что подсветку нужно делать только до 3000-й колонки. :set synmaxcol=200
matchparen.vim Загружается по умолчанию. Для поиска соответствующей скобки использует регулярные выражения. Отключить плагин: :h matchparen

NB: Вам это понадобится только, если вы действительно испытываете неудобство от замедления работы. В большинстве случаев всё упомянутое работает достаточно хорошо.

Замедленное редактирование очень больших файлов

Основная проблема с большими файлами состоит в том, что Vim читает файл полностью. Это всё из-за внутреннего устройства буферов. (Обсуждение на vim_dev@)

Если вам нужно только читать файл, то хорошим решением будет tail hugefile | vim -.

Если вы сможете какое-то время пережить без подсветки синтаксиса и плагинов:

$ vim -u NONE -N

Это должно сделать навигацию значительно быстрее, особенно потому, что для подсветки синтаксиса не будут использоваться регулярные выражения. Необходимо также сказать Vim, чтобы он не использовал своп-файлы и файлы viminfo, чтобы избежать долгих задержек при их записи:

$ vim -n -u NONE -i NONE -N

Подведя итог, скажем: постарайтесь не использовать Vim, если собираетесь работать с действительно огромным файлом. :\

Вставка в скобках (или почему мне всё время приходится переустанавливать 'paste'?)

Режим вставки в скобках (bracketed paste mode) позволяет эмуляторам терминала различать набираемый текст и текст, который вставляется из буфера обмена.

Сталкивались ли вы с таким, что после вставки в Vim кода программы всё выглядело испорченным?

Это происходит только тогда, когда вы вставляете при помощи cmd+v, shift-insert, middle-click и т.д., потому что вы просто вбрасываете текст в эмуляторе терминала. Vim не знает, что вы вставляете текст, он думает, что вы просто нереально быстрый наборщик. И, соответственно, пытается подстроить отступы и всё портит.

Очевидно, что проблема не возникает, если вы вставляете из регистров Vim, например, "+p, потому что в этом случае Vim знает, что вы действительно вставляете текст.

Чтобы обойти это, вам необходимо использовать :set paste, чтобы текст вставлялся как есть. См. :h 'paste' :h 'pastetoggle'.

Если вы сыты по горло постоянным переключением 'paste', посмотрите в сторону прекрасного плагина, который будет это делать за вас: bracketed-paste.

Дополнительно вот здесь почитайте статью автора плагина.

Neovim: Neovim пытается сгладить всё это и устанавливает режим вставки в скобках автоматически, если эмулятор терминала такое поддерживает.

Задержки в терминале при использовании клавиши escape

Если вы живёте в командной строке, то, скорее всего, используете так называемый эмулятор терминала, такой как xterm, gnome-terminal, iTerm2 и т.д. (в противоположность реальному терминалу).

Как и их предки, эмуляторы терминалов для управления такими вещами, как перемещение курсора, изменения цвета текста и т.д. используют эскейп-последовательности (или управляющие последовательности). Это просто строки ASCII-символов, которые начинаются с символа escape (в caret-нотации изображается как ^[). При получении такой строки эмулятор терминала ищет в базе данных terminfo соответствующее действие.

Чтобы сделать проблему более понятной, я расскажу сначала про таймауты мапингов. Они возникают, когда между мапингами есть неопределённость:

:nnoremap ,a  :echo 'foo'<cr>
:nnoremap ,ab :echo 'bar'<cr>

Оба эти мапинга работают, как и ожидается, но после того, как было набрано ,a, будет задержка в 1 секунду, потому что Vim ждёт, будет ли следующей клавишей b.

Escape-последовательности представляют собой такую же проблему:

Вы можете проверить это примерно так: vim -u NONE -N и набрать i<c-v><left>, и вы увидите вставленную последовательность, которая начинается с пары ^[, изображающей символ escape.

В итоге, Vim находится в жёстких временных рамках, когда различает набранный символ <esc> и настоящую escape-последовательность.

По умолчанию Vim использует :set timeout timeoutlen=1000, поэтому он задерживается при неопределённости мапингов и кодов клавиш на 1 секунду. Это разумное значение для мапингов, но для кодов клавиш можно задать свой таймаут, и это будет хорошим решением проблемы в целом:

set timeout           " для мапингов
set timeoutlen=1000   " значение по умолчанию
set ttimeout          " для кодов клавиш
set ttimeoutlen=10    " небольшое незаметное значение

В :h ttimeout вы найдёте небольшую таблицу, в которой показаны взаимоотношения между этими опциями.

Если между Vim и эмулятором терминала у вас находится tmux, добавьте так же это в ваш ~/.tmux.conf:

set -sg escape-time 0

Сброс поиска в функции

Однако, обе эти вещи не происходят, если вы делаете их в функции! Поэтому не так просто подсвечивать слова поиска из функции или повторить изменения текста, сделанные с её помощью.

Справка: :h function-search-undo

Технические особенности

Вместо NUL используется символ новой строки

Символ NUL (\0) в файле хранится в памяти как символ новой строки (\n), а в буфере отображается как ^@.

Дополнительно см. man 7 ascii и :h NL-used-for-Nul.