В предыдущих главах вы научились перемещаться по тексту, не прикасаясь к мыши (если нет - вернитесь к главе 3, перемещения - фундамент всего остального). Но текст мы пишем не для того, чтобы по нему ходить. Пора начать его менять: вставлять, удалять, копировать, заменять.
Здесь кроется первая развилка, отделяющая "человека, который установил Vim" от "человека, который умеет Vim". Новичок относится к редактированию как к набору изолированных трюков: "вот эта клавиша удаляет символ, вот эта вставляет строку". Опытный пользователь видит за ними систему: небольшой набор операторов, которые комбинируются с перемещениями из главы 3 и дают сотни осмысленных команд.
Эта глава - мост. Мы намеренно остаёмся на "командах-однокнопочниках" (x, dd, yy, p), чтобы вы набили руку и почувствовали ритм normal-режима. А полноценную грамматику "оператор + движение" (d2w, ci(, yat) мы разберём в части II - главы 5 и 6. Не пытайтесь проглотить всё сразу: сначала рефлексы, потом теория.
Главная идея: редактирование без режима вставки
В обычном редакторе любая правка идёт через одно состояние: вы печатаете, удаляете backspace'ом, выделяете мышью. В Vim большая часть редактирования происходит в normal-режиме, где буквы - это команды. Режим вставки (Insert) вы включаете на короткие "очереди" набора текста и тут же выходите.
Почему так? Потому что код мы читаем и правим гораздо чаще, чем пишем с нуля. Поменять аргумент функции, удалить строку, продублировать блок, исправить регистр буквы - всё это не про "печатать", а про "оперировать структурой". Normal-режим оптимизирован именно под это.
Запомните цикл, который вы будете повторять тысячи раз в день:
Код: Выделить всё
[normal] переместился -> совершил правку -> вернулся в normal
Вход в режим вставки
Способов войти в Insert много, и это не избыточность - каждый экономит лишнее движение. Глупо нажимать i, а потом стрелками идти в конец строки, если есть A.
Код: Выделить всё
Клавиша | Что делает | Куда встаёт курсор
---------+-------------------+-------------------------------------------------
i | insert | перед текущим символом
I | Insert в начало | перед первым непробельным символом строки
a | append | после текущего символа
A | Append в конец | в конец строки
o | open below | новая строка ниже, курсор на ней
O | Open above | новая строка выше, курсор на ней
gi | go insert | туда, где вы в последний раз выходили из Insert
Команда gi неочевидна, но очень удобна: вы вышли из вставки, прыгнули куда-то посмотреть, и gi возвращает вас ровно в ту точку и сразу в Insert. Внутри Vim это работает через специальную метку `^ (подробнее о метках - в главе 9).
Было -> нажимаем -> стало. Курсор обозначен символом ▮.
Код: Выделить всё
Было: let count▮= 0
Нажимаем: a (затем печатаем " " и Esc - но смотрим только на позицию входа)
Стало: let count ▮= 0 (Insert активен ПОСЛЕ 't')
Код: Выделить всё
Было: ▮ return value; (курсор в начале, перед отступом)
Нажимаем: I
Стало: ▮return value; (Insert перед 'return', отступ пропущен)
Код: Выделить всё
Было: foo();▮
Нажимаем: o
Стало: foo();
▮ (открыта новая строка ниже, можно печатать)
Замена символов: r, R, s, S
Иногда не нужно входить в Insert ради одной буквы. Для точечной замены есть отдельные команды.
Код: Выделить всё
Клавиша | Действие | Остаётся в Insert?
---------+----------------------------------------------+--------------------
r | заменить один символ под курсором | нет
R | режим Replace (печать поверх текста) | да, до <Esc>
s | удалить символ и войти в Insert | да
S | удалить всю строку и войти в Insert (= cc) | да
R включает режим замены: всё, что вы печатаете, затирает существующие символы (как Insert/Overwrite в старых редакторах). Полезно для выравнивания ASCII-таблиц или правки чего-то по фиксированным колонкам.
s (substitute) - это "удалить символ под курсором и сразу печатать". По сути s == cl. А S стирает строку целиком (сохраняя отступ) и ставит вас в Insert - синоним cc.
Код: Выделить всё
Было: cont▮our (опечатка, нужно 'contour' -> допустим, нужна 'a': contaur)
Нажимаем: ra
Стало: conta▮ur (символ заменён, остаёмся в normal)
Код: Выделить всё
Было: color: red;▮
Нажимаем: S (печатаем "color: blue;" Esc)
Стало: color: blue;▮ (вся строка переписана, отступ сохранён)
Удаление: x, X, d
Код: Выделить всё
Клавиша | Действие
-----------+-------------------------------------------------
x | удалить символ под курсором
X | удалить символ слева от курсора (как backspace)
dd | удалить (вырезать) текущую строку целиком
D | удалить от курсора до конца строки (= d$)
d{motion} | удалить по перемещению (см. главу 5)
dd удаляет строку, но важно понимать: в Vim удаление - это всегда вырезание. Удалённый текст попадает в регистр и его можно вставить через p. Это не "потеря" - это "перемещение в буфер". О регистрах подробно - глава 8, но базовое следствие запомните сейчас: dd + p = "переместить строку вниз".
Оператор d сам по себе ничего не делает - он ждёт перемещение. dw удалит до начала следующего слова, d$ - до конца строки, dG - до конца файла. Это и есть та самая грамматика, которую мы развернём в главе 5. Здесь достаточно понять связку: d + куда = "удалить досюда".
Код: Выделить всё
Было: const ▮unusedVar = 42;
Нажимаем: dw
Стало: const ▮= 42; ('unusedVar ' удалено по движению w)
Код: Выделить всё
Было: line one
line ▮two <- эту строку убираем
line three
Нажимаем: dd
Стало: line one
line ▮three
c (change) - близнец d, но с ключевым отличием: после удаления он оставляет вас в режиме вставки. Это "удали и сразу печатай взамен".
Код: Выделить всё
Клавиша | Действие
-----------+----------------------------------------------
c{motion} | удалить по перемещению и войти в Insert
cc | заменить содержимое строки (сохранив отступ)
C | изменить от курсора до конца строки (= c$)
s | изменить один символ (= cl)
Код: Выделить всё
Было: function ▮oldName() {
Нажимаем: cw (печатаем "newName" Esc)
Стало: function newName▮() {
Код: Выделить всё
Было: return ▮someValue + offset;
Нажимаем: C (печатаем "result;" Esc)
Стало: return result;▮ (всё от курсора до конца строки заменено)
Копирование и вставка: y, yy, p, PТонкость: классический cw ведёт себя как ce (меняет до конца слова, а не до начала следующего), чтобы не съедать пробел после слова. Это историческое исключение, и оно вам скорее на руку.
В Vim "копировать" - это yank (y). Слово "copy" вы не услышите, привыкайте к y.
Код: Выделить всё
Клавиша | Действие
-----------+-----------------------------------------------------------------------
y{motion} | скопировать (yank) по перемещению
yy | скопировать строку целиком
Y | в Vim = yy (строка целиком), но в Neovim по умолчанию = y$ (см. ниже)
p | вставить после курсора / ниже строки
P | вставить перед курсором / выше строки
Различие Vim vs Neovim про Y. В классическом Vim Y - это синоним yy (копировать строку целиком). А в Neovim по умолчанию (начиная с 0.6) Y смаплен на y$ - копирует от курсора до конца строки, а не всю строку. Это сделали ради консистентности с D (d$) и C (c$), но именно это различие легко обжечься, если вы привыкли к Vim. Вывод простой: чтобы скопировать строку целиком надёжно и одинаково в обоих редакторах, всегда пишите yy, а не Y.
Код: Выделить всё
Было: config.set(▮)
Нажимаем: yiw потом o и p (примерно; здесь главное p)
...
Практичнее: дублируем строку.
Было: const PORT = 8080;▮
Нажимаем: yyp
Стало: const PORT = 8080;
const PORT = 8080;▮ (строка продублирована ниже)
Код: Выделить всё
Было: secondLine▮
firstLine
Нажимаем: ddp
Стало: firstLine
secondLine▮ (строки поменялись местами)
Системный буфер обменаВажный нюанс про регистры (глава 8): обычное p берёт из "безымянного" регистра, который перезаписывается при любом удалении. Классическая боль новичка: "скопировал слово, потом удалил другое, вставил - вылезло удалённое". Решение существует (регистр "0 хранит только последний yank, а "_ - "чёрная дыра" для удалений без засорения буфера), и мы разберём его в главе 8. Пока просто знайте об этой ловушке.
Чтобы скопировать из Vim в браузер (или наоборот), нужен системный буфер. В Vim это регистры + и *: "+y копирует в системный буфер, "+p вставляет из него.
В Neovim 0.10+ всё проще: при работе в терминале (включая SSH) поддержка OSC52 включается автоматически, и "+y уходит в локальный системный буфер без xclip/wl-clipboard и сторонних плагинов. Тонкость: автодетект OSC52 срабатывает только если опция clipboard не задана. Если выставить clipboard=unnamedplus (частый совет из старых гайдов), автоопределение OSC52 отключается. Поэтому для удалённой/контейнерной работы в 2026 часто разумнее оставить clipboard пустым и пользоваться явными "+y/"+p. Подробности настройки - в главах 8 и 20.
Отмена и повтор: u, Ctrl-R, UЕщё важная оговорка: встроенный OSC52-провайдер - это fallback. Neovim подключает его автоматически только тогда, когда clipboard не задан и не найден внешний инструмент буфера (pbcopy/pbpaste на macOS, xclip/xsel на X11, wl-copy/wl-paste на Wayland и т.п.). На локальном маке, где pbcopy есть всегда, Neovim возьмёт именно его, и авто-OSC52 локально может вообще не активироваться - это нормально. OSC52 особенно ценен как раз там, где таких утилит нет: на голой удалённой машине или в минимальном контейнере по SSH - тогда копирование уходит в буфер вашего локального терминала через escape-последовательность.
Код: Выделить всё
Клавиша | Действие
---------+--------------------------------------------------------------
u | отменить последнее изменение (undo)
Ctrl-R | вернуть отменённое (redo)
U | отменить *все* изменения на последней редактированной строке
U - особый зверь: он отменяет все правки на строке, где вы их делали, причём сам U тоже отменяется через u. Используется редко, но знать о нём полезно, чтобы случайно не запаниковать, если нажали.
Vim хранит не линейную, а древовидную историю изменений (undo tree): если вы отменили правку и сделали другую, старая ветка не теряется. Навигация по дереву встроена в ядро: g-/g+ ходят по состояниям в хронологическом порядке (включая "осиротевшие" ветки), а команды :earlier/:later умеют прыгать по времени и числу изменений (:earlier 10m, :later 5). Никакого встроенного :Undotree ни в Vim, ни в Neovim нет - для визуального дерева отмен ставят сторонний плагин (mbbill/undotree или jiaoshijie/undotree).
Знакомство с точкой: .
Команда . (точка) повторяет последнее изменение. Это, без преувеличения, одна из самых мощных клавиш в Vim, и здесь мы знакомимся с ней лишь поверхностно - целая глава 7 посвящена тому, как строить повторяемые правки.
Что считается "изменением" для точки? Любая команда, которая поменяла текст: x, dd, cw...Esc, p, ~ и т.д. Перемещения и команды поиска изменением не считаются - точка их не повторяет.
Базовый рецепт "правка + навигация + точка":
Код: Выделить всё
Было: foo; bar; baz; qux; (хотим убрать каждую ';')
Курсор на первой ';'
Нажимаем: x -> "foo bar; baz; qux;" (удалили первую)
Нажимаем: f; -> перешли к следующей ';' (это движение, не правка)
Нажимаем: . -> "foo bar baz; qux;" (повторили удаление)
Нажимаем: ; -> повторили f; (см. главу 3) к следующей
Нажимаем: . -> и так далее
Объединение строк: J и gJ
Код: Выделить всё
Клавиша | Действие
---------+-----------------------------------------------------
J | склеить текущую строку со следующей, вставив пробел
gJ | склеить без добавления пробела
gJ склеивает "как есть", сохраняя отступы и не добавляя пробел - нужно, когда вы собираете что-то, где пробел был бы лишним.
Со счётчиком: 3J склеит три строки в одну. Логика счётчика тут чуть особенная - J берёт "текущую + следующую", а 3J - текущую и две следующих (итого три строки).
Код: Выделить всё
Было: const url =▮
"https://example.com";
Нажимаем: J
Стало: const url = ▮"https://example.com"; (склеено через пробел, отступ убран)
Код: Выделить всё
Было: "part1"▮
"part2"
Нажимаем: gJ
Стало: "part1"▮"part2" (склеено без пробела)
Код: Выделить всё
Команда | Действие
-----------------+----------------------------------------------------------------
~ | переключить регистр символа под курсором (и сдвинуться вправо)
gu{motion} | сделать строчными по перемещению
gU{motion} | сделать ЗАГЛАВНЫМИ по перемещению
g~{motion} | инвертировать регистр по перемещению
guu / gUU / g~~ | применить ко всей строке
gu, gU, g~ - это полноценные операторы, как d и c. Значит, они подчиняются той же грамматике: gUiw сделает заглавным слово целиком (это, кстати, канонический пример "вывода команды на лету": вы знали gU и знали текстовый объект iw - и скомбинировали их, не заучивая отдельно). guG опустит до конца файла в нижний регистр.
Код: Выделить всё
Было: ▮constant_name
Нажимаем: gUiw
Стало: ▮CONSTANT_NAME (всё слово в верхнем регистре)
Код: Выделить всё
Было: The QUICK brown FOX▮
Нажимаем: g~~ (инвертировать регистр всей строки)
Стало: tHE quick BROWN fox▮
Лучшие практики 2026
- Думайте "глагол + объект", а не заучивайте комбо. Уже на этом этапе старайтесь видеть cw, dd, gUiw не как отдельные заклинания, а как "оператор, применённый к чему-то". Запомнив N перемещений и M операторов, вы получаете NxM команд. Эту мысль мы доведём до конца в главах 5-6, но закладывать её стоит сейчас.
- Делайте правки повторяемыми. Предпочитайте операторные команды (cw, ciw, gUiw), которые точка . повторит целиком, ручному ковырянию в Insert. Это окупается на каждой однотипной правке. На современном Neovim даже свои маппинги можно делать dot-repeatable через operatorfunc + g@ без плагина vim-repeat - но это уже глава 7.
- Не "живите" в режиме вставки. Insert - для коротких очередей набора. Чем раньше вы вернётесь в normal (<Esc> / Ctrl-[), тем точнее будут единицы отмены и тем доступнее вся мощь normal-режима.
- Берегите буфер при копировании. Помните про ловушку "yank, потом delete затёр буфер". В реальном конфиге это лечат регистром "0 и "чёрной дырой" "_ (глава 8). Уже сейчас не удивляйтесь, если p вставил не то.
- На SSH/в контейнерах полагайтесь на авто-OSC52 Neovim 0.10+, а не вешайте clipboard=unnamedplus вслепую - последнее как раз ломает автоопределение OSC52. Явные "+y/"+p надёжнее.
- Путать i/a и терять символ при "замене". Чтобы поменять одну букву, не нужно i, backspace, перепечатывание - есть r. Чтобы переписать слово - cw, а не выделение мышью.
- Думать, что dd "теряет" текст. Удаление в Vim - это вырезание. Текст в регистре, его вернёт p. Это, наоборот, фича: ddp, yyp.
- Ожидать "дробную" отмену внутри Insert. Один заход в Insert = одно изменение для u. Напечатали абзац одним заходом - u снесёт его целиком. Выходите в normal чаще.
- Считать, что . повторит перемещение. Точка повторяет только изменение текста. f; или /foo она не повторяет (для повтора f/t есть ;/,, для поиска - n/N, см. главы 3 и 10).
- Забывать про разницу J и gJ. J добавляет пробел и подчищает отступы - иногда это не то, что нужно. Для "склеить точь-в-точь" берите gJ.
- Слепо ставить clipboard=unnamedplus по совету из старого гайда. На Neovim 0.10+ при работе по SSH это глушит авто-OSC52, и копирование "молча" перестаёт уходить в локальный буфер.
- Дубли и перестановки. Создайте файл из строк alpha, beta, gamma. Используя только yy, dd, p, P: продублируйте beta, затем поменяйте местами alpha и gamma. Сделайте всё без режима вставки.
- Сила точки. Напишите строку a, b, c, d, e, f и удалите все запятые с пробелами, используя x/df, один раз, а затем ; и . для остального. Прочувствуйте ритм "движение - точка".
- Переименование через cw. Возьмите строку let tmp = tmp + tmp;. Встаньте на первое tmp, замените его на total через cw, затем перейдите к следующему tmp (через w/f) и повторите правку точкой .. Получите let total = total + total;.
- Регистр-оператор. Дана строка make this a constant. Превратите constant в CONSTANT командой gUiw. Затем инвертируйте регистр всей строки через g~~ и верните обратно u.
- Сборка строки. Разбейте URL на три строки вручную (через o), а потом соберите обратно: одной строкой через J, проверьте разницу с gJ. Обратите внимание, как J ставит пробелы.
- Вход с умом. Дан отступленный блок кода. Потренируйте I (в начало содержимого), A (в конец строки), o/O (новые строки), и gi (вернуться в последнюю точку вставки). Засеките, сколько лишних перемещений вы сэкономили.
- Большая часть редактирования живёт в normal-режиме; Insert - короткие очереди набора.
- Вход в Insert: i/I, a/A, o/O, gi - заглавная версия означает "к краю/масштабнее".
- Замена без Insert: r (один символ), R (overwrite-режим), s/S (удалить и печатать).
- Удаление - это вырезание: x/X, dd/D, d{motion}. Текст уходит в регистр, его вернёт p.
- Изменение c/cc/C = "удалить и сразу печатать", и оно повторяемо точкой.
- Копирование = yank (y/yy), вставка p (после/ниже) и P (перед/выше). Системный буфер - "+. Помните: для копирования строки целиком пишите yy - Y в Neovim по умолчанию это y$, а не yy.
- Отмена u, повтор Ctrl-R, U - откат правок строки. Изменение = всё между входом и выходом из Insert.
- . повторяет последнее изменение - основа повторяемого редактирования (глава 7).
- J/gJ склеивают строки (с пробелом / без).
- ~, gu, gU, g~ меняют регистр; gu/gU/g~ - полноценные операторы (gUiw).