Почти каждый новичок в Vim рано или поздно натыкается на одну и ту же боль. Скопировал слово (yiw), нашёл место, где его надо вставить, по пути удалил лишний фрагмент (daw) - и p вдруг вставляет не то, что копировал, а то, что удалил. Знакомо? Это не баг и не "глюк" редактора. Это прямое следствие того, как устроены регистры - и того, что вы пока используете их вслепую.
Регистры - это не один буфер обмена, а целая система именованных ячеек памяти. Vim хранит в них не только последнее скопированное, но и историю удалений, последний поиск, имя текущего файла, результаты вычислений и многое другое. Понимание регистров превращает Vim из "текстового редактора с горячими клавишами" в инструмент с богатой рабочей памятью: вы перестаёте терять скопированное, начинаете жонглировать несколькими фрагментами одновременно и аккуратно интегрируетесь с системным буфером обмена.
Эта глава - про то, какие регистры существуют, почему они ведут себя именно так, и как выстроить рабочие привычки, которые избавят вас от "проклятия исчезнувшего yank" раз и навсегда.
Что такое регистр
Регистр - это именованная ячейка, в которой Vim хранит текст (или, в некоторых случаях, иную информацию). У каждого регистра есть однобуквенное имя. Чтобы обратиться к регистру, перед командой копирования/удаления/вставки ставят " (двойную кавычку) и имя регистра.
Базовый синтаксис:
Код: Выделить всё
"<имя><операция>
- "ayy - скопировать (yy) текущую строку в регистр a.
- "ap - вставить (p) содержимое регистра a.
- "bdd - удалить (dd) строку, поместив её в регистр b.
Важная деталь: каждый регистр запоминает не только текст, но и тип выделения - посимвольный (yiw), построчный (yy) или блочный (<C-v> + y). От этого зависит, как именно p будет вставлять: построчный регистр всегда вставляется новой строкой, посимвольный - встык к курсору. Это объясняет, почему yy затем p создаёт новую строку, а yiw затем p вставляет слово прямо в текущую.
Безымянный регистр ""
Безымянный регистр (имя - двойная кавычка ") - это "рабочий стол" Vim. Сюда по умолчанию попадает результат любой операции копирования (y), удаления (d, x, c) и изменения. Команда p/P без указания регистра вставляет именно его.
Ключевой момент, порождающий путаницу: в безымянный регистр пишут и yank, и delete. Скопировали слово - оно в "". Удалили другое слово - теперь в "" лежит удалённое, а скопированное "перетёрто". Поэтому p вставляет последнее затронутое, а не последнее скопированное.
Спасение от этой проблемы - нумерованные и чёрнодырочный регистры, к которым мы сейчас перейдём.
Нумерованные регистры "0-"9
Vim ведёт небольшую историю автоматически, раскладывая её по нумерованным регистрам. Здесь работают два разных механизма, и их важно не путать.
Регистр "0 - "последний yank"
Регистр 0 (ноль) хранит только последнее скопированное (y). Операции удаления и изменения его не трогают. Это и есть лекарство от "проклятия исчезнувшего yank": что бы вы ни удаляли после копирования, в "0 по-прежнему лежит то, что вы скопировали.
Код: Выделить всё
Было: foo bar
Копируем слово foo: yiw (теперь "" = foo, "0 = foo)
Удаляем bar: (курсор на bar) daw (теперь "" = bar, а "0 = foo!)
Вставляем yank: "0p -> вставится foo, а не bar
Регистры с 1 по 9 образуют сдвиговый стек удалений и изменений (но только "крупных" - как минимум построчных или захватывающих больше одной строки/значимый фрагмент). Самое свежее удаление попадает в "1, предыдущее сдвигается в "2, и так далее до "9, после чего старейшее теряется.
Это удобно, когда вы удалили несколько фрагментов подряд и хотите достать "то, что было до прошлого":
Код: Выделить всё
"1p - вставить последнее удалённое
"2p - предпоследнее
Именованные регистры "a-"zТонкость: мелкие удаления внутри одной строки (x, dw, diw) попадают в малый регистр удаления "- (дефис), а не в нумерованный стек. В стек "1-"9 идут построчные и многострочные удаления.
Двадцать шесть именованных регистров (a-z) - ваше личное хранилище под полным контролем. Vim никогда не записывает в них автоматически: всё, что туда попадает, вы кладёте сами.
Код: Выделить всё
"ayy - скопировать строку в регистр a
"byiw - скопировать слово в регистр b
"ap - вставить содержимое a
Заглавные имена "A-"Z - режим добавления (append)
Если обратиться к регистру заглавной буквой, Vim не перезапишет его, а допишет новое содержимое к существующему. "A дописывает в тот же регистр, что и "a.
Это превращает регистр в накопитель. Классический сценарий - собрать в один буфер строки, разбросанные по файлу:
Код: Выделить всё
Было - три строки в разных местах файла, надо собрать вместе.
1. Очистить регистр: на первой строке "ayy (a = строка1)
2. На второй строке: "Ayy (a = строка1 + строка2)
3. На третьей строке: "Ayy (a = строка1 + строка2 + строка3)
4. В нужном месте: "ap -> вставит все три
Ex-вставка регистров: :put
У вставки есть и Ex-форма - команда :put, которая всегда вставляет содержимое регистра построчно (как новую строку), независимо от того, посимвольный регистр или построчный. Это особенно удобно в скриптах и в связке с :g, где курсор и обычный p неуместны:
Код: Выделить всё
:put a - вставить регистр a новой строкой ПОСЛЕ текущей
:put! a - вставить регистр a новой строкой ПЕРЕД текущей (с !)
:0put a - вставить в самое начало файла (после строки 0)
:$put a - вставить в конец файла
:put =strftime('%Y-%m-%d') - вставить результат выражения построчно
Системный буфер обмена: "+ и "*
До сих пор всё происходило внутри Vim. Чтобы обмениваться текстом с другими приложениями (браузером, IDE, мессенджером), нужны специальные регистры, связанные с буфером обмена операционной системы.
- "+ - основной системный буфер обмена ("clipboard"), тот самый, что наполняется по Ctrl-C / Cmd-C в других программах.
- "* - на X11 (Linux/BSD) это отдельный буфер первичного выделения (PRIMARY selection): текст, выделенный мышью, который вставляется средней кнопкой. На Windows и macOS "* и "+ ссылаются на один и тот же системный буфер.
Код: Выделить всё
"+yy - скопировать строку в системный буфер (можно вставить Ctrl-V в браузере)
"+p - вставить из системного буфера в Vim
"+yiw - скопировать слово в системный буфер
Специальные регистры (только для чтения и служебные)В классическом Vim для работы с "+/"* требуется сборка с поддержкой +clipboard (проверьте :echo has('clipboard')). В Neovim используется отдельный механизм провайдеров буфера обмена - об этом ниже.
Помимо буферов для текста, Vim предоставляет набор регистров со специальным смыслом. Большинство из них доступны только для чтения - вставлять из них можно, записывать обычными командами нельзя.
Код: Выделить всё
Регистр | Содержимое
---------+------------------------------------------------------------------------------
"_ | Чёрная дыра (blackhole). Запись сюда исчезает бесследно, чтение даёт пустоту
"% | Имя текущего файла
"# | Имя "альтернативного" файла (предыдущего буфера, куда ведёт <C-^>)
"/ | Последний шаблон поиска
": | Последняя выполненная команда командной строки (Ex)
". | Последний вставленный текст (то, что вы напечатали в режиме вставки)
"= | Регистр выражений (expression register) - вычисляет и подставляет результат
Самый практически важный из служебных. Когда вы удаляете текст с приставкой "_, он не попадает ни в один регистр - ни в безымянный, ни в нумерованные. Это идеальный способ удалить мусор, не затирая ваш буфер обмена.
Код: Выделить всё
"_dd - удалить строку "насовсем", "" и "0 остаются нетронутыми
"_diw - удалить слово, не трогая регистры
Регистры "%, "#, "/, ":, ".
Эти регистры удобно вставлять как в текст, так и в командную строку через <C-r> (см. следующий раздел):
Код: Выделить всё
"%p - вставить имя текущего файла в текст
:%s/<C-r>//ЗАМЕНА/ - подставить в команду :s последний поисковый паттерн (из "/)
Регистр выражений "=
"= - особенный: при обращении к нему Vim открывает приглашение, куда вы вводите выражение, и подставляет результат его вычисления. Это мост между редактированием и скриптингом (Vimscript/Lua, глава 22).
Код: Выделить всё
В режиме вставки: <C-r>=2+2<CR> -> вставит 4
<C-r>=strftime('%Y-%m-%d')<CR> -> вставит сегодняшнюю дату
В normal-режиме: "=10*60<CR>p -> вычислит 600 и вставит
Не обязательно выходить в normal-режим, чтобы вставить регистр. Прямо в режиме вставки нажмите <C-r> (Ctrl-R) и затем имя регистра:
Код: Выделить всё
В режиме вставки:
<C-r>a - вставить регистр a
<C-r>0 - вставить последний yank
<C-r>+ - вставить из системного буфера
<C-r>" - вставить безымянный регистр
<C-r>% - вставить имя файла
<C-r>= - открыть приглашение регистра выражений
Интеграция с системным буфером: clipboardПолезный вариант: <C-r><C-r>a и <C-r><C-o>a вставляют содержимое регистра "дословно", не интерпретируя спецсимволы и автоотступ - пригодится, когда вставляемый текст содержит, например, отступы, которые "уезжают" из-за autoindent.
Постоянно набирать "+ перед каждым копированием утомительно. Опция clipboard позволяет "срастить" внутренние регистры Vim с системным буфером, чтобы обычные y/p работали с ним напрямую.
- set clipboard=unnamed - связывает безымянный регистр с "* (PRIMARY на X11).
- set clipboard=unnamedplus - связывает безымянный регистр с "+ (системный clipboard). Обычно это то, что нужно на Linux/macOS/Windows.
Код: Выделить всё
vim.opt.clipboard = 'unnamedplus'
Важная оговорка для Neovim 0.10+ и SSH
Звучит удобно, но есть существенный подводный камень, особенно актуальный в 2026 году. Neovim 0.10+ умеет автоматически использовать OSC 52 - escape-последовательность терминала, через которую можно скопировать текст в системный буфер даже по SSH, без xclip/xsel/wl-clipboard и без сторонних плагинов. Автодетект OSC 52 включается, только если выполнены условия: Neovim запущен в TUI, терминал поддерживает OSC 52, не найдено других clipboard-инструментов, и - опция clipboard НЕ задана.
Если вы выставите clipboard=unnamedplus глобально, авто-OSC 52 отключится. На удалённой машине или в контейнере, где нет clipboard-провайдера, это означает, что копирование "молча" перестанет уходить в локальный буфер.
Поэтому рекомендация 2026 года такая:
- Локально, где есть нативный провайдер (pbcopy на macOS, wl-copy/xclip на Linux) - unnamedplus удобен и безопасен.
- По SSH / в контейнерах - лучше оставить clipboard пустым (тогда сработает авто-OSC 52) и пользоваться явными маппингами "+y/"+p, либо принудительно сконфигурировать провайдер OSC 52 через встроенный модуль:
Код: Выделить всё
-- Форсировать OSC 52, если авто-детект не сработал.
-- vim.g.clipboard - это таблица (dict) с полями name/copy/paste, а не строка.
local osc52 = require('vim.ui.clipboard.osc52')
vim.g.clipboard = {
name = 'OSC 52',
copy = {
['+'] = osc52.copy('+'),
['*'] = osc52.copy('*'),
},
paste = {
['+'] = osc52.paste('+'),
['*'] = osc52.paste('*'),
},
}
Практические рецептыПодводный камень мультиплексоров: tmux/screen могут ломать OSC 52. Решение - включить в tmux set -g allow-passthrough on (или set-clipboard on), либо отключить авто-детект через vim.g.termfeatures = { osc52 = false } и форсировать провайдер. И учтите: OSC 52 надёжно работает в основном на copy; вставка (paste) через него поддерживается далеко не всеми терминалами.
Замена слова без потери буфера обмена
Классическая задача: скопировали слово и хотите заменить им несколько других слов в тексте. Через обычный p ничего не выйдет - первый же diw затрёт буфер. Решение опирается на регистр "0:
Код: Выделить всё
1. Копируем "образец": yiw (он лёг в "" и в "0)
2. Идём к цели, выделяем: viw (визуально выделили слово-жертву)
3. Заменяем из "0: "0p ("0 не затирается удалением, всё работает)
Вставка поверх выделения, не теряя буфер
Ещё более чистый приём - комбинация чёрной дыры и P:
Код: Выделить всё
viw"_dP
Код: Выделить всё
-- Neovim: вставить поверх выделения, не затирая регистр
vim.keymap.set('x', '<leader>p', '"_dP', { desc = 'Paste over selection' })
Вместо глобального unnamedplus многие в 2026 предпочитают держать внутренний и системный буферы раздельно и обращаться к "+ явными маппингами. Так внутренние y/d не засоряют системный буфер, и наоборот:
Код: Выделить всё
vim.keymap.set({'n','x'}, '<leader>y', '"+y', { desc = 'Yank to system clipboard' })
vim.keymap.set({'n','x'}, '<leader>Y', '"+Y', { desc = 'Yank line to system clipboard' })
vim.keymap.set({'n','x'}, '<leader>p', '"+p', { desc = 'Paste from system clipboard' })
Если вы пишете составной маппинг, который делает yank -> трансформацию -> paste, не используйте безымянный регистр как "черновик" - он перетрёт буфер пользователя. Берите выделенный именованный регистр (например z) или чёрную дыру:
Код: Выделить всё
Внутри маппинга: "zyiw ... <преобразование> ... "zp
Лучшие практики 2026
- Не выставляйте clipboard=unnamedplus вслепую. На локальной машине с провайдером - пожалуйста, но при работе по SSH/в контейнерах это глушит авто-OSC 52 в Neovim 0.10+. Альтернатива - пустой clipboard + явные "+y/"+p, либо явно сконфигурированный провайдер OSC 52 через табличный vim.g.clipboard (см. рецепт для SSH выше).
- Держите внутренний ("") и системный ("+) буферы раздельно через маппинги <leader>y/<leader>p. Так удаления не затирают то, что вы скопировали для вставки в другое приложение.
- "0 - ваш "надёжный yank". Помните, что он переживает любые удаления. Рецепт замены без потери буфера - viw"0p или "_dP.
- "_ - для "грязных" удалений. Удаляете строку, которая вам не нужна как буфер? Делайте "_dd. В маппингах визуальной вставки - vnoremap <leader>p "_dP.
- Заглавные регистры ("A-"Z) - для сбора фрагментов, особенно в связке с :g/pattern/yank A.
- Изолируйте промежуточные регистры в маппингах - служебный yank должен идти в выделенный именованный или чёрнодырочный регистр, а не в "".
- <C-r> экономит переключения режимов - вставляйте регистры прямо в insert и в командной строке; <C-r><C-w> подставляет слово под курсором в :s.
- "Проклятие исчезнувшего yank". Скопировали yiw, потом daw, потом p - вставилось удалённое. Причина: и yank, и delete пишут в "". Лечение - "0p или "_d.
- clipboard=unnamedplus по SSH "не копирует". Текст не уходит в локальный системный буфер, потому что unnamedplus отключил авто-OSC 52. Уберите опцию или настройте провайдер OSC 52 табличным vim.g.clipboard (см. рецепт для SSH).
- p в визуальном режиме затирает регистр. Обычный p поверх выделения кладёт выделенный текст в "", и при следующей вставке вы получаете его, а не образец. Используйте "_dP (или маппинг <leader>p -> "_dP).
- Путаница "* и "+. На macOS/Windows они идентичны, на X11 - нет: "* это PRIMARY (выделение мышью), "+ - настоящий clipboard. Для переносимых конфигов работайте через "+.
- Ожидание, что мелкое удаление попадёт в "1. x и diw идут в малый регистр "-, а не в нумерованный стек. В "1-"9 копятся построчные/многострочные удаления.
- Забыли про +clipboard в Vim. Если :echo has('clipboard') возвращает 0, классический Vim собран без поддержки системного буфера - нужна другая сборка (vim-gtk, vim-x11) или OSC 52.
- autoindent "ломает" вставку кода через <C-r>. При вставке отступленного текста отступы накладываются повторно и текст "уезжает". Используйте "дословную" вставку <C-r><C-r>a или <C-r><C-o>a.
- Лечим исчезнувший yank. Возьмите строку alpha beta gamma. Скопируйте alpha (yiw), затем удалите gamma (наведитесь и daw), а потом замените beta на alpha, не перепечатывая. Подсказка: viw"0p. Повторите тот же трюк через "_dP и сравните.
- Сбор фрагментов в append-регистр. В файле с десятком строк, часть которых содержит ERROR, соберите все такие строки в регистр e и вставьте их в конец файла. Сначала вручную через "eyy/"Eyy, затем одной командой :g/ERROR/yank E.
- История удалений. Удалите подряд три разные строки (dd, dd, dd). Затем восстановите вторую по счёту с конца удалённую строку, не вспоминая её содержимое. Подсказка: вставьте "1p и понажимайте ..
- Регистр выражений. В режиме вставки впишите в текст результат 127 * 8, текущую дату (strftime('%Y-%m-%d')) и имя текущего файла - всё через <C-r>= и <C-r>%, не выходя из insert.
- Системный буфер по-разному. Настройте два способа скопировать строку в системный буфер: (а) явным "+yy; (б) через маппинг <leader>y -> "+y. Проверьте вставку в любое внешнее приложение. Если работаете по SSH - убедитесь, что копирование уходит (при необходимости настройте провайдер OSC 52 табличным vim.g.clipboard).
- Чистый маппинг. Напишите маппинг <leader>d для normal-режима, который удаляет строку "в чёрную дыру" ("_dd), и убедитесь, что после него "0 и "" сохранили прежнее содержимое (проверьте через :reg).
- Регистры - это система именованных ячеек памяти, а не один буфер обмена. Обращение: "<имя><операция>, например "ayy, "ap.
- Безымянный "" наполняется и при yank, и при delete - отсюда классическая путаница.
- "0 хранит только последний yank и переживает удаления - главное лекарство от "исчезнувшего скопированного".
- "1-"9 - сдвиговый стек крупных удалений; "- - малый регистр для мелких. По истории удобно "прокручивать" через "1p + ..
- "a-"z - ваши личные ячейки; заглавные "A-"Z дописывают (append), что отлично работает с :g.
- "+ - системный буфер обмена (переносимо между ОС); "* - PRIMARY на X11, идентичен "+ на macOS/Windows.
- "_ - чёрная дыра для удалений без засорения буфера; рецепт вставки поверх выделения - "_dP.
- Служебные "% "# "/ ": ". дают доступ к имени файла, поиску, последней команде; "= вычисляет выражения.
- В insert и командной строке регистры вставляются через <C-r>; <C-r><C-w> - слово под курсором.
- clipboard=unnamedplus срастит "" с системным буфером, но в Neovim 0.10+ глушит авто-OSC 52 - по SSH предпочитайте пустой clipboard или явный табличный провайдер OSC 52.